# What Is the MessageRouter in BitChat's Architecture?

> Discover the MessageRouter in BitChat's architecture. This core component manages messaging, routing, and secure sessions, connecting UI to transport layers.

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

---

**The MessageRouter acts as the central nervous system of BitChat's messaging layer, bridging high-level UI components with low-level transport protocols while managing outbox persistence, secure session routing, and store-and-forward fallback mechanisms.**

In the [permissionlesstech/bitchat](https://github.com/permissionlesstech/bitchat) repository, the `MessageRouter` class located in [`bitchat/Services/MessageRouter.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/MessageRouter.swift) implements the critical coordination logic required for decentralized messaging. It abstracts the complexity of transport selection and message reliability, ensuring that `ChatViewModel` and other consumers can invoke `sendPrivate()` without managing network intermittency or peer availability directly.

## Transport Selection and Routing Logic

The router evaluates transport availability through methods like `reachableTransport` and `connectedTransport` to determine the optimal delivery path for a given peer. According to the source implementation, this selection logic evaluates the current network topology and connection state before committing to a transmission strategy.

The core transport selection occurs in [[`MessageRouter.swift`](https://github.com/permissionlesstech/bitchat/blob/main/MessageRouter.swift)](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/MessageRouter.swift#L82-L88), where the router iterates through available transports (Mesh, Nostr, BLE) to identify viable pathways. When a direct transport is available, the router immediately dispatches the message; otherwise, it triggers fallback mechanisms.

## Outbox Management and Persistence

To prevent message loss during network partitions, the router maintains an in-memory outbox with disk persistence through `MessageOutboxStore`. The enqueue logic implements FIFO eviction and per-peer limits to prevent unbounded memory growth.

The [`enqueue`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/MessageRouter.swift#L62-L80) function retains copies of outgoing private messages until acknowledgements are received, respecting a 24-hour TTL. For durability, the router persists the outbox state via [`MessageOutboxStore`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/MessageRouter.swift#L149-L158), restoring queued messages on application launch and merging recovered state into the active routing table.

## Secure Session Handling

When a transport supports encrypted delivery, the router manages secure session establishment before transmission. The [`secure transmission path`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/MessageRouter.swift#L101-L113) verifies that authentication has completed before marking messages for secure dispatch.

If authentication completes after messages are queued, the router invokes `retrySecurePrivateMessagesAfterAuthentication` to re-evaluate the outbox and transmit eligible messages through the newly authenticated secure channel.

## Courier Deposit and Store-and-Forward

When no direct transport is available, the router seals payloads using the recipient's static Noise key and deposits them with intermediary peers (couriers) or internet bridges. This store-and-forward mechanism ensures delivery even when sender and recipient are never simultaneously online.

The [`attemptCourierDeposit`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/MessageRouter.swift#L66-L100) function handles the cryptographic sealing and handoff to available couriers. The router monitors carrier status through the [`courierBecameAvailable`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/MessageRouter.swift#L106-L132) callback, immediately offering queued messages to new carriers that enter proximity.

## Retry Logic and Bridge Coordination

For messages deposited with internet bridges, the router implements periodic retry logic to handle transient connectivity failures. The [`retryBridgeCourierDeposits`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/MessageRouter.swift#L84-L95) function re-attempts submission for messages that remain undelivered, using exponential backoff to avoid overwhelming the bridge infrastructure.

When `flushOutbox` is triggered—typically when a peer becomes reachable—the router iterates through pending messages and attempts immediate delivery, bypassing the courier layer if a direct transport is now available.

## Delivery Acknowledgements and Cleanup

The router processes delivery acknowledgements through [`markDelivered`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/MessageRouter.swift#L99-L110), removing retained copies from the outbox and invoking UI callbacks like `onMessageCarried`. For messages that exceed the 24-hour TTL or surpass per-peer caps, the [`cleanupExpiredMessages`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/MessageRouter.swift#L85-L92) routine purges stale entries and notifies the UI via `onMessageDropped`.

## Implementation Patterns from the Source Code

Applications interact with the router through a streamlined API that abstracts the underlying complexity. The `ChatViewModel` initializes the router with available transports and delegates message dispatch to this central coordinator.

Sending a private message requires only the content and recipient metadata:

```swift
let router = MessageRouter(transports: availableTransports)
router.sendPrivate(
    "Hey, did you see the new update?",
    to: peerID,
    recipientNickname: "Alice",
    messageID: UUID().uuidString
)

```

The router exposes callback closures for UI updates:

```swift
router.onMessageCarried = { messageID, peerID in
    // Update UI to show "carried by a peer" indicator
    chatUI.showCarrierNotice(messageID: messageID, for: peerID)
}

router.onMessageDropped = { messageID in
    // Handle TTL expiration or delivery failure
    chatUI.showDeliveryFailed(messageID: messageID)
}

```

When network conditions change, explicit flushing ensures immediate delivery attempts:

```swift
// Triggered when a peer's connection status changes
router.flushOutbox(for: peerID)

```

For secure session recovery, the router provides:

```swift
router.retrySecurePrivateMessagesAfterAuthentication(
    for: [peerIDAlias1, peerIDAlias2]
)

```

## Key Files in the BitChat Routing System

- **[[`MessageRouter.swift`](https://github.com/permissionlesstech/bitchat/blob/main/MessageRouter.swift)](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/MessageRouter.swift)** – Core implementation of routing logic, outbox management, courier coordination, and retry mechanisms.
- **[[`Transport.swift`](https://github.com/permissionlesstech/bitchat/blob/main/Transport.swift)](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/Transport.swift)** – Protocol defining the interface for Mesh, Nostr, BLE, and other low-level transport implementations consumed by the router.
- **[[`MessageOutboxStore.swift`](https://github.com/permissionlesstech/bitchat/blob/main/MessageOutboxStore.swift)](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/Courier/MessageOutboxStore.swift)** – Persistent storage layer for the router's outbox, handling serialization and recovery across app launches.
- **[[`ChatViewModel.swift`](https://github.com/permissionlesstech/bitchat/blob/main/ChatViewModel.swift)](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/ViewModels/ChatViewModel.swift)** – Example high-level consumer that instantiates the router and binds UI actions to routing methods.
- **[[`MessageRouterTests.swift`](https://github.com/permissionlesstech/bitchat/blob/main/MessageRouterTests.swift)](https://github.com/permissionlesstech/bitchat/blob/main/bitchatTests/Services/MessageRouterTests.swift)** – Comprehensive unit tests validating transport selection, courier fallback, and outbox persistence behaviors.

## Summary

- The **MessageRouter** serves as the central abstraction between BitChat's UI layer and its decentralized transport implementations.
- It implements **transport selection logic** to choose between Mesh, Nostr, BLE, or courier-based delivery based on real-time peer availability.
- The **outbox system** provides FIFO-ordered, persistent queuing with TTL enforcement and per-peer limits to ensure message durability without unbounded growth.
- **Store-and-forward capabilities** leverage Noise-encrypted courier deposits and internet bridge fallbacks when direct transport is unavailable.
- **Automatic retry mechanisms** handle bridge deposits and secure session re-authentication, flushing the outbox when connectivity is restored.
- Delivery lifecycle management includes **acknowledgement processing**, **expiration cleanup**, and UI callbacks for carried or dropped messages.

## Frequently Asked Questions

### What triggers the MessageRouter to select a specific transport?

The router evaluates transport availability through `reachableTransport` and `connectedTransport` checks at [L82-L88](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/MessageRouter.swift#L82-L88). It selects the first viable transport that reports connectivity to the target peer, prioritizing direct channels over courier-based store-and-forward.

### How does the MessageRouter handle message persistence across app restarts?

The router serializes its in-memory outbox to disk via `MessageOutboxStore` before termination and restores state on launch at [L149-L158](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/MessageRouter.swift#L149-L158). This merge process recovers undelivered messages and reconstructs the routing queue without requiring re-transmission from the UI layer.

### What happens when a message exceeds the 24-hour TTL?

The [`cleanupExpiredMessages`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/MessageRouter.swift#L85-L92) routine periodically scans the outbox for entries exceeding the TTL threshold. Expired messages are purged from both memory and persistent storage, and the `onMessageDropped` callback notifies the UI to update message status indicators.

### How does the MessageRouter differ from the Transport layer in BitChat?

While **Transport** implementations (defined in [[`Transport.swift`](https://github.com/permissionlesstech/bitchat/blob/main/Transport.swift)](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/Transport.swift)) handle protocol-specific wire transmission, the **MessageRouter** manages cross-transport decision logic, message queuing, and delivery guarantees. The router consumes transport interfaces but adds store-and-forward semantics, retry logic, and outbox persistence that individual transports do not implement.