Skip to content

Mesh chat

Twenty thousand phones in a field is a dead network. Backstage carries messages between them over Bluetooth with no infrastructure at all.

It is a separate app from the ticket, deliberately: the wallet is a web app because a guard or an attendee should not have to install anything, and the mesh cannot be a web app because Web Bluetooth has no peripheral mode and does not exist on iOS Safari. A browser cannot advertise, and a node that can only listen is not a mesh node.

The wallet links to it from under the QR. The ticket works entirely without it.

What it does

Channels. #lost-and-found, #gate-3, #campsite. The group is whoever knows the passphrase, and the key comes from that passphrase through Argon2id — so a passphrase somebody says out loud at a campsite is still expensive to brute-force from a captured frame.

Direct messages. X25519 to agree a key, ChaCha20-Poly1305 to use it, with the two directions on separate keys so a message cannot be replayed back at its own sender. Identities are ephemeral per event: no account, nothing to subpoena, and nothing linking this festival to the next one.

Announcements. From the organizer, originating at the box, Ed25519-signed and not encrypted — "Gate 4 is now open" is not a secret, and the property that matters is that nobody else can say it. They relay at priority, so they cross a saturated field.

Friend finder. Two people exchange a beacon key once, and after that each phone advertises a rotating token the other recognises; the app plays warmer and colder on signal strength. The token rotates every window, because a constant identifier broadcast in a field is how early Bluetooth trackers became a stalking tool.

The boxes join in

This is the part that makes it work in practice. BLE mesh's worst real-world failure is two clusters of people thirty metres apart who never hear each other, and no amount of relaying fixes a gap nobody can bridge.

The venue boxes are super-nodes: they hear the mesh on their own radios and bridge it between themselves over the LAN. Two groups at opposite ends of a field are then one hop apart through the wire.

What it promises

Nothing. Best-effort store-and-forward, and that is the whole guarantee.

What it has been measured to do, in the simulator:

CrowdDeliveryp95 latency
500 phones in a hall98.8%293ms
2,000 phones99.9%292ms
5,000 phones99.4%556ms
50 phones in a hall86.1%

The fifty-phone row is the honest one. A sparse field is simply not connected, so delivery there is bounded by whether a path exists at all — and what helps is a message carried in somebody's pocket until they walk into the next group, not more relaying.

Honest physics

  • BLE is 10–30 metres indoors, and what makes that range vary so much is what is between the two radios, which at a concert is bodies.
  • iOS restricts advertising aggressively when the screen is locked. A phone in a pocket is a much worse node than a phone in a hand. The mitigations — foreground use, box bridging, local notifications — reduce the problem rather than remove it.
  • Android is better and not unlimited: background scanning is throttled, and some OEM power managers kill it outright.
  • Contents are confidential; the fact of a transmission is not. A BLE mesh broadcasts to everybody in range by construction, and a design claiming otherwise would be lying.

Status

The protocol is written, specified and verified in all three languages — Go for the box, Kotlin for Android, Swift for iOS — against fixtures the Go side generates. What is not done is the two platform radio layers, which need devices. See the protocol and ROADMAP.md.

MIT licensed.