# How Multi-Hop Relays Extend BitChat's Bluetooth Range: A Technical Deep Dive

> Discover how BitChat's multi-hop relays build a Bluetooth mesh network, extending range beyond 10 meters with TTL forwarding and signed topology announcements. Learn the technical details.

- Repository: [permissionlesstech/bitchat](https://github.com/permissionlesstech/bitchat)
- Tags: deep-dive
- Published: 2026-08-08

---

**Multi-hop relays in BitChat create a Bluetooth Low Energy mesh network where messages hop from device to device, extending communication range far beyond the standard 10-meter Bluetooth limit through TTL-controlled forwarding, signed topology announcements, and deterministic relay policies.**

BitChat is an open-source, permissionless messaging protocol developed by permissionlesstech/bitchat that enables offline communication via Bluetooth Low Energy (BLE). While a single Bluetooth connection is typically limited to roughly 10 meters, the implementation of **multi-hop relays** allows the network to function as a mesh, forwarding encrypted messages through intermediate devices to reach peers that are several hops away. This article examines the specific Swift implementations and architectural decisions that enable this range extension.

## Bluetooth Mesh Architecture Overview

BitChat separates concerns between the radio hardware and the routing logic to make multi-hop relaying reliable and testable. According to [`docs/BLE-ARCHITECTURE-V3.md`](https://github.com/permissionlesstech/bitchat/blob/main/docs/BLE-ARCHITECTURE-V3.md) (lines 31-38), the architecture isolates all CoreBluetooth interaction within the **BLELinkLayer**, while a dedicated **mesh engine** running on a serial queue owns all protocol state and routing decisions.

This layered design means the same deterministic multi-hop routing algorithm runs both in simulation environments and on physical devices. The mesh engine maintains neighbor tables and topology maps, allowing the system to forward packets across multiple intermediate hops without requiring direct radio contact between the original sender and final recipient.

## Topology Discovery via Signed Announcements

Before messages can traverse multiple hops, devices must discover the network topology. Each node periodically broadcasts a **SignedAnnouncement** containing up to ten neighbor IDs, as documented in [`WHITEPAPER.md`](https://github.com/permissionlesstech/bitchat/blob/main/WHITEPAPER.md) (lines 71-73).

```swift
// Every device emits a signed announcement roughly every 4 seconds (isolated)
// or every 15-30 seconds when part of a connected mesh.
func broadcastAnnouncement() {
    let announcement = SignedAnnouncement(neighbors: knownNeighborIDs)
    bleService.send(announcement, ttl: maxTTL)
}

```

These announcements are flooded through the mesh, allowing every node to build a shallow view of the local graph. When a bidirectional path exists between sender and recipient, packets are **source-routed** along that specific path. If no direct path is known, the system falls back to controlled flooding, ensuring messages can still reach distant nodes via multi-hop relays even when the complete topology is unknown.

## The Relay Decision Engine

The core intelligence for multi-hop forwarding resides in [`bitchat/Services/RelayController.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/RelayController.swift) (lines 39-48). The `RelayController.decide()` method evaluates every inbound packet to determine whether it should be forwarded, how much TTL (Time To Live) to preserve, and what delay to insert for network desynchronization.

```swift
let decision = RelayController.decide(
    ttl: packet.ttl,
    senderIsSelf: senderID == localPeerID,
    recipientIsSelf: PeerID(hexData: packet.recipientID) == localPeerID,
    isEncrypted: packet.type == MessageType.noiseEncrypted.rawValue,
    isDirectedEncrypted: /* DM, courier, ping/pong, nostrCarrier */ true,
    isFragment: packet.type == MessageType.fragment.rawValue,
    isDirectedFragment: packet.type == MessageType.fragment.rawValue && packet.recipientID != nil,
    isHandshake: packet.type == MessageType.noiseHandshake.rawValue,
    isAnnounce: packet.type == MessageType.announce.rawValue,
    degree: currentDegree,
    highDegreeThreshold: 6
)

```

The method returns a **RelayDecision** struct containing `shouldRelay`, a decremented `newTTL`, and a jitter-derived `delayMs`. Directed traffic such as DMs, Noise handshakes, and courier envelopes receive "always-relay" treatment, guaranteeing they traverse several hops without being truncated by the TTL cap.

## Integration with the Receive Pipeline

When a packet arrives via BLE, [`bitchat/Services/BLE/BLEReceivePipeline.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/BLE/BLEReceivePipeline.swift) (lines 46-57) consults the relay controller to determine forwarding behavior. The pipeline calls `relayDecision()`, which encapsulates the logic for multi-hop extension.

```swift
let relayDecision = BLEReceivePipeline.relayDecision(
    for: packet,
    senderID: senderID,
    localPeerID: localPeerID,
    degree: meshDegree,
    highDegreeThreshold: 6
)

if relayDecision.shouldRelay {
    // Enqueue the packet to be sent after `relayDecision.delayMs`
    transport.scheduleRelay(packet,
                            ttl: relayDecision.newTTL,
                            after: relayDecision.delayMs)
}

```

If the decision indicates `shouldRelay`, the pipeline schedules the packet for transmission to the next hop with the updated TTL and a slight jitter delay. This process repeats at each intermediate device until the message reaches its destination or the TTL expires.

## Fragmentation for Multi-Hop Delivery

BLE's link layer MTU is approximately 469 bytes, which poses challenges for multi-hop transmission of larger payloads. As noted in [`WHITEPAPER.md`](https://github.com/permissionlesstech/bitchat/blob/main/WHITEPAPER.md) (lines 65-68), BitChat implements **fragmentation and reassembly** to allow large messages to travel many hops without exceeding the low-MTU limit.

Packets larger than the BLE MTU are split into fragments that are independently relayed through the mesh. Each fragment is treated as a distinct packet by the `RelayController`, receiving its own TTL and relay decision. Intermediate nodes reassemble fragments before forwarding when possible, ensuring that payload size does not constrain the physical distance a message can travel via multi-hop relays.

## Privacy and Security Across Hops

A critical aspect of the multi-hop design is that intermediate relays see only opaque ciphertext. Because end-to-end encryption is applied at the application layer, relay devices cannot read message contents. The transport layer re-encrypts packets for each hop's BLE connection, preserving privacy while still allowing the `BLELinkLayer` to route packets through untrusted intermediate devices, as referenced in [`bitchat/Services/BLE/BLEService.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/BLE/BLEService.swift) (lines 4987-5023).

## Summary

- **Mesh Architecture**: The separation of `BLELinkLayer` from the mesh engine enables testable, deterministic multi-hop routing that runs identically in simulation and production.
- **Topology Awareness**: Signed announcements containing neighbor IDs propagate across hops, enabling source-routed delivery when paths are known and controlled flooding when they are not.
- **Deterministic Relaying**: `RelayController.decide()` implements TTL-aware forwarding with jitter delays, ensuring messages traverse multiple hops without causing network congestion.
- **Fragmentation Support**: Large payloads are split into MTU-sized fragments that can be independently relayed and reassembled, preventing size limitations from restricting range extension.
- **Privacy Preservation**: End-to-end encryption ensures intermediate multi-hop relays cannot read message contents, only forward opaque packets.

## Frequently Asked Questions

### How many hops can a BitChat message traverse?

BitChat implements a TTL-based mechanism where `RelayController` decrements the TTL at each hop. While the exact maximum depends on the initial TTL value and network conditions, the combination of source routing for known paths and controlled flooding for discovery allows messages to reliably travel several hops. The jitter-based delays and TTL caps prevent infinite propagation while ensuring sufficient reach to extend well beyond the 10-meter Bluetooth limit.

### What is the difference between source routing and flooding in BitChat?

When a device has a complete topology map from received announcements, it uses **source routing**, encoding the specific multi-hop path into the packet header. If the destination is unknown, the system falls back to **flooding**, where `BLEReceivePipeline` broadcasts the packet to all neighbors except the sender. The `RelayController` applies different policies to each: source-routed packets follow a predetermined path, while flooded packets rely on TTL limits to control propagation scope.

### How does BitChat prevent infinite relay loops?

The **TTL (Time To Live)** field in every packet prevents infinite loops by decrementing at each hop. When `RelayController.decide()` processes a packet, it returns a `RelayDecision` with `newTTL` set to the current value minus one. If the TTL reaches zero, the packet is discarded rather than relayed. Additionally, the system tracks `senderIsSelf` to prevent nodes from re-broadcasting packets they originated, and `degree`-based thresholds help high-connectivity nodes throttle broadcast traffic.

### Are multi-hop relays in BitChat battery-intensive?

The relay system is designed for efficiency through **jitter-based desynchronization** and selective forwarding. Rather than immediately rebroadcasting every received packet, `RelayController` introduces a small, randomized delay (`delayMs`) that prevents synchronized flooding storms. Furthermore, only directed messages (DMs, handshakes) receive "always-relay" status with no TTL cap, while broadcast traffic is throttled, conserving battery life on devices acting as intermediate relays.