How BitChat Outbound Packet Policy and Write Buffering Work: A Deep Dive into the BLE Stack
BitChat uses a two-layer outbound system where BLEOutboundLinkPlanner determines packet fragmentation based on per-link ATT limits, and BLEOutboundWriteBuffer manages priority-ordered, byte-capped queues to ensure critical control traffic is never dropped.
The BitChat messenger routes messages over Bluetooth Low Energy (BLE) in a decentralized, permissionless mesh. Sending data reliably over BLE requires handling small MTUs, variable link quality, and competing traffic types—from urgent Noise handshake frames to multi-megabyte file transfers. The repository permissionlesstech/bitchat solves this with an outbound packet policy that decides how to split payloads, and a write buffering system that schedules those fragments without exhausting memory or starving high-priority traffic.
Outbound Packet Policy: Link-Aware Fragmentation Planning
The packet policy answers three questions for every outbound message: what is the maximum fragment size, how many fragments are needed, and what priority should the scheduler assign. This logic lives in two planner types and a set of policy constants.
Link Planning with BLEOutboundLinkPlanner
When BitChat discovers a BLE peripheral, it queries the GATT layer for ATT limits:
peripheralWriteLimits— the maximum bytes the peripheral can receive in a single write request or write command.centralNotifyLimits— the maximum bytes the central (this device) can receive in a notification.
BLEOutboundLinkPlanner.plan(...) selects the smallest viable limit and returns a BLEOutboundLinkPlan containing:
| Field | Purpose |
|---|---|
fragmentChunkSize |
The byte size of each fragment, computed by BLEOutboundPacketPolicy.fragmentChunkSize(forLinkLimit:) |
packetPriority |
A BLEOutboundWritePriority value encoding urgency |
This plan becomes the contract for all fragmentation that follows.
Fragment Planning with BLEOutboundFragmentPlanner
Given a payload and a link plan, BLEOutboundFragmentPlanner.makePlan(...) produces a BLEOutboundFragmentPlan. This struct contains an array of BLEOutboundFragmentTransferRequest objects, each sized to fit within the fragmentChunkSize. The planner also annotates each fragment with priority metadata derived from the original message type.
Write Buffering: Priority Queues with Byte Caps
Once fragments are planned, they enter BLEOutboundWriteBuffer, the central staging area before CoreBluetooth transmission. The buffer is structurally simple—[UUID: [BLEPendingWrite]]—but its eviction policy is carefully tuned for mesh networking.
Priority Levels in BLEOutboundWritePriority
Priority is a comparable struct with two fields: level (primary sort) and suborder (tie-breaker). The four standard levels are:
high(level 0) — Noise handshake and other control frames. Never dropped if avoidable.fragment(totalFragments:)(level 1) — Mesh message fragments. Higher fragment counts get slightly higher suborder to discourage head-of-line blocking for large messages.fileTransfer(level 2, suborderInt.max - 1) — File chunks. Throttled but not starved.low(level 2, suborderInt.max) — Background traffic. First to be trimmed under pressure.
Enqueueing and Automatic Trimming
The enqueue(data:for:priority:capBytes:) method in BLEOutboundWriteBuffer.swift inserts writes in sorted order, then enforces the per-link cap:
var total = queue.reduce(0) { $0 + $1.data.count }
while total > capBytes && !queue.isEmpty {
let removed = queue.removeLast() // drop lowest-priority items
trimmedBytes += removed.data.count
total -= removed.data.count
}
The returned EnqueueResult reports whether the new write survived and how many bytes were discarded. This guarantees that a burst of file-transfer data cannot evict a pending Noise handshake frame for the same peripheral.
Taking and Discarding Writes
When CoreBluetooth signals that a peripheral is ready to accept data, the stack calls:
let pendingWrites = buffer.takeAll(for: peripheralID)
This drains the entire queue for that peripheral in priority order. If the peripheral disconnects, discardAll(for:) removes the entry immediately to free memory.
Prepending for Retransmission
In rare cases—typically after a failed write with response—the stack may need to retry fragments ahead of newer traffic. prepend(_:for:) inserts writes at the front while maintaining priority ordering among the prepended items.
Practical Usage: Sending a Fragmented Mesh Message
The following Swift snippet demonstrates the full flow from link discovery to buffer enqueueing:
import Bitchat
let peripheralID = "A1:B2:C3:D4:E5:F6"
let maxWrite = 120 // discovered ATT write limit
let maxNotify = 90 // discovered notification limit
// Build link plan from discovered limits
let linkPlan = BLEOutboundLinkPlanner.plan(
peripheralWriteLimits: [maxWrite],
centralNotifyLimits: [maxNotify]
)
// Prepare a 350-byte mesh payload
let payload = Data(repeating: 0xAB, count: 350)
// Fragment according to plan
let fragmentPlan = try BLEOutboundFragmentPlanner.makePlan(
request: BLEOutboundFragmentTransferRequest(
payload: payload,
linkPlan: linkPlan,
peripheralID: peripheralID
)
)
// Enqueue fragments with priority-based buffering
var buffer = BLEOutboundWriteBuffer()
for fragment in fragmentPlan.fragments {
let priority = BLEOutboundWritePriority.fragment(
totalFragments: fragmentPlan.fragments.count
)
let result = buffer.enqueue(
data: fragment,
for: peripheralID,
priority: priority,
capBytes: 1024 // per-link memory budget
)
// Check result.trimmedBytes if you need to log evictions
}
When CoreBluetooth invokes the delegate method indicating space is available, drain the buffer:
let readyToSend = buffer.takeAll(for: peripheralID)
for write in readyToSend {
peripheral.writeValue(
write.data,
for: characteristic,
type: .withResponse // or .withoutResponse per policy
)
}
Key Source Files
| File | Responsibility |
|---|---|
BLEOutboundLinkPlanner.swift |
Translates ATT limits into actionable link plans |
BLEOutboundFragmentPlanner.swift |
Splits payloads into size-constrained fragments |
BLEOutboundWriteBuffer.swift |
Priority queue with byte-cap enforcement and trimming |
BLEOutboundFragmentTransferScheduler.swift |
Orchestrates planner → buffer → CoreBluetooth flow |
BLEOutboundWriteBufferTests.swift |
Validates ordering, cap behavior, and eviction logic |
All files are located under bitchat/Services/BLE/ in the repository.
Summary
- Outbound packet policy in BitChat dynamically selects fragment sizes based on per-link ATT write and notify limits discovered at connection time.
BLEOutboundLinkPlannerandBLEOutboundFragmentPlannerseparate the concerns of limit detection and payload splitting, producingBLEOutboundFragmentTransferRequestobjects ready for transmission.- Write buffering via
BLEOutboundWriteBuffermaintains per-link priority queues with configurable byte caps, automatically discarding lowest-priority traffic when memory pressure exceeds the limit. - The four priority levels—
high,fragment,fileTransfer, andlow—ensure that critical control frames are never starved by bulk data transfers. - The buffer's
takeAll,discardAll, andprependAPIs integrate cleanly with CoreBluetooth's asynchronous write callbacks and disconnection events.
Frequently Asked Questions
How does BitChat prevent large file transfers from blocking urgent control messages?
The BLEOutboundWriteBuffer uses priority-ordered eviction. When the per-link byte cap is exceeded, it removes items from the tail of the queue—always lowest-priority first. Since control frames use BLEOutboundWritePriority.high (level 0) and file transfers use fileTransfer (level 2), critical traffic is protected even during bulk uploads.
What happens to buffered data when a BLE peripheral disconnects?
The stack calls buffer.discardAll(for: peripheralID), which removes the entire queue entry for that peripheral. This prevents memory leaks in transient mesh topologies where peers may appear and disappear frequently.
Can BitChat retransmit failed fragments ahead of newer traffic?
Yes. The prepend(_:for:) method in BLEOutboundWriteBuffer allows the stack to insert retry fragments at the head of the queue. These prepended items maintain their internal priority ordering, ensuring that a retransmitted high-priority fragment still precedes a new low-priority packet.
Where is the maximum ATT write size detected in the codebase?
Peripheral capabilities are discovered during GATT service exploration and passed to BLEOutboundLinkPlanner.plan(peripheralWriteLimits:centralNotifyLimits:). The smallest applicable limit drives the fragmentChunkSize returned in the link plan, ensuring compliance with both local and remote constraints.
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 →