The Village Hall
An application that needs nothing outside the room. The example is a bingo night.
No server to reach, no database to query and no account to create — not as a simplification for a demo, but because a program that keeps its own record of what happened does not need them. Keeping that record is what Puppeteer, the substrate we build on, gives every phone.
The example is a parish bingo. One phone leads the evening, the phones that sell cards and call the balls attach to it, and every player's phone keeps its own game, over whatever Wi-Fi the hall already has: a distributed system that fits in one room.
Phones only·A journal on each·Join with a code
Get the codeThe question
What does an application still need from the outside world, when everyone using it is already in the same room?
Everything that takes part is in the room.
The same evening, drawn twice: as people with their notebooks, and as the topology underneath.
Organizer here: every sale and every ball goes through the one writer.
Organizer here: every sale and every ball goes through the one writer.
Every phone works on its own copy. Take the organizer's phone away and the journal of the evening is still on two phones, and every player still has its game.
Ncubo.Puppeteer 3.0 · iroh over QUIC · Android
The operations did not go away. They stopped being machinery.
Every phone here runs on Puppeteer, which keeps the program's own record of what happened. With that record, four operations stop needing machinery of their own, and two more capabilities come with it. Each one is shown as it happened on the phones.
“For a journaled program, deployment is replay, replication is sharing history, backup is copying the program, and offline operation is delayed replay.”
Paper 05 · The Journal as Substrate
Deployment is replay
A program opens an evening by replaying its journal, and a new version does the same with an evening the old one created: the rules gained a constructor rather than changing one, because the journal re-runs what it already wrote.
Restart the organizer. The night is still there.
The organizer's app is killed and reopened from its own journal. The next ball reaches the caller and the player, who reconnected on their own.
Three Android emulators · 25 Sep 2026 · cut: about 38 seconds passed between the close and the next ball
Replication is sharing history
A phone that joins the hall receives the journal from entry zero and is current, without writing a line of its own.
The phone that joins gets the whole story
The organizer adds a phone and gets its code; the phone enters with it and fills with everything that already happened. From then on it reads its own copy.
Two Android emulators · 25 Sep 2026 · pauses between gestures cut; the join itself is in real time
Backup is copying the program
The evening is a folder in the app's private storage. Copying the folder is the backup.
Offline operation is delayed replay
A player that was away is sent what it missed when it presents itself again; a seller that cannot reach the organizer keeps selling into a notebook of its own, handed over and checked when it reaches the organizer again.
Offline for a minute. Nothing lost.
A player's phone goes into airplane mode while two balls are called. When it comes back they arrive on their own, in order, and the card marks itself.
Three Android emulators · 25 Sep 2026 · cut: coming back took about 15 seconds
And two more that come with the same record
A message is an operation
When a ball is called, every phone receives the operation and runs it on its own journal — the same one the organizer ran — instead of receiving a board.
One call, applied on every phone
A ball is called on the organizer's phone. About a second later it is the last ball on the caller's board and marked on a player's card.
Three Android emulators · 25 Sep 2026 · one continuous take, in real time
A browser is another stage
The player's page runs the same subject on the same substrate, compiled for the browser, with nothing installed.
No install. A page is the player.
A first visit in a phone's browser opens the player's card, and a ball called on the organizer's phone is marked on the page about two seconds later.
Android emulators · 25 Sep 2026 · a local build of the web player, not the published page; the engine's start-up was cut
None of these was built for this application. They come with keeping the program's own record of what happened, and this is what they look like when the thing keeping it is a phone in a parish hall rather than a datacenter. The recordings were made on Android emulators and edited for length; what was cut is written under each one.
Three programs, and only one of them knows there are phones.
BingoEngine
The card, the balls, the figures in play, the round, and the correction when a ball is keyed wrong. It knows nothing about phones.
Village
The rules of the evening: who runs it, who sells at the door, who bought which cards, the prizes promised and the requests to undo a sale. The part a hall recognizes and a game engine has no business knowing.
The app
An application for Android. It hosts the Puppeteer actors, holds the journals, draws the screens and carries messages between handsets over iroh.
Neither domain project references Puppeteer. Both compile against the base class library and nothing else, so Puppeteer enters at the application and never reaches the rules.
What Puppeteer isNo server. The room is the network.
The organizer's phone makes itself the one writer. Every other phone connects to it over iroh, which dials a phone by its public key rather than its address: there is no server in the middle, and the code a phone scans says how to find the organizer.
The handshake is a QR code on the organizer's screen: how to reach it, its public key — which also authenticates and encrypts the link — and invitations good for a single connection each. That is the whole of what one phone needs to know about another.
Once a phone is through the door it receives the entire history from entry zero, and arrives current without replaying a decision of its own. It does not recompute the evening. It receives it.
That pass is for the phones that work the evening: selling cards at the door or calling the balls. A player joins with the code the seller shows at the sale — one link that opens the app, or the same screen in a browser when the app is not installed — and gets something different: not the history, but each ball as it comes out.
“cloud and microservice ecosystems become libraries the program invokes rather than habitats the program inhabits”
Paper 07 · After the Substrate
No database. A folder on each handset.
Every phone in the room keeps its own journal, and a journal is a directory in the application's private storage. There is no schema to migrate, no connection string, no engine to install and nothing to be reachable.
This is what makes the operational questions collapse into one another. Backing up the evening is copying the folder. Handing it to another phone is sending the same entries. Coming back after the signal dropped is replaying what arrived late.
And the journal is small, because of what it records: each operation by name, not the state it left behind. In Puppeteer's published laboratories an entry encodes to between 33 and 96.7 bytes, where the same operation written out in full takes 118 to 910.7 — and compressing the written-out form afterwards closes the gap only to fourfold, because the saving is in how the operation is named rather than in how the bytes are packed. A night of bingo is a small file.
Written once, by the organizer · the same entry lands in every folder, in the same place
The card is not stored. It is derived.
The number printed on a card is not a key into a table of cards. It is the card: twenty-four numbers fall out of it by a keyed hash, the same way on every phone, so a phone that has never heard of that card draws it correctly the moment it has the number.
So a card is never transmitted, stored or synchronized. A player's phone is told which numbers they bought — which cards are whose was written when the sale happened — and rebuilds the cards from them.
Cardface Of(CardNumber number)
stream = HMACSHA256("bingo75.cardface.v1", number)
columns = five bands drawn from that streamThe card is computed, never sent.
The browser shows card 2. The app receives only the number and draws the same card by itself: the same 24 numbers, in the same order.
Android emulators · 25 Sep 2026 · two builds of the rules, one in the app and one in the browser; a wait was shortened
The players are told verbs, not state.
When a ball is called, no phone receives a board. Each one receives the fact that a ball came out, and applies it to its own journal — the same operation the organizer performed, performed again locally.
A phone that closes and reopens presents itself again, and the answer carries every ball it missed; what it had already marked was never only in memory. And a ball carries the round it belongs to: keying the wrong number twice in one evening is two corrections, not a repetition of the first.
ListenAs(role)
.Told("Ball").With<int>("number")
.Command("game.RegisterDraw(@number);")
.Told("Correction").With<int>("wrong").With<int>("right")
.Command("game.CorrectDraw(@wrong, @right);")“Recording cross-actor messages as program operations preserves continuity edge by edge, without orchestration.”
Paper 04 · Preserving Semantic Continuity Across Actors
Replayed, every phone shows 43 · nothing was edited
The room turned out not to be a requirement.
Everything above works because everyone is on the same Wi-Fi. On 23 September the same evening ran with players in four different homes, each behind a different router, and nobody opened a port or arranged a tunnel.
One player's phone changed network in the middle of it. The link dropped, came back as the same device with the same cards, and the evening carried on.
Nobody else in the round noticed.
Measured on Android emulators. In the round itself the phone dropped and came back as the same device.
What carries this is iroh: it reaches a device by the identity it holds rather than by the address it happens to have.
The boundary of the demonstration.
A system is credible in proportion to what it declines to assert. Four things this page is not saying:
- The byte figures quoted are Puppeteer's own, measured in the laboratories published with Paper 05 — not measurements of this application.
- This is not a resilience result from a hall. The recordings above were made on Android emulators: players come back on their own and are sent what they missed, and sellers keep selling while the organizer is away.
- The 472 green tests cover the two domain projects: the rules of the game and the rules of the evening.
- Everyone is on one network, for everything demonstrated above. That is not a limitation working around the absence of a server — it is the claim: the hall is the network. The band before this one is where it stops being the limit.
Follow it further.
Velada, on Android
The evening as the hall uses it: one application, and the phone decides whether it leads, works the door or the balls, or plays.
Coming to Google PlayThe Journal as Substrate
The paper that states the four equivalences, with the laboratories that measure them.
Read the paperPeer to peer, across any network
What changes when the devices are not in one room: connected over iroh, found by identity rather than address, with nothing opened on a router.
Read itThe Stage Manager
Five cases on the two mechanisms this evening is built from: one character standing on several machines, and two characters speaking.
See the seriesIf this bears on what you are building, tell us which part.
Start a technical conversation