Committed randomness
Physics is re-derivable from the input-log, but some things aren't — board layout, progression offers, the degen wheel, bot builds. Those come from per-player randomness decks: a commit-reveal scheme where the server publishes a Merkle root up front, then reveals individual leaves on demand with proofs, and a revealed seed lets anyone re-derive the entire deck.
Physics is re-derivable from the input-log — but some things aren’t. Board layout, progression offers, the degen wheel and bot builds are decided by per-player randomness decks: committed up front, revealed on demand, and fully re-derivable from a single revealed seed.
#What needs committed randomness
The kernel stays purely deterministic. But the decisions around the physics — what the board looks like, which upgrades you're offered, where the wheel lands, how a bot builds — can't be recovered from the shot-log alone. If those were just server rolls, you'd be back to trusting the house. So they come from a commit-reveal scheme across seventeen named domains:
#The per-player deck
A deck expands each (player × domain × draw) into a counter-indexed slot. Each slot's 32 bytes of entropy are an HMAC of the secret seed over a domain-separated message — so no two slots collide, and one slot tells you nothing about another.
slot = { playerId, domain, counter }
leaf = HMAC_SHA256(key = seed,
msg = "rr-random-slot-v1" || matchId || playerId || domain || counter)
-> 32 bytes#Commit up front, reveal on demand
The public commitment is a domain-separated Merkle root over the leaf hashes, after canonically sorting the slots — so the root can't depend on the order the server happened to build them in. The secret deck (root + leaves + seed) lives server-side and is never shipped into a live match. Later, revealing one leaf hands over that value with a Merkle proof — and nothing else: not the seed, not any future leaf.
- commit
- at lock_match — the randomness root + leaf count go on-chain, roster frozen.
- reveal
- at settle — the seed is revealed; any leaf can be proven against the root.
- scope
- one reveal exposes one player's one leaf — never the seed or other leaves.
#Unbiased bounded draws
A leaf is 32 bytes; a game needs "pick 1 of 5" or "8..12 oranges". That mapping uses rejection sampling over SHA-256 of the leaf plus a context hash, discarding the biased tail so every outcome in range is equally likely. Because the context is mixed in, the same leaf yields different, independent values for different questions — each accompanied by a receipt binding {context, min, max, value} that anyone can re-check.
#Attestation and re-derivation
The commitment is ed25519-signed against the roster hash and a lock timestamp — optionally anchored on Solana — so the root is provably tied to this match's players at this moment. And the final backstop: reveal the seed and verifyRandomnessTranscript rebuilds the entire deck, checking the root and every leaf. That's the auditor's proof the server didn't cherry-pick its entropy.