Skip to main content

Going to production

The demo omits several things a production integration needs. This page lists the gap between it and something you would put money through.

What the demo skips

MissingWhy it matters
Player accounts and sessionsThe demo takes a playerRef from a text box
Wallet, balance, entry debitNobody pays to enter, and nobody gets paid
Real provider sessionsdemoSessionUrl() returns a fake URL
PersistenceSettlements live in a Map and die on restart
A queue behind the webhookIt settles inline; a real one enqueues and returns
AuthenticationEvery page is public
KYC, responsible gaming, jurisdictionWhich players may play at all

All of those are on your side of the boundary, and SlotBattle will not grow them. See What SlotBattle does not do.

Do these before you take money

Debit the entry before opening the battle, and make it reversible. Battles can end failed or cancelled. Decide your refund policy before you need one, and make a battle.failed webhook trigger it automatically rather than through a support ticket.

Settle inside a transaction, keyed for idempotency. Delivery is at-least-once, so a duplicate will arrive eventually and must not pay twice.

Never settle from the socket. It is best-effort and has no replay.

Check ok per seat. A completed battle can contain a failed seat.

Mint provider sessions per player, server-side. game_url is a live money session. It appears in no SlotBattle response, log, event or webhook; hold the same line in your own logs.

Verify the boundary holds

npm run build
grep -r "sbk_" dist/ && echo "LEAK" || echo "clean"

Run that in CI. A key in a client bundle is not something code review reliably catches; it is something you catch by grepping the built artifact.

Also confirm lib/slotbattle.ts is never reachable from a client component. The runtime guard catches it in development; the grep catches what the guard cannot.

Operational reality

503 unavailable is normal under load. The instance caps in-flight battles. Retry with exponential backoff and jitter; this is the one routinely retryable error.

Viewer tokens expire in 15 minutes. Re-mint on reconnect, and when a page sits open longer.

Signing-key rotations should be invisible. The operator appoints a new key and leaves the old one verifying until it retires, so tokens already in browsers keep working. Viewers suddenly taking 401 on the socket means the rotation was done without a window. Re-minting is your mitigation, and the rotation is worth reporting.

Deploys can fail battles in flight. A battle mid-recording depends on recorders that are already running. Handle battle.failed as a routine outcome, not an exception.

Watch a real recording end to end. A recorder without GPU acceleration falls back to about 2 fps: the recording succeeds and is unwatchable. No automated check catches this, so a human has to look.

Final checklist

  • No sbk_ in any client bundle, checked in CI
  • Webhook signature verification rejects a tampered body, tested rather than assumed
  • Settlement is idempotent under duplicate delivery
  • Seats with ok: false are excluded from payouts
  • battle.failed and battle.cancelled trigger refunds automatically
  • 503 is retried with backoff; 400/409 are not
  • A late-joining viewer renders a correct lobby
  • Viewer tokens are re-minted on reconnect
  • game_url appears in no log you control
  • A full recording has been watched by a human

Where to go next