A balance store that can double-spend or go negative is not a balance store
Three properties hold no matter how operations interleave or how many times they are delivered. They are why amounts are integers in a minor unit and never floats, and they are stated as constraints in the reference.
And every one is enforced by SQLite, not by a check in a handler that a race can skip.
Atomic
Each operation is one batch — a D1 transaction. The ledger entry and the balance change commit together or not at all.
There is never a recorded movement with no matching balance change, and never a balance change with nothing recording it.
Idempotent
Every operation carries a caller-supplied ref, written as a UNIQUE ledger row.
A replay inserts a duplicate ref, which aborts the transaction — and the operation returns the balance unchanged.
A payout delivered twice pays once.
Overdraft-safe
A debit or a hold is applied by an UPDATE guarded by the account’s constraint:
CHECK (balance >= 0 AND held >= 0 AND held <= balance)Never negative. Never over-reserved.
A movement that would break solvency aborts — including against a balance another concurrent operation just lowered — and surfaces ledger/insufficient_funds.
Which is why correctness lives in the schema
The constraint is on the table. Every path that moves a balance goes through it — the in-process API, the HTTP routes, a future path nobody has written yet.
A guarantee that lives in one code path is a guarantee the second code path does not have.
Amounts are integers in the minor unit
Never floats. So arithmetic is exact, and 0.1 + 0.2 is not a conversation anybody has to have about somebody’s balance.
A currency’s decimals only says how to display them. See currencies.
What this buys a wager
Hold the stake when the bet is placed, resolve it when the outcome lands.
The hold is what makes the stake unavailable without spending it — the balance is unchanged, the available amount drops, and the held <= balance half of the constraint is what stops a player reserving the same chips twice.
Then capture or release. Both are idempotent by the same ref rule, so a settlement delivered twice settles once.