# How BitChat Implements Relay Flood Control Using Degree-Based TTL Clamping

> BitChat prevents Bluetooth mesh flooding with degree-based TTL clamping. Discover how it limits packet TTL for dense network regions and high-degree nodes.

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

---

**BitChat prevents Bluetooth mesh flooding by dynamically clamping packet TTL values based on node graph density, applying stricter limits to high-degree nodes in dense network regions.**

BitChat is an open-source messaging protocol designed for permissionless Bluetooth mesh networks. To protect devices from battery drain and bandwidth exhaustion caused by uncontrolled packet propagation, the codebase employs **relay flood control using degree-based TTL clamping** that adapts to local network topology. The implementation centers on the `RelayController.decide` method in [`bitchat/Services/RelayController.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/RelayController.swift), which evaluates each packet against the forwarding node's graph degree before determining relay parameters.

## The Core Logic: RelayController.decide

The flood control mechanism lives in `RelayController.decide` (lines 10-95), a Swift function that receives the current packet **TTL** (time-to-live) and the node's current **graph degree** (number of neighboring links). This method separates network regions into dense and sparse categories using a configurable threshold, then applies protocol-specific clamps.

### Detecting Dense vs. Sparse Regions

The controller distinguishes between dense and sparse mesh topologies using `highDegreeThreshold`, imported from `TransportConfig.bleHighDegreeThreshold`. 

- **Dense graphs**: Nodes with `degree >= highDegreeThreshold`
- **Sparse graphs**: Nodes with `degree < highDegreeThreshold`
- **Very sparse**: Specifically identified when `degree <= 2`

This classification occurs at the start of the decision process and determines which TTL caps apply to subsequent traffic.

### Fragment Traffic Handling

For **media fragments** and live-voice frames, the controller applies specialized caps to prevent high-bandwidth content from flooding the mesh. According to the implementation in [`RelayController.swift`](https://github.com/permissionlesstech/bitchat/blob/main/RelayController.swift) (lines 54-66):

- **Dense graphs**: Use `TransportConfig.bleFragmentRelayTtlCapDense` (hardcoded to 5)
- **Sparse graphs**: Use `TransportConfig.bleFragmentRelayTtlCap` (hardcoded to 7)

The algorithm clamps the incoming TTL against the appropriate cap, then decrements by one before relaying. This ensures fragments travel shorter distances in well-connected regions while maintaining reachability across sparse chains.

### Broadcast Traffic Clamping

After fragment handling, the controller evaluates **broadcast traffic** (announcements, board posts, and generic messages) using a `ttlLimit` closure (lines 74-83). The clamping rules follow this hierarchy:

1. **Dense nodes** (`degree >= highDegreeThreshold`): TTL limited to `max(2, min(ttlCap, 5))`
2. **Very sparse nodes** (`degree <= 2`): Original `ttlCap` preserved (no reduction)
3. **Moderate density**: TTL limited to 7 for announces/urgent posts, or 6 for normal traffic, never dropping below 2

This tiered approach prevents dense clusters from amplifying floods while preserving message propagation across long chain-like networks where each hop is costly.

## Configuration Constants in TransportConfig

All thresholds and caps are centralized in [`bitchat/Services/TransportConfig.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/TransportConfig.swift) (lines 6-12 and 27-31). Key constants include:

- `bleHighDegreeThreshold`: Defines the boundary between dense and sparse graphs
- `bleFragmentRelayTtlCapDense`: 5-hop limit for fragments in dense areas
- `bleFragmentRelayTtlCap`: 7-hop limit for fragments in sparse areas
- Base TTL caps for broadcast traffic (6 or 7 depending on urgency)

Centralizing these values allows network operators to tune flood control behavior without modifying the core logic in `RelayController`.

## Why Graph Degree Matters for Flood Control

In **dense mesh regions**, a node with many neighbors can reach hundreds of devices within a few hops. Without clamping, a single broadcast could trigger exponential relay proliferation, exhausting device batteries and saturating the 2.4 GHz spectrum. By capping TTL to 5 or lower for high-degree nodes, BitChat contains the flood radius before it engulfs the entire dense cluster.

Conversely, in **sparse topologies** where nodes form long chains or isolated islands, each relay represents a necessary bridge rather than a redundant amplification. The algorithm therefore preserves higher TTL values for low-degree nodes, ensuring messages can still traverse the maximum configurable distance to reach isolated recipients.

