How Bitchat Implements Real-Time Communication: Nostr Relays and Bluetooth Mesh
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 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 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, 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 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 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
- User composes message → UI constructs
NostrEvent - Event passes to
NostrTransport.send(event:) - Transport delegates to
NostrRelayManager.shared.sendEvent(event) - Manager queues or transmits based on relay connectivity state
- WebSocket transmission to connected Nostr relays
- Inbound events filtered through
RelayController MessageRouterdistributes to UI and triggers notificationsNostrProcessedEventStorepersists 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
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 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
NostrRelayManagermanages WebSocket connections to decentralized Nostr relays with automatic queuing and retry logicRelayControllerapplies per-message policies (TTL, jitter, privacy) to prevent network abuseMeshTopologyTrackerenables Bluetooth mesh discovery for direct local communication without internet infrastructureNostrTransportprovides a unified API hiding the complexity of relay selection and failoverMessageRouterandNostrProcessedEventStoreensure 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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →