Provably fair, and what that actually means
Every session mints a seed at creation — 128 bits from the platform CSPRNG — and commits its SHA-256 hash up front, shown to every player before a single die is rolled.
The seed is revealed when the session ends — which is the moment a wager settles, and why the stake sits in a hold until then rather than moving on the way in.
An auditor hashes the revealed seed to check it against the commitment, then replays the algorithm to verify every roll. So the house cannot have chosen or re-rolled an outcome.
Determinism is what makes it verifiable
The pattern helpers draw through this stream rather than reaching for Math.random, and the reference has the surface.
Given the seed and a cursor, every value is fixed and reproducible.
The Durable Object advances the cursor as a model draws and persists it, so the stream never repeats and survives hibernation.
ctx.random.next() // float in [0, 1)
ctx.random.int(1, 6) // a die
ctx.random.pick(items) // a uniform choiceA model declares money movements; it does not perform them
apply and resolve are pure — they cannot touch a database.
So instead of moving money themselves, they declare effects that the Durable Object settles through the ledger afterwards.
{ op: "hold", userId, currency, amount, ref }
{ op: "capture", ref, amount?, memo? }
{ op: "release", ref }
{ op: "credit", userId, currency, amount, ref, memo? }
{ op: "debit", userId, currency, amount, ref, memo? }
{ op: "transfer", ... }That keeps the model deterministic — a requirement for replay and provable fairness — while still letting a game hold a stake, capture a loss, or pay a win.
Refs are built from the session and the state
Every effect carries a ref — the ledger’s idempotency key.
Because a model is deterministic, a replayed transition re-emits effects with the same refs, so applying them twice is a no-op: a payout pays once.
Build them stably:
`${ctx.sessionId}:round-3:alice:stake`Never from a clock or a random source. See idempotency refs — the same rule, from the ledger’s side.
A hold a player cannot cover rejects the action
The transition is never persisted.
Which is the correct order: the ledger’s overdraft constraint is the authority on whether a stake is affordable, and the game does not get to record a bet the player could not make.
Persistent tables
A game with mode: "table" is long-lived: active from creation, players join and leave between rounds — buy in, cash out — the model settles each round, and the table stays open until it is closed or empties.
onJoin and onLeave are where a table settles somebody in or out: release their open holds, cash them out, deal them in next round.
Both are optional. Omit them for a match-mode game, or a table that needs no per-join bookkeeping.
The regulation is yours
How any of this maps to real money, and the regulation that implies, is the adopter’s concern.
Pithy provides the mechanics: a verifiable random stream, a bet book, and a ledger with three invariants that hold under concurrency.
It provides no opinion on whether your chips are money, and the ledger says the same from its side.
Install the ledger capability for any wagering game. Without it, a model’s effects have nothing to settle against.