Every game needs a backend, and the list is always the same: who is this player, how do they find someone to play, what game are they playing, how good are they, who is ahead, and what can they spend. Six questions — and on Cloudflare, six capabilities you add rather than six services you operate. Plus the one nobody warns you about, which is getting onto the Play Store at all.
The questions every game asks
None of this is a framework you adopt. Each one is a package that installs into the Worker you already have, writes its own migrations and bindings, and leaves the rest of your app alone.
Four ways, and every one of them ends at a session id. A room code — short, shareable, short-lived — for the person sitting next to you. A direct invite by email or screen name, pending until it is accepted. A friend graph formed by mutual accept. And an open queue: one Durable Object per game pairs waiting players, bucketed by Cloudflare's own edge geolocation and by skill, widening each player's band the longer they wait until someone qualifies.
A second Durable Object holds every online player's socket and pushes match-found, invite-received and friend-request in real time, over the Hibernation API — so a connection waiting on nothing bills no duration.
The session is the same for every game: membership bound to a signed-in user, a lifecycle, hidden per-player state, an alarm-driven deadline, a durable result in your D1. What a game is plugs in behind that, and three patterns cover most of them.
Simultaneous — everyone submits a hidden choice and the server resolves them together; battle ships on it, a duel or an N-player free-for-all of secret attacks and blocks. Turn-based — the helper owns turn order; connect-n ships on it, which is tic-tac-toe at 3×3, Connect Four at 7×6 and gomoku at 15×15 from the same model. Wagering table — a persistent table with a bet book and settlement; craps ships on it.
Those three are examples, not the menu. Write your own game against the pattern that fits and register it; only the rules are yours.
The server holds what no client may: the hidden hand, the shuffle, the roll. A game model's logic is pure and cannot touch a database, so a move is validated and resolved in one authoritative place.
For anything with stakes, every session mints a random seed at creation and publishes its SHA-256 hash up front, then reveals the seed when the session ends — so a player can replay the seed afterwards and verify every roll the table made.
Two numbers per player, per pool: a skill rating that moves both ways with every result, and an experience total that only ever rises. They are separate deliberately — one number cannot both rank a player fairly and reward them for turning up.
The algorithm is a choice, not a hardcode. Elo for transparent 1v1, Glicko-2 when a returning player should not be rated on stale form, TrueSkill when you need to rate teams. Register your own if none of them fit. Wiring a 1v1-only algorithm to a four-player game fails on deploy rather than on the first result.
Pools are named, so several games can share a ladder or each can keep its own — and a pool is exactly what the open queue buckets on.
Submit a score, read the standings — daily, weekly and all-time from the same submission. A rating estimates what will happen; a leaderboard records what did, and they are different products for a reason.
A finished session publishes to it one way, so the board cannot disagree with the result that produced it.
A per-user balance in your own D1 — chips, gold, gems, credits, whatever you call it. What makes it a ledger rather than a column is that a stake can be held before a game and settled after: a hold a player cannot cover rejects the action, and a deterministic model that replays re-emits the same effect and pays exactly once.
Buying the currency is the payments capability, and a product's catalog entry can grant into the ledger directly — so a purchase on any of five rails tops up the same balance.
Before an Android build reaches production, a new personal developer account owes Google a closed test: twelve testers, opted in, for fourteen continuous days. Lose one on day nine and the clock effectively restarts, and no screen in the Play console tells you where you stand.
That is the capability nobody puts on a comparison table, and it is the one that decides your launch date.
What that composes to
Each command installs a package, writes its block into pithy.config.ts, wires the bindings — including the Durable Object namespaces and their migration tags, per environment — and runs its migrations.
pithy init # the Worker, its config, per-env wrangler.jsonc
bun install
pithy add auth # who the player is
pithy add multiplayer # authoritative sessions on a Durable Object
pithy add matchmaking # room codes, invites, friends, a matched queue
pithy add rating # Elo, Glicko-2 or TrueSkill, per pool
pithy add leaderboard # daily, weekly, all-time
pithy add ledger # balances, holds, settlement
pithy migrate # one ordered registry, per database
bun run dev # http://localhost:8787
Nothing above calls a Pithy-operated service, because there isn't one. Every row lands in a D1 on your own account.
What you would compose
Passwordless sessions, mobile and web both first-class. Magic link, email OTP, Google, Apple.
pithy add auth
Language for your app — negotiated per request, rendered through one seam. No tables, no bindings, no error codes of its own.
pithy add i18n
Rank your players. Submit a score, read the standings — daily through all-time.
pithy add leaderboard
A per-user balance for your app's economy — chips, gold, gems, credits. Correct by construction.
pithy add ledger
Room codes, invites, friends and matched queues. Every path lands players in one session.
pithy add matchmaking
Authoritative sessions on Durable Objects. The server holds what no client can be trusted with.
pithy add multiplayer
Five payment rails resolving to one cross-rail entitlement, in your own Worker and your own D1.
pithy add payments
Skill and experience per player, per pool. Elo, Glicko-2 and TrueSkill ship built in.
pithy add rating
The roster, the invitations and the fourteen-day clock Google Play makes you run.
pithy add testers
In practice
A player signs in with a magic link. They open a room and read the code out to a friend, who taps in. Both stake from their balance, and the stake is escrowed rather than trusted to either client.
The session lives in a Durable Object that holds each player's hidden hand, resolves each turn, and enforces a deadline with an alarm. When it ends it writes a durable result to your D1, settles the escrow, publishes to the leaderboard, and updates both players' ratings — which is what the next queue match will bucket on.
Six capabilities, composed. No game backend anyone operates, and every row in a database you own. The dice, if there were any, would be verifiable after the fact — the session commits to its seed before the first roll and reveals it at the end.
And before any of it reaches Android, a new personal developer account owes Google a closed test: twelve testers, opted in for fourteen continuous days. Lose one on day nine and the clock effectively restarts, and no screen in the Play console tells you where you stand. That is the seventh capability, and it is the one that decides your launch date.
What it costs
A session costs Durable Object duration and requests for as long as it is live, plus the D1 writes for its result. A leaderboard costs D1 writes on submit and reads on view. There is no per-player fee, no per-match fee and no floor you pay before your first player arrives.
The leaderboard and multiplayer packages ship their own cost workings against real Cloudflare rates. What matters here is the shape: this scales with play, not with your headcount or your ambitions.
Cloudflare sets and changes these prices; their pricing page is the authority, not this one. Pithy itself is free and always will be.
Add one capability, deploy it, and see the shape before you commit to the rest.