What Is the Source-Based Routing Protocol v2 in BitChat? Implementation Guide
BitChat's Source-Based Routing Protocol v2 enables efficient unicast delivery across mesh networks by embedding an explicit list of intermediate relays (the source route) directly into packet headers, falling back to flood broadcast when routes fail.
The Source-Based Routing Protocol v2 (SBR v2) in the permissionlesstech/bitchat repository represents a significant evolution from the legacy flooding mechanism, allowing senders to specify exact forwarding paths through the mesh. This protocol extension maintains backward compatibility with Version 1 clients while introducing structured routing data that reduces unnecessary network traffic. All routing logic is cryptographically secured through Ed25519 signatures that cover the entire packet payload, including the embedded route list.
Overview of Source-Based Routing v2
Packet Structure and Versioning
SBR v2 introduces a fixed 16-byte header (expanded from 14 bytes in Version 1) to accommodate the routing metadata. The additional 2 bytes support a 4-byte payload length field required for larger unicast transmissions.
Key structural changes include:
- Protocol Version: Packets explicitly set to Version 2; Version 1 packets maintain the legacy 2-byte payload length limit
- HAS_ROUTE Flag: The header flag
HAS_ROUTE (0x08)is only valid whenversion ≥ 2—legacy clients must ignore this bit - Source Route Field: A variable-length list positioned after the RecipientID containing:
Count(1 byte): Number of intermediate hops NHops(N × 8 bytes): Peer IDs of the relay nodes
Routing Identifiers
Each peer in the BitChat mesh is identified by an 8-byte routing ID (PeerID.routingData), derived from the short 16-character hexadecimal peer ID. As implemented in localPackages/BitFoundation/Sources/BitFoundation/PeerID.swift, this compact identifier minimizes header overhead while maintaining unique node addressing across the network.
Topology Discovery and Route Calculation
BitChat builds its mesh topology through confirmed edges—bidirectional peer relationships where both nodes acknowledge each other as neighbors.
The discovery process relies on the Neighbor List TLV (Type 0x04) embedded within IdentityAnnouncement packets. When a node receives neighbor information from a peer, it validates the edge only when reciprocity is confirmed (both peers list each other). The MeshTopologyTracker.swift file maintains this graph structure, providing the pathfinding substrate for source route calculation.
Routes are calculated to find paths of ≤ 4 intermediate hops through nodes that have been observed speaking Protocol v2, ensuring compatibility and minimizing latency across the mesh.
Security and Relay Logic
Cryptographic Protection
The protocol maintains end-to-end integrity through Ed25519 signatures that cover the entire packet, including the source route list. Any modification to the route by intermediate nodes invalidates the signature, causing receiving nodes to drop the packet immediately. This prevents route hijacking and ensures that only the original sender authorizes the forwarding path.
Relay Forwarding Mechanism
When a node receives a packet via BLERouteForwardingPolicy.swift, it executes the following validation sequence:
- Verify
version ≥ 2andHAS_ROUTEflag presence - Locate its own
routingDatain the hop list - Forward the packet to the next peer in the sequence (or the final recipient if last hop)
- Fall back to flood/broadcast if the next hop is unreachable
All fragmented payloads maintain Version 2 headers and carry identical route lists to preserve the unicast path across multiple physical frames.
iOS Implementation Specifics
The iOS client implements strict origination policies to prevent routing loops and unnecessary overhead. Defined in BLESourceRouteOriginationPolicy.swift, a source route is attached only when all six conditions are met:
- The sender is the local peer (no re-signing of forwarded packets)
- The recipient is not a broadcast address
- TTL > 1
- The recipient is not directly connected
- A complete v2 path exists with ≤ 4 intermediary hops, all speaking Protocol v2
- No recent failure entry exists in
BLESourceRouteFailureCache
Failure Detection and Fallback
If a routed transmission does not receive an inbound acknowledgment from the recipient within 10 seconds, the route is marked as failed in BLESourceRouteFailureCache.swift for 60 seconds. During this window, the system falls back to flooding; after expiration, routing attempts resume automatically.
Code Examples
Creating a Source-Routed Packet
The following Swift implementation from BitchatPacket.swift demonstrates constructing a Version 2 packet with an explicit route:
import BitFoundation
// PeerID objects for sender, recipient, and intermediate hops
let sender = myPeerID
let recipient = remotePeerID
let hop1 = intermediatePeerA
let hop2 = intermediatePeerB
// Build route using 8-byte routingData
let route = [hop1.routingData!, hop2.routingData!]
// Construct Version 2 packet with embedded route
var packet = BitchatPacket(
version: .v2,
senderID: sender.routingData!,
recipientID: recipient.routingData!,
payload: myPayloadData,
route: route
)
// Sign entire packet including route
packet.sign(with: mySigningKey)
Implementing Relay Forwarding Logic
The forwarding logic in BLERouteForwardingPolicy.swift processes incoming routed packets:
func forward(_ packet: BitchatPacket) {
guard packet.version >= .v2,
packet.flags.contains(.hasRoute),
let route = packet.route,
!route.isEmpty else {
// No valid source route: fall back to flood
flood(packet)
return
}
// Locate self in the route sequence
guard let myIndex = route.firstIndex(of: myPeerID.routingData!) else {
// Not in route path: ignore or flood
flood(packet)
return
}
// Determine next hop (intermediate or final recipient)
let nextHopData: Data
if myIndex + 1 < route.count {
nextHopData = route[myIndex + 1]
} else {
nextHopData = packet.recipientID
}
// Unicast to next hop
sendToPeer(
data: packet.encode(),
peerID: PeerID(routingData: nextHopData)!
)
}
Validating Confirmed Edges
Before forwarding, nodes verify topology through MeshTopologyTracker.swift:
let mesh = MeshTopologyTracker.shared
guard mesh.isConfirmedEdge(between: myPeerID, and: nextHopPeerID) else {
// Unconfirmed edge: abort routing, use broadcast
flood(packet)
return
}
Key Source Files
The Source-Based Routing Protocol v2 is implemented across the following core files:
docs/SOURCE_ROUTING.md— Complete protocol specification covering versioning, route format, and security modellocalPackages/BitFoundation/Sources/BitFoundation/PeerID.swift— DefinesroutingDataconversion and 8-byte mesh ID generationlocalPackages/BitFoundation/Sources/BitFoundation/BinaryProtocol.swift— Parses headers and validatesHAS_ROUTEflagsbitchat/Services/BLE/BLESourceRouteOriginationPolicy.swift— iOS policy engine for route attachment decisionsbitchat/Services/BLE/BLESourceRouteFailureCache.swift— Manages route failure states and timeout logicbitchat/Services/BLE/BLERouteForwardingPolicy.swift— Core relay implementation and next-hop selectionbitchat/Services/MeshTopologyTracker.swift— Maintains confirmed-edge graph for route calculationbitchat/Services/BitchatPacket.swift— Packet construction and encoding APIbitchat/Services/TransportConfig.swift— Routing configuration flags and protocol constants
Summary
- Source-Based Routing Protocol v2 upgrades BitChat packets to 16-byte headers with embedded explicit relay lists
- The
HAS_ROUTE (0x08)flag activates only in Version 2+, with strict backward compatibility for legacy clients - Routes consist of 1-byte hop count followed by 8-byte PeerID segments placed after the RecipientID field
- Confirmed edges from bidirectional Neighbor List TLVs (Type 0x04) provide the topology graph for pathfinding
- Ed25519 signatures cover the entire packet including routes, preventing tampering by intermediate nodes
- iOS clients apply strict origination policies (6 conditions) and 60-second failure caching to optimize mesh efficiency
- Automatic fallback to flood broadcast occurs when routes fail, next hops are unreachable, or edges are unconfirmed
Frequently Asked Questions
What is the difference between Version 1 and Version 2 packets in BitChat?
Version 1 packets use a 14-byte fixed header with a 2-byte payload length limit and do not support routing flags. Version 2 expands the header to 16 bytes (adding a 4-byte payload length field) and introduces the HAS_ROUTE (0x08) flag to indicate the presence of a source route list. Legacy Version 1 clients safely ignore the routing flag while continuing to process standard broadcasts.
How does BitChat ensure source routes have not been tampered with?
The protocol uses Ed25519 digital signatures that cryptographically cover the entire packet, including the source route list and payload. Because the signature is computed over the complete serialized bytes, any modification to the route by an intermediate relay invalidates the signature. Receiving nodes verify signatures before processing, dropping packets that fail authentication.
What happens when a source route fails in BitChat v2?
When a routed transmission receives no acknowledgment within 10 seconds, the route is marked as failed in BLESourceRouteFailureCache.swift and excluded from routing decisions for 60 seconds. During this period, the sender falls back to flood broadcast to ensure message delivery. After the cache entry expires, the system attempts source routing again to detect topology healing.
How does a BitChat node discover available routes?
Nodes build a mesh topology through Neighbor List TLVs (Type 0x04) exchanged in IdentityAnnouncement packets. The MeshTopologyTracker.swift module constructs a graph of confirmed edges, where an edge exists only if both peers acknowledge each other as neighbors. Source routes are calculated as paths of ≤ 4 hops through this confirmed-edge graph, ensuring all intermediate nodes are known and reachable.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →