How BitChat Implements Decentralized Peer-to-Peer Messaging: Bluetooth Mesh and Nostr Protocol
BitChat achieves decentralized peer-to-peer messaging by combining a Bluetooth mesh network for offline local communication with a Nostr relay network for global internet reach, using Noise encryption for links and a proprietary private-envelope format for end-to-end security.
The permissionlesstech/bitchat repository implements a hybrid decentralized messaging system that operates entirely without centralized servers. By fusing two complementary transport layers—Bluetooth Low Energy (BLE) for offline peer-to-peer mesh networking and the Nostr protocol for online relay-based messaging—BitChat ensures message delivery whether devices are physically adjacent or continents apart. This architecture guarantees privacy through local cryptographic operations, with all encryption and signing performed on-device before transmission.
Dual-Transport Architecture
BitChat’s core innovation lies in its seamless integration of two distinct communication layers:
- Bluetooth Mesh (Offline) – Enables direct, local-area communication without cellular or Wi-Fi connectivity
- Nostr Relay Network (Online) – Provides global reach through public Nostr relays when mesh connectivity is unavailable
The system automatically selects the optimal transport based on peer availability, falling back from BLE to Nostr transparently. Both layers share a common packet model and cryptographic foundation, ensuring consistent security regardless of transmission method.
Bluetooth Mesh Transport Implementation
Discovery and Link Management
The Bluetooth mesh functionality resides primarily in BLEService.swift, which manages device discovery and connection state. The BLERadioController scans for peers advertising the BitChat service UUID (F47B5E2D‑…‑B5C), supporting both central and peripheral roles simultaneously.
Each established link undergoes a Noise-IK handshake via NoiseEncryptionService to derive a unique shared secret for per-link encryption. The BLELinkAuthState.swift file tracks the authentication state of each connection, ensuring forward secrecy for live traffic.
Packet Structure and Framing
Messages transmitted over BLE use a compact binary protocol defined in BinaryProtocol.swift. The BinaryProtocol.encode function constructs packets with the following structure:
- Binary header (version, type, TTL, timestamp, flags, payload length)
- Sender and recipient IDs
- Optional route hops for multi-hop forwarding
- Optional compression preamble
- Payload data
- Optional signature
// Construct a BitChat packet for BLE transmission
let packet = BitchatPacket(
type: MessageType.message.rawValue,
senderID: myPeerID.data,
recipientID: nil, // nil indicates public broadcast
timestamp: UInt64(Date().timeIntervalSince1970 * 1000),
payload: Data(envelope.jsonString().utf8),
signature: nil,
ttl: TransportConfig.messageTTLDefault
)
let binary = BinaryProtocol.encode(packet)! // Automatically padded for BLE MTU
Fragmentation and Routing
Large payloads exceeding BLE MTU limits are automatically fragmented by BLEOutboundFragmentTransferScheduler and reassembled by BLEFragmentHandler using BLEFragmentAssemblyBuffer. The mesh supports multi-hop routing when the hasRoute flag is set—peers forward packets hop-by-hop until the TTL expires or the destination is reached.
Deduplication is handled by MessageDeduplicator and BLESelfBroadcastTracker, which prevent routing loops and duplicate processing across the mesh.
Nostr Protocol and Private Envelopes
When internet connectivity is available or when BLE peers are unreachable, BitChat utilizes the Nostr relay network through NostrProtocol.swift. Unlike standard Nostr DMs, BitChat implements a proprietary private-envelope format using three nested layers of encryption.
The Three-Layer Envelope Structure
- Rumor – An unsigned
NostrEventof kind 14 (DM) containing the plaintext message - Seal – The rumor encrypted with the recipient’s public key, then signed with the sender’s long-term Schnorr key via
createSeal - Gift-Wrap – The sealed event encrypted with a freshly generated ephemeral Schnorr key and signed via
createGiftWrap, producing a kind 1059 event
The resulting envelope uses the format v2: + Base64URL(nonce || ciphertext || tag) with HKDF-derived symmetric keys. This BitChat-specific implementation employs XChaCha20-Poly1305 encryption and is not compatible with NIP-44.
Encryption Implementation
All cryptographic operations occur locally; relays only receive opaque ciphertext. The NostrProtocol class handles both encryption and decryption:
// Build a BitChat private envelope manually
let envelope = try NostrProtocol.createPrivateMessage(
content: "Secret hello",
recipientPubkey: recipientPubkeyHex,
senderIdentity: myNostrIdentity
)
Incoming Nostr events are decrypted via decryptPrivateMessage, extracting the inner rumor’s content for delivery to the UI.
Automatic Transport Fallback
BitChat implements intelligent transport selection in BLEService.sendMessage. The system first attempts delivery via sendPrivateMessage over the Bluetooth mesh. If no mesh link is available, the message automatically wraps in a BitChat private envelope and publishes through NostrRelayManager.
This fallback mechanism ensures message delivery operates as a unified ChatTransportEventCoordinator, abstracting the underlying transport complexity from the user interface.
Security Architecture
Link-Level Security
Noise Encryption on BLE links guarantees confidentiality and forward secrecy for local traffic. Each peer-to-peer connection maintains independent session keys derived from the Noise-IK handshake.
End-to-End Security
BitChat Private Envelopes provide end-to-end encryption over Nostr, with sender identity authenticated by the seal’s Schnorr signature. The use of ephemeral keys for gift-wrapping ensures that even Nostr relays cannot correlate sender and recipient metadata.
Optional Compression
The BinaryProtocol implements optional zlib compression when payload size reduction is beneficial, storing the original length for safe decompression. This optimization applies only to BLE transport to maximize throughput over limited bandwidth.
Summary
- BitChat combines Bluetooth Mesh (offline) and Nostr relays (online) for universal decentralized peer-to-peer messaging
- BLE transport uses
BLEService.swiftwith Noise-IK handshake, packet fragmentation, and multi-hop routing viaBinaryProtocol.swift - Nostr transport implements a proprietary three-layer envelope (gift-wrap → seal → rumor) using XChaCha20-Poly1305 encryption in
NostrProtocol.swift - Automatic fallback occurs when
BLEService.sendMessagedetects unavailable mesh peers, seamlessly switching toNostrRelayManager - End-to-end security is enforced locally with per-link Noise encryption and ephemeral-key envelopes, ensuring relays and intermediaries process only opaque ciphertext
Frequently Asked Questions
How does BitChat work without internet connectivity?
BitChat utilizes a Bluetooth mesh network implemented in BLEService.swift that operates independently of internet infrastructure. Devices discover each other via BLE advertising (service UUID F47B5E2D‑…‑B5C) and form multi-hop ad-hoc networks. Messages fragment automatically to handle BLE MTU limitations and route through intermediate peers using TTL-based forwarding, enabling communication in offline environments such as disaster zones or remote areas.
What encryption algorithms does BitChat use?
BitChat employs XChaCha20-Poly1305 for Nostr envelope encryption and Noise-IK for Bluetooth link encryption. The Nostr implementation uses HKDF for key derivation and Schnorr signatures for authentication. For BLE transport, the NoiseEncryptionService establishes per-session symmetric keys that provide forward secrecy, ensuring captured traffic cannot be decrypted even if long-term private keys are compromised later.
How is BitChat's Nostr implementation different from standard Nostr DMs?
BitChat uses a proprietary private-envelope format that is incompatible with NIP-44. While standard Nostr DMs typically use simple encryption, BitChat implements a three-layer structure: an unsigned rumor (kind 14), a sealed envelope encrypted to the recipient, and a gift-wrapped event (kind 1059) encrypted with an ephemeral key. This architecture prevents relays from identifying conversation participants or accessing message content, whereas standard Nostr events may expose metadata.
Can messages hop through multiple devices to reach distant recipients?
Yes, the Bluetooth mesh supports multi-hop routing when the hasRoute flag is set in the packet header. BLEService.swift forwards packets hop-by-hop until they reach the destination or the TTL (time-to-live) counter expires. The MessageDeduplicator prevents infinite loops, while BLEFragmentHandler ensures large messages reassemble correctly across multiple hops. This topology allows messages to traverse distances beyond direct Bluetooth range by relaying through intermediate BitChat devices.
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 →