№ IV · MMXXVI projects

№ XII · 2026 · play-money, parked

Provably Fair Casino

A play-money web casino — blackjack, roulette, slots, and an even-money bet — where every outcome is derived from HMAC-SHA256 over a seed the house committed to before the player wagered. Anyone can recompute any result. FastAPI + async SQLAlchemy + Postgres behind an Astro frontend, 196 tests, eleven subagent-driven plans. A front-end over a friends' Discord economy; built for the build.

What it is

A friends' Discord server runs a fake-currency economy through the UnbelievaBoat bot — the usual collect-income, buy-roles loop. This is a polished web casino bolted onto that economy: log in with Discord, move cash from the bot into casino chips, play four games, move chips back out. No real money touches it at any point, and that is the entire design premise — with the stakes fake, the only interesting problem left is proving the games are honest.

Four games ship: Bet (a single even-money gamble), Slots, Roulette, and Blackjack — the last one fully interactive, with a deal / hit / stand / double lifecycle. A public verifier page lets anyone, no login, recompute an outcome from its seeds.

The provably-fair guarantee

Every player holds a three-part seed: a secret 256-bit server seed the house generates, a client seed the player can set themselves, and a nonce that increments by one with every bet. The trick is the order of reveal:

How a bet is sealed — and checked

Every instant game runs through one lifecycle function. It reads the player's active seed, computes the outcome at the current nonce, writes an immutable bets row recording the seed id, nonce, client seed, wager, payout and the full outcome JSON, then debits the wager and credits any winnings against the chip ledger — all in a single transaction — and finally increments the nonce. The bet row is the permanent receipt.

Blackjack needed one extra rail. Because it is interactive, the server holds an in-progress deck between requests — and the naive implementation returned the whole deck object in the API response, which meant a player could read the next card in their browser's dev tools. The fix strips the undealt deck and the cursor from every blackjack response; the client only ever sees cards already face-up. It was caught in a review pass, not in production.

The money model

Two ledgers, never allowed to drift. UnbelievaBoat owns the "cash" balance; the casino's own Postgres owns the "chips" balance. The bot's API is touched on exactly two operations — deposit and withdraw — and never during play. A bet only ever moves chips inside the casino's own ledger.

That ledger is the most defensive piece of the backend. Balance changes go through one function that locks the wallet row FOR UPDATE, refuses any debit that would push the balance negative, and appends an immutable chip_transactions row recording the signed delta and the resulting balance. Deposits and withdrawals carry idempotency keys, so a double-clicked button or a retried request can never move money twice.

Architecture

┌──────────────────┐      ┌────────────────────┐
│ Astro + TS       │ ←──→ │ FastAPI            │
│ static frontend  │      │ Discord OAuth ·    │
│ lobby · 4 games  │      │ sessions · CORS    │
└──────────────────┘      └─────────┬──────────┘
                                    │
       ┌────────────────────────────┼────────────────────────┐
       ▼                            ▼                        ▼
┌────────────────┐      ┌────────────────────┐   ┌────────────────────┐
│ provably-fair  │      │ chip wallet        │   │ UnbelievaBoat API  │
│ HMAC-SHA256    │      │ FOR UPDATE ·       │ ←→│ deposit / withdraw │
│ + commitments  │      │ append-only ledger │   │ only               │
└───────┬────────┘      └─────────┬──────────┘   └────────────────────┘
        │                         │
        ▼                         ▼
┌──────────────────────────────────────────────────┐
│ PostgreSQL — users · seeds · chip_wallets ·       │
│ chip_transactions · cash_transfers · bets         │
└──────────────────────────────────────────────────┘

The frontend is a static Astro site that talks to the backend cross-origin with credentialled fetches; the backend is a FastAPI app with Discord OAuth2, cookie sessions, and a CORS allowlist of exactly one origin. All dynamic DOM on the frontend is built through small el() / setChildren() helpers that set textContent — never innerHTML — so backend-supplied strings can't become script. A security hook enforced that rule mid-build when an early lobby draft interpolated a username into markup.

How it was built

Same discipline pattern as Mintly and Odvolanie. One design spec, then the work decomposed into eleven sequential plans — provably-fair core, backend + Discord auth, chip wallet, deposit/withdraw, the four game engines, seed rotation and the verifier, then the frontend foundation and pages. Each plan executed subagent-driven: a fresh implementer per task following test-first development, a spec-compliance review, then a code-quality review, fixes folded back, and a whole-plan review before merge.

The reviews earned their place. They caught a missing CORS middleware that would have blocked every frontend request, the blackjack deck leaking through the API, and a fistful of smaller issues — missing CHECK constraints, an unlinked reference id, a 32-bit column where a 64-bit one belonged. 196 tests passed at the end, every one written before the code it covers.

Why it isn't launched

The casino is feature-complete and the test suite is green. What stands between it and the friend group is purely operational: a Discord application has to be registered for OAuth, and the live UnbelievaBoat economy needs an API token wired in. Ten minutes of clicking, none of it code.

And it was never meant to earn — it is a play-money toy for a specific group of friends, with no revenue path by design, so it doesn't compete for the launch energy the income projects get. It got a clean park instead. A DEV_MODE flag was added on the way out: with it on, the casino auto-creates a local player with a starting chip stack and skips the bot entirely, so the whole thing is fully playable solo without any Discord setup at all.

Stack

What's actually here

A complete, runnable reference for the one problem worth solving in a casino: making "trust us" unnecessary. The commitment scheme means the house cannot lie about an outcome after the fact, the ledger means it cannot lose track of a balance, and the verifier means none of that has to be taken on faith. The play-money framing isn't a limitation — it's what keeps the project a pure study of verifiable randomness instead of something with a licence problem.

If the friends ever want it wired into their server, the ten operational minutes are all that's left. Until then it runs solo in dev mode, and it stands as a study in commitment schemes, transactional ledgers, and shipping a provably-honest system end to end.