BitChat BLE File Transfer Fragmentation: Handling Large Media Over 512-Byte MTU

BitChat automatically fragments any media payload exceeding the 512-byte BLE MTU into sequenced packets, reassembles them atomically on the receiving side, and only surfaces validated, complete files to the user interface.

BitChat, an open-source messaging application maintained by permissionlesstech, implements a robust BLE file transfer fragmentation system to overcome Bluetooth Low Energy's inherent payload limitations. When users share photos, videos, or audio clips that exceed the standard 512-byte Maximum Transmission Unit (MTU), the protocol automatically handles packet segmentation and reassembly without exposing incomplete transfers to the UI.

The BLE MTU Constraint

Bluetooth Low Energy (BLE) enforces a strict 512-byte MTU limit on data payloads. Large media files—such as high-resolution images or audio recordings—quickly exceed this threshold. Rather than dropping these transfers, BitChat employs automatic fragmentation within BitchatPacket.swift, splitting oversized payloads into manageable chunks while preserving the integrity of the original media through sequence metadata.

The Six-Step Fragmentation Pipeline

1. MTU-Aware Packet Fragmentation

The sender initiates transfer by constructing a BitchatPacket containing the raw media bytes. In BitchatPacket.swift, the initializer automatically detects payloads exceeding the BLE MTU and fragments them into a sequence of smaller packets. Each fragment embeds fragmentation metadata—including the fragment index and total fragment count—in the packet header, enabling ordered reconstruction on the receiving device.

2. CoreBluetooth Transport

Once fragmented, individual packets are dispatched via BLEService. The service leverages CoreBluetooth's CBPeripheralManager to transmit each fragment over the BLE link. Because the initial fragmentation ensures every chunk fits within the 512-byte limit, the lower-level Bluetooth stack never encounters oversized payloads, preventing transmission failures at the hardware level.

3. Ordered Fragment Reassembly

On the receiving side, the internal BLEReceivePipeline buffers incoming fragments as they arrive. Using the fragment ID and sequence numbers stored in the packet headers, the pipeline groups related fragments and reconstructs the original byte array only after the final fragment arrives. This guarantees that downstream components receive complete, contiguous media data.

4. Payload Validation

The fully reassembled payload passes to BLEFileTransferHandler.handle(_:), which coordinates validation through BLEIncomingFileValidator. This stage verifies that the payload is well-formed, confirms the MIME type is supported, and checks that the file size complies with the app-wide BCH-01-002 storage quota before allowing persistence.

5. Atomic File Persistence

Validated files are written to disk via BLEIncomingFileStore.saveIncomingFile. The store implements atomic visibility by first writing data to a temporary location with a live-capture prefix. This ensures partially received files never appear as user-visible media. Only after the complete file is successfully written does the store move it into the permanent "incoming" media directory.

6. UI Delivery and Cleanup

Finally, BLEFileTransferHandler instantiates a BitchatMessage referencing the saved file and delivers it to the UI layer through the deliverMessage closure supplied by BLEService. If the UI rejects the message or delivery fails, the handler immediately removes the temporary file, ensuring no orphaned fragments or incomplete media remain on the device.

Key Implementation Details

Fragmentation Metadata Structure. Each packet header contains sequence information that BLEReceivePipeline uses to reassemble fragments in the correct order, even if they arrive out of sequence due to BLE link-layer retries.

Quota Enforcement. Before persistence, BLEFileTransferHandler invokes environment.enforceStorageQuota (specification BCH-01-002) to verify sufficient device storage exists, preventing transfers that would exhaust available space.

Private Media Support. For packets marked as private, the handler reserves a stable message ID and records a durable receipt during the transfer process. This mechanism allows senders to retry transmission if the receiver later discards the file, ensuring reliable delivery of sensitive content across the fragmented transport.

Code Examples

The following Swift snippets demonstrate the fragmentation flow for sending and receiving large media files:

// Sending a large audio clip that exceeds 512 bytes
let rawData = try Data(contentsOf: audioURL)
let packet = BitchatPacket(payload: rawData, ...)
BLEService.shared.send(packet)
// Receiving side validation inside BLEFileTransferHandler
func handle(_ packet: BitchatPacket, from peerID: PeerID) -> Bool {
    return storeIncomingPayload(
        packet.payload,
        from: peerID,
        senderNickname: "Alice",
        timestamp: Date(),
        isPrivate: false,
        usesDurableReceipts: false,
        env: environment)
}
// Atomic persistence to prevent partial file visibility
let destination = environment.saveIncomingFile(
    filePacket.content,
    filePacket.fileName,
    "audio/incoming",
    "m4a",
    "audio")

Summary

  • Automatic Fragmentation: BitchatPacket.swift splits payloads exceeding 512 bytes without developer intervention.
  • Ordered Reassembly: The internal BLEReceivePipeline reconstructs media using fragment index metadata before validation.
  • Atomic Visibility: BLEIncomingFileStore uses temporary live-capture prefixes to prevent users from seeing incomplete files.
  • Quota Protection: The BCH-01-002 enforcement prevents storage overflow before files are committed to disk.
  • Reliable Delivery: Durable receipts enable retry logic for private media transfers across potentially lossy BLE connections.

Frequently Asked Questions

What is the maximum file size BitChat can transfer over BLE?

While the BLE transport itself handles payloads through fragmentation, BitChat imposes upper limits via the BCH-01-002 storage quota enforcement. The BLEFileTransferHandler validates available space using environment.enforceStorageQuota before calling BLEIncomingFileStore.saveIncomingFile, meaning the practical limit depends on device capacity rather than a fixed byte threshold.

The system implements atomic visibility through BLEIncomingFileStore, which writes all incoming data to temporary files with a live-capture prefix. Only after BLEFileTransferHandler validates the complete reassembled payload and successfully moves the file into the permanent media directory does it create the BitchatMessage and invoke deliverMessage to surface the content in the UI. Failed deliveries trigger immediate cleanup of temporary fragments.

How are fragmented packets sequenced during transfer?

During packet creation in BitchatPacket.swift, fragmentation metadata—including the fragment index and total fragment count—is embedded in each packet header. The BLEReceivePipeline buffers incoming fragments and uses this metadata to concatenate them in the correct order before passing the complete payload to BLEFileTransferHandler.handle(_:).

What distinguishes private media handling in BitChat's BLE file transfer?

For private transfers, BLEFileTransferHandler reserves a stable message ID and records a durable receipt before completing the transfer. This allows the sender to retry transmission if the receiver later discards the file, ensuring reliable delivery of sensitive content even when operating across the fragmented BLE transport layer.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →