Fallback Mechanism for BitChat's Mesh-Only Delivery Using Nostr: Implementation Guide
BitChat automatically routes messages over Nostr relays when Bluetooth mesh delivery fails, using a proprietary private-envelope encryption scheme based on secp256k1 key agreement, HKDF-SHA256 derivation, and XChaCha20-Poly1305 to ensure end-to-end confidentiality across public infrastructure.
The permissionlesstech/bitchat repository implements a resilient messaging protocol that prioritizes local Bluetooth mesh networking while seamlessly activating the fallback mechanism for BitChat's mesh-only delivery using Nostr when mesh connectivity is unavailable. This dual-mode architecture ensures message delivery across diverse network conditions, from offline mesh islands to internet-connected Nostr relay networks. Understanding this fallback is essential for developers building censorship-resistant communication tools that must bridge local radio and global relay topologies.
How Mesh-to-Nostr Fallback Works
BitChat routes messages primarily over a local Bluetooth mesh. When a mesh-only peer cannot reach the destination via the mesh—whether due to out-of-range radios or broken mesh paths—the app automatically triggers the fallback mechanism.
The system detects mesh delivery failures through route timeouts or broken path notifications. Once triggered, the message transitions from local radio transmission to Nostr relay propagation, ensuring delivery even when mesh connectivity is disrupted.
The Private Envelope Encryption Process
The fallback mechanism for BitChat's mesh-only delivery using Nostr relies on a proprietary private-envelope format that encrypts payloads before they touch public relays. This process involves three cryptographic stages:
Key Agreement
The sender and receiver derive a shared secret using Nostr-compatible secp256k1 elliptic curve key agreement. As implemented in the source, this establishes a unique session key between communicating parties using their existing Nostr key pairs.
Key Derivation
The shared secret feeds into HKDF-SHA256 to produce a 32-byte encryption key. According to privacy-assessment.md lines 58-61, this derivation uses standard parameters to ensure key separation from the initial secp256k1 output.
Encryption
The plaintext payload is encrypted using XChaCha20-Poly1305, providing authenticated encryption with a 192-bit nonce to prevent replay attacks.
The resulting envelope is opaque to Nostr relays. Only the recipient's static Nostr private key can decrypt the content. However, this design differs from standard Nostr encryption methods like NIP-17, NIP-44, or NIP-59, meaning it does not provide forward secrecy against later compromise of the recipient's static key.
Bridge Events and Relay Publishing
When the bridge capability is enabled, mesh-only devices can publish to Nostr without direct internet connectivity through bridge nodes.
Signing Bridge Events
The mesh-only device signs a bridge event using a per-cell Nostr identity based on geohash location. According to privacy-assessment.md lines 63-65, this ensures radio-only messages cannot enter the Nostr network without the author's cryptographic signature.
The bridge event is the only way a mesh-only peer can push data onto Nostr, preventing unauthorized message injection from offline devices.
Relay Interaction
The bridge event includes the encrypted envelope and publishes to geohash-based Nostr identities. The [NostrTransport.swift](https://github.com/permissionlesstech/bitchat/blob/main/bitchatTests/Services/NostrTransportTests.swift) module handles relay connections and requires an OK true response (per NIP-01 specifications) to confirm successful publication, as documented in privacy-assessment.md lines 62-63.
Implementation Details: Swift Code Examples
The implementation spans several key files including NostrTransport.swift, NostrIdentityBridge.swift, and the PrivateEnvelope logic within the BitFoundation module. The following simplified Swift code illustrates how the fallback is assembled:
// 1️⃣ Derive the shared secret using the recipient’s Nostr public key
let sharedSecret = try Secp256k1.sharedSecret(
privateKey: myNostrKey.privateKey,
peerPublicKey: recipientNostrKey.publicKey
)
// 2️⃣ Derive an encryption key via HKDF‑SHA256
let hkdf = HKDF(
hashFunction: .sha256,
salt: Data(),
info: Data(),
outputSize: 32
)
let encryptionKey = hkdf.deriveKey(inputKeyMaterial: sharedSecret)
// 3️⃣ Encrypt the plaintext payload
let sealed = try XChaCha20Poly1305.seal(
message: plaintextData,
using: encryptionKey,
nonce: .random()
)
// 4️⃣ Wrap the sealed payload in a BitChat private envelope
let envelope = PrivateEnvelope(
ciphertext: sealed.ciphertext,
nonce: sealed.nonce,
tag: sealed.tag,
recipientTag: recipientNostrKey.tag // day‑rotating tag
)
// 5️⃣ Build a bridge event (if mesh‑only peer has bridge capability)
let bridgeEvent = NostrEvent(
kind: .bridge,
content: envelope.serialized(),
tags: [["p", recipientNostrKey.publicKeyHex]],
createdAt: Date()
)
// 6️⃣ Sign and publish the event via NostrTransport
let signed = try nostrIdentityBridge.sign(event: bridgeEvent)
nostrTransport.publish(event: signed) { result in
// Success requires an `OK true` from at least one relay
}
Privacy and Security Considerations
The fallback mechanism preserves end-to-end confidentiality even when messages traverse public Nostr relays. Relays only observe the recipient's public-key tag, event timing, size, and generic network metadata—never the plaintext content.
Because the envelope uses the same format for both direct Nostr messages and bridge courier drops, the mesh-only peer’s payload remains encrypted end-to-end throughout the transition. However, developers should note that the proprietary format lacks perfect forward secrecy, as compromise of the recipient's static Nostr private key could decrypt historical messages stored by relays.
Summary
- BitChat prioritizes Bluetooth mesh routing but automatically falls back to Nostr when mesh paths fail or destinations are out of range.
- The private-envelope protocol uses secp256k1 key agreement, HKDF-SHA256 key derivation, and XChaCha20-Poly1305 encryption to protect message content on public relays.
- Bridge events allow mesh-only devices to publish via Nostr relays using per-cell geohash identities, requiring
OK trueconfirmations per NIP-01. - The implementation resides in
NostrTransport.swift,NostrIdentityBridge.swift, and related BitFoundation modules. - While the fallback provides strong confidentiality, it is not compliant with NIP-17, NIP-44, or NIP-59 and lacks forward secrecy against static key compromise.
Frequently Asked Questions
How does BitChat detect when to use the Nostr fallback?
BitChat monitors mesh routing tables and connection timeouts. When a mesh-only peer cannot establish a route to the destination—either due to physical distance exceeding radio range or network partition—the system automatically triggers the fallback mechanism for BitChat's mesh-only delivery using Nostr, transparently switching transport layers without user intervention.
What cryptographic standards does the private envelope use?
The envelope implements a custom stack using secp256k1 for initial key agreement, HKDF-SHA256 for key derivation, and XChaCha20-Poly1305 for authenticated encryption. This differs from standard Nostr encryption methods like NIP-44, offering compatibility with existing Nostr keys while using modern authenticated encryption ciphers.
Can mesh-only devices publish directly to Nostr relays?
No, mesh-only devices cannot directly reach internet-connected Nostr relays. Instead, they sign bridge events that are relayed by bridge-capable nodes on their behalf. These events use geohash-based identities and carry the encrypted payload, ensuring the mesh-only author retains cryptographic attribution without requiring internet connectivity.
Does the fallback mechanism provide forward secrecy?
No. Because the encryption relies on the recipient's static Nostr private key for decryption, compromise of that key could expose historical messages. The protocol prioritizes offline-first mesh messaging and compatibility with existing Nostr identities over perfect forward secrecy, as documented in the repository's privacy assessment.
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 →