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.