How fairness works

Before a case goes live we lock in what is inside and the order it sits in, and we publish a fingerprint of that lock. After the case is done we hand over the key so anyone can check it.

The plain-English version

  1. We pick the cards. A case is a fixed list of real, graded, vaulted cards. We write the list of card ids down, sorted, and hash it. That hash is the manifestHash. It proves the set of cards cannot change after publish.
  2. We draw a secret seed. 32 random bytes. We publish only its hash, the seedHash. The seed itself stays secret while the case is selling.
  3. We publish a commitment. One more hash that ties the fairness version, the seed hash and the manifest hash together. This is the fingerprint shown on the case page and in the table below.
  4. The seed shuffles the cards. A standard Fisher-Yates shuffle driven by a keystream derived from the seed places each card in a numbered slot. Same seed and same manifest always give the same order, so anyone can reproduce it later.
  5. Each purchase takes one slot. Your browser sends a random clientSeed with the order. We hash the secret seed, the purchase number (nonce) and your clientSeed, and use that hash to pick one of the remaining slots. The proof of that pick is stored on your pull and shown on your order.
  6. We reveal the seed. When a case sells out or is retired, the seed is published. From that moment the whole thing is checkable: the commitment, the shuffle and every pick. The verifier does it in your browser.

The exact formulas

These are copied from the comments at the top of our fairness module. Version string: packrip-fairness-v1. All hashes are SHA-256 over UTF-8 text, printed as lowercase hex.

publish time
manifest      = JSON array of item ids, sorted (what is in the box)
manifestHash  = sha256(manifest)
seed          = 32 random bytes (hex), kept secret until the case sells out or retires
seedHash      = sha256(seed)
commitment    = sha256(`${version}|${seedHash}|${manifestHash}`)
order         = fisherYates(manifest, keystream(seed))   -> slot index i holds order[i]
purchase time (per unit)
nonce         = number of units sold in this case before this one (0-based)
clientSeed    = entropy supplied by the buyer's browser (or empty string)
remaining     = sorted list of slot indexes still AVAILABLE
pick          = int(sha256(`${seed}|${nonce}|${clientSeed}`)) mod remaining.length
slotIndex     = remaining[pick]

Keystream and shuffle details

The shuffle needs a stream of random numbers that only depends on the seed. We use HMAC-SHA256 in counter mode and unbiased rejection sampling:

keystream
block(counter) = HMAC-SHA256(key = seed, message = String(counter))
each block yields eight 32-bit big-endian unsigned integers (4-byte steps)

nextInt(n):
  limit = floor(2^32 / n) * n
  take v from the stream until v < limit
  return v mod n

fisherYates(arr):
  for i from arr.length - 1 down to 1:
    j = nextInt(i + 1)
    swap arr[i], arr[j]

For the per-purchase pick we use the first 12 hex characters (48 bits) of the selection hash so the integer stays exact in JavaScript, then reduce it modulo the number of remaining slots. Slots are always considered in ascending index order so the same inputs give the same pick on any machine.

What this proves, and what it does not

Known limitation, stated plainly
We know the full slot order before any card is revealed, because we generated the seed. Commit-reveal proves we did not change the contents or the order after publishing, and it proves we did not choose which slot you got. It does not make us blind to the order.
  • The manifest cannot change. Adding, removing or swapping a card after publish would change the manifest hash and break the commitment.
  • The order cannot change. The seed determines the shuffle. A different seed would not hash to the published seed hash.
  • We cannot steer your slot. Your pick depends on your clientSeed, which we do not see until your order is placed and which is part of the proof. The nonce is the count of units sold before yours, serialized in the database, so we cannot reorder purchases either.
  • We cannot predict the next buyer's slot. Because the next buyer's clientSeed is random and unknown to us, we cannot know which slot they will get, so we cannot time our own purchases around good slots.
  • Owners and staff are prohibited from purchasing. This is a rule in our terms and an internal policy, not a mathematical guarantee. Anyone with access to the seed could, in principle, know the order; the clientSeed mechanism above is what stops that knowledge from being useful for picking a slot.
  • Slot picks are uniform. Every remaining slot has the same chance on every purchase. Our test suite checks this statistically.

Every published case

Commitments are shown the moment a case is published. The seed is returned by the verifier once the case has sold out or been retired.

Booster Box CaseLiveSeed sealed
Verify
case id
cmuczrpoz00d2l804k4b94daq
version
packrip-fairness-v1
slots
20
commitment
38927a609907...11ce2f
seedHash
deb9082b8652...9da50d
manifestHash
c4c30a795920...7acbe5
published
Tue, 22 Sep 2026 18:12:33 GMT
Prismatic CaseLiveSeed sealed
Verify
case id
cmuczrp2j00c6l804a89p8wg6
version
packrip-fairness-v1
slots
30
commitment
390d692b0ca4...b296df
seedHash
5afcddd8a708...625960
manifestHash
81efddcd265a...58c7fd
published
Tue, 22 Sep 2026 18:12:33 GMT
151 CaseLiveSeed sealed
Verify
case id
cmuczrodj00b0l8048kuhrtss
version
packrip-fairness-v1
slots
40
commitment
66cd0c61bb62...edbb3a
seedHash
b0707b61cca8...320158
manifestHash
f5ba3c72eb15...6b963d
published
Tue, 22 Sep 2026 18:12:32 GMT
Pokemon GrailLiveSeed sealed
Verify
case id
cmubyu8iu00g7ic04w9ale2dy
version
packrip-fairness-v1
slots
19
commitment
405190c756ef...e30f53
seedHash
bb0b2eafe644...8d4145
manifestHash
5f0d8f618132...ee74dc
published
Tue, 22 Sep 2026 00:58:45 GMT
Pokemon GoldLiveSeed sealed
Verify
case id
cmubyu7we00fbic041m5l3akq
version
packrip-fairness-v1
slots
30
commitment
091d92b77d5d...6bd622
seedHash
80325c73f528...72cc3a
manifestHash
4e4cdd41fd6a...d03cd9
published
Tue, 22 Sep 2026 00:58:44 GMT
Pokemon StarterLiveSeed sealed
Verify
case id
cmubyu77f00e5ic045h5yxl3z
version
packrip-fairness-v1
slots
40
commitment
c195d2c82932...f20662
seedHash
d13c8d1dd36c...ea678d
manifestHash
0d66ed74927c...6ee166
published
Tue, 22 Sep 2026 00:58:44 GMT