Controller+BETADownload

Wire protocol v1

The default transport is WebSocket with UTF-8 JSON messages. Each message has v: 1 and a type. Client frames are bounded to 16,384 bytes. Server state has a separate bounded budget; do not assume an unlimited message channel.

Join links

A join URL keeps its room credentials in the fragment:

https://game.example/join#v=1&room=ABC123&token=PLAYER_TOKEN_FROM_THE_HOST

The actual token is a random URL-safe credential, not the placeholder above. The controller opens /ws on the same origin. An optional path fragment field selects another validated path on that same origin. Duplicate credential/path fields, unsafe schemes and cross-origin path tricks are rejected.

The fragment avoids putting credentials in ordinary HTTP request logs. It still needs to be treated as a credential: do not publish a live join link in permanent documentation.

Authenticate before sending input

{"v":1,"type":"hello","role":"player","room":"ABC123","token":"PLAYER_TOKEN_FROM_THE_HOST","name":"Player","capabilities":["input.touch","view.private"]}

The server replies with welcome, including the assigned player ID and a reconnect ticket. A host uses separate host authority. The showcase service also supports a separate spectator token; the developer-host adapter accepts game-host and player roles.

Input and events

An input frame has a monotonically increasing seq. axes and buttons may be sent as deltas, but when present they contain the complete four- or sixteen-value array. Values must be finite and within range. Optional motion contains bounded acceleration, gyroscope and quaternion fields.

Use an event frame for game-specific actions such as a guess or vote:

{"v":1,"type":"event","event":"vote","payload":{"target":"p2"}}

The game must check that the action is valid for that player and phase. Authentication does not make a player's claimed answer or vote result authoritative.

Public state and private views

state is explicitly public. view is sent to one player. The game host receives device-input, device-left and player-event from the developer adapter. Host-only commands can publish public state or choose a player's private view.

Keep secret roles, answers and code fragments in a separate server-side object. Serialize that separation explicitly instead of copying the entire game object into a broadcast.

Ping and reconnect

Echo a ping message's at field in a pong frame. Each side measures its own round trip; clocks do not need to agree.

A reconnect uses the previous resume ticket. On success the server rotates it and neutralizes the seat. The old socket loses authority. Tickets and desktop-control approval are different: reconnecting does not carry mouse/keyboard approval to a new connection.

Errors and extensions

Errors include unauthorized, invalid_resume, room_expired, room_full, invalid_axes, invalid_buttons, version_mismatch and rate_limited. Explain the useful action to the player and stop retrying credentials that cannot recover.

The optional native 32-byte input codec is a negotiated extension, not a silent replacement for JSON. Other codecs belong in the codec registry. Native apps do not execute arbitrary downloaded protocol or UI code.

Ping at values are opaque numeric correlation IDs. Clients keep monotonic timing locally and compute the round trip from the echoed ID. They do not need to send device uptime or use synchronized clocks.