Wire

Binary WebSocket. Each frame is [type_byte][msgpack_payload]. One byte selects the message; the body is whatever the payload decodes to.

Frame codes

CodeDirMessage
0x01C → SINIT_REQUEST
0x02S → CINIT_RESPONSE
0x03C → SSPIN_REQUEST
0x04S → CSPIN_RESPONSE
0x05C → SOPEN_REQUEST
0x06S → COPEN_RESPONSE
0x07C → SSTEP_REQUEST
0x08S → CSTEP_RESPONSE
0x09C → SCLOSE_REQUEST
0x0aS → CCLOSE_RESPONSE
0x0bC → SPROMO_ACCEPT
0x0cS → CPROMO_ACCEPT_RESPONSE
0xfeC → SPING
0xfdS → CPONG
0xffS → CERROR

Request payloads

init:   { sid }
spin:   { sid?, mode?, betIndex?, priceMultiplier?, params? }
open:   { sid?, mode?, betIndex?, priceMultiplier?, params? }
step:   { sid?, action: { type, ... } }
close:  { sid?  }
promo:  { sid?, accept: bool }

Response payloads

init:   { sid, balance, currency, currencyDecimals, allowedBets, defaultBetIndex,
          modes: [{ id, label?, stakeMultiplier, declaredRtp? }],
          promo?:  { id, bet, remaining, total?, label?, validTo? },
          resume?: { roundId, modeId, bet, ops, actionLog, awaiting?, openedAt? },
          demo? }

spin:   { roundId, ops, balance, bet, win, multiplier, type,
          promo?: { remaining, done } }

open:   { roundId, ops, balance, bet, awaiting? }
step:   { ops, awaiting? }                  // no money moves
close:  { roundId, ops, balance, win, multiplier, type }

promo:  { ok, promo?: { id, bet, remaining, total? } }
error:  { code, message }                   // see /errors

Awaiting hint

awaiting is the pause signal on a complex round. Present after open / step → the orchestrator stays open and the client must send STEP. Absent → the round is terminal and the client must send CLOSE.

awaiting = {
  type:     "pick_cell",        // enforced; mismatched STEP → INVALID_ACTION
  options?: [0, 1, 2, ... 24],  // optional whitelist
  deadline?: 30000,             // optional ms before autoclose
  prompt?:   "Pick a tile",
}

Ops

ops is an opaque list of visual instructions. Math authors define the shape; the client replays them. The orchestrator never inspects ops — it forwards them verbatim.