A per-user balance ledger for your app's economy — chips, gold, gems, credits, tokens — in your own D1. Currency-agnostic, and correct by construction.
@pithy-sh/ledger
pithy add ledgerThe primitive under in-game currency, rewards, buy-ins, prize pools and wagers. Currency-agnostic: chips, gold, gems, credits, tokens.
It composes with multiplayer — escrow a wager, settle a payout — but it stands alone perfectly well.
What it sits on
Balances are derived from entries, never stored as a mutable number. Every movement is an entry, and the balance is what the entries say it is.
The honest bit
The name suggests accounting and the use suggests casinos, and neither is the point. If your product has a balance a user can spend, you need the correctness properties of a ledger whether or not you call it one.
Correct by construction is the claim: no path in this package can produce a balance the entries do not justify.
If you build it yourself
Storing a number is easy. Being able to prove it is the whole job.
A balance derived from immutable entries can be audited; a mutable integer cannot, and the first dispute proves it.
Two requests arriving together must not both succeed against the same funds.
Holding a stake, then releasing or returning it — atomically, and correctly when a player disappears halfway.
A retried credit must not pay out twice, which means every movement carries a key.
Integers in the smallest unit, never floats, or you will find the rounding error the hard way.
pithy add ledgerComposes with
In the dashboard
Balances and the entries behind them, per user and per pool.
The dashboard is the admin interface for your own application: whatever this capability writes, your team can look up, check and act on from a browser. Reading is free, forever, with no time limit. Connect the project when you deploy.
Everything else
Each one composes the same way. Nothing you skip costs you anything.
Designed, not shipped
Written down, argued out, and not built yet. Each one links to the issue it is being designed in, so you can read the reasoning or disagree with it.
One command installs the package, wires its bindings into every environment your project declares, and runs its migrations.