How we make every game result verifiable
Every online game asks players to trust the server. The server decides what happens, and the player sees the result. We think players should not have to take that on faith, so every result in our games can be checked after the fact by anyone, using nothing but public math.
The approach is called commit and reveal. Here is how it works in our games, step by step.
1. We commit before you play
Each player has a seed pair. The server side of that pair is a secret: 32 random bytes, written as 64 hex characters. Before your first round, we show you the SHA-256 hash of that server seed.
A hash works like a sealed envelope. You can't read the seed from it, but once the seed is revealed, anyone can hash it and confirm it matches. That means we can't swap the seed later without being caught.
We go one step further and also show the hash of your next server seed, so the seed you get after a rotation is fixed in advance too.
2. You add your own randomness
The other half of the pair is your client seed. It's yours to set, up to 64 letters, numbers, dashes or underscores, and you can change it whenever you rotate. Because it feeds into every result, we can't pick a server seed that works against a seed you choose later.
A counter called the nonce goes up by one with every round you play, so each round gets fresh numbers from the same pair.
3. Results come from public math
Each result comes from one function:
HMAC_SHA256(server seed, "client seed:nonce:round")
The 32-byte output is split into eight numbers between 0 and 1, and the game turns those numbers into a result, for example the order of a shuffled deck. If a game needs more than eight numbers, the round counter moves on and the function runs again.
The same game logic runs on our servers and in your browser. It's a small, deterministic library with no hidden inputs, so the same seeds always produce the same result.
4. Reveal and verify
When you rotate your seed pair, the old server seed is revealed. You can then:
- Hash the revealed seed and check it matches the hash we showed you before you played.
- Recompute any round you played with that pair, using the server seed, your client seed and the nonce.
- Confirm the result matches what you saw.
If either check fails, you have proof something was wrong. That's the point: the system only works if we can be caught.
Shared rounds
Some games run one round for everyone at once, so there is no personal client seed to mix in. For those we take a secret seed and hash it ten million times in a row, building a chain. We publish the last hash before the first round, then use the chain in reverse, one link per round. Hashing any revealed link over and over leads back to the published hash, so no link can be swapped without breaking the chain.
To make sure we couldn't have searched for a convenient chain, every result is also mixed with the blockhash of a Solana slot that did not exist yet when we published the chain. Nobody, including us, knows that blockhash in advance.