# How BitChat Handles Message Routing Between Bluetooth and Nostr Transports

> Discover how BitChat's capability-driven abstraction layer routes messages seamlessly between Bluetooth and Nostr transports. Learn about peer reachability and local mesh vs remote relay delivery.

- Repository: [permissionlesstech/bitchat](https://github.com/permissionlesstech/bitchat)
- Tags: internals
- Published: 2026-08-07

---

**BitChat routes messages between Bluetooth Low-Energy (BLE) and Nostr transports using a capability-driven abstraction layer where the `Transport` protocol enables seamless switching based on peer reachability, allowing local mesh delivery via BLE and remote relay delivery via Nostr without changing the application API.**

BitChat implements a sophisticated dual-transport architecture that automatically routes messages between Bluetooth Low-Energy mesh networks and Nostr relay protocols. This mechanism, implemented in the permissionlesstech/bitchat repository, relies on a unified `Transport` protocol abstraction that inspects peer capabilities to determine optimal message delivery paths. Understanding how BitChat handles message routing between Bluetooth and Nostr transports reveals how modern decentralized chat applications can bridge local mesh networking with global relay infrastructure.

## The Transport Protocol Abstraction

At the core of BitChat's routing mechanism lies the **`Transport`** protocol defined in [`bitchat/Services/Transport.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/Transport.swift) (lines 31-57). This protocol abstracts all network communication behind a single interface, presenting a unified API to the rest of the application regardless of whether messages travel over BLE mesh or Nostr relays.

The protocol defines essential methods for message delivery and peer inspection, including `sendMessage(_:mentions:)` and `peerCapabilities(_:)`. By conforming to this protocol, both Bluetooth and Nostr implementations expose identical interfaces to higher-level components like `ChatViewModel` and `ChatTransportEventCoordinator`.

Two concrete transports implement this protocol:

- **`BLEService`** – Handles Bluetooth Low-Energy mesh communication, conforming via `extension BLEService: Transport` at the end of [`Transport.swift`](https://github.com/permissionlesstech/bitchat/blob/main/Transport.swift) (lines 107-113)
- **`NostrTransport`** – Manages Nostr relay network communication via pub/sub mechanisms, defined in [`bitchat/Services/NostrTransport.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/NostrTransport.swift)

## Peer Capabilities and Routing Decisions

BitChat determines the appropriate transport through **capability-driven dispatch** rather than hardcoded logic. Every peer advertises a `PeerCapabilities` value during discovery, defined in [`localPackages/BitFoundation/Sources/BitFoundation/PeerCapabilities.swift`](https://github.com/permissionlesstech/bitchat/blob/main/localPackages/BitFoundation/Sources/BitFoundation/PeerCapabilities.swift).

The routing decision follows this sequence:

1. **Capability Discovery** – Peers advertise capabilities including the `.nostrBridge` flag, indicating the peer can bridge to the Nostr network
2. **Reachability Query** – The view-model calls `transport.peerCapabilities(peerID)` to inspect the destination's supported transports
3. **Delivery Assessment** – The system invokes `transport.canDeliverPromptly(to:)` to determine which transport can reach the peer

For **Bluetooth-only peers**, `canDeliverPromptly(to:)` evaluates to `true` when the BLE mesh reports active links. For **Nostr bridge peers**, the Nostr transport overrides this method to return `true` for Nostr-identified peers while BLE returns `false`, automatically routing the message through the appropriate medium.

## Concrete Transport Implementations

### BLEService: Local Mesh Transport

`BLEService` implements the `Transport` protocol to manage the Bluetooth Low-Energy mesh network. When sending to peers within direct BLE range, this transport handles message fragmentation, mesh routing, and delivery confirmation. It emits transport-specific events like `.bluetoothStateUpdated` that inform the UI of local connectivity changes.

### NostrTransport: Global Relay Transport

`NostrTransport` conforms to the same `Transport` protocol but communicates with Nostr relays using the publish-subscribe pattern. When the routing logic identifies a bridge peer (one advertising `.nostrBridge` capabilities), messages queue through `NostrTransport.sendMessage(_:mentions:)`, which serializes them into Nostr events and publishes them to configured relays for global delivery.

## Unified Event Handling Across Transports

All transports emit a common **`TransportEvent`** enum containing cases like `.messageReceived` and `.bluetoothStateUpdated`. The `ChatTransportEventCoordinator` (located in [`bitchat/ViewModels/ChatTransportEventCoordinator.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/ViewModels/ChatTransportEventCoordinator.swift)) subscribes to these events and forwards them to the appropriate UI or service layers.

This architecture ensures that higher-level code remains oblivious to the underlying medium. Whether a message arrives via BLE mesh from a nearby device or via Nostr relay from a remote peer, the coordinator processes it identically through the `TransportEventDelegate` protocol.

## Implementation Examples

The following Swift code demonstrates the transport-agnostic routing logic:

```swift
// Query the transport for peer capabilities
let caps = transport.peerCapabilities(peerID)

// Route based on capabilities
if caps.contains(.nostrBridge) {
    // Automatically selects NostrTransport for bridge peers
    transport.sendMessage("Hello via Nostr", mentions: [])
} else {
    // Defaults to BLEService for local mesh peers
    transport.sendMessage("Hello via BLE", mentions: [])
}

```

Event handling remains consistent regardless of transport origin:

```swift
extension ChatViewModel: TransportEventDelegate {
    func didReceiveTransportEvent(_ event: TransportEvent) {
        switch event {
        case .messageReceived(let msg):
            // Processes messages from BLE or Nostr uniformly
            handleIncomingMessage(msg)
        case .bluetoothStateUpdated(let state):
            // Only BLEService emits this event
            updateBluetoothUI(state)
        default: break
        }
    }
}

```

## Summary

- **BitChat uses a `Transport` protocol abstraction** defined in [`Transport.swift`](https://github.com/permissionlesstech/bitchat/blob/main/Transport.swift) to unify BLE and Nostr implementations behind a single API
- **Capability-driven routing** relies on `PeerCapabilities` (specifically `.nostrBridge`) to determine whether to use local BLE mesh or global Nostr relays
- **Reachability checking** via `canDeliverPromptly(to:)` allows automatic transport selection without manual intervention
- **Unified event handling** through `ChatTransportEventCoordinator` ensures transport-agnostic message processing for the UI layer

## Frequently Asked Questions

### How does BitChat decide between BLE and Nostr for message delivery?

BitChat queries peer capabilities using `transport.peerCapabilities(peerID)` to check for the `.nostrBridge` flag. It then evaluates `canDeliverPromptly(to:)` on available transports; BLE returns true for local mesh peers while Nostr returns true for bridge peers, automatically selecting the viable route.

### What is the Transport protocol in BitChat?

The `Transport` protocol in [`bitchat/Services/Transport.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/Transport.swift) defines the interface that both BLE and Nostr implementations must conform to, including methods for sending messages, checking peer capabilities, and assessing delivery reachability. This abstraction allows the Chat layer to remain transport-agnostic.

### How does BitChat handle incoming messages from different transports?

All transports emit a unified `TransportEvent` enum consumed by `ChatTransportEventCoordinator`. Whether messages arrive via BLEService or NostrTransport, they generate identical `.messageReceived` events that the coordinator routes to the appropriate UI handlers without distinguishing the source medium.

### What are PeerCapabilities in BitChat?

`PeerCapabilities`, defined in [`localPackages/BitFoundation/Sources/BitFoundation/PeerCapabilities.swift`](https://github.com/permissionlesstech/bitchat/blob/main/localPackages/BitFoundation/Sources/BitFoundation/PeerCapabilities.swift), is a flags-based descriptor that peers advertise during discovery. The `.nostrBridge` capability specifically indicates that a peer can route messages to the Nostr relay network, triggering the routing logic to select NostrTransport for delivery.