Capsule: wire protocol
Use when adding, removing, or changing any field on a client↔server message, snapshot, or event.
Read first
- docs/design/protocol.md — full wire protocol
- §2.0 Boundary authority and guardrails
- §2.1
ClientMessage - §2.2
ServerMessage - §2.3
startpayload - §2.4
snapshotpayload (per-player, fog-filtered) - §2.4.1 Boundary inventory
- §2.5
Event(transient, one snapshot only) - §2.5.1 Projection contract summary
- §2.6 Replay playback state and vision
- §2.7 Observer analysis state
- docs/projection-audit-checklist.md — checklist for new projection-affecting snapshot fields, events, or observer modes
Code map
server/crates/protocol/src/lib.rs— authoritative Rust wire DTOs and compact transportserver/crates/contract/src/lib.rs— shared semantic DTOs re-exported by protocol, including start/snapshot contract records andDEFAULT_FACTION_IDserver/src/protocol.rs— server-shell adapter for typed kind conversion and legacy importsserver/crates/sim/src/protocol.rs— sim-facing adapter for typed kind conversionclient/src/protocol.js— mirror; must agree on every tag, field name, and shapeserver/src/map_handoffs.rs+client/src/map_editor_handoff.js— one-use HTTP Map Editor ↔ Lab map-transfer DTO; map bodies never enter URLsserver/src/lobby/mod.rs+client/src/config.js—PLAYER_PALETTEcross-surface mirror, guarded bynode tests/protocol_parity.mjs
Current lobby fields to remember
setName { name }updates the sender’s sanitized name during the lobby phase; countdown and in-game requests are ignored.selectMap { map }is the host-only map selector command.lobbycarriesmap(selected stable map name) andmaps[]({name, description, minPlayers, maxPlayers}catalog rows). Replay start metadata separately usesmapName.- Lab start metadata carries room/role identity, compatibility vision, initial camera, dirty state, and operation count.
- Start
capabilitiesdeclares room-time, vision-selection, and command affordances; never infer them from replay/dev/Lab modes. Privileged viewers choose all-player union, omniscient, or player views per connection, independent of command authority. Lab timeline controls use neutral room-time messages rather thanLabClientOp. - Privileged start payloads also carry the authoritative initial
observerViewselector. Use it to render the shared controls; do not reconstruct it from legacy Lab metadata. - Start payloads carry recipient-scoped
diagnosticsmetadata when projection policy enables movement-path overlays or observer analysis. Do not infer those affordances from room mode names. - Resolved AI matches send
observationReady(replay/log lookup). - The active protocol has no quickstart/debug lobby command or start-payload flag. Normal live countdown skipping for rooms with one or zero active humans is not a debug preset.
LobbyPlayercarriesteamId,factionId,aiProfileId?(canonical AI profile id), andisSpectator; spectators are lobby members but not active match players.- Lab setup import/export accepts only checkpoint-backed
LabCheckpointScenarioV1; legacy setup JSON is rejected.validateScenariopreviews catalog/path/payload/map bounds without mutating the room or accepting client-controlled server paths.metadata.lab.initialCameramay set the first Lab world-pixel center. /api/map-handoffsvalidates map data, caps records at 64, expires them after two minutes, and consumes each id once. LabexportMapreturns onlyLabMapDraftin reverse.
Invariants
- Mirror. Every protocol change touches both files and docs/design/protocol.md in the same commit.
- Parity. Run
node tests/protocol_parity.mjsafter protocol vocabulary, compact code/slot, prediction metadata, start/snapshot/replay DTO, default faction id, or lobby palette changes. - Fog is authoritative. Anything sent per-player (entity views,
target_idtracers, death/ positional events) must be gated on visibility/ownership. Never send a player an entity or position they can’t see. See docs/design/protocol.md §2.4 and docs/design/hardening.md. - Clients are untrusted. Validate and bound everything inbound: dedupe + cap ordinary unit lists
(
MAX_UNITS_PER_COMMAND) and lab command-limit-bypass unit lists (LAB_MAX_UNITS_PER_COMMAND), size-limit frames, range/overflow-check placement coords. See deployment.md and docs/design/hardening.md. rts-protocoldepends only onrts-contractamong workspace crates. Kind conversion that needs rules/sim vocabulary belongs in adapter modules.
Cross-capsule triggers
- Adding a snapshot field consumed by rendering → client-ui.md.
- Changing event emission from the sim → server-sim.md.