Personal playlist on a dedicated music player (browse/play/prev/next/
seek/volume/reorder/remove, .pls/.m3u import), plus per-person shared
listening: broadcast your track over presence, peers tune in and stream
it point-to-point over the files plane. Playback is timeline-synced
(play/pause/skip/seek mirror with no drift) with gapless prefetch of the
next track and independent per-source volume per listener.
In the 3-column layout the playlist gets its own card stacked under the
chat, with a resizable divider and its own scrollbar; other layouts keep
it in the Controls panel.
Breaking wire change: gossip protocol v5 (presence gains music fields),
so 0.6.0 peers cannot share a swarm with 0.5.x. Version bumped 0.5.1 ->
0.6.0; CHANGELOG updated.
Untrusted-input handling: broadcast track name sanitized and size
cap-checked at gossip ingest, fetched bytes confirmed audio before
decode, only the descriptor rides gossip (bytes go point-to-point, one
fetch in flight).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Steps 5-6 of game detection. BREAKING wire change — bump everyone.
Wire (step 5):
- PeerState.game: Option<String> (display label only — never appid/source).
- SelfPresence.game + to_state carry it (single self-state builder).
- GOSSIP_PROTO 2->3, GOSSIP_SIG_DOMAIN v3, version comment bumped together;
Cargo MINOR 0.3.0 -> 0.4.0 per VERSIONING.md. v2/v3 isolate into
different topics + signature domains, so a coordinated redeploy is
required (same as the W4 avatar bump).
- Gossip ingest sanitizes incoming game via sanitize_game_label (bidi/
control strip, 64-char/256-byte cap); empty -> None.
- Bonus security fix (Codex find): reject inbound gossip frames over a
128KB cap BEFORE serde_json::from_slice — a legit Announce with a full
48KB avatar is ~49KB, so this bounds allocation abuse with headroom.
Core wiring:
- Spawns the detector at startup; consumes its watch channel in the main
select. Detection runs continuously (for the local background); the
broadcast is gated by game_presence_enabled (opt-in, default OFF).
New commands: SetGamePresenceEnabled (immediate publish/clear, D8),
SetGameOverride, SetGameProcessMap. New event: GameChanged.
- game_presence_label sanitizes the outgoing label too.
Background switch (step 6):
- GUI handles GameChanged: stores current_game, swaps background to the
per-game override (config.game_backgrounds[id]) or falls back to the
W16 default; reuses the existing cached-handle path (no redraw flicker).
397 lib tests (all green), clippy --all-targets clean, full binary builds.
Remaining: step 7 UI (opt-in toggle, roster 'Playing' text, manual
override control, Settings game-backgrounds + process-map editors).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>