Provably fair, explained without the hand-waving
What a server seed hash actually commits to, where your client seed and nonce come in, and how to recompute any roll on CSDEGEN yourself in under a minute.
Every gambling site says it is fair. Provably fair means you do not have to take that on trust: every result is fixed before you bet, and after the fact you can recompute it yourself from three values and get the exact same answer the server got. This post walks through how that works on CSDEGEN, game by game.
The three ingredients
- Server seed: a random 64-character secret the server generates for you. You only ever see its SHA-256 hash while it is in use.
- Client seed: a value you control. You can replace it with anything you like (letters, digits, hyphens and underscores, up to 64 characters) whenever you rotate.
- Nonce: a counter that starts at 0 and goes up by one for every bet you place with that seed pair. It is what makes bet number 41 different from bet number 42.
Commit, then reveal
Before your first bet the server shows you the hash of its seed. A hash is a one-way fingerprint: it pins the seed down completely, but you cannot work backwards from the hash to the seed, and nobody can find a second seed with the same hash. So from that moment the server is locked in. It cannot swap the seed after seeing how you bet.
When you rotate seeds on the Provably Fair page, the old server seed is published in full and a fresh one is committed in its place. Hash the revealed seed (the page does this in your browser) and compare it to the hash you were shown earlier. If they match, every bet you made with that seed can now be recomputed.
From seeds to a number
Every game turns the seeds into one or more floats between 0 and 1. Float number i of a bet is:
float_i = HMAC_SHA256(key = serverSeed, message = clientSeed:nonce:i)
first 4 bytes, read big-endian as an unsigned integer
divided by 2^32Dividing a 32-bit integer by 2^32 always gives a value of at least 0 and strictly below 1, spread evenly. What each game does with those floats is below. The important part: the function that turns floats into a result is literally the same code on the server and on the verifier page. There is no second copy that could drift.
| Game | Floats | How they are used |
|---|---|---|
| Dice | 1 | roll = floor(f0 × 10000), shown as 0.00 to 99.99 |
| Spin | 1 | slot = floor(f0 × 15): 0 is green, 1 to 7 red, 8 to 14 black. One seed per round (see below) |
| Mines | 24 | Fisher-Yates shuffle of the 25 tiles; the first N tiles are the mines |
| Keno | 10 | Fisher-Yates shuffle of the 40 tiles; the first 10 are the draw |
| Upgrader | 1 | under wins if f0 < chance; over wins if f0 ≥ 1 − chance |
| Case open | 2 | cursor 0 picks the tier (gold, red, blue or grey) by the case's odds, cursor 1 the item inside it |
| Battle pull | 2 | same two cursors, message clientSeed:round:slot:cursor |
Spin rounds
Spin is a roulette everyone plays together, one round every 30 seconds, so it cannot use your personal seeds. Each round gets its own server seed instead. The moment a round opens, its seed is drawn and the SHA-256 hash is shown on the Spin page, before anyone can bet. Bets stay open for 22 seconds, then the strip rolls and stops on the slot the seed decided.
f0 = HMAC_SHA256(key = roundSeed, message = csdegen-spin:<round>:0) / 2^32
slot = floor(f0 × 15) 0 green (14x), 1-7 red (2x), 8-14 black (2x)The client seed csdegen-spin is fixed and public, and the round number is the nonce, so no bet can move the result. Once a round has rolled its seed is published in the round history. Pick Spin on the Provably Fair page, enter the round number and the seed (or pick a recent round), and it checks the hash and recomputes the slot. Red and black each cover 7 of the 15 slots and green 1, so every colour returns 14/15 (93.33%) of its stake on average.
Cases and battles
A case open uses the same message format with the cursor in place of i. Every case has four tiers (gold, red, blue and grey) with fixed odds in basis points: a 1% case drops gold 100 times in 10,000, red 900, blue 3,000 and grey 6,000. Cursor 0 picks the tier: t = floor(f0 × the sum of the odds), then the tiers are walked from grey up to gold, adding their odds until the running total passes t. Cursor 1 picks the item inside that tier: floor(f1 × the number of items in it). Each item is one exact skin with its wear and StatTrak already fixed, and the drop pays that item's price, so nothing else is rolled. Opening five cases at once reserves five consecutive nonces, one per case.
Item prices come from a weekly price list, and every case open and battle records which list it used. The verifier loads the case exactly as it was on that list before it recomputes the drop. Rounds opened on the older cases, which used four cursors (rarity, skin, wear and StatTrak), can still be checked by picking the legacy cases in the verifier.
Case battles have their own seed pair, created and hashed the moment the battle is created and shown in the battle room. The message for a pull is clientSeed:round:slot:cursor, so a result depends on the seat, not on the order people joined. Every pull is derived the instant the last seat fills and the seed is revealed when the battle ends. There is no external block to wait for, which is why battles here start immediately.
Checking a bet yourself
- Note the nonce and client seed of the bet you want to check. Every game shows them under the board next to a Verify link.
- Rotate your seeds on the Provably Fair page. The seed you just used is revealed and its hash is checked against the commitment automatically.
- Pick the game in the verifier, paste the revealed server seed, the client seed and the nonce, set the same bet options you played with, and press Verify.
- The verifier recomputes the floats and the result entirely in your browser. Your seeds are never sent anywhere. For a case or a battle pull it also loads that case's item list and prices for the round's price list. The list for a given price version never changes, so anyone can fetch it again and compare.
If you prefer not to trust our page either, everything above is standard HMAC-SHA256. Any language with a crypto library can reproduce the floats in a few lines.