# How BitChat Implements BLE Mesh Routing, TTL, and Multi-Hop Communication

> Learn how BitChat implements BLE mesh routing with its source-routing protocol, TTL, and multi-hop communication for efficient, low-latency message delivery.

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

---

**BitChat uses a deterministic source-routing protocol where senders embed the complete hop path into each BLE packet, enforcing a maximum of 4 intermediate hops with a TTL counter that decrements at each relay to prevent infinite loops and bound network latency.**

BitChat is an open-source decentralized messaging application that routes messages over Bluetooth Low Energy (BLE) mesh networks. The `permissionlesstech/bitchat` repository implements a custom source-routing architecture to solve the challenges of multi-hop communication in resource-constrained mobile environments. This design places routing intelligence at the sender, allowing intermediate nodes to simply forward packets based on embedded path instructions while TTL mechanisms prevent infinite forwarding cycles.

## Core Routing Architecture

The BLE mesh routing stack consists of three coordinated components that handle path selection, failure recovery, and packet forwarding.

### BLESourceRouteOriginationPolicy

Located in [`bitchat/Services/BLE/BLESourceRouteOriginationPolicy.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/BLE/BLESourceRouteOriginationPolicy.swift), this component determines whether messages travel directly, via single hop, or through multi-hop source routes. It enforces the maximum hop limit by consulting `TransportConfig.bleSourceRouteMaxIntermediateHops` and initializes the TTL value based on path length.

### BLESourceRouteFailureCache

Implemented in [`bitchat/Services/BLE/BLESourceRouteFailureCache.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/BLE/BLESourceRouteFailureCache.swift), this cache monitors route health and suppresses failing paths. When a hop fails, it records the failure and avoids retrying the same route for `TransportConfig.bleSourceRouteSuppressionSeconds` (default 60 seconds), forcing route recalculation.

### BLEService

