# How Bitchat Implements Real-Time Communication: Nostr Relays and Bluetooth Mesh

> Discover how Bitchat achieves real-time communication using Nostr relays for global access and Bluetooth mesh for efficient local peer discovery. Explore its innovative hybrid architecture.

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

---

**Bitchat implements real-time communication through a hybrid architecture combining Nostr protocol WebSocket relays for global reach and Bluetooth Low Energy mesh networking for low-latency local peer discovery.**

The open-source Bitchat app (permissionlesstech/bitchat) delivers instant messaging without centralized servers by layering two distinct transport mechanisms. The Nostr protocol handles message propagation across the internet through decentralized relays, while a local BLE mesh enables direct device-to-device communication when users are physically nearby.

## Core Components of the Real-Time Stack

### `NostrRelayManager` – WebSocket Connection Lifecycle

The **`NostrRelayManager`** class in [`bitchat/Nostr/NostrRelayManager.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Nostr/NostrRelayManager.swift) forms the backbone of internet-based real-time messaging. It maintains persistent WebSocket connections to multiple Nostr relays and implements a **resilient queuing system** for offline scenarios.

When a user sends a message, the manager checks relay connectivity. If the WebSocket is not yet open, events enter a local pending queue. Once the connection handshake completes, the manager automatically drains this queue. This ensures messages survive temporary network interruptions without user intervention.

### `NostrTransport` – Unified Transport Interface

The **`NostrTransport`** protocol in [`bitchat/Services/NostrTransport.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/NostrTransport.swift) abstracts the complexity of the Nostr layer from the rest of the application. It conforms to a `Transport` protocol used throughout the messaging stack.

All outgoing `NostrEvent` objects flow through `NostrTransport.shared.sendEvent(_:)`. Incoming events arrive via WebSocket callbacks and feed directly into the `MessageRouter`. The shared singleton pattern ensures consistent state across the entire app.

### `RelayController` – Intelligent Message Routing

In [`bitchat/Services/RelayController.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/RelayController.swift), the **`RelayController`** applies policy decisions per-message. It evaluates:

- **TTL (time-to-live)** – Prevents infinite rebroadcast loops
- **Delay jitter** – Obscures timing patterns for privacy
- **Relay policies** – Respects per-relay filtering rules

This component prevents network flooding and keeps bandwidth usage minimal—a critical consideration for mobile devices.

### `MessageRouter` – Inbound Event Distribution

The **`MessageRouter`** in [`bitchat/Services/MessageRouter.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/MessageRouter.swift) receives filtered, validated events and dispatches them to appropriate destinations: chat view controllers, notification services, or background processors. It coordinates with `MessageDeduplicationService` to suppress duplicate displays of the same event arriving from multiple relays.

## Local Real-Time Communication via BLE Mesh

### `MeshTopologyTracker` – Peer Discovery

When internet connectivity is unavailable or suboptimal, **`MeshTopologyTracker`** in [`bitchat/Services/MeshTopologyTracker.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/MeshTopologyTracker.swift) activates. It uses **Bluetooth Low Energy advertisements** to detect nearby Bitchat instances, building a dynamic mesh topology of reachable peers.

The companion **`MeshSightingsTracker`** maintains a routing table of recently seen devices, enabling multi-hop forwarding across the local mesh.

### Direct Peer Routing

Once the mesh identifies a viable path, `NostrTransport` may route events through this local channel instead of internet relays. This yields **sub-second latency** for nearby users and functions entirely offline—a significant advantage over purely cloud-dependent messaging apps.

## Message Flow: End-to-End

1. User composes message → UI constructs `NostrEvent`
2. Event passes to `NostrTransport.send(event:)`
3. Transport delegates to `NostrRelayManager.shared.sendEvent(event)`
4. Manager queues or transmits based on relay connectivity state
5. WebSocket transmission to connected Nostr relays
6. Inbound events filtered through `RelayController`
7. `MessageRouter` distributes to UI and triggers notifications
8. `NostrProcessedEventStore` persists hash for deduplication

When BLE mesh discovery succeeds, steps 4-5 may substitute local mesh transmission for WebSocket relay transmission.

## Code Example: Sending a Real-Time Message

```swift
import Bitchat

// Construct a standard Nostr text event
let event = NostrEvent(
    kind: .textNote,
    content: "Meet me at the corner",
    createdAt: Date(),
    tags: [],
    isRelay: true
)

// Transmit through the shared transport layer
await NostrTransport.shared.send(event)

```

The `send(event:)` call returns once the event enters the transmission pipeline—either immediately via WebSocket, or after queuing for the next available relay or mesh peer.

## Exactly-Once Delivery Guarantee

**`NostrProcessedEventStore`** in [`bitchat/Services/NostrProcessedEventStore.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/NostrProcessedEventStore.swift) maintains a persistent hash set of all processed event IDs. This prevents duplicate processing when:

- The same event arrives from multiple relays
- A device reconnects and receives replayed events
- Mesh rebroadcasts create redundant deliveries

The store operates across app sessions using local persistence, ensuring reliable deduplication even after restarts.

## Summary

- **`NostrRelayManager`** manages WebSocket connections to decentralized Nostr relays with automatic queuing and retry logic
- **`RelayController`** applies per-message policies (TTL, jitter, privacy) to prevent network abuse
- **`MeshTopologyTracker`** enables Bluetooth mesh discovery for direct local communication without internet infrastructure
- **`NostrTransport`** provides a unified API hiding the complexity of relay selection and failover
- **`MessageRouter`** and **`NostrProcessedEventStore`** ensure correct event distribution and exactly-once processing semantics

## Frequently Asked Questions

### How does Bitchat handle offline message sending?

Bitchat queues outgoing messages in `NostrRelayManager` when no relay connection exists. The pending queue automatically drains once any configured relay becomes reachable. This happens transparently without user action.

### What protocol does Bitchat use for internet-based real-time messaging?

Bitchat uses the **Nostr protocol** over WebSocket connections. Nostr is a decentralized, censorship-resistant pub/sub system where clients connect to any available relay to publish and subscribe to events.

### Can Bitchat work without internet connectivity?

Yes. When devices are within Bluetooth Low Energy range, `MeshTopologyTracker` establishes a local mesh network. Messages route directly between peers, achieving real-time communication entirely offline.

### How does Bitchat prevent duplicate messages?

The `NostrProcessedEventStore` persists SHA-256 hashes of every processed event ID to local storage. Incoming events matching known hashes are silently discarded, guaranteeing exactly-once delivery semantics across sessions.