# What Are the Core Components of the Bitchat Architecture? A Deep Dive into the Dual-Transport Messaging Stack

> Explore the core components of the Bitchat architecture, a dual-transport messaging stack merging local Bluetooth-mesh with global Nostr-relay for seamless communication.

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

---

**Bitchat's core architecture centers on a dual-transport messaging stack that combines a local Bluetooth-mesh layer with a global Nostr-relay layer, unified through a transport-agnostic protocol abstraction.**

The bitchat project (permissionlesstech/bitchat) implements a privacy-first, peer-to-peer messaging system designed to function seamlessly across both offline mesh networks and internet-connected relays. Understanding the core components of the bitchat architecture reveals how the system achieves resilience, privacy, and seamless failover between local and global transports.

## The Dual-Transport Foundation: BLEService and NostrRelayManager

At the heart of bitchat lie two complementary transport implementations that the application orchestrates automatically.

### BLEService: The Bluetooth-Mesh Engine

**BLEService** implements the local Bluetooth-mesh transport in [`bitchat/Services/BLE/BLEService.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/BLE/BLEService.swift) (class definition at line 78). This component handles:

- **Peer discovery** and link-state management for nearby devices
- **Noise-protocol handshakes** for secure session establishment
- **Fragment re-assembly** for large message and file transfers
- **Secure private-media transfers** over the mesh

The BLEService contains the complete mesh engine and operates independently of internet connectivity, enabling messaging in offline scenarios such as festivals, protests, or disaster zones.

### NostrRelayManager: The Global Relay Gateway

**NostrRelayManager** manages WebSocket connections to Nostr relays in [`bitchat/Nostr/NostrRelayManager.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Nostr/NostrRelayManager.swift) (defined at line 31). Its responsibilities include:

- Encoding and decoding Nostr events
- Event **deduplication** across multiple relays
- **Exponential back-off** for reconnections
- **Outbound message queuing** during network unavailability

This component ensures messages reach recipients even when they are outside Bluetooth range, using the decentralized Nostr relay network as a fallback.

## Transport Protocol Abstraction: The Unified Interface

Both transport layers conform to a common **`Transport`** protocol defined in [`bitchat/Services/Transport.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/Transport.swift) (line 7). This abstraction enables the rest of the application to send messages without knowledge of the underlying medium.

```swift
// Sending a message transparently chooses Bluetooth first, then Nostr if needed
chatViewModel.sendMessage("Hello, mesh!", mentions: [], to: nil)

// Private direct messages use the same interface with a recipient PeerID
let peer = PeerID(str: "ab12cd34ef56...")
chatViewModel.sendMessage("Secret note", mentions: [], to: peer)

```

The transport layer internally prioritizes BLEService when peers are nearby, falling back to NostrRelayManager only when necessary.

## Synchronization and State Management

### GossipSyncManager: Periodic Mesh Synchronization

**GossipSyncManager** ([`bitchat/GossipSyncManager.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/GossipSyncManager.swift)) performs periodic "gossip" sync across the Bluetooth mesh. Instantiated within `BLEService.startServices()`, it exchanges:

- Public message packets
- File-transfer fragments
- Pre-key bundles for encryption

This gossip protocol keeps local state consistent across nearby peers without requiring a central coordinator.

### RequestSyncManager: On-Demand Synchronization

