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
- 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. - 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. - 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.
- 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.
- Each purchase takes one slot. Your browser sends a random
clientSeedwith 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. - 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.
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]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:
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
- 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.
- case id
- cmuczrpoz00d2l804k4b94daq
- version
- packrip-fairness-v1
- slots
- 20
- commitment
- 38927a609907...11ce2f38927a609907de5e3669d1507693d7674bcb0fb693385ded2d7ab86f0611ce2f
- seedHash
- deb9082b8652...9da50ddeb9082b86521018a147935fb7c46847e65e1268426a8ad1d5fc3507679da50d
- manifestHash
- c4c30a795920...7acbe5c4c30a795920efe75b63e85607eec405b4f5845d85405179ce011420917acbe5
- published
- Tue, 22 Sep 2026 18:12:33 GMT
- case id
- cmuczrp2j00c6l804a89p8wg6
- version
- packrip-fairness-v1
- slots
- 30
- commitment
- 390d692b0ca4...b296df390d692b0ca482e94d501fc7a248c66030e8b23ef640a2632352a00f6eb296df
- seedHash
- 5afcddd8a708...6259605afcddd8a7087b718f857a786e31036a85527e31c13d66df97b3e9124c625960
- manifestHash
- 81efddcd265a...58c7fd81efddcd265a9bde25da919e4aeeb97967e114c597e15439426ef96cd758c7fd
- published
- Tue, 22 Sep 2026 18:12:33 GMT
- case id
- cmuczrodj00b0l8048kuhrtss
- version
- packrip-fairness-v1
- slots
- 40
- commitment
- 66cd0c61bb62...edbb3a66cd0c61bb6233cf587b53e7aaae0abb1b767891664729812ccd5b5995edbb3a
- seedHash
- b0707b61cca8...320158b0707b61cca8033da8f2a9f4e0a73031b394b36c09ca0de7c32609b06c320158
- manifestHash
- f5ba3c72eb15...6b963df5ba3c72eb15771c9a4d7d30ac7433e9f94e59e39ee3e3649d59663c3f6b963d
- published
- Tue, 22 Sep 2026 18:12:32 GMT
- case id
- cmubyu8iu00g7ic04w9ale2dy
- version
- packrip-fairness-v1
- slots
- 19
- commitment
- 405190c756ef...e30f53405190c756ef0da325d94c7e7f4df42683c2551077e6f654031d4e5637e30f53
- seedHash
- bb0b2eafe644...8d4145bb0b2eafe6445d8ccd81e8dc75dfefbceb017d4b612f05b601cac7d04c8d4145
- manifestHash
- 5f0d8f618132...ee74dc5f0d8f618132dd1ad09fa32f2b2332e989fe6a1ced0fc8d74b36fbc344ee74dc
- published
- Tue, 22 Sep 2026 00:58:45 GMT
- case id
- cmubyu7we00fbic041m5l3akq
- version
- packrip-fairness-v1
- slots
- 30
- commitment
- 091d92b77d5d...6bd622091d92b77d5d5203f74cda1d2803c04ac119a6b8fc8ebf2c8c9041f1696bd622
- seedHash
- 80325c73f528...72cc3a80325c73f52870dc2f641b79c34cc67b31762e9c77da0d5f00ccab054b72cc3a
- manifestHash
- 4e4cdd41fd6a...d03cd94e4cdd41fd6a86987646bc4b59e0e5caf8bdd84d856660b2f2339acfb5d03cd9
- published
- Tue, 22 Sep 2026 00:58:44 GMT
- case id
- cmubyu77f00e5ic045h5yxl3z
- version
- packrip-fairness-v1
- slots
- 40
- commitment
- c195d2c82932...f20662c195d2c8293259f6f75ac2781a0e204253cbf1ce78f6aa2aac5624e301f20662
- seedHash
- d13c8d1dd36c...ea678dd13c8d1dd36c39a81eeda205985fed7239a24116c00756e65d612755d4ea678d
- manifestHash
- 0d66ed74927c...6ee1660d66ed74927c940b0c787babbfff891bdc9b72674e88b5958efc725beb6ee166
- published
- Tue, 22 Sep 2026 00:58:44 GMT