Bitchat uses layered encryption across its two transports. Over the Bluetooth mesh, live peer sessions are protected by the Noise Protocol Framework; courier envelopes destined for offline recipients are sealed with a one-way Noise variant. Over the Nostr internet path, private messages travel inside a proprietary double-sealed envelope encrypted with XChaCha20-Poly1305. Every private payload — messages, delivery acknowledgements, read receipts — rides inside one of these schemes; intermediate relay nodes forward opaque ciphertext and never see plaintext.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/permissionlesstech/bitchat/llms.txt
Use this file to discover all available pages before exploring further.
Live Sessions: Noise XX
When two devices are in BLE range and at least one initiates a direct connection, they perform a three-message NoiseXX handshake before any application data flows.
- Mutual authentication — both parties prove possession of their static Curve25519 private key by the end of the handshake.
- Forward secrecy — fresh ephemeral keys are generated for every handshake. Compromise of a device’s long-term static key does not expose past sessions.
- Identity hiding — static public keys are encrypted inside the handshake. A passive observer on the radio sees only ephemeral key material until after the first DH exchange.
- Replay protection — the implementation uses a 1024-message sliding window to reject duplicate nonces.
- Automatic rekey — sessions rekey after one hour or 10,000 messages, whichever comes first.
Swift Class Signatures
The core classes from the implementation:Error Handling
Offline Seals: Noise X
When neither a live BLE link nor a Nostr path can deliver a message promptly, the sender seals it into a courier envelope using the one-way NoiseX pattern:
Nostr Private Messages
When two mutual favorites are not in BLE range but both have internet access, Bitchat routes private messages over Nostr relays. The format is Bitchat’s own proprietary double-sealed envelope — it reuses NIP-17 and NIP-59 kind numbers purely for relay-compatibility reasons, but the cryptography is entirely different.Envelope Structure
Kind 14 — Inner Message
The actual message content. Unsigned; never published directly to any relay.
Kind 13 — Seal
The kind 14 content, encrypted and placed inside a sender-signed event. Sender identity is inside the ciphertext.
Kind 1059 — Gift Wrap
The kind 13 seal, encrypted again inside an outer envelope signed by a randomly-generated one-time key. This is what relays actually store.
p tag), event timing and size, and network metadata. They do not see message content or the sender’s stable identity.
Encryption: XChaCha20-Poly1305
Each encryptedcontent field is formatted as:
v2: prefix identifies the encoding version. Encryption uses XChaCha20-Poly1305 with a 24-byte random nonce. The implementation derives a ChaCha20 subkey via HChaCha20 on the first 16 bytes of the nonce, then encrypts with the remaining 8 bytes padded to a 12-byte ChaCha20 nonce.
Key Derivation
Keys come from secp256k1 ECDH between the sender’s and recipient’s Nostr key pairs, fed through HKDF-SHA256. The derivation reuses the"nip44-v2" info label for historical reasons but follows a different key schedule from NIP-44 and is not interoperable with it.
Timestamp Randomization
Outer seal and envelope event timestamps are randomized by up to ±15 minutes. The real message timestamp is encrypted inside the kind 14 inner payload and never visible to relays.Padding
Payload length is a known metadata leak. Bitchat addresses this selectively: Padded packet types (noiseEncrypted, noiseHandshake):
Noise-encrypted packets are padded toward bucket boundaries of 256, 512, 1024, or 2048 bytes before encryption. Padding is PKCS#7 style — each pad byte contains the pad length. Because the pad length must fit in one byte, a frame that would need more than 255 bytes of padding to reach its bucket is emitted unpadded.
Unpadded packet types (everything else):
Public channel messages, signed announces, gossip-sync packets, fragmented transfers, group messages, and board posts all go out at their natural length.
Cryptographic Primitives Summary
| Algorithm | Use | Details |
|---|---|---|
| Curve25519 (X25519) | Noise DH operations | All Noise XX and Noise X handshakes |
| ChaCha20-Poly1305 | Noise AEAD cipher | Session encryption, handshake key material, outbox sealing |
| SHA-256 | Noise hash, peer ID derivation | Handshake transcript, chaining key, peer ID fingerprint |
| HKDF-SHA256 | Noise KDF, Nostr key derivation | Session key split, Nostr ECDH key derivation |
| HChaCha20 | XChaCha20 subkey derivation | Nostr envelope encryption |
| XChaCha20-Poly1305 | Nostr envelope AEAD | Kind 13 and kind 1059 content fields |
| secp256k1 ECDH | Nostr key agreement | Shared secret for Nostr envelope keys |
| Ed25519 | Packet signatures | Signed announces, packet authentication |
| AES-GCM | Local identity state | Some protected local identity storage |