Found in [`bitchat/Services/BLE/BLEService.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/BLE/BLEService.swift), this service executes the actual packet encoding and forwarding. It maintains a per-session `sourceRouteFailures` cache and handles the per-hop TTL decrement and link ID extraction.

## Source-Routing Mechanics

BitChat employs source routing rather than distributed routing tables, making paths deterministic and debugging predictable.

### Route Construction

When originating a message, the `BLESourceRouteOriginationPolicy.route()` function evaluates the destination peer ID against known BLE links. For direct neighbors, it omits the route list. For distant peers, it constructs a hop list containing up to 4 intermediate `BLEIngressLinkID` values representing the chain of devices between sender and recipient.

### Packet Structure and TTL Encoding

Each `BitchatPacket` contains an 8-bit `ttl` field and an `isSourceRouted` boolean flag (the RSR bit). During packet signing, both TTL and RSR bits are temporarily cleared from the signature payload per `BLEAnnounceHandlingPolicy` to ensure cryptographic integrity, then re-inserted before transmission.

The binary layout follows this structure:

```

| Header | TTL | RSR Flag | Route Hops (link IDs) | Encrypted Payload |

```

The TTL value initializes to the hop count plus one (`route.hops.count + 1`), ensuring sufficient lifetime to reach the destination while bounding propagation.

## Multi-Hop Forwarding Logic

Intermediate nodes process source-routed packets through a standardized forwarding pipeline that enforces TTL constraints.

### Per-Hop Processing

When `BLEService.handleReceivedPacket` receives a packet, it performs the following validation:

1. **TTL Check**: If `packet.ttl == 0`, the packet drops immediately to prevent infinite loops.
2. **Route Extraction**: The service pops the next `BLEIngressLinkID` from the `sourceRoute` array.
3. **TTL Decrement**: The service decrements `packet.ttl` by one.
4. **Forward**: The packet transmits to the next link via BLE write operations.

This process repeats until TTL reaches zero at the final destination, at which point `MessageRouter` delivers the payload to the application layer.

### Deterministic Path Enforcement

Because the sender embeds the complete path, relays do not perform route lookups. This design minimizes processing overhead on battery-constrained mobile devices and eliminates route discovery broadcast storms common in traditional mesh protocols.

## Route Failure Handling

The mesh implements proactive failure detection to avoid black-holed packets.

### Suppression Windows

When a hop fails (timeout or link loss), `BLESourceRouteFailureCache` records the incident. The cache suppresses the failed route for `TransportConfig.bleSourceRouteSuppressionSeconds` (60 seconds), causing subsequent messages to trigger new route construction or fall back to flood-fill broadcast mode. Successful deliveries refresh a confirmation window of `TransportConfig.bleSourceRouteConfirmationWindowSeconds` (10 seconds).

### Fallback Strategies

If source routing fails due to topology changes, senders can revert to BLE broadcast flooding, trading efficiency for delivery guarantees until the policy constructs a new valid source route.

## Implementation Examples

The following Swift snippets demonstrate the core routing workflow.

Route origination and packet preparation:

```swift
// Determine optimal path
let route = BLESourceRouteOriginationPolicy.route(
    destination: destPeerID,
    maxHops: TransportConfig.bleSourceRouteMaxIntermediateHops
)

// Construct packet with TTL
var packet = BitchatPacket(type: .message)
packet.ttl = UInt8(route.hops.count + 1)
packet.isSourceRouted = true
packet.sourceRoute = route.hops
packet.sign(using: mySigningKey)

```

Intermediate node forwarding logic inside `BLEService`:

```swift
func handleReceivedPacket(_ packet: BitchatPacket) {
    // TTL enforcement prevents infinite loops
    guard packet.ttl > 0 else { return }
    
    // Extract next hop and decrement TTL
    let nextLink = packet.sourceRoute.removeFirst()
    packet.ttl -= 1
    
    // Forward to next BLE link
    send(to: nextLink, packet: packet)
}

```

Failure cache interaction:

```swift
// Inside BLESourceRouteFailureCache
if let failure = cache.recordFailure(for: route, at: now) {
    if failure.isSuppressed {
        // Trigger route reconstruction
        rebuildRoute()
    }
}

```

## Configuration Constants

The routing behavior is governed by compile-time configuration values:

- **`TransportConfig.bleSourceRouteMaxIntermediateHops`**: Caps routes at 4 intermediate hops to maintain latency under 30-60ms per hop on iOS devices.
- **`TransportConfig.bleSourceRouteSuppressionSeconds`**: 60-second window during which failed routes are avoided.
- **`TransportConfig.bleSourceRouteConfirmationWindowSeconds`**: 10-second window that confirms route viability after successful delivery.

## Summary

- **Source routing** embeds complete hop paths in packet headers, eliminating distributed routing tables on intermediate nodes.
- **TTL enforcement** bounds packet lifetime to the hop count plus one, preventing infinite loops in cyclic mesh topologies.
- **Four-hop maximum** keeps end-to-end latency predictable and manageable on mobile BLE stacks.
- **Failure suppression** caches route failures for 60 seconds, automatically triggering path recalculation without manual intervention.
- **Deterministic forwarding** allows intermediate devices to operate as simple relays, reducing CPU and battery consumption.

## Frequently Asked Questions

### How does BitChat prevent infinite packet loops in BLE mesh networks?

BitChat prevents infinite loops through a TTL (time-to-live) counter embedded in every `BitchatPacket`. The sender initializes TTL to the number of intermediate hops plus one. Each relay node decrements the counter before forwarding, and any node receiving a packet with TTL equal to zero drops it immediately. This hard limit ensures packets expire after traversing the maximum configured path length.

### Why does BitChat use source routing instead of traditional mesh routing tables?

Source routing places path calculation entirely on the sender, allowing intermediate nodes to forward packets by simply reading the next hop from a pre-encoded list rather than consulting distributed routing tables. This design reduces memory footprint and CPU usage on battery-constrained mobile devices, eliminates route convergence delays, and makes network behavior deterministic and easier to debug.

### What happens when an intermediate node in a source route goes offline?

When a node fails to forward a packet, `BLESourceRouteFailureCache` records the failure and suppresses that specific route for 60 seconds. The sender detects the failure and either reconstructs a new source route using available links or falls back to broadcast flooding. This suppression mechanism prevents wasteful retries over broken paths while encouraging rapid adaptation to topology changes.

### How is the maximum hop count of 4 determined for BitChat mesh networks?

The 4-hop limit in `TransportConfig.bleSourceRouteMaxIntermediateHops` represents a trade-off between network reach and latency on iOS and Android BLE stacks. Each hop introduces approximately 30-60ms of processing and connection latency; limiting paths to 4 hops keeps end-to-end delivery under 250ms while maintaining reliable packet delivery across typical mobile device densities.