Documentation
Meshbell is a neighborhood scale emergency instrument. Verification is the substrate, not a feature. This page says how, and where the edges are.
Overview
A hazard warning in Meshbell is a small signed record. It is named by the hash of its own bytes, signed with a device key, and never altered. A correction is a new record that points at the old one, which is what lets a warning and its correction travel together and lets any device re-check anything it receives.
Your phone has two links, and neither one needs the internet
This is the whole connection model, and the app's Connect tab is where you see both of your links. The third row below is not a phone link at all: it is what the radios do between themselves, one hop further out.
| Medium | What it reaches | How it starts |
|---|---|---|
| Bluetoothfrom your phone | A pocket radio: a companion LoRa node in your bag. This is the only way a phone gets on the air, because no iPhone has a LoRa radio inside it. | You tap Connect. A scan costs battery and takes an exclusive claim on the radio, so a person starts it, never the app. |
| Wi-Fifrom your phone | A neighborhood bridge: one always-on box on the local network, holding the record and serving it to phones and browsers. | Automatically at launch. The app looks for a bridge on whatever Wi-Fi you are on, and there is a manual address field for networks that block discovery. |
| LoRaradio to radio | The long leg. Two prepared people with pocket radios reach each other across the blocks with nothing in between; every additional radio repeats it onward. | Nothing on the phone starts it. Your phone never speaks LoRa, so this leg begins the moment two radios are tuned to the same channel, whether a phone is attached to either of them or not. |
The whole chain, end to end: your phone over Bluetooth to the pocket radio in your bag, that radio over LoRa across the blocks to another radio, that radio into a bridge, and the bridge over Wi-Fi to every phone and every browser on the neighborhood network.
Both links feed one journal and one keyring. A record that arrives over the air and one that arrives over Wi-Fi land in the same log and get the same check. Turning the radio off does not stop bridge reception, because they are separate paths to the same place.
Everything above the network is built so that losing a connection, a server, or a model costs you an annotation, never the alert.
Which of these are you?
There are three ways to be ready, and the rest of this page is written for all three. Find yours, then read its stations first.
- Just a phone
- Free, and enough to receive. You hear what a bridge in range or an internet connection can hand you, and nothing is on the air until someone near you has a radio. Read the overview and working offline, then the ways to prepare on the front page.
- A pair of radios
- About
$30each, one in your bag and one in a neighbor's. That is the long leg: the two of you reach each other across the neighborhood with no cell, no internet and nothing in between, and every radio anyone else adds repeats it further. Read radios and hardware and radio bands by region. - A bridge host
- One always-on box,
~$50-80, that turns the neighborhood's own Wi-Fi into a communications center any browser can join, with no app and no account. Read setting up a bridge, what a bridge put on the air and more than one bridge.
The trust model
Meshbell keeps two questions apart, because they are different facts and blurring them is how bad information spreads.
| Question | What answers it | What it can say |
|---|---|---|
| Which record is this, and is it intact? | The content address and the signature check, both run on your device | bound · preview · tampered |
| Who signed it, and can I check? | The keyring on your device | verified · could-not-check · forged |
The second axis is deliberately three valued. A forgery, checked against a key you hold and failed, is not the same fact as an unknown key, which is no way to check yet. Collapsing them into one word "invalid" would hide exactly the distinction a person needs. A message is stored as trusted only when the keyring says verified.
Verified off the air, with no prior copy
A short radio frame is a preview: a pointer at a record, shown but never trusted and never used to invent a body. A record can also be sent in full as a self-authenticating envelope, split across several frames and reassembled on the far side. A device that has never seen the record before can then rebuild it, re-derive its content address, and check its signature, reaching a verified verdict on a pure radio neighborhood with no internet anywhere in the loop.
Frames arrive unauthenticated over LoRa, and the fragments of a split record are unsigned in transit. Only the reassembled record is verified. An incomplete group says so and stays a preview.
The instruction firewall
The one thing an emergency app must never do is turn a rumor into an order. Meshbell enforces that in the structure of the code, not by review discipline.
- The screen that renders an instruction, meaning what to do, cannot be constructed from an unverified message. The type system refuses it.
- Guidance text comes from a bundled, versioned playbook keyed by id. It never comes from free text on the wire and never from a language model. A model may summarize a situation, in prose, labeled as inferred. It has no field in which to put a command.
- A relayed official alert is shown as a neighbor vouching, not as official. Its verdict says so in words: relayed official alert, not treated as official here.
The promise, in one line. The app never launders a rumor into an instruction, and that sentence is a property of the code rather than a mission statement.
The coordination layer
Verified alerts answer what is happening and whether you can trust it. A neighborhood in an emergency also has ordinary logistics: who is accounted for, where the shelter is, who has a generator. Meshbell carries a second, deliberately separate layer for that work, native in the app's Neighborhood tab and in a browser for anyone without the app.
This layer is unverified by design. It is not content addressed, not signed, and not part of the alert rail. It is mutable shared state that neighbors write and edit, so treat it like a note taped to a door. The instruction firewall still holds: nothing here can render as an official instruction, and the trust model above governs alerts only.
What it holds
| Surface | What it is | Privacy shape |
|---|---|---|
| Safe roll-call | One tap "I'm safe" check-in. Everyone sees an aggregate count against an approximate household total. | No names, addresses or locations, and no roster. The count is withheld until a handful have checked in, so a single household cannot be inferred. |
| Resource lists | Shared, editable lists: shelter and warming, water and supplies, power and charging, and a self-listed who can help. Items can carry a place label. | Anyone on the bridge can add or edit. Self-listed only. No one is added by someone else. |
| Board | Short posts tagged to a channel (info, offer, request, question) and windowed by recency, posted under a name you type. | Whatever name you type. No account, no verified identity. |
| Printable snapshot | A one page /coord/print render of the roll-call, the lists and the latest board, for when batteries die. | The same aggregate only roll-call, so it is safe to tape to a physical board. |
Why it is held apart
Mixing mutable neighbor notes into a signed, append-only alert log would break both. So the coordination store is its own thing: local network only, mutable, and served by the bridge beside the verified feed rather than inside it. An operator holds an admin token for housekeeping, such as clearing test data or pruning a runaway board, and the admin routes fail closed without it. Posting and checking in need no credential at all. The two layers share a screen, never a trust boundary.
Working offline
Degradation is the expected state, not an error. When a data path is down, Meshbell serves the last known good data labeled with its age, "as of 40 minutes ago", rather than a confident looking blank. The zero model tier, an old phone with nothing clever on it, is a first class product tier. An emergency app that only works on the newest hardware fails the people most likely to need it.
Reception continues while Meshbell is in the background, and iOS can relaunch it to hand back a radio connection after it has been closed. See listening in the background for what that does and does not cover. It has never listened when the phone is switched off, and it will not claim to.
Radios and hardware
Meshbell runs on your phone alone. Radios extend its reach when the network is down. A neighborhood mesh is built from inexpensive off-the-shelf LoRa hardware running the open MeshCore firmware.
| Piece | Cost | Role |
|---|---|---|
| A companion LoRa radio, for example a Heltec node | ~$30 | Your phone pairs with it over Bluetooth to send and hear bulletins. A companion serves one phone at a time. If another phone or app holds it, yours is told so plainly instead of spinning. |
| More radios around the neighborhood | ~$30 each | Each one is a hop, and each repeats a record once and then stops so the air stays clear. Three or more begin real multi-hop reach across blocks and ridgelines. |
| An always-on bridge, usually a Raspberry Pi | ~$50-80 | Holds a durable log and serves it over local Wi-Fi on port 4190. It will use an internet uplink when one exists, and it needs none. |
US radio settings. Meshbell's reference bench runs 910.525 MHz, bw 62.5 kHz, sf7, cr 4/5, inside the license free 902-928 MHz band, with a 172 B companion link envelope. Always read your radio's configuration back before transmitting. The region default may be a band you are not licensed to use.
Radio bands by region
Your phone has no LoRa radio inside it, so it never touches the band. The radio's own firmware owns that setting. Meshbell reads the tuning back off the radio when it connects, prints it, and says plainly when the radio is not on the channel your region uses.
910.525 MHz is the North American station. Every region below has its own channel, placed inside the license free band that region allows. Where the MeshCore community publishes a recommended channel for a country, Meshbell adopts that channel and its settings exactly, so the MeshCore repeaters already standing there can carry Meshbell bulletins. Where the community has published none, Meshbell picks a channel that sits clear of the busiest part of the band.
| Region | Band | Meshbell channel | Duty cycle or listen before talk |
|---|---|---|---|
| United States and Canada | 902-928 MHz | 910.525 MHz | No duty limit, so a bridge can beacon as often as it needs to. Up to 36 dBm, antenna included. This is MeshCore's own recommendation for the United States and Canada, so existing MeshCore repeaters here can carry Meshbell bulletins. |
| Europe and United Kingdom | 869.4-869.65 MHz | 869.618 MHz · sf8 · cr 4/8 | 10% duty cycle, about six minutes of transmit time an hour. Up to 29 dBm. The United Kingdom applies the same limits, so there is no separate setting for it. This is MeshCore's own recommendation for Europe and the United Kingdom, so existing MeshCore repeaters here can carry Meshbell bulletins. |
| Czech Republic | 869.4-869.65 MHz | 869.432 MHz | The same 10% duty cycle and 29 dBm ceiling as Europe, on a channel of its own. This is MeshCore's own recommendation for the Czech Republic, so existing MeshCore repeaters there can carry Meshbell bulletins. |
| Netherlands | 869.4-869.65 MHz | 869.618 MHz | The same 10% duty cycle and 29 dBm ceiling as Europe. This is MeshCore's own recommendation for the Netherlands, so existing MeshCore repeaters there can carry Meshbell bulletins. It shares Europe's frequency but runs the default sf7 and cr 4/5, so the two do not hear each other even on the same frequency. |
| Portugal | 869.4-869.65 MHz | 869.618 MHz · cr 4/6 | The same 10% duty cycle and 29 dBm ceiling as Europe. This is MeshCore's own recommendation for Portugal, so existing MeshCore repeaters there can carry Meshbell bulletins. It shares Europe's frequency but runs sf7 and cr 4/6, so the two do not hear each other even on the same frequency. |
| Australia | 915-928 MHz | 916.575 MHz · cr 4/8 | No duty limit. Up to 30 dBm. The band starts at 915 MHz here, so the North American channel is not legal. This is MeshCore's own recommendation for Australia, so existing MeshCore repeaters there can carry Meshbell bulletins. |
| New Zealand | 915-928 MHz | 917.375 MHz | No duty limit. Up to 30 dBm. The band starts at 915 MHz here too, so the North American channel is not legal. This is MeshCore's own recommendation for New Zealand, so existing MeshCore repeaters there can carry Meshbell bulletins. |
| Brazil | 915-928 MHz | 923.125 MHz · sf8 · cr 4/8 | No duty limit. Up to 30 dBm. This is MeshCore's own recommendation for Brazil, so existing MeshCore repeaters there can carry Meshbell bulletins. |
| Southeast Asia | 920-923 MHz | 921.875 MHz | 10% duty cycle in the strictest country of the group. Up to 17 dBm. One grouped setting for Thailand, Singapore, Malaysia and Indonesia, so check your own country before leaving a bridge running. The community publishes no channel for these countries, so this one is Meshbell's own, chosen clear of the busiest part of the window. |
| Vietnam | 920-923 MHz | 920.25 MHz · sf8 | 10% duty cycle. Up to 17 dBm, and the rules here move, so check the current ones before leaving a bridge running. This is MeshCore's own recommendation for Vietnam, so existing MeshCore repeaters there can carry Meshbell bulletins. |
| India | 865-868 MHz | 866.5 MHz | 1% duty cycle, about 36 seconds of transmit time an hour. Up to 16 dBm. The radio cannot hold itself to a limit this tight, so the bridge has to pace itself. The community publishes no channel for India, so this one is Meshbell's own, chosen clear of the busiest part of the band. |
| South Korea | 920.9-923.3 MHz | 921.4 MHz | No duty limit, but the radio has to listen before it talks. Up to 23 dBm. |
| Japan | 920.6-928 MHz | 924.6 MHz · bw 125 kHz | 10% duty cycle and a listen before every transmission. Up to 16 dBm. The wider bandwidth is what keeps a single burst short enough for Japan's rules. |
| Russia | 864-865 MHz | 864.5 MHz | 0.1% duty cycle, about 3.6 seconds of transmit time an hour, unless the radio listens before it talks. Up to 16 dBm. |
Unless a row says otherwise, a channel runs sf7, cr 4/5 and bw 62.5 kHz. A row that follows a MeshCore channel carries whatever that channel uses, which is why several show a different spreading factor or coding rate, and Japan runs bw 125 kHz to fit inside Japan's rules. Two radios hear each other only when frequency, bandwidth, spreading factor and coding rate all match.
Radios come in two families, one cut for 902-928 MHz and one cut for 863-870 MHz, and between them they cover every region on this list, but China's 470 MHz band needs a different radio chip and a different antenna, so it is not a setting anyone can switch to.
Three regions are not ready. Japan, South Korea and Russia expect a radio that listens before it talks, and the MeshCore firmware does not do that yet, so those three stay off until it does. A region also carries a power ceiling, and power is a separate setting on the radio, so changing the channel is not the same as lowering the power. Always read your radio's configuration back before transmitting.
Setting up a bridge
A bridge is one always-on node in the neighborhood, usually a Raspberry Pi, that carries the verified feed and the coordination layer over local Wi-Fi so every neighbor's phone or browser can reach them with no cell and no internet. It is the second of the phone's two links: Bluetooth reaches a pocket radio, Wi-Fi reaches a bridge, and either one works with no internet at all.
The bridge's own Wi-Fi, so a neighbor needs nothing
With a second Wi-Fi adapter the bridge runs its own open network, named meshbell- and then your street. It is a dead end on purpose: it reaches the bridge and nothing else, not the house, not the internet, not the other phones on it. A neighbor needs no app and takes four steps. Join the Wi-Fi whose name starts with meshbell-. A page opens by itself, the way a hotel network opens its sign-in page; tap Continue. If the phone asks about the internet, tap Use Without Internet, because this network has none on purpose. If you close that page, open a browser and type meshbell.lan. Set the network up once with sudo ./install-ap.sh --interface wlan1 --ssid meshbell-oak-st.
If you are the host, one prepared neighbor
The installer ships inside the app and is exported straight off your phone, so nothing is downloaded. Unpack it on the Pi, run the install script, and it prints the address to hand out. The node comes up self contained: it serves the coordination screens, an installable web app for browser-only neighbors, a QR code of that address, and an admin view for housekeeping. It advertises itself on the local network as _meshbell._tcp, and the app does the finding. There is nothing to configure by hand and no account to create.
If you are a neighbor who has never heard of a mesh
Join the neighborhood's Wi-Fi and open the Neighborhood tab. The app looks for the bridge by itself and connects. When discovery needs a hand, it asks the plain questions, is this the right network and is the bridge awake, and offers a manual address field. If a computer can reach the bridge but this phone cannot, the two are almost always on different Wi-Fi networks.
The bridge delivers, it never decides. A bridge can pass a verified alert along, but it cannot make one true. Signatures are checked on your own device, and in a plain browser they are checked in that browser. A compromised or malicious bridge can drop or delay messages. It can never forge a trusted one. That is why the trust model lives on the phone and not on the node.
Listening in the background
The phone keeps the radio link while Meshbell is in the background, and iOS can relaunch Meshbell to hand a connection back after the app has been closed. Three things make that work together: the system preserves the Bluetooth connection, the app asks to be restored with it, and it remembers that you wanted the radio connected, so it can redo the handshake the firmware requires on every new connection.
The honest edge of it. iOS may still stop the app, and a companion radio serves one phone at a time, so a radio another phone is holding is not available to yours. Background listening also does not survive a phone that is switched off. What it does mean is that a bulletin arriving while the phone is in a pocket is delivered rather than missed.
What a bridge put on the air
Two facts that are easy to mistake for one: the radio accepted a message, and the message actually went out. The first is what a phone learns when it hands frames to a companion radio. The second is measured from the radio firmware's own transmit counters, and only a bridge reads those.
A bridge keeps a receipt for everything it transmits: what went out, who signed it, which link carried it, how many pieces it was cut into, the channel, and whether the counters confirmed it left the antenna. The app shows those receipts on the What went out screen when a bridge is connected.
There is no phone-side receipt, and Meshbell does not invent one. When you send, confirm or dispute a report from the app, it tells you the radio took your message and says plainly that it cannot confirm it went on the air. A flood packet has no addressee, so nothing acknowledges it. Claiming delivery would be the flattering answer, not the true one.
Plain text from other radios
MeshCore radios carry ordinary text messages between people who are not running Meshbell. A bridge can hear that traffic, and it keeps it strictly apart from the verified feed. It is marked unsigned, which is deliberately a different word from could not be verified: an unverified bulletin is a signed record whose key this device does not hold, while plain text carries no signature at all.
Channel messages carry no sender key, so a name typed inside the body is not an identity and Meshbell never treats it as one. This traffic is never stored, never appears in the verified feed, never counts toward corroborating anything, and can never become an alert. The bridge reports how much of it it has heard, and nothing more.
Radios that sign for themselves
A MeshCore radio holds its own signing key and the firmware will sign with it. A bridge operator can enroll a radio, after the radio proves it holds the key, and from then on that radio can originate a signed bulletin with no phone involved: a panic button by the door, or a beacon that reports a condition.
A device signs as a device. It is signal grade and never an approver, because a box is not a person and holding a radio is not the same as holding somebody's key. Anywhere the app shows who signed a record, a device-signed one says so.
A stolen radio is a stolen key. A phone key is protected by the phone. A radio's key is protected by the radio, which is a small object that lives outdoors and may have no PIN. Enroll a radio only if you are willing to stand behind whatever it signs, and revoke it the moment it goes missing. Revoking stops a bridge accepting anything new from it; it does not retract what was already passed along.
More than one bridge
Your phone among several bridges
A neighborhood may end up with more than one bridge. The app collects every bridge it can discover, prefers the one answering best, and moves to another when the current one goes quiet. An address you typed yourself always wins over one that was discovered.
Bridges linked to each other
Two bridges that can reach each other on a network share what they have heard, so the neighborhood next door joins without anyone re-entering anything. A linked bridge is untrusted by design: everything it hands over goes through the same signature check as anything else before it is stored, so a bad peer can waste a bridge's disk and can never put a false bulletin in front of a neighbor.
Neighbors link bridges the ordinary ways: a second Wi-Fi adapter in the Pi, a guest network on the house next door, or a pair of directional Wi-Fi units between two roofs.
Linking bridges adds coverage, not resilience. Wi-Fi runs on mains power, and mains power is the first thing an emergency takes. Linked bridges help on an ordinary day, and on the very common day when the internet is out but the lights are on. They are not a way to reach further in a blackout. Reaching further with no power and no network is what the radio is for.
Reaching Meshtastic radios
Meshtastic is the other widely used LoRa mesh, and the two firmwares cannot hear each other over the air even on the same frequency, because the packet formats differ. A bridge can carry Meshbell bulletins onto a Meshtastic radio anyway, over a link written from the published message format with none of the Meshtastic project's code, which is why nothing from it is in the app.
It runs on the bridge only and it is off until an operator turns it on. People running the stock Meshtastic app see nothing: Meshbell rides a private channel number their app does not display. It is a way to reach a neighbor who already carries a Meshtastic radio, not a way to talk to the Meshtastic network at large.
Architecture
The domain logic is pure and portable. It builds and tests on a Raspberry Pi with no Apple hardware in the loop, so the parts that decide whether a message is trustworthy are verified continuously rather than only at release. The iOS app is a thin renderer over it.
- Canonical JSON, sorted keys and pinned ASCII, gives every device a byte identical pre-image, so a content address computed on a phone matches one computed on a node.
- Ed25519 signing and verification, one implementation across Apple and Linux.
- An append-only, insert-only log. Records are never updated or deleted, so history including corrections is preserved and can be re-checked.
- A cross-language conformance gate: the same frames are decoded by the app and by an independent Python node and asserted byte for byte, so the two cannot silently drift.
Honest limits
Stated plainly, because an emergency tool that oversells itself is dangerous.
- Meshbell is an early beta. It has carried a signed bulletin across real LoRa radios and verified it on a phone. It is not a replacement for official emergency services, so keep the alerts you already have turned on.
- The bench record is a single hop. Multi-hop relay is designed and implemented, not yet measured in the field, and no distance figure on this site is bench measured.
- Records too large for one frame are split, and multi-fragment reassembly has not yet been observed completing on the phone at the bench. An incomplete group stays a preview and says so.
- Signing keys live in the iOS keychain, on this device only. They are extractable by anything that can already read the app's keychain. There is no hardware enclave protection here and we will not claim one.
- Records are signed, not encrypted. Meshbell is not private messaging. Assume anything on the air can be read by anyone in range.
- Key distribution, how your device learns which public key belongs to whom, is active work. A signature proves key holding. Binding a key to a real person is a separate and deliberate problem, solved today by meeting in person.
- Background reception is real but not a guarantee. iOS may stop the app, and a companion radio serves one phone at a time, so a radio another phone is holding is not available to yours.
- The coordination layer is unverified by design and separate from the signed alert rail. It is useful for logistics, never a source of official instruction, and it is local network only, so it reaches whoever is on the bridge rather than the wider mesh.
- Meshbell and Meshtastic radios still cannot hear each other over the air, because the packet formats differ. A bridge can carry bulletins onto a Meshtastic radio over a separate link, and that link is off unless an operator turns it on.