# BitChat BLE Mesh TTL: What Is the Default Hop Limit for Multi-Hop Relay?

> Discover the default TTL hop limit for BitChat's BLE mesh relay. Learn how the 7 hop limit balances reachability and efficiency.

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

---

**TL;DR:** BitChat's Bluetooth Low Energy mesh network enforces a default Time-To-Live (TTL) of **7 hops** for multi-hop relay, ensuring packets traverse a maximum of seven intermediary nodes before expiration to balance network reachability with power consumption and radio interference.

BitChat (permissionlesstech/bitchat) implements a decentralized BLE mesh protocol where understanding the **TTL for multi-hop relay** is critical for optimizing message propagation and debugging connectivity boundaries. The mesh uses a decrementing counter mechanism to prevent infinite broadcast loops while allowing sufficient reach across the network topology.

## Default TTL Configuration in BitChat

The origin TTL value is hardcoded as a transport-layer constant. In [`TransportConfig.swift`](https://github.com/permissionlesstech/bitchat/blob/main/TransportConfig.swift), the system defines the maximum relay depth:

```swift
public static let messageTTLDefault: UInt8 = 7   // default TTL for BLE messages

```

This `UInt8` value initializes every new `BitchatPacket` with a hop budget of 7. When a packet enters the mesh, this counter represents the remaining relay operations permitted before the message must be consumed rather than forwarded.

## Calculating Hop Count from TTL Values

BitChat derives the actual transmission distance by comparing the origin TTL against the received TTL. The [`MeshPingPayload.swift`](https://github.com/permissionlesstech/bitchat/blob/main/MeshPingPayload.swift) file contains the authoritative calculation logic:

```swift
public static func hopCount(originTTL: UInt8, receivedTTL: UInt8) -> Int? {
    guard originTTL >= receivedTTL else { return nil }
    return Int(originTTL - receivedTTL) + 1
}

```

This function returns `nil` for invalid packet states (where the received TTL exceeds the origin, indicating corruption or misconfiguration). For valid packets, it computes the traversed distance by measuring the decrement difference and adding 1 to account for the final receiving node that processes the packet without retransmitting it.

## Relay Logic and TTL Enforcement

Relay nodes implement strict validation before forwarding. The forwarding decision logic requires `packet.ttl > 0` as a prerequisite for retransmission. When a node relays a packet, it performs a wrapping decrement to safely handle unsigned integer underflow:

```swift
var forwarded = packet
forwarded.ttl &-= 1   // decrement TTL

```

If the TTL reaches 0 after decrementing, the packet is processed locally but dropped from the outbound queue. This mechanism ensures that `REQUEST_SYNC` packets and other messages with TTL 0 or 1 are never relayed, as confirmed by [`BLESourceRouteOriginationPolicyTests.swift`](https://github.com/permissionlesstech/bitchat/blob/main/BLESourceRouteOriginationPolicyTests.swift).

## Practical Implementation Examples

Creating a packet with the default hop limit requires referencing the transport configuration:

```swift
let packet = BitchatPacket(
    type: .announce,
    ttl: TransportConfig.messageTTLDefault,
    payload: myPayload
)

```

To audit network performance, calculate the actual hop distance upon receipt:

```swift
if let hopCount = MeshPingPayload.hopCount(
    originTTL: received.originTTL,
    receivedTTL: received.ttl
) {
    print("Packet travelled \(hopCount) hops")
}

```

For custom relay implementations, enforce the TTL constraint before forwarding:

```swift
func shouldRelay(packet: BitchatPacket) -> Bool {
    return packet.ttl > 0
}

```

## Key Source Files

The TTL implementation spans the following files in the permissionlesstech/bitchat repository:

- **[`TransportConfig.swift`](https://github.com/permissionlesstech/bitchat/blob/main/TransportConfig.swift)** ([`bitchat/Services/TransportConfig.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/TransportConfig.swift)): Defines `messageTTLDefault = 7` and other transport-layer constants governing BLE behavior.
- **[`MeshPingPayload.swift`](https://github.com/permissionlesstech/bitchat/blob/main/MeshPingPayload.swift)** ([`localPackages/BitFoundation/Sources/BitFoundation/MeshPingPayload.swift`](https://github.com/permissionlesstech/bitchat/blob/main/localPackages/BitFoundation/Sources/BitFoundation/MeshPingPayload.swift)): Implements the `hopCount(originTTL:receivedTTL:)` function used to derive transmission distance.
- **[`BLEServiceTests.swift`](https://github.com/permissionlesstech/bitchat/blob/main/BLEServiceTests.swift)** ([`bitchatTests/BLEServiceTests.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchatTests/BLEServiceTests.swift)): Unit tests verifying TTL initialization, decrementing, and boundary handling in the BLE stack.
- **[`BLEOutboundLinkPlannerTests.swift`](https://github.com/permissionlesstech/bitchat/blob/main/BLEOutboundLinkPlannerTests.swift)** ([`bitchatTests/Services/BLEOutboundLinkPlannerTests.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchatTests/Services/BLEOutboundLinkPlannerTests.swift)): Validates that relay candidates are selected only when `TTL > 0` and that the decrement operation occurs before transmission.
- **[`BLESourceRouteOriginationPolicyTests.swift`](https://github.com/permissionlesstech/bitchat/blob/main/BLESourceRouteOriginationPolicyTests.swift)** ([`bitchatTests/Services/BLESourceRouteOriginationPolicyTests.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchatTests/Services/BLESourceRouteOriginationPolicyTests.swift)): Demonstrates that packets with TTL values of 0 or 1 (such as synchronization requests) are correctly identified as non-relayable.

## Summary

- BitChat's BLE mesh uses a **default TTL of 7 hops** defined by `TransportConfig.messageTTLDefault` in [`TransportConfig.swift`](https://github.com/permissionlesstech/bitchat/blob/main/TransportConfig.swift).
- The `hopCount()` method in [`MeshPingPayload.swift`](https://github.com/permissionlesstech/bitchat/blob/main/MeshPingPayload.swift) calculates traversed distance by comparing the packet's origin TTL against its received TTL.
- Relay nodes use wrapping subtraction (`&-= 1`) to decrement TTL and cease forwarding when the counter reaches 0, preventing infinite propagation.
- This 7-hop limit optimally balances mesh network coverage against battery consumption and wireless congestion constraints inherent to BLE communications.

## Frequently Asked Questions

### What is the maximum number of hops in BitChat's BLE mesh?

The maximum transmission distance is strictly capped at **7 hops**, as defined by the `messageTTLDefault` constant in [`TransportConfig.swift`](https://github.com/permissionlesstech/bitchat/blob/main/TransportConfig.swift). Once a packet has traversed seven relay nodes, the TTL reaches 0 and the packet is discarded rather than forwarded, creating a hard boundary on network diameter.

### How does BitChat calculate the number of hops a packet traveled?

BitChat uses the `MeshPingPayload.hopCount(originTTL:receivedTTL:)` function to compute distance. This method subtracts the received TTL from the origin TTL and adds 1 to account for the final receiving node. If the received TTL exceeds the origin TTL (indicating protocol violation), the function returns `nil`.

### What happens when a packet's TTL reaches zero?

When a packet's TTL counter reaches 0, the current node processes the payload locally but **does not forward** the packet to other mesh peers. This termination condition prevents broadcast storms and infinite loops, as verified by the test coverage in [`BLESourceRouteOriginationPolicyTests.swift`](https://github.com/permissionlesstech/bitchat/blob/main/BLESourceRouteOriginationPolicyTests.swift).

### Can the default TTL be modified in BitChat?

While `TransportConfig.messageTTLDefault` is defined as a static constant, developers can override the TTL value during `BitchatPacket` initialization by passing a custom `UInt8` value. However, exceeding the default 7 hops is discouraged as it violates the protocol's power management assumptions and may cause increased radio interference in dense mesh deployments.