PROVABLY FAIR
Implementation
Commit first. Reveal later.
We generate a random 32-byte server seed, written as 64 hexadecimal characters. Before play, we share its SHA-256 hash. After the seed is revealed, hashing it again must give the same value.
import crypto from 'node:crypto';
const serverSeed = crypto.randomBytes(32).toString('hex');
const serverSeedHash = crypto.createHash('sha256')
.update(serverSeed, 'utf8')
.digest('hex');Turn seeds into results
HMAC-SHA256 combines the server seed with a message containing the client seed and game details. A nonce counts plays using a seed pair. A cursor starts at zero and increases only if a generated number must be skipped.
// Case Opening: one message for each attempt.
const message = `case-v1:${clientSeed}:${nonce}:${cursor}`;
const digest = crypto.createHmac('sha256', serverSeed)
.update(message, 'utf8')
.digest();
const value = digest.readUInt32BE(0);
const limit = Math.floor(2 ** 32 / 100000) * 100000;
// If value >= limit, increment cursor and try again.
const ticket = value < limit ? value % 100000 : null;Skipping the small remainder at the top of the number range avoids giving some tickets an extra chance. Upgrader and Case Battles use the same principle with the full 256-bit digest.
Reproduce the exact calculation
The HMAC key is the seed’s 64-character text, not decoded hex bytes. Case Battles and Upgrader encode their message as a compact JSON array. The Games page shows each message and how its result is used.
A matching seed hash confirms the revealed seed matches the earlier commitment. A matching roll confirms the supplied inputs reproduce that result. Item odds and game rules still determine what that result awards.

