The BLE mesh is the offline backbone of Bitchat. Every nearby device that runs the app automatically joins the mesh — no infrastructure, no pairing, and no accounts required. Packets relay through intermediate peers so that a message originating on one side of a crowd can reach someone on the other side without any single device bridging them directly. The mesh degrades gracefully under partition and reconnects silently when proximity is restored.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/permissionlesstech/bitchat/llms.txt
Use this file to discover all available pages before exploring further.
Physical Bluetooth-capable hardware is required to participate in the mesh. iOS Simulator and macOS Simulator instances cannot advertise, scan, or connect over BLE, and will not appear as peers to real devices.
Topology
Every Bitchat device runs as a GATT central and a GATT peripheral simultaneously. As a peripheral it advertises a well-known service UUID so peers can discover it; as a central it scans for that same UUID and initiates connections to discovered peripherals. This dual role means the local connection graph is undirected: any two devices in radio range can connect, and the mesh emerges from the union of all those pairwise links without any coordinator. Connection scheduling is RSSI-gated — the stack prefers peers with stronger signal when deciding which connections to maintain and which to drop — and scanning runs on a duty cycle to bound battery drain. The result is a dense-but-bounded local neighbourhood rather than a star topology anchored on a single hub.Flood Control
Relaying in Bitchat is a deterministic controlled flood tuned by the local connection degree of each node. The goal is to reach every reachable peer while limiting redundant transmissions.Time-to-Live
Packets originate with TTL 7. Each relay decrements the TTL by one; a packet with TTL 0 is not forwarded. Relays clamp the TTL to match observed graph density:| Condition | TTL behaviour |
|---|---|
| Dense graph (≥ 6 links) | Broadcast TTL capped at 5 |
| Thin chain (≤ 2 links) | Relayed at full incoming depth |
| Directed traffic | Relayed at TTL − 1, no capping |
Deduplication
Each node maintains an LRU seen-set (1 000 entries, 5-minute expiry) keyed by the combination of sender ID, timestamp, packet type, and a payload digest. When a duplicate arrives, the node drops it. If a relay has already scheduled a retransmission and a duplicate arrives before the timer fires, the scheduled relay is cancelled — duplicate suppression wins.Relay Jitter
Rather than forwarding immediately, relays wait a random 10–220 ms before retransmitting. The jitter window is wider when the local graph is dense, giving duplicate suppression more time to cancel the relay before it fires. This reduces airtime waste in crowded environments where multiple relays may have received the same packet.Fanout Subsetting
Broadcast messages (public chat, group messages) are re-sent to a deterministic, message-ID-seeded subset of outbound links — approximately log₂ of the local degree — rather than every connected peer. The seed is derived from the message ID so every relay makes the same subset decision independently; this is not random sampling, and it is not a probabilistic guarantee. Packets that must reach everyone use full fanout:- Announce packets
- Fragment packets
- Gossip sync (
requestSync) packets
Directed Traffic
Noise handshake packets, private (Noise-encrypted) messages, and courier envelopes are directed traffic. They relay deterministically with TTL − 1 and a tight jitter window, and are never subject to fanout subsetting. Every connected peer that is not the ingress link receives the packet, because the destination cannot be inferred without decrypting it.Routing
Topology Discovery
Every announce packet carries up to 10 direct-neighbor IDs — the 8-byte peer IDs of the announcing device’s currently connected peers. Receiving nodes update a local shallow topology map with this information. Edges expire after 60 seconds of silence; an edge is only used for route calculation when both endpoints have announced each other (bidirectional confirmation prevents stale or spoofed adjacency claims).Source Routing (V2)
When a bidirectionally-confirmed path exists to a destination, the originating device can attach an explicit source route to the packet (see Packet Format for the wire layout). Source routing is only valid in version 2 packets and is only originated when:- The packet is authored locally (relays never rewrite another peer’s packet)
- The packet is directed to a single recipient
- The TTL is greater than 1
- The recipient is not directly connected
- A complete v2 path of at most 4 intermediate hops exists in the confirmed topology graph
Flooding Fallback
Whenever a source route cannot be constructed — because the topology map is incomplete, a hop is unreachable, or the packet is destined for a broadcast address — delivery falls back to the controlled flood described above. V1 peers always receive flooded packets; source-routed packets are never sent to nodes that have not been observed speaking v2.Presence and Discovery
Bitchat uses signed announce packets to propagate peer presence across the mesh.Announce Cadence
| State | Announce interval |
|---|---|
| Isolated (no connections) | Every 4 seconds |
| Connected to at least one peer | Jittered 15–30 seconds |
Reachability Window
A verified announce retains a peer as reachable for 60 seconds after the last contact. If no fresh announce arrives in that window, the peer is considered gone and its topology edges expire.Announce Payload
Each announce packet carries the following fields, encoded as TLVs:| Field | Description |
|---|---|
| Nickname | Human-readable display name (UTF-8, ≤ 255 bytes) |
| Noise static public key | 32-byte Curve25519 key used for Noise key agreement |
| Ed25519 signing public key | 32-byte key used to verify packet signatures |
| Capability flags | Advertised feature bits (optional; absent on older clients) |
| Direct neighbor IDs | Up to 10 × 8-byte peer IDs for topology discovery |
| Bridge geohash | Coarse geohash cell for Nostr bridge rendezvous (optional) |
Announce packets publish the Noise static key, signing key, and nickname in cleartext. A passive BLE listener in radio range can enumerate participants and link a device across time and location. Rotating on-air identity (epoch-rotating peer IDs, static-key disclosure inside the encrypted handshake) is planned future work.
Fragmentation
Packets that exceed the BLE link MTU are split into ~469-byte fragments before transmission.Fragment Format
Each fragment carries:- An 8-byte fragment ID that ties all fragments of the same packet together
- A 2-byte index and 2-byte total count header
- A 1-byte original packet type field (so the reassembled packet can be dispatched correctly)
- The fragment payload data
Independent Relay
Fragments are relayed independently through the mesh. They do not need to travel the same path or arrive in order. Each receiving node reassembles them as they arrive.Reassembly Limits
| Parameter | Value |
|---|---|
| Concurrent assemblies | 128 |
| Assembly timeout | 30 seconds |
| Maximum reassembled size | 1 MiB |