How BitChat Performs BLE File Transfers and Fragment Assembly
BitChat transfers files over Bluetooth Low Energy by fragmenting payloads into BLE-sized packets and reassembling them using a per-sender buffer that validates sequence numbers and enforces a 30-second timeout.
BitChat implements peer-to-peer file sharing over BLE mesh networks by breaking large payloads into fragments that respect the protocol’s MTU limitations. According to the permissionlesstech/bitchat source code, the BLE file transfer pipeline handles packetisation, transmission, and reassembly through dedicated Swift classes that manage memory buffers and cryptographic validation.
The Six-Phase BLE File Transfer Pipeline
BitChat’s file transfer mechanism operates through six distinct stages, each handled by specific components in the BitFoundation module.
Packetisation and Fragmentation
When initiating a transfer, the system reads the file into memory and wraps it in a BitchatFilePacket. The binary representation is then divided into fragments that do not exceed the BLE MTU, which defaults to approximately 512 bytes. The BLEOutboundPacketPolicy class determines whether a packet requires fragmentation based on current MTU constraints.
Transmission via BLEService
Each fragment is handed to BLEService.sendPacket(_:), which adds a fragment header containing the sequence number, total fragment count, and sender ID. The service queues these fragments in BLEOutboundWriteBuffer for transmission across the mesh network.
Reception and Initial Processing
Incoming fragments arrive at BLEService.handleReceivedPacket(_:), which parses the fragment header and routes the data to a BLEFragmentAssemblyBuffer instance keyed by the sender’s identity.
Fragment Assembly Logic
The BLEFragmentAssemblyBuffer stores fragments in an in-memory map keyed by the combination of 8-byte sender ID and file ID. This composite key prevents collision between concurrent transfers from different peers. The buffer maintains fragments in a map indexed by their monotonic sequence numbers, allowing out-of-order delivery tolerance.
When the final fragment arrives, the buffer concatenates payloads in sequence order. If the fragment sequence contains gaps and the 30-second timeout expires, the buffer discards the partial data to prevent memory leaks.
File Completion and Storage
Once reassembly completes, the full payload passes to BLEIncomingFileStore.storeIncomingFile(_:), which writes the data to Downloads/ or a temporary live-capture directory. The store generates a receipt notification that triggers UI updates indicating download completion.
Acknowledgement
BitChat optionally sends a lightweight BLEAckPacket back to the sender to confirm successful reassembly. The sender’s BLEFragmentAssemblyBuffer processes this acknowledgement to clear its transmission state and update progress indicators.
How Fragment Assembly Works in Detail
The reassembly process in BLEFragmentAssemblyBuffer.swift implements several safety mechanisms to ensure reliable data reconstruction.
Reassembly Key Structure
The buffer creates unique storage keys by concatenating the sender’s 8-byte identifier with the file identifier. This ensures that fragments belonging to different files or different peers never collide in the shared buffer space.
Timeout and Memory Management
A 30-second inactivity timer runs for each active assembly operation. If fragments remain incomplete after this period, the buffer purges the partial data. This timeout prevents memory leaks and accommodates peer-ID rotation in the mesh network.
Sequence Validation and Ordering
Fragments carry monotonic sequence numbers that the buffer validates before storage. While the system tolerates out-of-order delivery by storing fragments in a sequence-numbered map, reconstruction only proceeds when all sequence numbers from 0 to N-1 are present.
Cryptographic Safety
Before storage, fragment payloads undergo validation using BitchatPacket.toBinaryDataForSigning(). This ensures that reassembly only occurs on authentic data that matches the sender’s cryptographic signature.
BLE File Transfer Implementation Examples
The following Swift code demonstrates the API surface for implementing and debugging BLE file transfers in BitChat.
Sending a File
To initiate a transfer, create a BitchatFilePacket and pass it to the shared BLE service:
let fileURL = URL(fileURLWithPath: "/path/to/video.mov")
let filePacket = try BitchatFilePacket(fileURL: fileURL)
BLEService.shared.sendPacket(filePacket)
Handling Incoming Fragments
Inside BLEService.swift, received packets undergo header extraction and routing:
func handleReceivedPacket(_ packet: BitchatPacket) {
guard let fragment = BLEFragmentHeader.from(packet) else { return }
BLEFragmentAssemblyBuffer.shared.store(fragment: fragment, from: packet.senderID)
}
Reassembling Fragments
When the buffer detects a complete sequence, it returns the concatenated data for persistence:
if let completeData = BLEFragmentAssemblyBuffer.shared.tryReassemble(
senderID: senderID,
fileID: fileID) {
try BLEIncomingFileStore.shared.storeIncomingFile(
data: completeData,
from: senderID,
fileName: originalFileName)
}
Tracking Transfer Progress
Developers can inspect pending fragment counts to update progress bars:
let pending = BLEFragmentAssemblyBuffer.shared.pendingFragments(
for: senderID, fileID: fileID)
print("Received \(pending.count) / \(expectedCount) fragments")
Summary
- BLE file transfers in BitChat utilize six pipeline phases: packetisation, transmission, reception, assembly, completion, and acknowledgement.
BLEFragmentAssemblyBuffer.swiftmanages reassembly using composite keys of sender ID plus file ID, with a 30-second timeout for partial data.- Fragments default to 512-byte MTU limits and include monotonic sequence numbers for ordering validation.
- The
BLEIncomingFileStorepersists completed files to device directories after cryptographic validation viatoBinaryDataForSigning().
Frequently Asked Questions
What is the maximum file size BitChat can transfer over BLE?
BitChat does not enforce a hardcoded file size limit; however, practical limits depend on available RAM for the BLEFragmentAssemblyBuffer and the 30-second timeout window. Large files transfer successfully provided the mesh delivers fragments within the timeout period and the receiving device has sufficient memory to hold the reassembly map.
How does BitChat handle lost or out-of-order fragments?
The BLEFragmentAssemblyBuffer stores fragments in a map keyed by sequence number, allowing out-of-order delivery. If sequence gaps exist, the buffer waits for the missing fragments until the 30-second timeout expires. Missing fragments after timeout cause the partial file to be discarded, requiring the sender to retry the transfer.
What happens if a BLE file transfer is interrupted?
If transmission interrupts due to connection loss or timeout, the BLEFragmentAssemblyBuffer automatically purges partial data from memory. The BLEIncomingFileStore only commits files when tryReassemble() returns complete data, ensuring no corrupted partial files persist to disk.
Where are received files stored in BitChat?
Received files are written by BLEIncomingFileStore.swift to standardized directories such as Downloads/ for standard transfers or temporary live-capture folders for media streams. The store handles directory creation and file naming while generating UI receipts upon completion.
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 →