Provably fair

Every outcome is fixed before you bet. You can prove it.

A house-game result on BetIT is derived from a seed the server committed to in advance, a seed you control and a counter — then published with everything needed to recompute it. Nobody steers a result, and you never have to take our word for it.

Committed before you bet
You see the SHA-256 hash of the server seed before your first round. The seed can't be swapped afterwards without breaking the hash.
You control a seed
Your client seed is half of every input. Change it whenever you like — the house can't pick a server seed that favours it against an input it hasn't seen.
Recompute in your browser
Rotate the pair and the retired seed is published. The verifier runs the same TypeScript as the server, offline, and shows every step.

01How a round is derived

Before you betEvery roundAfter rotation01Server seed32 random bytes, as hex02CommitmentSHA-256(serverSeed)hashGenerated by the server.Secret while the pair isactive.Published before your firstbet. The seed can't changewithout breaking it.shown to you as the seed-pair hash, before any betkey03Client seed · nonceyours · 1, 2, 3 …04HMAC-SHA256key serverSeedmsg domain:client:nonce:cursor05Byte streamblock(0) ‖ block(1) ‖ …06SamplinguniformInt · shufflesampleUnique07Outcomenumbers · mines · cardsmsg32 Bu32You choose the seed. Thenonce counts rounds on thepair.One 32-byte block per cursor. Nothingrandom happens outside it.Read 4 bytes at a time,big-endian.Rejection sampling on 4-bytewords. No modulo bias.Then paid from the versionedpaytable, rounded down.08Revealrotate → serverSeed09RecomputeverifyRound() in your browserthe same seeds and the same code, in your browsercompareRotating your pair publishesthe retired seed.Every round on the pair, byte forbyte.01Server seed32 random bytes, as hexGenerated by the server. Secret while the pair isactive.02CommitmentSHA-256(serverSeed)Published before your first bet. The seed can'tchange without breaking it.03Client seed · nonceyours · 1, 2, 3 …You choose the seed. The nonce counts rounds on thepair.04HMAC-SHA256key serverSeedmsg domain:client:nonce:cursorOne 32-byte block per cursor. Nothing randomhappens outside it.05Byte streamblock(0) ‖ block(1) ‖ …Read 4 bytes at a time, big-endian.06SamplinguniformInt · shufflesampleUniqueRejection sampling on 4-byte words. No modulo bias.07Outcomenumbers · mines · cardsThen paid from the versioned paytable, roundeddown.08Revealrotate → serverSeedRotating your pair publishes the retired seed.09RecomputeverifyRound() in your browserEvery round on the pair, byte for byte.
Domain betit/v1/<game>Cursor 0, 1, 2 … one HMAC block eachPayout floor(stake × multiplier ÷ 10000), stake included

02How each game reads the stream

Each game starts at cursor 0 of its own domain. All amounts are integers in the smallest unit; multipliers are basis points and always include the stake.

KenosampleUnique(10, [1..40])
Ten unique numbers of 1–40, kept in draw order. Hits are your picks that appear in the draw; the paytable for your risk profile and pick count sets the multiplier.40 bytes · every (risk, picks) pays 98.5–99.1 % RTP
MinessampleUnique(k, [0..24])
The k mine positions on the 5×5 grid are fixed before your first pick. Each safe pick multiplies by the exact odds, less a 1 % edge, rounded down.4·k bytes · 99 % RTP at every cash-out point
MolessampleUnique(n, [0..6]) per step
For each of the 8 steps the stream fixes n of the 7 holes — all at start, from the continuing stream. Only resolved steps are ever published.4·n·steps bytes · 0.99 per step taken
RouletteuniformInt(37)
One number 0–36 — the number itself, not a wheel index. Each bet class pays from the versioned paytable; zero loses every outside bet.4 bytes · 36/37 = 97.2973 % RTP with the v1 table
WheeluniformInt(slices)
One slice index of the 10–50 on the wheel. The multiplier is read from the versioned paytable for your risk level; the wheel simply stops with that slice under the pointer.4 bytes · every (risk, slices) table pays exactly 97.00 % RTP
Blackjackshuffle([0..311])
A Fisher–Yates shuffle of the 6-deck shoe. Card c: rank = (c mod 52) mod 13, suit = ⌊(c mod 52) / 13⌋. Dealt from index 0 — player, dealer up, player, dealer hole — then as your actions require; the verifier replays them.≈ 1 250 bytes · S17 · DAS · 3:2 · no insurance

