Provable Fairness

How we guarantee transparent, verifiable randomness for every pack.

What we commit up front

Before you open a pack, we publish a serverSeedHash — the SHA-256 hash of a secret serverSeed generated in advance using crypto.randomBytes(32).toString('hex'). This acts as our public commitment:

  • You can't guess the real seed from the hash.
  • We can't change it afterward without breaking the hash.

How your pack's random rolls are generated

Each card or rarity slot in the pack corresponds to one entry in a list of ranges (each having a minRangeand maxRange).

To generate random numbers, we use HMAC-SHA512 with this logic:

for digestIndex = 0, 1, 2...
  digest = HMAC_SHA512(serverSeed, `${clientSeed}:${nonce}:${digestIndex}`)
  digest gives 64 bytes → 16 random 4-byte values
  each 4-byte chunk = one random number
  map each to its range:
      num = bytesToUInt32(chunk)
      rand = floor((num / 2^32) * (range.max - range.min + 1)) + range.min

Each digest yields 16 random numbers. If the pack needs more than 16 (for example, 24 numbers to decide rarities and bonuses), we increment digestIndex to get additional digests — no randomness is ever reused.

Those random numbers are then mapped to card outcomes based on your pack's configured rarity and odds.

What we return to you

After your pack opens, you'll receive:

  • serverSeed and serverSeedHash (so you can confirm commitment)
  • clientSeed and nonce used
  • digestIndices used (usually 0 and possibly 1)
  • randomNumbers: all generated values before rarity mapping
  • cards: the final drawn cards
  • oddsUsed: the exact rarity/odds table used for this pack
  • oddsHash: a SHA-256 hash of that odds table (so anyone can confirm it later)

How you can verify us (any time)

  1. Check the commitment: Verify SHA-256(serverSeed) matches the serverSeedHash you saw before opening.
  2. Recreate the digests: For each digest index, compute HMAC_SHA512(serverSeed, "clientSeed:nonce:digestIndex").
  3. Recreate random numbers: Slice each 64-byte digest into 16 chunks of 4 bytes, convert to unsigned 32-bit integers, and map to your ranges exactly as shown above.
  4. Recreate card outcomes: Use oddsUsed to rebuild the odds distribution. Map each random number to its corresponding card. You should get the exact same results we gave you.

You can also use the Run verifier page to verify your results. Use the Response Details → View details button in your Opening History page to copy the raw response and paste the ClientSeed, Nonce, ServerSeed and OddsUsed into the verifier.