**RequestSyncManager** ([`bitchat/RequestSyncManager.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/RequestSyncManager.swift)) handles explicit sync requests, such as when a user joins a new location channel. It tracks pending **EOSE (End-Of-Stored-Events)** callbacks from relays, ensuring complete message history retrieval.

## Message Integrity: Deduplication and Broadcast Tracking

Two specialized components prevent duplicate processing:

| Component | Location | Purpose |
|-----------|----------|---------|
| **MessageDeduplicator** | [`bitchat/MessageDeduplicator.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/MessageDeduplicator.swift) | De-duplicates inbound events across both BLE and Nostr transports |
| **BLESelfBroadcastTracker** | [`bitchat/BLESelfBroadcastTracker.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/BLESelfBroadcastTracker.swift) | Prevents locally-sent broadcasts from being re-processed when echoed by peers |

These mechanisms are essential for maintaining consistency in a distributed system where messages may arrive through multiple paths.

## Identity and Encryption Layer

Bitchat employs dual encryption strategies:

- **Noise Protocol** ([`bitchat/Services/NoiseEncryptionService.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/NoiseEncryptionService.swift)): End-to-end encryption for Bluetooth-mesh traffic
- **Proprietary XChaCha20-Poly1305 envelope** ([`bitchat/Nostr/NostrIdentityBridge.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Nostr/NostrIdentityBridge.swift)): Encryption wrapper for Nostr private messages

The NostrIdentityBridge bridges the application-specific private-envelope format to standard Nostr events, preserving compatibility with the broader Nostr ecosystem while adding custom security layers.

## Channel and Location Routing

Bitchat maps **geographic geohash channels** (block, neighborhood, city tiers) to Nostr relay subscriptions. This location-based routing system, documented in [`docs/GeohashPresenceSpec.md`](https://github.com/permissionlesstech/bitchat/blob/main/docs/GeohashPresenceSpec.md), enables geographically-scoped group chats.

```swift
// Subscribe to a location channel using geohash "dr5rsj"
let filter = NostrFilter(kinds: [1], tags: ["#geohash": ["dr5rsj"]])
NostrRelayManager.shared.subscribe(filter: filter, handler: { event in
    // Process incoming event…
})

```

NostrRelayManager constructs these filters automatically when users join location-based channels.

## Persistence and Store-And-Forward

Offline resilience depends on several store implementations:

- **`CourierStore`** ([`bitchat/Stores/CourierStore.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Stores/CourierStore.swift)): Queues outbound courier envelopes for delayed delivery
- **`BoardStore`** ([`bitchat/Stores/BoardStore.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Stores/BoardStore.swift)): Persists board posts
- **`BLEIncomingFileStore`** ([`bitchat/Stores/BLEIncomingFileStore.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Stores/BLEIncomingFileStore.swift)): Manages private-media receipts

These stores ensure no messages are lost when recipients are temporarily unreachable.

## UI Integration: Delegate Pattern

The architecture exposes state changes to the view layer through **`BitchatDelegate`** and **`TransportEventDelegate`** protocols. Declared in `bitchat/bitchat.entitlements` and consumed throughout [`ChatViewModel.swift`](https://github.com/permissionlesstech/bitchat/blob/main/ChatViewModel.swift), these delegates deliver:

- Decrypted public messages
- Private-message envelopes
- Connection-state changes

This separation maintains clean boundaries between transport logic and presentation logic.

## Summary

- **BLEService** provides local Bluetooth-mesh messaging with Noise encryption and fragment handling
- **NostrRelayManager** enables global reach through WebSocket connections to decentralized relays
- **Transport protocol abstraction** unifies both layers behind a common interface
- **GossipSyncManager and RequestSyncManager** handle periodic and on-demand state synchronization
- **MessageDeduplicator and BLESelfBroadcastTracker** ensure exactly-once message semantics
- **Dual encryption layers** protect traffic appropriate to each transport's threat model
- **Geohash-based channel routing** enables location-scoped conversations
- **Store-and-forward persistence** guarantees message delivery despite intermittent connectivity

## Frequently Asked Questions

### How does bitchat decide between Bluetooth and Nostr for message delivery?

The transport layer automatically prioritizes BLEService when target peers are within Bluetooth range. If no mesh path exists, the message routes through NostrRelayManager. This decision is transparent to calling code due to the Transport protocol abstraction in [`bitchat/Services/Transport.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/Transport.swift).

### What encryption does bitchat use for private messages?

For Bluetooth-mesh traffic, bitchat uses the **Noise Protocol** implemented in [`bitchat/Services/NoiseEncryptionService.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/NoiseEncryptionService.swift). For Nostr relay traffic, it uses a **proprietary XChaCha20-Poly1305 envelope** defined in [`bitchat/Nostr/NostrIdentityBridge.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Nostr/NostrIdentityBridge.swift), providing authenticated encryption compatible with the Nostr ecosystem.

### Can bitchat function completely offline?

Yes. When no internet connectivity exists, BLEService maintains full messaging capability within Bluetooth range (typically 10-100 meters depending on device and environment). Messages queue in CourierStore for Nostr delivery when connectivity returns, and file transfers complete directly over the mesh via fragment re-assembly.

### How does bitchat prevent the same message from appearing multiple times?

MessageDeduplicator ([`bitchat/MessageDeduplicator.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/MessageDeduplicator.swift)) maintains a cache of seen message identifiers across both transports. BLESelfBroadcastTracker ([`bitchat/BLESelfBroadcastTracker.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/BLESelfBroadcastTracker.swift)) specifically prevents echo effects where a device receives its own broadcast back through mesh hops. Together they ensure users see each message exactly once.