# How the Bitchat Client-Side Architecture Is Structured: A Layer-by-Layer Guide

> Explore the Bitchat client-side architecture layer by layer. Understand its UI, ViewModels, Core Services, Cryptography, Sync, and Data Models for secure decentralized messaging.

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

---

**Bitchat's client is a Swift iOS/macOS application organized into six distinct layers—UI, ViewModels, Core Services, Cryptography, Sync, and Data Models—enabling secure, decentralized messaging over multiple transport protocols.**

This article examines the client-side architecture of [bitchat](https://github.com/permissionlesstech/bitchat), an open-source peer-to-peer chat client. Understanding how the bitchat client-side is structured reveals how the app achieves clean separation between presentation logic, business operations, and cryptographic operations while supporting simultaneous BLE mesh, Nostr, and TCP/UDP transports.

## Architectural Overview

The bitchat client-side architecture follows **MVVM** (Model-View-ViewModel) with heavy reliance on **dependency injection** and **protocol-oriented design**. Every major component is replaceable, making the codebase testable and adaptable to new transport mechanisms.

The six primary layers are:

- **Views** — SwiftUI components that render conversation lists, message threads, and composer interfaces
- **ViewModels** — `ObservableObject` classes that bridge user actions to service layer operations
- **Core Services** — High-level business logic for networking, encryption, routing, and presence
- **Cryptography** — Noise protocol implementation for end-to-end encryption
- **Sync & Gossip** — Peer-to-peer state replication and message deduplication
- **Data Models & Identity** — Plain Swift structs defining domain entities and cryptographic identity management

## App Launch and Dependency Injection

The entry point [`BitchatApp.swift`](https://github.com/permissionlesstech/bitchat/blob/main/BitchatApp.swift) establishes the entire dependency graph. Unlike many iOS apps that rely on singletons, bitchat constructs a fresh `AppRuntime` on launch that instantiates all services and injects them into view-models.

According to the source in [`BitchatApp.swift`](https://github.com/permissionlesstech/bitchat/blob/main/BitchatApp.swift), the bootstrap sequence:

1. Creates `Transport`, `NoiseEncryptionService`, `GossipSyncManager`, and `GeohashPresenceService`
2. Passes these to `ChatViewModelBootstrapper.build()`
3. Assigns the resulting `ChatViewModel` to the root view's environment

```swift
// Simplified representation of the bootstrap flow
let runtime = AppRuntime()
let services = runtime.instantiateServices()
let viewModel = ChatViewModelBootstrapper.build(with: services)
rootView.environmentObject(viewModel)

```

This injection pattern keeps view-models free of hardcoded dependencies, allowing identical view code to run against mocks in previews and tests.

## ViewModels: State Management and Coordination

The bitchat client-side structure places significant logic in view-models. [`ChatViewModel.swift`](https://github.com/permissionlesstech/bitchat/blob/main/ChatViewModel.swift) serves as the central observable state holder, but the architecture delegates specialized concerns to **coordinator objects**.

[`ChatViewModelBootstrapper.swift`](https://github.com/permissionlesstech/bitchat/blob/main/ChatViewModelBootstrapper.swift) wires approximately seven coordinators:

| Coordinator | Responsibility |
|-------------|--------------|
| `ChatPublicConversationCoordinator` | Public channel message flows |
| `ChatPrivateConversationCoordinator` | Direct message handling |
| `ChatMediaTransferCoordinator` | Image/audio upload and download |
| `ChatComposerCoordinator` | Input validation and draft management |
| `GeoPresenceTracker` | Location-based peer discovery |
| `BluetoothAlertCoordinator` | BLE permission and state handling |

Each coordinator conforms to a protocol, enabling test doubles and alternative implementations.

### Example: Sending a Message Through the ViewModel

```swift
struct MessageComposer: View {
    @ObservedObject var chatVM: ChatViewModel
    @State private var text = ""

    var body: some View {
        HStack {
            TextField("Type a message…", text: $text)
            Button("Send") {
                // Triggers: validation → MessageRouter → encryption → transport
                chatVM.sendMessage(text: text, media: nil)
                text = ""
            }
        }
    }
}

```

The `sendMessage` method in `ChatViewModel` constructs a `BitchatMessage` struct, then delegates routing to [`MessageRouter.swift`](https://github.com/permissionlesstech/bitchat/blob/main/MessageRouter.swift).

## Core Services: Routing and Transport Abstraction

The service layer in bitchat's client-side architecture abstracts all networking complexity. [`MessageRouter.swift`](https://github.com/permissionlesstech/bitchat/blob/main/MessageRouter.swift) implements the central routing decision engine.

When `sendMessage` reaches the router, it evaluates:

1. **Destination type** — public group, private conversation, or broadcast
2. **Available transports** — BLE mesh proximity, Nostr relay connectivity, or direct TCP/UDP
3. **Encryption requirements** — Noise session existence with recipient

```swift
// Conceptual router logic based on MessageRouter.swift implementation
func route(message: BitchatMessage, to destination: Destination) {
    if canUseBLEMesh(destination) {
        sendViaBLE(message)
    } else if nostrRelayAvailable {
        sendViaNostr(message)
    } else {
        sendViaTransport(message) // TCP/UDP fallback
    }
}

```

[`Transport.swift`](https://github.com/permissionlesstech/bitchat/blob/main/Transport.swift) provides the underlying abstraction. Concrete implementations handle platform-specific networking while presenting a uniform `send(data:to:)` interface.

## Cryptography Layer: Noise Protocol Implementation

Bitchat's client-side security depends on [`Noise/SecureNoiseSession.swift`](https://github.com/permissionlesstech/bitchat/blob/main/Noise/SecureNoiseSession.swift), implementing the [Noise Protocol Framework](https://noiseprotocol.org/) for zero-round-trip encryption.

[`NoiseEncryptionService.swift`](https://github.com/permissionlesstech/bitchat/blob/main/NoiseEncryptionService.swift) manages the session lifecycle:

- Creates `SecureNoiseSession` instances on first contact with a peer
- Rotates keys according to Noise patterns
- Handles pre-key bundling from [`Identity/SecureIdentityStateManager.swift`](https://github.com/permissionlesstech/bitchat/blob/main/Identity/SecureIdentityStateManager.swift)

Each session maintains cryptographic state independent of the transport carrying its packets. This separation means encrypted messages survive transport switching—a message may encrypt over Noise, fragment for BLE, fail, retry over Nostr, and decrypt identically at the recipient.

## Sync and Gossip: Decentralized State Replication

The bitchat client-side structure includes sophisticated peer-to-peer synchronization via [`Sync/GossipSyncManager.swift`](https://github.com/permissionlesstech/bitchat/blob/main/Sync/GossipSyncManager.swift) and [`Sync/RequestSyncManager.swift`](https://github.com/permissionlesstech/bitchat/blob/main/Sync/RequestSyncManager.swift).

These components implement **epidemic broadcast** for consistency without central servers:

- `GossipSyncManager` periodically exchanges `RequestSyncPacket` structures with discovered peers
- `RequestSyncManager` handles incoming sync requests and constructs delta responses
- [`Sync/SyncResponseRateLimiter.swift`](https://github.com/permissionlesstech/bitchat/blob/main/Sync/SyncResponseRateLimiter.swift) prevents sync flooding

The sync layer operates below message routing but above raw transport, ensuring conversation history converges even when participants intermittently connect through different network paths.

## Identity and Persistence

Cryptographic identity lives in [`Identity/SecureIdentityStateManager.swift`](https://github.com/permissionlesstech/bitchat/blob/main/Identity/SecureIdentityStateManager.swift), which:

- Generates X25519 key pairs on first launch
- Batches pre-keys for offline peer initialization
- Persists secrets via [`KeychainManager.swift`](https://github.com/permissionlesstech/bitchat/blob/main/KeychainManager.swift)

The keychain integration uses iOS SecItem APIs with `kSecAttrAccessibleAfterFirstUnlock` for background message decryption. Group membership and message content use plain Swift structs ([`Models/BitchatPeer.swift`](https://github.com/permissionlesstech/bitchat/blob/main/Models/BitchatPeer.swift), `Models/BitchatMessage+Media.swift`) serialized to the app's private storage.

## Key Source Files

| File Path | Architectural Role |
|-----------|-------------------|
| [`BitchatApp.swift`](https://github.com/permissionlesstech/bitchat/blob/main/BitchatApp.swift) | Application entry and dependency graph construction |
| [`ViewModels/ChatViewModel.swift`](https://github.com/permissionlesstech/bitchat/blob/main/ViewModels/ChatViewModel.swift) | Central observable state holder |
| [`ViewModels/ChatViewModelBootstrapper.swift`](https://github.com/permissionlesstech/bitchat/blob/main/ViewModels/ChatViewModelBootstrapper.swift) | Factory for view-models and coordinators |
| [`ViewModels/GeohashSubscriptionManager.swift`](https://github.com/permissionlesstech/bitchat/blob/main/ViewModels/GeohashSubscriptionManager.swift) | Location-based presence observation |
| [`Services/Transport.swift`](https://github.com/permissionlesstech/bitchat/blob/main/Services/Transport.swift) | Multi-protocol networking abstraction |
| [`Services/MessageRouter.swift`](https://github.com/permissionlesstech/bitchat/blob/main/Services/MessageRouter.swift) | Message routing logic across transports |
| [`Services/NoiseEncryptionService.swift`](https://github.com/permissionlesstech/bitchat/blob/main/Services/NoiseEncryptionService.swift) | Noise session management |
| [`Services/GeohashPresenceService.swift`](https://github.com/permissionlesstech/bitchat/blob/main/Services/GeohashPresenceService.swift) | Geographic peer discovery |
| [`Noise/SecureNoiseSession.swift`](https://github.com/permissionlesstech/bitchat/blob/main/Noise/SecureNoiseSession.swift) | Concrete Noise protocol implementation |
| [`Sync/GossipSyncManager.swift`](https://github.com/permissionlesstech/bitchat/blob/main/Sync/GossipSyncManager.swift) | Peer-to-peer gossip synchronization |
| [`Identity/SecureIdentityStateManager.swift`](https://github.com/permissionlesstech/bitchat/blob/main/Identity/SecureIdentityStateManager.swift) | Cryptographic identity lifecycle |
| `Models/BitchatMessage+Media.swift` | Message data model with media support |
| [`Utils/MessageDeduplicator.swift`](https://github.com/permissionlesstech/bitchat/blob/main/Utils/MessageDeduplicator.swift) | Duplicate message suppression |

## Summary

- **Bitchat's client-side architecture** separates UI, state, services, cryptography, sync, and persistence into distinct layers
- **MVVM with coordinators** keeps view code simple while isolating complex business logic
- **Dependency injection** throughout enables testing and transport flexibility
- **Noise protocol implementation** in [`SecureNoiseSession.swift`](https://github.com/permissionlesstech/bitchat/blob/main/SecureNoiseSession.swift) provides modern encryption
- **Gossip-based sync** allows decentralized operation without central servers
- **Multi-transport support** (BLE, Nostr, TCP/UDP) is transparent to UI code through [`Transport.swift`](https://github.com/permissionlesstech/bitchat/blob/main/Transport.swift) abstraction

## Frequently Asked Questions

### What architecture pattern does bitchat use?

Bitchat uses **MVVM** with coordinator objects. SwiftUI views observe `ObservableObject` view-models, which delegate to specialized coordinators for concerns like public conversations, private messages, media transfer, and Bluetooth state. This pattern keeps views thin and testable.

### How does bitchat handle multiple transport protocols?

The [`MessageRouter.swift`](https://github.com/permissionlesstech/bitchat/blob/main/MessageRouter.swift) service evaluates destination type, network availability, and encryption state to select among BLE mesh, Nostr relays, or direct TCP/UDP. The [`Transport.swift`](https://github.com/permissionlesstech/bitchat/blob/main/Transport.swift) abstraction hides protocol specifics from higher layers.

### Where is the encryption implemented in bitchat?

End-to-end encryption lives in [`Noise/SecureNoiseSession.swift`](https://github.com/permissionlesstech/bitchat/blob/main/Noise/SecureNoiseSession.swift), implementing the Noise Protocol Framework. [`NoiseEncryptionService.swift`](https://github.com/permissionlesstech/bitchat/blob/main/NoiseEncryptionService.swift) manages session creation and key rotation, while [`Identity/SecureIdentityStateManager.swift`](https://github.com/permissionlesstech/bitchat/blob/main/Identity/SecureIdentityStateManager.swift) handles long-term key storage in the iOS keychain.

### How does bitchat synchronize messages without a server?

[`Sync/GossipSyncManager.swift`](https://github.com/permissionlesstech/bitchat/blob/main/Sync/GossipSyncManager.swift) implements epidemic broadcast: peers exchange `RequestSyncPacket` structures to discover missing messages. The protocol converges on consistent history through pairwise synchronization, with rate limiting in [`Sync/SyncResponseRateLimiter.swift`](https://github.com/permissionlesstech/bitchat/blob/main/Sync/SyncResponseRateLimiter.swift) to control bandwidth.