BLE Service Policies for Handling Outbound Packets in BitChat
BitChat's BLE layer consults the BLEOutboundPacketPolicy type to determine padding requirements, queuing priority, fragmentation size, and message deduplication for every packet before it is written to a Bluetooth link.
BitChat is an open-source, peer-to-peer messaging application that leverages Bluetooth Low Energy (BLE) to create resilient mesh networks. Understanding the BLE service policies for handling outbound packets in BitChat is essential for debugging connectivity issues or extending the protocol, as these policies govern how data is packaged, prioritized, and fragmented across the mesh. The decision-making logic is centralized in BLEOutboundPacketPolicy.swift and consumed by BLEService.swift during the transmission lifecycle.
The Four Policy Dimensions
The BLEOutboundPacketPolicy type in the permissionlesstech/bitchat repository encapsulates four distinct decision points that shape outbound traffic.
Padding Strategy
The policy determines whether a packet payload must be padded to the BLE MTU (512 bytes) via the padsBLEFrame(for:) method. This method inspects the MessageType and applies padding only to Noise-encrypted packets and handshake packets. All other packet types—including voice frames—are transmitted unpadded, conserving bandwidth for latency-sensitive media streams.
Priority Classification
Queuing order is governed by priority(for:data:), which returns a BLEOutboundWritePriority value. Regular chat messages receive high priority, ensuring conversational responsiveness. File-transfer payloads are downgraded to fileTransfer priority, while fragmented packets receive a specialized fragment priority that carries the total fragment count. This hierarchy ensures that small control messages and live text are drained from the outbound queue before large file chunks.
Fragmentation Logic
Large payloads are divided using fragmentChunkSize(forLinkLimit:), which calculates the maximum safe payload per BLE frame. The implementation subtracts a constant framing overhead of 42 bytes (13 + 8 + 8 + 13) from the negotiated link MTU, then enforces a hard floor of 64 bytes:
let maxChunk = BLEOutboundPacketPolicy.fragmentChunkSize(forLinkLimit: 512)
// Returns max(64, 512 - 42) = 470 bytes
This ensures that even on constrained links, fragments remain large enough to maintain protocol efficiency without exceeding BLE frame limits.
Message Identification
Duplicate suppression relies on messageID(for:), which forwards to BLEIngressLinkRegistry.messageID(for:) to generate a deterministic identifier. This message ID is attached to each fragment, allowing receiving nodes to detect and discard retransmissions or routing loops.
How BLEService Orchestrates Policy Application
The integration occurs in BLEService.swift, where the four policies are applied at specific transmission phases marked leave, send, and queue:
- Priority Resolution:
BLEOutboundPacketPolicy.priority(...)is invoked first to obtain theBLEOutboundWritePriority, which determines the packet's position in the outbound queue. - Conditional Padding: If
padsBLEFramereturnstrue, the service appends zero-padding to align the payload with the 512-byte MTU before fragmentation. - Smart Fragmentation: For payloads exceeding the calculated chunk size, the service iteratively calls
fragmentChunkSizeto produce appropriately-sized segments, each enqueued as a separate write operation. - Metadata Attachment: The message ID from
messageID(for:)is embedded in each fragment header for downstream deduplication.
BLEOutboundLinkPlanner.swift additionally utilizes the fragmentation API when preparing per-link transmission schedules, while BLESourceRouteOriginationPolicy.swift (documented in SOURCE_ROUTING.md) filters eligible links before the policies above are applied.
Practical Implementation Examples
The following Swift snippets demonstrate how to query the policy decisions directly:
// Determine if a packet requires MTU padding
let shouldPad = BLEOutboundPacketPolicy.padsBLEFrame(for: packet.type)
// Retrieve the transmission priority for queueing
let priority = BLEOutboundPacketPolicy.priority(for: packet, data: payload)
// Calculate the optimal fragment size for a 512-byte MTU link
let maxChunk = BLEOutboundPacketPolicy.fragmentChunkSize(forLinkLimit: 512)
These utility methods provide a clean interface for extending BitChat's protocol handler or debugging transmission behavior.
Summary
- Padding is selective: Only Noise-encrypted and handshake packets are padded to 512 bytes; voice and other data remain unpadded.
- Priority is hierarchical: Regular messages receive high priority, file transfers medium, and fragments carry explicit count metadata.
- Fragmentation is bounded: Payload chunks enforce a minimum of 64 bytes and account for 42 bytes of protocol overhead.
- Policy centralization: All rules reside in
BLEOutboundPacketPolicy.swiftand are consumed byBLEService.swiftandBLEOutboundLinkPlanner.swiftto manage the mesh transmission lifecycle.
Frequently Asked Questions
Which packet types are padded to the BLE MTU in BitChat?
According to the source code in BLEOutboundPacketPolicy.swift, only Noise-encrypted packets and handshake packets are padded to the full 512-byte MTU. Voice frames and other message types are deliberately sent unpadded to minimize latency and power consumption.
How does BitChat prioritize different types of network traffic?
The priority(for:data:) method assigns high priority to regular messages, fileTransfer priority to file uploads, and a specialized fragment priority (carrying the total fragment count) to segmented packets. This ensures that time-sensitive chat messages are written to the Bluetooth link before bulk data transfers.
What is the minimum fragment size enforced by BitChat's BLE policies?
The fragmentChunkSize(forLinkLimit:) method enforces a hard floor of 64 bytes. Even if the link MTU is small, fragments will never be smaller than this threshold, calculated as max(64, linkLimit - 42) where 42 bytes accounts for framing overhead.
How does BitChat prevent duplicate packet processing across the mesh?
Each outbound packet receives a deterministic message ID generated by BLEIngressLinkRegistry.messageID(for:) and accessed via BLEOutboundPacketPolicy.messageID(for:). This ID is attached to every fragment, allowing receiving nodes to identify and discard duplicates during reassembly or routing.
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 →