# How BitChat's Intelligent Transport Selection Routes Private Messages: BLE Mesh, Nostr, and Courier Fallbacks

> Discover how BitChat intelligently routes private messages using BLE mesh, Nostr, and courier fallbacks. Ensure secure, reliable communication with its advanced transport selection.

- Repository: [permissionlesstech/bitchat](https://github.com/permissionlesstech/bitchat)
- Tags: deep-dive
- Published: 2026-08-04

---

**BitChat routes every private message through a priority-ranked decision engine that prefers secure BLE mesh connections, falls back to reachable peers, and automatically delegates to trusted couriers or Nostr bridges when direct delivery fails.**

BitChat's peer-to-peer chat protocol eliminates centralized servers by dynamically selecting the optimal transport layer for each message. According to the permissionlesstech/bitchat source code, this **intelligent transport selection** is implemented in [`MessageRouter.swift`](https://github.com/permissionlesstech/bitchat/blob/main/MessageRouter.swift), where a four-step evaluation cascade balances latency, security, and delivery guarantees.

## How the MessageRouter Evaluates Transport Options

The core routing logic resides in `MessageRouter.sendPrivate(_:to:recipientNickname:messageID:)` at lines 92-162. The router maintains an `outbox` of queued messages and queries each registered `Transport` implementation—currently BLE mesh and Nostr bridge—for connectivity state.

Two helper methods provide the foundation for transport selection:

```swift
private func reachableTransport(for peerID: PeerID) -> Transport? {
    transports.first { $0.isPeerReachable(peerID) }
}
private func connectedTransport(for peerID: PeerID) -> Transport? {
    transports.first { $0.isPeerConnected(peerID) }
}

```

These queries determine whether the recipient appears in peer advertisements (`isPeerReachable`) or has an established session (`isPeerConnected`). The router then applies its four-tier priority system.

## The Four-Tier Transport Selection Algorithm

| Priority | Condition | Transport Used | Courier Backup? |
|----------|-----------|----------------|---------------|
| **1** | Connected **and** secure session exists | Secure BLE mesh (Noise protocol) | No |
| **2** | Connected but **no** secure session | Connected mesh (unauthenticated) | **Yes** |
| **3** | Only reachable (advertised, not connected) | Reachable mesh | **Yes, if delivery not prompt** |
| **4** | No transport available | Queued only (outbox) | **Yes, immediate** |

Each tier modifies message state and courier behavior differently.

### Tier 1: Secure Direct Delivery

When `connectedTransport(for:)` returns a transport where `canDeliverSecurely(to:)` is true—meaning a completed Noise protocol handshake exists—the router takes the fastest path:

```swift
if let transport = connectedTransport(for: peerID),
   transport.canDeliverSecurely(to: peerID) {
    enqueue(message, for: peerID)
    secureTransmissions.insert(PeerMessageKey(peerID: peerID, messageID: messageID))
    transport.sendPrivateMessage(content, …)
    return
}

```

The message is marked as `secureTransmission`, enabling optimized retry logic later. No courier involvement occurs—privacy is preserved through direct encryption.

### Tier 2: Connected but Unauthenticated

If the peer is connected without a Noise session, the router still uses the mesh but adds courier redundancy:

```swift
if let transport = connectedTransport(for: peerID) {
    enqueue(message, for: peerID)
    secureTransmissions.remove(PeerMessageKey(peerID: peerID, messageID: messageID))
    transport.sendPrivateMessage(content, …)
    attemptCourierDeposit(messageID: messageID, for: peerID)
    return
}

```

The message is sent immediately, but `attemptCourierDeposit` seals and hands a copy to eligible couriers as insurance against delivery failure.

### Tier 3: Reachable-Only Peers

For peers visible in advertisements but without active connections:

```swift
if let transport = reachableTransport(for: peerID) {
    enqueue(message, for: peerID)
    transport.sendPrivateMessage(content, …)
    if !transport.canDeliverPromptly(to: peerID) {
        attemptCourierDeposit(messageID: messageID, for: peerID)
    }
}

```

The `canDeliverPromptly` check distinguishes between transports that will likely succeed quickly versus those experiencing delays. Nostr bridges returning `false` here trigger immediate courier delegation.

### Tier 4: Complete Transport Failure

When no transport reports reachability:

```swift
} else {
    var unsent = message; unsent.sendAttempts = 0
    enqueue(unsent, for: peerID)
    attemptCourierDeposit(messageID: messageID, for: peerID)
}

```

The message enters the outbox with zero send attempts and relies entirely on courier or bridge infrastructure.

## Courier Selection and Bridge Backup

The `attemptCourierDeposit` method at lines 66-78 implements BitChat's **store-and-forward** resilience. It operates in two phases:

1. **Bridge deposit**: Request Nostr relay storage via `requestBridgeCourierDeposit`
2. **Mesh courier deposit**: Select trusted peers to physically carry the message

```swift
private func attemptCourierDeposit(messageID: String, for peerID: PeerID) {
    guard let recipientKey = courierDirectory.noiseKey(peerID),
          let entry = queuedMessage(messageID, for: peerID) else { return }
    
    // Phase 1: Internet bridge
    requestBridgeCourierDeposit(entry, for: peerID, recipientKey: recipientKey)
    
    // Phase 2: Mesh couriers
    let remainingSlots = Self.maxCouriersPerMessage - entry.depositedCourierKeys.count
    guard remainingSlots > 0 else { return }
    
    for transport in transports {
        guard let courierTransport = transport as? MeshCourierTransporting else { continue }
        let couriers = eligibleCouriers(on: transport,
                                        recipientKey: recipientKey,
                                        excluding: entry.depositedCourierKeys,
                                        limit: remainingSlots)
        // Send to selected couriers...
    }
}

```

### Courier Eligibility Scoring

The `eligibleCouriers` method (lines 40-60) ranks candidates by:

- **Mutual favorite status**: Peers marked as favorites in both directions receive highest priority
- **Verification state**: Cryptographically verified identities outrank unverified strangers
- **Connection health**: Currently connected couriers preferred over cached snapshots
- **Deduplication**: Already-used couriers excluded via `depositedCourierKeys`

Results are sorted favorites-first and capped at `maxCouriersPerMessage` to limit network overhead.

## Outbox Persistence and Retry Logic

BitChat's transport selection includes durable delivery guarantees through [`MessageOutboxStore.swift`](https://github.com/permissionlesstech/bitchat/blob/main/MessageOutboxStore.swift). Key mechanisms:

- **FIFO per-peer queues**: Each recipient maintains up to 100 pending messages
- **Secure transmission tracking**: The `secureTransmissions` set limits retries for already-encrypted messages
- **Periodic sweeps**: Background tasks retry bridge deposits and purge expired entries (30-day default TTL)

When a courier connects or a transport state changes, `courierBecameAvailable` triggers re-evaluation of pending deposits without user intervention.

## Integrating Transport Selection in Applications

Developers interact with the routing layer through the `MessageRouter` public interface:

```swift
let router = MessageRouter(transports: [bleTransport, nostrTransport])

// Send with automatic transport selection
router.sendPrivate(
    "Meet at coordinates 40.7128, -74.0060",
    to: recipientPeerID,
    recipientNickname: "field-agent-7",
    messageID: UUID().uuidString
)

// React to infrastructure changes
router.courierBecameAvailable(newCourierPeerID)

// Monitor delivery status
bleTransport.eventDelegate = myDelegate

```

The `TransportEvent` protocol provides asynchronous callbacks for `peerConnected`, `peerSnapshotsUpdated`, and `messageDeliveryStatusUpdated`, enabling UI synchronization without polling.

## Summary

- **Four-tier cascade**: BitChat evaluates secure connected, connected, reachable, and unavailable states in strict priority order
- **Automatic courier delegation**: Every non-secure path triggers sealed message deposits to trusted peers or Nostr bridges
- **Privacy-preserving redundancy**: Courier messages use recipient Noise keys for encryption—intermediate peers cannot read content
- **Durable outbox**: Per-peer queues with retry logic ensure eventual delivery across intermittent connectivity

## Frequently Asked Questions

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

The router queries all registered transports equally through `connectedTransport(for:)` and `reachableTransport(for:)`. The first transport returning true for connectivity checks wins priority, with secure BLE connections preferred over Nostr bridges when both are available. If neither reports prompt delivery capability, both receive courier deposits.

### What makes a peer eligible to carry messages for others?

Eligibility requires: (1) a known Noise public key in `courierDirectory`, (2) either mutual favorite status or cryptographic verification, and (3) current connectivity or recent presence in peer snapshots. Favorites are always prioritized over verified strangers, and each message caps courier count via `maxCouriersPerMessage`.

### Can messages be read by courier peers?

No. The `attemptCourierDeposit` method seals messages using the recipient's static Noise key from `courierDirectory.noiseKey`. Couriers receive encrypted envelopes they cannot decrypt—only the intended recipient possesses the corresponding private key.

### What happens if all selected couriers fail to deliver?

The outbox entry persists with incremented `sendAttempts`. Periodic sweeps retry bridge deposits, and the router invokes `eligibleCouriers` again when new peers connect. Messages expire after the configured TTL (default 30 days) and are removed by `cleanupExpiredMessages`.