Real-time transports
Backlog Hold'em provides two ways to perform the same room workflow.
HTTP commands with SSE events
This is the frontend default.
- Commands are sent as HTTP
POSTrequests. EventSourceopensGET /events/rooms/{code}.- The server sends an authoritative snapshot immediately.
- Every mutation publishes another complete public snapshot.
- A keepalive comment is sent every 15 seconds.
An SSE event looks like:
id: 7
event: room
data: {"code":"A1B2C3","version":7,...}The event ID is the room version. The current implementation sends a fresh snapshot on connection; it does not retain an event log for replay.
Bidirectional WebSocket
The frontend connects to:
/ws/rooms/{code}?participantId={participantId}Commands use a correlation ID:
{
"kind": "command",
"type": "vote",
"id": "client-generated-uuid",
"payload": { "value": "5" }
}Supported mutation types are vote, reveal, reset, topic, demo-team, and end. HTTP/SSE clients use the matching POST /api/rooms/{code}/{type} endpoint.
The server responds with a correlated acknowledgement:
{
"kind": "ack",
"type": "vote",
"id": "client-generated-uuid",
"room": { "code": "A1B2C3", "version": 7 }
}Errors use kind: "error" and an error string. Independent room changes use kind: "event"; the initial state uses kind: "snapshot".
Shared semantics
Both modes invoke the same application methods and publish the same public room representation. Neither adapter receives unrevealed vote values or current-round revision counts. Completed history and an ended session's summary are public to room viewers. This avoids protocol-specific authorization or game logic.
Event hub behavior
The process-local hub maintains subscribers by room code. Each subscriber has a bounded channel. Publication is non-blocking; an update is dropped for a subscriber whose queue is full. Since every event is a complete versioned snapshot, a later update supersedes a dropped one.
Scaling boundary
The current hub works only inside one Go process. Multiple backend replicas would need:
- a distributed event adapter such as Redis or NATS;
- sticky connections or shared subscription routing;
- database optimistic locking or transactions based on room version;
- an explicit presence/connection model.