## Code Examples

The following Swift code demonstrates how `RelayController.decide` processes a normal broadcast packet in a dense network region:

```swift
// Example: deciding whether to relay a normal broadcast packet
let decision = RelayController.decide(
    ttl: 7,                         // packet arrived with full TTL
    senderIsSelf: false,
    recipientIsSelf: false,
    isEncrypted: false,
    isDirectedEncrypted: false,
    isFragment: false,
    isDirectedFragment: false,
    isHandshake: false,
    isAnnounce: true,               // an announce packet
    isRequestSync: false,
    isUrgentBoardPost: false,
    isVoiceFrame: false,
    degree: 8,                      // node sees 8 neighboring links
    highDegreeThreshold: TransportConfig.bleHighDegreeThreshold
)
// decision.shouldRelay == true
// decision.newTTL == 4   // dense-graph clamp (max 5) then -1 for the hop

```

For fragment traffic on sparse nodes, the controller permits longer propagation paths:

```swift
// Example: fragment traffic on a sparse node
let fragmentDecision = RelayController.decide(
    ttl: 7,
    senderIsSelf: false,
    recipientIsSelf: false,
    isEncrypted: false,
    isDirectedEncrypted: false,
    isFragment: true,
    isDirectedFragment: false,
    isHandshake: false,
    isAnnounce: false,
    isRequestSync: false,
    isUrgentBoardPost: false,
    isVoiceFrame: false,
    degree: 3,                           // sparse graph
    highDegreeThreshold: TransportConfig.bleHighDegreeThreshold
)
// fragmentDecision.newTTL == 6  // uses bleFragmentRelayTtlCap (7) then -1

```

## Summary

- **BitChat** implements flood control through `RelayController.decide` in [`bitchat/Services/RelayController.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/RelayController.swift), which clamps TTL based on node degree.
- **Dense graphs** (degree >= threshold) use stricter caps: 5-hop maximum for fragments and aggressive broadcast clamping.
- **Sparse graphs** preserve higher TTL values (up to 7 hops) to ensure reachability across chain-like topologies.
- **Configuration** is centralized in [`TransportConfig.swift`](https://github.com/permissionlesstech/bitchat/blob/main/TransportConfig.swift), enabling topology-specific tuning without logic changes.
- The mechanism balances **battery preservation** in dense clusters against **network reachability** in sparse regions.

## Frequently Asked Questions

### How does BitChat determine if a node is in a dense or sparse network region?

BitChat uses the `highDegreeThreshold` constant from [`TransportConfig.swift`](https://github.com/permissionlesstech/bitchat/blob/main/TransportConfig.swift) to classify nodes. If a node's current graph degree (number of visible neighbors) meets or exceeds this threshold, the `RelayController.decide` method treats it as a dense-graph node and applies stricter TTL caps. Nodes with degree values of 2 or below are treated as very sparse and retain their original TTL values.

### Why are fragment packets treated differently from broadcast messages?

Media fragments and voice frames consume significantly more bandwidth than text broadcasts. According to the source code in [`RelayController.swift`](https://github.com/permissionlesstech/bitchat/blob/main/RelayController.swift) lines 54-66, fragments utilize separate TTL constants—specifically `bleFragmentRelayTtlCapDense` (5 hops) versus `bleFragmentRelayTtlCap` (7 hops)—to prevent high-bandwidth content from flooding dense mesh clusters while still allowing transmission across sparse networks.

### What prevents TTL clamping from breaking message delivery in sparse networks?

The algorithm specifically checks for very sparse conditions (`degree <= 2`) and bypasses aggressive clamping for these nodes. As implemented in the `ttlLimit` closure (lines 74-83), sparse nodes either retain the full TTL cap or use higher limits (6-7 hops) compared to dense nodes (capped at 5). This ensures messages can traverse long chain-like topologies where each intermediate hop is necessary for end-to-end delivery.

### Where can I modify the degree threshold and TTL caps?

All tunable parameters reside in [`bitchat/Services/TransportConfig.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/TransportConfig.swift). The `bleHighDegreeThreshold` defines the boundary between dense and sparse classifications, while `bleFragmentRelayTtlCap` and `bleFragmentRelayTtlCapDense` control fragment propagation limits. Modifying these constants changes flood control behavior across the entire mesh without requiring changes to [`RelayController.swift`](https://github.com/permissionlesstech/bitchat/blob/main/RelayController.swift).