How BitChat Enables Decentralized Peer-to-Peer Messaging: Bluetooth Mesh and Nostr Protocol
BitChat enables decentralized peer-to-peer messaging by combining a Bluetooth mesh network for offline local communication with the Nostr relay network for global internet reach, using Noise encryption for Bluetooth links and custom XChaCha20-Poly1305 private envelopes for Nostr transport.
BitChat, developed by permissionlesstech/bitchat, implements a hybrid architecture that ensures message delivery regardless of internet connectivity. The system automatically routes communications through a local Bluetooth mesh when offline, falling back to Nostr relays when WAN access is available, all while maintaining end-to-end encryption across both transports.
Dual-Transport Architecture
BitChat operates on two complementary transport layers that share a common cryptographic foundation. This design ensures that users can exchange messages in completely offline environments while retaining the ability to reach global recipients when connected.
- Bluetooth Mesh Layer: Handles direct, local-area communication without internet access. Implemented primarily in
BLEService.swift, this layer manages device discovery, multi-hop routing, and link-level encryption using the Noise protocol. - Nostr Relay Network: Provides global reach through public Nostr relays. The
NostrProtocol.swiftfile implements BitChat-specific private envelopes that encrypt messages using XChaCha20-Poly1305 before transmission over standard Nostr infrastructure.
Both layers use the same message packet model, ensuring consistent end-to-end security regardless of which transport handles the data.
Bluetooth Mesh Transport for Offline Messaging
The Bluetooth mesh functionality transforms mobile devices into nodes of a multi-hop ad-hoc network capable of relaying messages across physical distances without internet infrastructure.
Discovery and Link Establishment
The BLERadioController within BLEService.swift manages device discovery by scanning for the BitChat service UUID (F47B5E2D‑…‑B5C). The implementation supports both central and peripheral roles simultaneously, allowing devices to accept incoming connections while actively scanning for peers.
Each established link initiates a Noise-IK handshake through NoiseEncryptionService to derive a unique shared secret. This provides forward secrecy and authentication for all subsequent traffic on that specific Bluetooth connection, stored and tracked in BLELinkAuthState.swift.
Packet Structure and Binary Protocol
Messages transmitted over Bluetooth use a compact binary format defined in BinaryProtocol.swift. The BitchatPacket structure includes:
let packet = BitchatPacket(
type: MessageType.message.rawValue,
senderID: myPeerID.data,
recipientID: nil,
timestamp: UInt64(Date().timeIntervalSince1970 * 1000),
payload: Data(envelope.jsonString().utf8),
signature: nil,
ttl: TransportConfig.messageTTLDefault
)
let binary = BinaryProtocol.encode(packet)! // padded for BLE MTU
The binary protocol encodes a header containing version, type, TTL, timestamp, flags, and payload length, followed by sender/recipient IDs, optional route hops, optional compression preamble, the encrypted payload, and an optional signature. This format minimizes airtime usage while supporting mesh routing features.
Fragmentation and Reassembly
Given BLE MTU limitations, large messages undergo fragmentation handled by BLEOutboundFragmentTransferScheduler and BLEFragmentHandler.swift. The BLEFragmentAssemblyBuffer reconstructs incoming fragments into complete messages before decryption and delivery to the application layer.
Routing and Deduplication
When a packet includes the hasRoute flag, peers forward it hop-by-hop until it reaches the destination or the TTL expires. The BLESelfBroadcastTracker and MessageDeduplicator prevent routing loops and duplicate processing, ensuring efficient bandwidth utilization in dense mesh networks.
Nostr Protocol Integration for Global Reach
When Bluetooth peers are unavailable, BitChat seamlessly transitions to Nostr relays while maintaining message confidentiality through a proprietary envelope format incompatible with standard NIP-44.
BitChat Private Envelope Construction
The NostrProtocol.swift file implements a three-layer envelope system:
- Rumor Creation: Generate an unsigned
NostrEventof kind 14 (DM) containing the plaintext message. - Seal Encryption: Encrypt the rumor with the recipient’s public key using
createSeal, then sign the sealed event with the sender’s long-term Schnorr key. - Gift-Wrap: Encrypt the sealed event with a freshly generated ephemeral Schnorr key using
createGiftWrap, producing a kind 1059 event.
The resulting envelope format (v2: + Base64URL(nonce || ciphertext || tag)) uses HKDF-derived symmetric keys and XChaCha20-Poly1305 authenticated encryption. This process ensures that Nostr relays only store opaque ciphertext while preserving sender authentication.
Automatic Transport Fallback
The transport selection logic resides in BLEService.sendMessage and the ChatTransportEventCoordinator. When invoked, the system first attempts transmission via sendPrivateMessage over the Bluetooth mesh. If no path exists, the message undergoes the private envelope creation process described above and publishes via NostrRelayManager.
Incoming Nostr events trigger decryptPrivateMessage, which unwraps the gift-wrap and seal layers to extract the inner rumor’s content for UI delivery.
End-to-End Security Implementation
BitChat employs transport-specific encryption mechanisms that provide defense in depth:
- Bluetooth Links: Noise-IK handshakes establish per-link encryption with forward secrecy, implemented in
NoiseEncryptionService. - Nostr Transport: XChaCha20-Poly1305 via
XChaCha20Poly1305Compatprovides authenticated encryption for the private envelope, while Schnorr signatures verify sender identity. - Optional Compression: The
BinaryProtocolapplies zlib compression only when it reduces payload size, storing the original length for safe decompression.
All cryptographic primitives are wrapped in type-safe services and validated before use, ensuring that private keys never leave the secure enclave during normal operations.
Practical Implementation Examples
To send a message through BitChat’s automatic transport selection:
// Send a text message – the service picks the best transport automatically
bleService.sendMessage(
"Hello from the mesh!",
mentions: [],
to: nil // nil ⇒ public broadcast; give a PeerID for a DM
)
For testing or manual relay transmission, construct a private envelope directly:
// Build a BitChat private envelope manually (useful for testing)
let envelope = try NostrProtocol.createPrivateMessage(
content: "Secret hello",
recipientPubkey: recipientPubkeyHex,
senderIdentity: myNostrIdentity
)
Summary
- BitChat achieves decentralized peer-to-peer messaging through a dual-layer architecture combining Bluetooth mesh and Nostr relays.
- The Bluetooth mesh layer (
BLEService.swift) provides offline capability via multi-hop routing, Noise-IK encryption, and adaptive fragmentation. - The Nostr layer (
NostrProtocol.swift) enables global reach using a proprietary gift-wrap/seal/rumor envelope format with XChaCha20-Poly1305 encryption. - Automatic fallback logic attempts Bluetooth delivery first, transitioning to Nostr relays only when mesh connectivity is unavailable.
- All communications maintain end-to-end encryption appropriate to their transport, with no plaintext exposure to intermediaries or relays.
Frequently Asked Questions
How does BitChat handle messaging without internet connectivity?
BitChat uses a Bluetooth mesh network where devices act as relays for one another. When you send a message, BLEService.swift attempts to transmit it through local Bluetooth connections using sendPrivateMessage. If the recipient is not directly in range, other BitChat devices forward the packet hop-by-hop until delivery or TTL expiration, all without requiring internet access.
What encryption standards does BitChat use for decentralized peer-to-peer messaging?
BitChat employs two complementary encryption schemes. For Bluetooth links, it uses Noise-IK handshakes (NoiseEncryptionService) to establish forward-secure channels between directly connected peers. For Nostr transport, it uses XChaCha20-Poly1305 authenticated encryption within a custom private envelope format (NostrProtocol.swift), combined with Schnorr signatures for sender verification.
How does BitChat prevent infinite message loops in the Bluetooth mesh?
The implementation includes BLESelfBroadcastTracker and MessageDeduplicator services that maintain seen-message caches. Additionally, every BitchatPacket contains a TTL (time-to-live) field decremented at each hop. When TTL reaches zero or a device recognizes it has already processed the message ID, forwarding stops immediately, preventing broadcast storms and bandwidth exhaustion.
Is BitChat compatible with standard Nostr clients?
No, BitChat uses a proprietary private envelope format (v2: + Base64URL encoding) that is incompatible with NIP-44. While the underlying Nostr event structure follows Nostr protocol specifications (using kind 1059 for gift-wraps), the specific HKDF key derivation and XChaCha20-Poly1305 implementation in NostrProtocol.swift are unique to BitChat. Standard Nostr clients cannot decrypt BitChat messages without implementing the same cryptographic pipeline.
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 →