Overnight.Bet · provably fair demo

How this demo's provably-fair dice works

A code-level explanation for developers who want to verify the scheme rather than take it on faith: a real round played over curl, and an independent recomputation. Play-money, 18+.

Play-money only 18+ No account needed

This is play money. Overnight.Bet is a free, play-money product. There are no deposits, no payouts and no real-money wagering. Nothing on this page is a purchase, a stake or a prize, no balance is created and nothing is credited to anyone. The numbers below are an illustration of a single round.

18+. This demonstration of a gambling mechanic is intended for adults only. If play stops being entertainment, see responsible play.

No account, nothing stored. This page needs no registration and no login. It writes nothing: no account row, no bet row and no counter. It sets no cookie, and the process serving it never receives your IP address. The ordinary web-server and reverse-proxy logging described in the privacy notice applies here as on every page.

The commit: a hash published before you choose anything

Every round starts at the demo page, which mints a round id and derives a server seed from a per-process key held only in memory — not a stored value. Quoted verbatim from Product/demo/server.mjs:

const MASTER = crypto.randomBytes(32);
const serverSeedFor = (roundId) =>
  crypto.createHmac('sha256', MASTER).update(`ob-demo-round:${roundId}`).digest('hex');

The page shows sha256(server_seed) — the commitment — and withholds the seed until you play. By the time you choose a client seed, the server has already locked in its secret, and you hold a receipt to check it against later.

The formula, as implemented

This is not a description of the scheme — Product/demo/fair.js and Product/demo/games/dice.js are byte-identical to the casino's own server/fair.js and server/games/dice.js, verified against the hashes of the files running inside the live casino container. SHA-256 of the two files this page runs: fair.js = b9b00806427eeee4058f552a35bcb32649ea0f67336b2b015a30caa65c46410d, games/dice.js = a8f5fa330f478629eda01bb6c23887e320099a52585011145be2e2080bb9a906.

export function* byteStream(serverSeed, clientSeed, nonce) {
  let round = 0;
  while (true) {
    const buf = crypto
      .createHmac('sha256', serverSeed)
      .update(`${clientSeed}:${nonce}:${round}`)
      .digest();
    for (const b of buf) yield b;
    round++;
  }
}

export function floats(serverSeed, clientSeed, nonce, count) {
  const stream = byteStream(serverSeed, clientSeed, nonce);
  const out = [];
  for (let i = 0; i < count; i++) {
    let v = 0;
    for (let j = 0; j < 4; j++) v = v * 256 + stream.next().value;
    out.push(v / 2 ** 32);
  }
  return out;
}
export function roll(serverSeed, clientSeed, nonce) {
  const [f] = floats(serverSeed, clientSeed, nonce, 1);
  return Math.floor(f * 10001) / 100; // 0.00 – 100.00
}

Concretely: take HMAC_SHA256(key = server_seed, message = "<clientSeed>:<nonce>:0"), read its first four bytes as a big-endian uint32, divide by 2**32, multiply by 10001, floor it, divide by 100. The house edge lives in one constant: the payout multiplier is 99 / chance, not 100 / chance.

A worked example, played live over curl

No account, no cookie, no browser — one real round played against this demo:

$ curl -s "https://overnight.bet/demo/provably-fair?format=json"
{ "round": "0ca73e9fe9c1d9f59aab6c70f74bb91c",
  "serverSeedHash": "02b3e9764434b106becb9930103a3e13964e2ec83f64ee55307d3ce43285e332", ... }

$ curl -s "https://overnight.bet/demo/provably-fair/play?round=0ca73e9fe9c1d9f59aab6c70f74bb91c\
&clientSeed=ArticleOB17verify&nonce=3&mode=under&target=25.75&format=json"
{ "serverSeed": "86cfc8d66e9793750bf62e5d5d53fd8173434b7ed9af2b32163f81ae85b49b6e",
  "serverSeedHash": "02b3e9764434b106becb9930103a3e13964e2ec83f64ee55307d3ce43285e332",
  "hmacMessage": "ArticleOB17verify:3:0", "roll": 8.08, "chance": 25.75, "multiplier": 3.8446, "win": true }

Neither request set or received a cookie. The commitment was published before the client seed existed anywhere, and the revealed seed hashes to exactly that commitment.

Independent verification, standard library only

A from-scratch reimplementation, not the demo's own code, run locally against the round above:

import hashlib, hmac

server_seed = "86cfc8d66e9793750bf62e5d5d53fd8173434b7ed9af2b32163f81ae85b49b6e"
client_seed = "ArticleOB17verify"
nonce       = 3
commitment  = "02b3e9764434b106becb9930103a3e13964e2ec83f64ee55307d3ce43285e332"

assert hashlib.sha256(server_seed.encode()).hexdigest() == commitment

digest = hmac.new(server_seed.encode(),
                   f"{client_seed}:{nonce}:0".encode(),
                   hashlib.sha256).digest()
roll = int(int.from_bytes(digest[:4], "big") / 2**32 * 10001) / 100
assert roll == 8.08
print("commitment OK, roll OK:", roll)   # -> commitment OK, roll OK: 8.08

Or skip trusting either implementation and ask the live casino's own public verifier, a different process on a different host running the real game's code:

$ curl -s "https://overnight.bet/api/fair/verify?game=dice\
&serverSeed=86cfc8d66e9793750bf62e5d5d53fd8173434b7ed9af2b32163f81ae85b49b6e\
&clientSeed=ArticleOB17verify&nonce=3"
{"serverSeedHash":"02b3e9764434b106becb9930103a3e13964e2ec83f64ee55307d3ce43285e332","roll":8.08}

What this proves, and what it does not

ProvesThe server fixed its secret before your input existed and cannot change it afterward without the reveal failing the hash check. The roll is a deterministic function of seed, client seed and nonce that anyone can recompute.
Does not proveThat the server seed was drawn fairly in the first place — the scheme checks the seed was not changed after commitment, not how it was generated.
Does not proveThat the odds are fair in some absolute sense — the house edge (the constant 99, not 100) is a documented, deliberate design choice the hash scheme does not check or hide.
Is notReal money, a licensed product, or account behavior. This is a play-money, no-account demo that reveals its seed immediately because nothing is at stake; an account round instead reveals on rotation, so a whole range of nonces can be checked at once.

Play a round yourself

Head back to the guest demo and play your own round — no account, no cookie. Add ?format=json to either route to drive it from a script instead of a browser.