Arena games have no per-player seed pair. The server still commits to a seed before anyone enters; the message binds the outcome to the participants instead of a nonce. House fee 5 %, the rest goes to the winner.

Jackpotbetit/v1/jackpot:<roundId>:<salt>:<cursor>
The salt is the SHA-256 of the entry ids in ticket order, so the draw is bound to the final list of players. The winning ticket is uniform over the pot — 8-byte rejection sampling, since a pot can exceed 2³² units.
Coinflipbetit/v1/coinflip:<matchId>:<creatorEntryId>:<opponentEntryId>:0
The first byte of the block decides: even is heads, odd is tails. The opponent's entry id does not exist until they join, so nobody can know a seed's outcome in advance.
Rock-paper-scissorsbetit/v1/rps:<matchId>:<creatorEntryId>:<opponentEntryId>:<cursor>
Your picks decide. On a tie the stream draws a fresh pair for both players (uniformInt(3) each) until the match is decided; every reroll is recorded in the result.

03Verify it yourself

The exact message format, and a frozen test vector you can check with any HMAC-SHA256 tool.

Byte streamspec v1
block(cursor) = HMAC-SHA256(
 key = utf8(serverSeed), // the 64-char hex string itself, not its bytes
 msg = utf8("betit/v1/<game>:<clientSeed>:<nonce>:<cursor>")
)
stream = block(0) ‖ block(1) ‖ block(2) ‖ … // 32 bytes each, cursor from 0
uniformInt(n): v = next 4 bytes as u32; limit = ⌊2³² / n⌋ · n; // v < limit ? v mod n : next 4 bytes
Vector 1 from any shellserverSeed = 64 zeros · clientSeed betit · nonce 1
$ printf '%s' "0000000000000000000000000000000000000000000000000000000000000000" | shasum -a 256
60e05bd1b195af2f94112fa7197a5c88289058840ce7c6df9693756bc6250f55

$ printf '%s' "betit/v1/keno:betit:1:0" | openssl dgst -sha256 -hmac "0000000000000000000000000000000000000000000000000000000000000000"
8b0dc157a6cee2cc… // first 8 bytes of block 0 → keno draw 8 21 28 2 10 14 5 12 24 31
serverSeed
4f5c1b2a9d8e7f60123456789abcdef0fedcba9876543210a1b2c3d4e5f60718
SHA-256 (commitment)
eea407868f1190ae6afb1ed569b7207fa6dbbd8eb9284f6a074f19d2846bd0ba
clientSeed / nonce
my-client-seed_01 / 7
keno block 0, first 8 bytes
b9ed7bc078ad365a
keno draw
33 34 36 21 17 7 32 5 2 22
mines, k = 3
8 0 7
moles, n = 2, steps 1–3
[0 4] [5 2] [2 4]
blackjack shoe, first 10
34 6 9 90 105 28 188 102 45 305

Open vector 2 in the verifier — with picks 1 21 33 34 36 and a 1 000 000 stake it pays 16 500 000 (4 of 5 hits, 16.5000× on the classic table).

The verifier is the same code the server runs: packages/core/src/fairness.ts (commitment, stream, sampling) and packages/core/src/games/verify.ts (full-round replay), bundled into this site. The machine-readable spec with all three vectors is at /api/fairness/spec, and every settled round is public at /api/fairness/round/<id>.