# How BitChat Integrates with Nostr Relays for Extended Global Messaging

> Discover how BitChat integrates with Nostr relays for global messaging. Learn how it uses Nostr events and a NostrRelayManager for secure, decentralized communication.

- Repository: [permissionlesstech/bitchat](https://github.com/permissionlesstech/bitchat)
- Tags: integration-guide
- Published: 2026-07-27

---

**BitChat extends its Bluetooth mesh network to internet scale by wrapping encrypted messages in Nostr events (kind-1059) and delegating all relay communication to the `NostrRelayManager`, which maintains WebSocket connections, handles offline queuing, and verifies Schnorr signatures without centralized servers.**

BitChat is a decentralized messaging application that combines local Bluetooth mesh networking with global internet fallback using the **Nostr protocol**. When peers move out of Bluetooth range, the app seamlessly switches to Nostr relays to carry end-to-end encrypted private messages, leveraging the open-source relay ecosystem rather than operating proprietary infrastructure. This architecture allows users to communicate globally while maintaining the same cryptographic protections used in local mesh mode, with fine-grained control over relay selection and Tor routing.

## Architecture Overview: Bridging Local Mesh to Global Nostr

BitChat’s global messaging layer re-uses the existing Nostr relay infrastructure through a clean abstraction that separates identity management, transport encoding, and connection handling.

The high-level flow works as follows:

- **Identity bridging**: Each device generates a **Nostr identity** via `NostrIdentityBridge`, which can derive per-geohash identities for location-based channels.
- **Transport selection**: The `ChatViewModel` decides whether a peer is reachable locally (Bluetooth) or requires internet fallback, handing messages to `NostrTransport` when necessary.
- **Message encoding**: Private payloads are wrapped in BitChat-specific **private-envelope** events using `NostrEmbeddedBitChat`, encrypted with **XChaCha20-Poly1305**, and encoded as Nostr `kind-1059` events.
- **Relay management**: `NostrTransport` delegates all network I/O to the singleton `NostrRelayManager`, which maintains a pool of WebSocket connections to configurable relays.
- **Inbound verification**: Each relay runs an off-main-thread pipeline that verifies **Schnorr signatures**, de-duplicates events, and dispatches verified messages to registered handlers.

## Core Components for Nostr Integration

BitChat’s Nostr integration relies on specialized components that handle distinct responsibilities:

### NostrIdentityBridge

Located in [`bitchat/Nostr/NostrIdentityBridge.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Nostr/NostrIdentityBridge.swift), this class bridges the BitChat keychain to Nostr keypairs. It derives per-geohash identities for location-based channels and exposes the current Nostr identity to transport layers.

### NostrTransport

Implemented in [`bitchat/Services/NostrTransport.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/NostrTransport.swift), this class conforms to the `Transport` protocol and serves as the internet fallback layer. It resolves recipient `npub` identifiers to hex public keys, encrypts content using `encodePMForNostr`, and forwards events to `NostrRelayManager`. It also manages **ACK pacing** through the `AckPacer` class to avoid rate-limit throttling.

### NostrRelayManager

The central coordinator in [`bitchat/Nostr/NostrRelayManager.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Nostr/NostrRelayManager.swift) manages WebSocket lifecycles. It maintains two relay lists: `builtInRelays` (a hard-coded list of DM-capable relays) and `customRelays` (user-augmented). The manager handles lazy connection establishment via `ensureConnections(to:)`, implements jittered exponential backoff for reconnections, and respects Tor routing settings.

### NostrEmbeddedBitChat

This helper in [`bitchat/Nostr/NostrEmbeddedBitChat.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Nostr/NostrEmbeddedBitChat.swift) provides static methods to format BitChat-specific envelopes. It constructs the `kind-1059` private-message events and ACK notifications that carry the encrypted BitChat payloads.

## Sending Messages: From BitChat to Nostr Events

When `ChatViewModel` determines that a peer requires Nostr transport, the message flows through several encryption and encoding stages before reaching the relay network.

First, `NostrTransport.sendPrivateMessage` (lines 54-66) resolves the recipient’s Nostr public key via the identity bridge. The content is then encrypted and wrapped using `NostrEmbeddedBitChat.encodePMForNostr` (lines 60-63), which produces a payload encrypted with XChaCha20-Poly1305.

The transport creates a Nostr event using `NostrProtocol.createPrivateMessage` with `kind-1059` and hands it to `NostrRelayManager.shared.sendEvent` (lines 45-46). If no relay connection is active, the event is queued via `enqueuePendingSend` (lines 70-78) until a socket becomes available.

## Managing Relay Connections and Policies

`NostrRelayManager` implements sophisticated connection handling to ensure message delivery across unreliable networks.

### Relay Selection

The manager distinguishes between `builtInRelays` (hard-coded defaults enabled only when users have mutual favorites, location permissions, or active location channels) and `customRelays` (user-defined endpoints). The default policy is applied via `applyDefaultRelayPolicy` (lines 8-13).

### Connection Handling

Connections are opened lazily through `ensureConnections(to:)` (lines 31-42), which respects Tor bootstrap states and implements automatic reconnection with jittered exponential delays. The `connectToRelay` flow (lines 108-129) establishes WebSocket sessions and configures the inbound pipeline.

### Inbound Verification

Each connected relay spawns an `InboundFrameRouter` that parses WebSocket frames and verifies Schnorr signatures using `event.isValidSignature()`. The `precheckInboundEvent` and `deliverVerifiedInboundEvent` methods (lines 99-115) ensure only cryptographically valid events reach the UI layer, preventing spam and forgery.

## Practical Code Examples

### Sending a Private Message via Nostr

```swift
let transport = NostrTransport(
    keychain: KeychainManager.shared,
    idBridge: NostrIdentityBridge()
)

let peerID = PeerID(str: "…")                 // Short or full Noise ID
let message = "Hello from BitChat!"
let messageID = UUID().uuidString

transport.sendPrivateMessage(
    message,
    to: peerID,
    recipientNickname: "Bob",
    messageID: messageID
)

```

This resolves the recipient’s Nostr pubkey, encrypts the content with XChaCha20-Poly1305, and transmits it via available relays.

### Queuing During Offline States

```swift
// The app is offline or Tor is still bootstrapping
transport.sendPrivateMessage("Queued msg", to: peerID, recipientNickname: "Bob", messageID: "1234")

```

When `NostrRelayManager` has no active connection, the event enters `messageQueue`. Once any target relay connects, `flushMessageQueue` automatically transmits pending messages.

### Subscribing to Inbound Events

```swift
let filter = NostrFilter(kinds: [1059], authors: nil /*…*/)
NostrRelayManager.shared.subscribe(
    filter: filter,
    id: "bitchat-pm-sub",
    handler: { event in
        BitChatMessageProcessor.process(event)
    },
    onEOSE: {
        print("Initial history loaded")
    }
)

```

The subscription queues until relays connect, then processes incoming events through the signature-verification pipeline before handing them to the chat UI.

### Adding Custom Relays

```swift
NostrRelaySettings.addCustomRelay(url: "wss://my-relay.example.com")

```

This posts a change notification observed by `NostrRelayManager` (lines 57-67), which reloads the custom list and immediately calls `ensureConnections(to:)` for the new URLs.

## Relay-Aware User Interface

The settings UI in [`bitchat/Views/AppInfoView.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Views/AppInfoView.swift) (lines 623-695) exposes `NostrRelayManager.builtInRelayURLs` and `NostrRelayManager.relays` to display connection health. Users can view built-in relay status, add custom endpoints, and monitor transmission errors without editing configuration files.

## Summary

- **BitChat uses Nostr relays** as an internet-scale fallback when Bluetooth mesh connections are unavailable, avoiding centralized server infrastructure.
- **Messages are encrypted** using XChaCha20-Poly1305 and wrapped in Nostr `kind-1059` events via `NostrEmbeddedBitChat` before transmission.
- **The `NostrRelayManager` singleton** handles all WebSocket lifecycle management, including lazy connections, exponential backoff reconnection, and offline message queuing.
- **Security is enforced** through Schnorr signature verification in the inbound pipeline and strict relay selection policies that respect user privacy settings.
- **Users retain control** over which relays to use, with support for both built-in defaults and custom URLs, plus optional Tor routing for metadata protection.

## Frequently Asked Questions

### What type of Nostr events does BitChat use?

BitChat uses **kind-1059** events for private messages. These events contain XChaCha20-Poly1305 encrypted payloads created by `NostrEmbeddedBitChat.encodePMForNostr`, ensuring that relay operators cannot read message content. ACK receipts for delivery and read notifications use the same private-envelope format.

### How does BitChat handle messaging when relays are offline?

When no WebSocket connections are available, `NostrRelayManager.sendEvent` automatically queues events in `messageQueue` via `enqueuePendingSend`. The manager monitors connection states, and once any target relay connects (triggering `connectedConnection(for:)`), `flushMessageQueue` transmits all pending messages without user intervention.

### Can Nostr relays read BitChat message contents?

No. BitChat encrypts all message content locally using **XChaCha20-Poly1305** before placing it in the Nostr event's `content` field. Relays only see encrypted blobs and Schnorr signatures; they cannot decrypt the actual message text, which is only readable by the intended recipient holding the corresponding private key.

### How do users configure which Nostr relays BitChat uses?

Users can add custom relays through `NostrRelaySettings.addCustomRelay`, which persists URLs and notifies `NostrRelayManager` to establish new connections immediately. The UI in [`AppInfoView.swift`](https://github.com/permissionlesstech/bitchat/blob/main/AppInfoView.swift) displays both the hard-coded `builtInRelays` and user-added endpoints, allowing manual deletion of specific relays or reliance solely on custom infrastructure.