BitChat BLE Transport Architecture V3: Four-Layer Stack Explained

The BitChat BLE transport architecture V3 is a four-layer packet-radio stack composed of the BLELinkLayer, Mesh Engine, Feature Modules, and App Boundary, each isolated to specific serial queues to guarantee deadlock-free operation.

The BitChat BLE transport architecture V3, documented in docs/BLE-ARCHITECTURE-V3.md inside the permissionlesstech/bitchat repository, restructures Bluetooth Low Energy messaging into a strictly layered, queue-isolated stack. This design replaces the previous monolithic transport with clearly separated responsibilities that scale from low-level radio control up to application-facing protocols.

BLELinkLayer: The CoreBluetooth Radio Interface

The BLELinkLayer manages the direct CoreBluetooth interface and is the only component that touches the iOS Bluetooth stack. Its responsibilities include scanning and advertising, connection scheduling, MTU negotiation, and managing write and notification back-pressure buffers. It also handles link-state restoration and isolates all of this state on a dedicated BLE serial queue.

This layer communicates upward by emitting LinkEvent objects and downward by receiving LinkCommand instructions. In bitchat/BLELinkLayer.swift, the BLELinkLayer type enforces that every buffer mutation occurs on bleQueue. For unit testing, the stack provides a SimulatedLinkLayer that mimics the same interface without hardware.

// Create a command to write bytes on the BLE link
let cmd = LinkCommand.send(data: payload, to: linkID)

// Dispatch the command on the BLE queue – the only place that can touch the link buffers
BLELinkLayer.shared.dispatch(command: cmd)

Mesh Engine: The Protocol Heart

The Mesh Engine sits directly above the link layer and functions as the core of the BitChat BLE transport architecture V3. It owns a single serial queue (mesh.message) that holds all mesh-level state, including the packet codec, fragmentation, deduplication, relay policy, peer registry, topology, gossip sync, and Noise orchestration. All engine logic runs as synchronous single-writer code, which eliminates data races without locks.

The central entry point is implemented in bitchat/BLEMeshEngine.swift as a synchronous handler with the signature handle(event, now) -> [Effect] that produces deterministic outputs. Supporting stores such as BLEPeerRegistryStore, defined in bitchat/BLEPeerRegistryStore.swift, provide lock-backed state that the engine and transport layers share safely.

func handleLinkEvent(_ event: BLELinkEvent) {
    // Engine-confined entry point
    onEngine { engine in
        engine.ingestDecodedPacket(event.packet, from: event.linkID)
    }
}

Feature Modules: Extensible Functional Components

Feature Modules represent individual capabilities within the BitChat BLE transport architecture V3. Each module owns its internal state and registers for the specific message types it handles, so adding a new capability requires creating a new module rather than modifying the engine core. Current modules include courier, board, pre-keys, private-media, file transfer, voice, diagnostics, groups, and verification or vouch logic.

Because module state lives on the same engine serial queue, they interact with the mesh through defined ports without introducing cross-thread chatter. Concrete examples include BLEMeshPingTracker and BLEPrivateMediaSessionStore.

final class StickerModule: FeatureModule {
    private var stickers = [Sticker]()
    
    init(engine: MeshEngine) {
        engine.register(messageType: .sticker, handler: self.handleSticker)
    }

    private func handleSticker(_ payload: Data) {
        // Process on the engine queue
        onEngine { _ in
            // … update stickers …
        }
    }
}

App Boundary: The Transport Façade

The App Boundary, also referred to as the Transport Core, is a thin façade that insulates the rest of the application from the complexity of the underlying stack. It exposes a small Transport protocol in bitchat/Transport.swift that covers lifecycle, identity, snapshots, basic messaging, and Noise wrappers.

Capability protocols are discovered dynamically via as? casting. For example, the app can check for MeshBridgingTransport or PanicResettingTransport without depending on a monolithic god-protocol. This layer is main-thread and transport-confined; it calls into the engine through onEngine and never synchronously dispatches to the BLE queue.

if let meshTransport = transport as? MeshBridgingTransport {
    meshTransport.sendMessage(to: peerID, payload: data)
}

Concurrency Contract and Deadlock Prevention

The BitChat BLE transport architecture V3 enforces a strict directional concurrency contract across all four layers. Data and commands always flow from the main or test threads into the engine queue, then into the BLE queue, and optionally into crypto queues. The stack never allows reverse-direction synchronous calls.

This unidirectional design guarantees deadlock-free operation because no lower layer can block waiting for a higher layer. BLELinkLayer.swift stays confined to bleQueue, BLEMeshEngine.swift stays confined to mesh.message, and the app boundary stays on the main thread.

Summary

  • The BLELinkLayer owns the CoreBluetooth interface and all radio-level buffers on bleQueue.
  • The Mesh Engine executes all protocol logic—codec, deduplication, Noise, and topology—on a single serial queue via synchronous handlers.
  • Feature Modules extend functionality by registering for message types without modifying engine code.
  • The App Boundary exposes a minimal Transport protocol and discovers capabilities like MeshBridgingTransport through casting.
  • A unidirectional concurrency contract ensures main → engine → BLE → crypto queue ordering, preventing deadlocks.

Frequently Asked Questions

What are the four layers in the BitChat BLE transport architecture V3?

The four layers are the BLELinkLayer, the Mesh Engine, the Feature Modules, and the App Boundary (Transport Core). Each layer has a single responsibility and is confined to its own serial queue or thread context.

How does the BitChat V3 stack prevent deadlocks?

The architecture enforces a unidirectional concurrency contract where calls flow from the main thread to the engine queue, then to the BLE queue, and finally to optional crypto queues. Because lower layers never synchronously call upward, circular waits are impossible.

What is the role of the Mesh Engine in BitChat BLE transport?

The Mesh Engine is the protocol heart that owns packet codec, fragmentation, deduplication, relay policy, peer registry, and Noise orchestration. It runs all logic on a dedicated serial queue and exposes a synchronous handle(event, now) -> [Effect] interface in BLEMeshEngine.swift.

How do you add a new feature to BitChat without changing the engine?

You create a new Feature Module that conforms to the module interface, owns its own state, and registers for the message types it handles with the engine. The engine does not need modification, as demonstrated by existing modules like BLEMeshPingTracker and BLEPrivateMediaSessionStore.

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 →