Skip to main content

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.

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.
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:
ConditionTTL behaviour
Dense graph (≥ 6 links)Broadcast TTL capped at 5
Thin chain (≤ 2 links)Relayed at full incoming depth
Directed trafficRelayed 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
The ingress link (the peer a packet arrived from) is always excluded from relay — this is split-horizon and prevents a packet from being immediately echoed back.

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
When source routing fails (no inbound packet from the recipient within 10 seconds of a routed send), that recipient falls back to flooding for 60 seconds before routing is attempted again.

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

StateAnnounce interval
Isolated (no connections)Every 4 seconds
Connected to at least one peerJittered 15–30 seconds
The rapid isolated cadence helps a newly-powered device discover peers quickly. The backed-off connected cadence reduces airtime consumption in an active mesh.

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:
FieldDescription
NicknameHuman-readable display name (UTF-8, ≤ 255 bytes)
Noise static public key32-byte Curve25519 key used for Noise key agreement
Ed25519 signing public key32-byte key used to verify packet signatures
Capability flagsAdvertised feature bits (optional; absent on older clients)
Direct neighbor IDsUp to 10 × 8-byte peer IDs for topology discovery
Bridge geohashCoarse 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

ParameterValue
Concurrent assemblies128
Assembly timeout30 seconds
Maximum reassembled size1 MiB
If any limit is exceeded, the partial assembly is discarded. The sender is responsible for retransmission if the higher-level protocol requires reliability.

Source-Routed Fragments

When the parent packet carries a source route (v2 only), all fragments inherit the same route and version. Without this rule, fragments would fall back to flooding, negating the bandwidth benefit of source routing for large transfers such as file transfers.

Build docs developers (and LLMs) love