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>
First slice of in-chat file/photo sharing (dedicated file plane, images
inline + file chips, session-only). This stage adds the wire types and
the pure, unit-tested logic; no transport or UI yet.
- protocol: new FILES_ALPN / FILES_PROTO (peerspeak/files/1) for the
dedicated file-transfer plane. Bump GOSSIP_PROTO 1->2 + sig domain v2
(Chat gained an attachment field, so cross-version peers fail fast
rather than half-work) and Cargo 0.2.0 -> 0.3.0 per VERSIONING.md.
BREAKING wire change: all peers must run >= 0.3.0.
- new src/files.rs: ChatAttachment descriptor (name/size/kind/id; bytes
travel off-gossip), AttachmentKind, plus pure seams — sanitize_filename
(path-traversal/control-char/length-safe), size_within_cap, image
magic-byte sniffing + defensive limited decode (decode-bomb guard),
32-byte request parsing, human_size. 13 unit tests.
- GossipMessage::Chat and RoomEvent::ChatMessage carry an optional
ChatAttachment; send_chat takes Option<ChatAttachment>. Untrusted
inbound descriptors are filename-sanitized + size-validated on ingest.
serde(default) keeps the field forward-compatible at the JSON layer;
+round-trip and pre-v2 back-compat tests.
The attachment id is a random 32-byte handle (rand, already a dep), not
a content hash — the fetch is authenticated + encrypted + member-gated,
so no crypto-hash dep is needed.
349 lib tests pass, clippy clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Establishes VERSIONING.md: SemVer 0.x (MINOR = breaking wire change) for the
release version, and per-plane protocol versions enforced on the wire so
incompatible peers fail fast and legibly instead of via silent decode/signature
errors.
⚠️ BREAKING WIRE CHANGE — all peers must run >= 0.2.0 to interoperate (ALPNs and
gossip subscription topics changed). A pre-0.2.0 peer (e.g. an un-resynced
dopedart) can no longer connect, by design, and now fails at the handshake.
- New src/protocol.rs: single source of truth for AUDIO/FRIENDS/GOSSIP_PROTO,
the derived ALPNs (peerspeak/audio/1, peerspeak/friends/1), GOSSIP_SIG_DOMAIN,
and versioned_topic(). Unit tests assert ALPN/domain strings match their
integer versions (no silent drift) + that topic namespacing is deterministic.
- Unified ALPNs: audio was b"peerspeak-audio" (unversioned, and duplicated in
iroh_impl.rs + core/mod.rs) -> peerspeak/audio/1 from protocol.rs; friends
re-exports protocol::FRIENDS_ALPN (was peerspeak/friends/0 -> /1).
- Gossip: subscribe to versioned_topic(ticket.topic_id) so different gossip
versions never share a swarm; the raw topic_id stays the room identity and
what signatures bind. GOSSIP_SIG_DOMAIN centralized into protocol.rs.
- Cargo.toml 0.1.0 -> 0.2.0.
316 lib tests / clippy --all-targets clean. VERSIONING.md documents the bump
rules, the "I changed X -> what do I bump" table, and a release checklist.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>