This site uses one functional cookie to keep feature rollouts consistent for you. Nothing is set until you choose. See the privacy notice.
Dev notes
The idea is sports betting with the loss taken out. You lock a deposit for a term, bet it freely on real lines, and at the end you get the original deposit back no matter your record. What you keep forever is the record — every bet, every swing, a bankroll curve, a profile you can show off. The catch is that holding people's deposits and investing the float is the legally hard part, so the first cut draws a hard line: the ledger is real, the dollars are simulated. Every deposit, stake, payout and refund is a real double-entry row, and "deposit $100" is a button, not a charge. This is what I built into the API behind this site, and why I built it in that order.
The first decision was the one I could not take back later, so I spent the most time on it: money is never a number in a column. It is derived from a ledger, and every movement is a set of lines that sum to zero across three accounts — the user's bankroll, an escrow that holds the locked principal, and the house. A wallet's balance is the running total of its user-account lines, computed on read, never stored.
It is more code than a balance column, and it is the whole reason the fake-money version can become the real-money version without a rewrite. Placing a bet moves the stake from the user to escrow; settling it pays out or sweeps to the house; the term ends and the principal comes back — and every one of those is a pair that nets to zero, checked by a pure function before it ever reaches Postgres.
// a $25 win at +122, in ledger lines that sum to 0 user payout +2500 // stake returned from escrow escrow payout -2500 user payout +3050 // profit paid by the house house payout -3050
The refund at the end is the part that makes it "no-loss": it returns exactly the principal, whatever the record. A wallet up 40% and a wallet that went to zero both get the same deposit back. The paper profit was always a stat, not cash — which is the honest version of what a real book's screenshot never tells you.
Anyone can buy the same odds API I did; the inputs are a commodity. The thing that compounds and cannot be bootstrapped is the output of play — a record of decisions made at a frozen line with a known outcome. Real books know whether you won money. They do not know whether you were good, because variance hides skill. The public proxy that cuts through it is closing-line value: did the price you took beat where the line closed.
So the odds worker snapshots every pull instead of overwriting, and at settlement I read the last snapshot before kickoff and stamp the closing odds and the CLV onto the bet. It is unrecoverable if you don't capture it live, which is exactly why it was in the first cut and not a "later." A bet at -110 that closes at -130 beat the market, and the number says so:
computeClv(-110, -130) → +7.91 // took 1.909, closed 1.769 computeClv(-130, -110) → -7.33 // the other side of the same move
Roll CLV up with ROI and volume over a few hundred bets and you get a sharp score — a single number a competitor can't fake on day one and that gets more accurate the longer someone plays. It is also the targeting signal for the one revenue line that needs no licensing: refer the provably-sharp users to a real book and take the affiliate fee. The moat and the first dollar are the same asset.
I built it as a stack, not a big bang: ledger and wallets, then odds ingestion, then placing a bet, then settlement, then the term-end unlock, then the profile and leaderboard, then the house view and referrals. Each one is a clean break — it has its own migration, its own tests, and it deploys on its own — so I could ship and check a stage before the next one built on it, and so a reviewer reads one idea at a time instead of a wall.
The settler is the piece I was most careful with, because the failure mode is paying twice. It is idempotent: it only grades open bets and skips events already marked final, so running it again over the same results is a no-op. The messy tail — pushes, voids, an exact spread cover, a postponed game — is graded first-class and tested first, because that is where settlement disputes actually live.
Odds and results both sit behind one interface, with three implementations: the real API, a fixtures provider that replays a captured slate for zero credits, and — for results — the quota-free scores endpoint. Dev, test and seeding never spend a credit, and the provider is chosen explicitly by an environment variable rather than guessed.
The rule I held to there was to fail loud, not fall back quietly. A dead vendor or an exhausted quota throws — it does not return an empty slate that reads as "no games today," which is the kind of silent default that becomes a 2am mystery. User traffic never touches the vendor at all: the events feed serves from the database, so quota is a worker's problem, not a scaling one.
Update — September 3, 2026
The API shipped as eight stacked backend PRs, and the front end is following the same discipline: small, stacked, each one deployable. The first slice is the part that can't go wrong, because it can't do anything — a read-only lobby.
The lobby at /zeroproof renders the public events board — upcoming games with their latest moneyline, spread and total lines — straight from the same DB-backed endpoint the settler reads, so no vendor call rides on a visitor. Under it sits the sharp leaderboard: players ranked by closing-line value rolled up with return and volume. There is nothing to click that spends anything, which is exactly why it's the honest first thing to ship.
The leaderboard endpoint returns the Auth0 sub per row, which is a user identifier and has no business on a public page. The front end derives a stable, opaque handle from it instead — the same player always reads the same token, and the token can't be walked back — and a test asserts the sub string never appears in the rendered DOM.
playerHandle("auth0|abc123") → "P-1F9K2" // stable, opaque
board.textContent → never contains "auth0|"