The problem
Every messaging app assumes a path to a server. Take the server away, in a blackout, a stadium, a village between towers, an aircraft cabin, and the app goes quiet even though the person you want is twenty metres away. The phones can see each other. The software cannot.
AirLink is built for exactly that gap. It carries text and voice between nearby devices using the radios they already have, with no internet, no SIM, no phone number, and no account. This report explains how, and is careful to say what has and has not been measured.
Two radios, one mesh
AirLink runs two transports at once and treats them as a single link layer.
Bluetooth Low Energy. Every device is both a central and a peripheral at the same time, so any two AirLink devices can find each other without deciding who is the "server". Discovery advertises only a service identifier and no payload, which keeps the advertisement small and reveals nothing about the user. Data moves over a pair of GATT characteristics, an inbox for writes and an outbox for notifications. The usable packet size is negotiated per link and re-read on every send rather than cached, taking the smaller of what the two sides allow, with a conservative fallback when the radio does not say.
Peer-to-peer Wi-Fi. On the same device, a Bonjour service advertises over TCP with Apple's peer-to-peer mode enabled, so two devices form a direct Wi-Fi link with no router or hotspot. When both sides can see each other, the one with the lower identifier dials, which avoids the two-connections-at-once race without a negotiation round. Keepalives are short so a walked-away peer is noticed in seconds.
Both transports feed one composite link. A message goes out over whichever links exist, and the mesh above does not know or care which radio carried it.
The packet
Every packet on the wire has a 41-byte header, big-endian:
magic (2) version (1) kind (1) ttl (1) id (16) recipient (8) timestamp (8) length (4) payloadThe magic bytes let a receiver resynchronise on a stream after a dropped chunk. The 16-byte id is random and is the only thing the mesh needs to recognise a packet it has already seen. The recipient is an 8-byte fingerprint, with all-ones meaning everyone. Payloads are capped at 8 KiB. There are four kinds: direct message, delivery receipt, hello, and voice.
Routing by flooding
There is no route discovery. A device that receives a packet it has not seen before, and that is not addressed to it, forwards it to every other link, after decrementing the hop count. A seen-table of recent ids, capped in size and age, stops loops. Packets older than six hours are dropped, with a fifteen-minute allowance for clock skew.
The starting hop count adapts to how crowded the room is: a device with fewer than three neighbours starts at eight hops, a device with more than five starts at five. Sparse rooms need reach; dense rooms need restraint.
Voice packets are the exception. They start with a hop count of one, are never relayed, and are never queued. A voice frame that is late is worthless, so it goes to the link the recipient was last seen on, or to every link if that is unknown, and then it is forgotten.
Store and forward
A message for someone who is not in range right now waits in the sender's outbox while the app is running. Each queued packet is offered to each peer once, and the whole outbox is flushed to every new peer as it connects, so a message can ride to its recipient on a third person's phone. Receivers drop anything older than six hours, so the outbox gives up at the same age, and a message that was never delivered is shown as failed rather than left sending. Delivery is only counted when a sealed receipt for the original id makes its way back.
Identity
Each device generates two long-term keys on first launch, an Ed25519 key for signatures and an X25519 key for agreement. They are written once with owner-only permissions and are never silently overwritten, even if the file becomes unreadable. A device's fingerprint is the first 8 bytes of the SHA-256 of both public keys.
To let people share who they are without reading hexadecimal aloud, forty bits of the fingerprint become a call ID in Crockford base-32, printed as two groups like A3F9-K2P1. The alphabet drops the characters that are easy to confuse, entry is normalised so a mistyped O or l still resolves, and three memorable patterns are reserved and never minted.
Sealing a message
Text is encrypted with HPKE, the IETF's hybrid public-key encryption standard, using X25519, SHA-256, and ChaCha20-Poly1305. The sender's own card and an Ed25519 signature over the message travel inside the ciphertext, so an eavesdropper sees a sealed box addressed to a fingerprint and nothing about who sent it. The message id is bound as associated data, which stops a relay from re-labelling a packet. Plaintext is padded to a bucket of 256 bytes up to 8 KiB before sealing, so message lengths leak only coarsely.
The store on disk is SQLite, and each message body and contact card is sealed again with a ChaCha20-Poly1305 key held in the Keychain. Tests confirm that a tampered row is refused, that ciphertext is what actually lands on disk, and that forgetting a contact erases everything about them.
Calls
A call starts with an invite carried as an ordinary sealed message over the mesh. Instead of a single ring that can be missed, the invite is a beacon: it repeats roughly every two seconds for a full minute, so a phone that was briefly out of range or asleep still rings when it comes back. A ring that stops hearing the beacon stops itself, and a stale invite is declined automatically.
Each call derives fresh keys. The two sides exchange ephemeral X25519 keys and run HKDF-SHA256 to produce one 32-byte key per direction, and each voice frame is sealed with ChaCha20-Poly1305 using a 4-byte counter as both nonce and associated data. Twenty bytes of overhead per frame.
Voice is Opus at 24 kHz mono, 20 ms frames, 24 kbit/s. Frames are sent down two roads at once: the mesh, always, and a direct UDP link discovered over Bonjour. The moment a frame arrives over UDP the direct road is trusted for the next 2.5 seconds, and duplicates arriving on both roads are dropped by a sliding replay window. Echo is handled first by the system's own voice processing, which ties the microphone and speaker together. Where a Mac refuses it, AirLink falls back to its own adaptive echo canceller, a normalised-LMS filter with a 4096-tap tail, about 170 ms at 24 kHz, which switches itself off if it is not helping. On iPhone the call shows up in the native call screen without being written to the phone's call history.
Finding the other device
AirLink began as a way to find a nearby Bluetooth device by walking towards it, and that remains part of the product. Signal strength is smoothed by a one-dimensional Kalman filter whose variance is reported on screen as an honest plus-or-minus. Distance uses a log-distance path-loss model anchored at −59 dBm at one metre, computed across both an open-space and a cluttered-room exponent, and shown as a range rather than a false-precision number. A least-squares slope over the recent window tells you whether you are getting warmer. When a device accepts connections, AirLink polls its radio three times a second instead of waiting for advertisements. Rotating Bluetooth addresses are stitched back into one track only under strict conditions: a 15-second handover window, an identical advertisement fingerprint, at most 8 dB of discontinuity, and exactly one candidate.
What is measured, and what is not
- The codec bound is tested, not assumed: an encoded 20 ms frame is under 200 bytes and a full second of speech is under 8 kB/s.
- The mesh, the outbox, the sealed store, identity persistence, call session keys, echo cancellation, and ranging are covered by 199 automated tests. Multi-hop relay is proven in tests over an in-memory transport.
- One measured session, on 5 September 2026, with a MacBook Pro, an iPhone 12 Pro Max and an iPad Air in one room: all 1,000 probe messages arrived and were acknowledged, each exactly once. One-hop latency had a median of 34 ms and a 90th percentile of 129 ms; two hops had a median of 102 ms. Eleven of fourteen calls connected.
- All three devices shared one Wi-Fi network, so Wi-Fi carried almost everything: a Bluetooth link was up for about five per cent of the session. Bluetooth-only operation, range, battery cost and mouth-to-ear voice latency have not been measured yet. We will publish those numbers when we have them rather than estimate them here.
Limitations we accept for now
Text messages use long-term keys, so there is no forward secrecy for text; that is a deliberate trade for a protocol that works between devices that may never be online together. Verification is by people comparing fingerprints. There is no jitter buffer on the voice receive path yet. And if the Keychain refuses to hand over the storage key, the store falls back to plaintext rather than losing the user's messages, a choice we intend to make visible in the interface.
Status
AirLink runs on macOS 14 and iOS 18 today, with a command-line front end sharing the same engine. The long-term plan is for it to become part of Kalon itself, after Air Kalon.