# BitChat Relay Controller Flood Control Policy: Mesh Network Traffic Management

> Master BitChat relay controller flood control. Learn how BitChat filters packets by type, TTL, and node degree to manage mesh network traffic and prioritize session data.

- Repository: [permissionlesstech/bitchat](https://github.com/permissionlesstech/bitchat)
- Tags: performance
- Published: 2026-07-31

---

**The BitChat relay controller implements a topology-aware flood control policy that filters packets based on type, TTL, and node degree, selectively relaying session-critical traffic while applying degree-based TTL caps to broadcasts and fragments.**

BitChat's mesh networking layer uses the `RelayController` class to prevent broadcast storms in dense peer-to-peer topologies. According to the permissionlesstech/bitchat source code, the controller analyzes every packet's characteristics alongside local network topology metrics to determine whether to forward, drop, or modify messages before transmission.

## How the RelayController Makes Forwarding Decisions

The `decide` method in [`RelayController.swift`](https://github.com/permissionlesstech/bitchat/blob/main/RelayController.swift) (lines 10-95) evaluates multiple packet properties to determine forwarding behavior. This centralized logic ensures consistent flood control across the mesh network.

### Dropping Link-Local and Expired Traffic

The controller immediately suppresses certain packet categories to prevent unnecessary network utilization. According to lines 30-37 of the source:

- **REQUEST_SYNC packets**: These link-local synchronization requests are never relayed (`shouldRelay = false`), as they are intended strictly for direct neighbors (lines 30-31).
- **Expired or local packets**: Any packet with TTL ≤ 1, or those originating from or destined for the local node, are dropped outright (lines 35-37).

### Prioritizing Session-Critical Traffic

Session maintenance and encrypted communications receive preferential treatment. As implemented in lines 40-48:

**Handshakes, directed encrypted packets, and directed fragments** are always relayed with a deterministic TTL decrement. The controller adds modest jitter to these critical flows—10-35 ms for handshake messages and 20-60 ms for other directed encrypted traffic—to prevent synchronization while ensuring timely delivery.

## Topology-Aware TTL Management

BitChat's flood control adapts to network density through degree-aware TTL (Time To Live) limitations. The controller examines the local node's degree (number of connected peers) to determine appropriate propagation bounds.

### Degree-Based Caps for Voice and Fragments

Live voice traffic and fragmented packets use specialized TTL caps that depend on neighborhood density. Lines 50-66 implement the following logic:

- **Dense graphs** (degree ≥ highDegreeThreshold): Fragments clamp to `TransportConfig.bleFragmentRelayTtlCapDense`.
- **Sparse graphs**: Use the regular fragment TTL cap.
- **Relay condition**: If the capped TTL ≤ 1, the packet is not relayed.

After determining eligibility, the controller applies a random delay within `TransportConfig.bleFragmentRelayMinDelayMs` to `TransportConfig.bleFragmentRelayMaxDelayMs` to prevent transmission collisions.

### Broadcast Traffic Clamping Rules

Public messages, node announcements, and urgent board posts follow a sophisticated TTL-clamping policy (lines 70-84):

- **Dense neighborhoods**: Maximum TTL 5 (minimum 2).
- **Thin chains** (degree ≤ 2): Original TTL preserved.
- **Standard conditions**: Preferred TTL 6 (or 7 for announces/urgent posts).

The final TTL is always decremented by 1 before forwarding to ensure propagation limits are enforced.

## Jitter Scheduling and Collision Avoidance

After establishing the appropriate TTL, the `RelayController` adds randomized transmission delays to reduce duplicate-suppression races. Lines 86-94 implement a tiered delay system based on node degree:

- **Degree 0-2**: 10-40 ms delay
- **Degree 3-5**: 60-150 ms delay
- **Degree 6-9**: 80-180 ms delay
- **Degree ≥ 10**: 100-220 ms delay

This widening delay range in dense topologies prevents simultaneous retransmissions that could overwhelm the mesh.

## Code Examples: Flood Control in Action

The following Swift examples demonstrate how `RelayController.decide()` evaluates different packet scenarios:

### Relaying a Public Message

```swift
let decision = RelayController.decide(
    ttl: 7,
    senderIsSelf: false,
    recipientIsSelf: false,
    isEncrypted: false,
    isDirectedEncrypted: false,
    isFragment: false,
    isDirectedFragment: false,
    isHandshake: false,
    isAnnounce: false,
    isRequestSync: false,
    isUrgentBoardPost: false,
    isVoiceFrame: false,
    degree: 4,                // moderate-degree node
    highDegreeThreshold: 6   // dense-graph threshold
)
// → shouldRelay = true, newTTL = 5, delayMs ≈ 120 ms

```

This example shows a standard public message from a moderate-degree node receiving a clamped TTL of 5 and approximately 120ms of jitter.

### Handling Voice Fragments in Dense Networks

```swift
let decision = 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: true,
    degree: 8,                // dense graph (≥ highDegreeThreshold)
    highDegreeThreshold: 6
)
// → uses bleFragmentRelayTtlCapDense, newTTL = 3, delayMs within fragment range

```

Dense network conditions trigger the fragment TTL cap, reducing the voice frame's propagation hop count to prevent flooding while maintaining audio stream continuity.

### Suppressing Link-Local Synchronization

```swift
let decision = RelayController.decide(
    ttl: 5,
    senderIsSelf: false,
    recipientIsSelf: false,
    isEncrypted: false,
    isDirectedEncrypted: false,
    isFragment: false,
    isDirectedFragment: false,
    isHandshake: false,
    isAnnounce: false,
    isRequestSync: true,          // link-local request
    isUrgentBoardPost: false,
    isVoiceFrame: false,
    degree: 3,
    highDegreeThreshold: 6
)
// → shouldRelay = false

```

Despite having sufficient TTL, REQUEST_SYNC packets are explicitly blocked from relaying to maintain link-local boundaries.

## Related Components and Configuration

The flood control policy relies on several supporting files in the BitChat repository:

- **[`TransportConfig.swift`](https://github.com/permissionlesstech/bitchat/blob/main/TransportConfig.swift)**: Defines TTL defaults and fragment relay caps (`bleFragmentRelayTtlCapDense`, `bleFragmentRelayMinDelayMs`, `bleFragmentRelayMaxDelayMs`).
- **[`BLEService.swift`](https://github.com/permissionlesstech/bitchat/blob/main/BLEService.swift)**: Invokes `RelayController.decide` when forwarding Bluetooth Low Energy packets.
- **[`BLERouteForwardingPolicy.swift`](https://github.com/permissionlesstech/bitchat/blob/main/BLERouteForwardingPolicy.swift)**: Applies the controller's decisions to actual routing paths.
- **[`MessageRateLimiter.swift`](https://github.com/permissionlesstech/bitchat/blob/main/MessageRateLimiter.swift)**: Works alongside the flood policy to limit per-content traffic.

## Summary

The BitChat relay controller flood control policy balances mesh network reliability with bandwidth conservation through:

- **Selective dropping** of REQUEST_SYNC and expired TTL packets at lines 30-37 of [`RelayController.swift`](https://github.com/permissionlesstech/bitchat/blob/main/RelayController.swift).
- **Priority relaying** of session-critical traffic with minimal deterministic jitter.
- **Degree-aware TTL clamping** that reduces broadcast propagation in dense networks while preserving reach in sparse topologies.
- **Tiered jitter scheduling** that increases delays proportionally to node degree, preventing transmission collisions.

## Frequently Asked Questions

### What happens to packets with TTL less than or equal to 1?

Packets with TTL ≤ 1 are dropped immediately by the `RelayController` and never relayed. According to lines 35-37 of [`RelayController.swift`](https://github.com/permissionlesstech/bitchat/blob/main/RelayController.swift), this suppression applies to prevent "time exceeded" packets from circulating in the mesh, effectively defining the boundary of flood control.

### How does BitChat prevent broadcast storms in dense mesh networks?

BitChat prevents broadcast storms through degree-based TTL clamping. When a node's degree (number of connected peers) exceeds the `highDegreeThreshold`, the controller clamps broadcast TTL to maximum 5 and fragment TTL to `bleFragmentRelayTtlCapDense`. Additionally, jitter delays increase from 10-40ms to 100-220ms as degree grows, reducing simultaneous retransmission probability.

### Why are REQUEST_SYNC packets never relayed?

REQUEST_SYNC packets are treated as link-local control messages intended only for immediate neighbors. As implemented at lines 30-31 of [`RelayController.swift`](https://github.com/permissionlesstech/bitchat/blob/main/RelayController.swift), these synchronization requests return `shouldRelay = false` regardless of TTL or network conditions, ensuring that topology discovery remains localized and doesn't propagate across the entire mesh.

### What is the difference between how voice fragments and broadcast messages are handled?

Voice fragments use `TransportConfig.bleFragmentRelayTtlCapDense` as a hard ceiling in dense graphs, while broadcast messages (public posts, announces) apply a graduated clamping policy: maximum TTL 5 in dense networks, original TTL preserved in thin chains (degree ≤ 2), and preferred TTL 6 or 7 for standard or urgent broadcasts. Both receive degree-proportional jitter, but fragments use the specialized fragment delay ranges defined in `TransportConfig`.