BitChat Private Envelope Format vs NIP-44: Architecture, Usage, and Comparison

BitChat uses a proprietary "private envelope" format built on secp256k1 ECDH, HKDF-SHA256, and XChaCha20-Poly1305 for offline courier delivery, sacrificing NIP-44's forward secrecy and interoperability for store-and-forward resilience.

The BitChat private envelope format is the messaging protocol's fallback mechanism for encrypted communications that cannot traverse standard Nostr relays. While NIP-44 represents the standardized approach to encrypted direct messages in the Nostr ecosystem, BitChat's envelope serves a specialized purpose: enabling offline, tag-routed message delivery through a decentralized courier system. This article examines how the format works, where it differs from NIP-44, and when each approach makes sense.


How the BitChat Private Envelope Works

The private envelope is defined in localPackages/BitFoundation/Sources/BitFoundation/CourierEnvelope.swift and implements a Noise-style static-key sealed payload with additional metadata for courier coordination.

Core Cryptographic Stack

Layer Implementation Purpose
Key agreement secp256k1 ECDH Derives shared secret between sender and recipient static keys
Key derivation HKDF-SHA256 Expands shared secret to 256-bit symmetric key
Symmetric encryption XChaCha20-Poly1305 AEAD encryption with 24-byte nonce
Routing 4-byte recipient tag Enables courier routing without exposing full public key

The secp256k1 curve choice is deliberate: it maintains compatibility with Bitcoin and Nostr public keys, allowing BitChat to reuse existing key material rather than requiring separate X25519 keys.

Unique Metadata Fields

BitChat's envelope includes three metadata components absent from NIP-44:

  • Expiry timestamp — Milliseconds since epoch; couriers discard expired envelopes
  • Copy budget — Redundancy limit for spray-and-wait storage strategies
  • Version flag — Distinguishes v1 (static-sealed) from v2 (adds copy-budget TLV)

The recipient tag deserves special attention. Calculated from the recipient's Noise static public key and the current UTC day, this rotating tag allows couriers to route envelopes without learning the recipient's identity. According to docs/privacy-assessment.md#L60, this design "enables offline delivery while minimizing metadata exposure."

TLV Serialization

The envelope serializes as a TLV (type-length-value) payload, making it suitable for two transport modes:

  1. Embedded verbatim into Nostr gift_wrap events (kind 1059)
  2. Direct placement into bridge-courier drops

NIP-44 Standard: The Established Approach

NIP-44 is Nostr's official encrypted direct message specification, implemented across most clients. Its design priorities differ fundamentally from BitChat's envelope.

NIP-44 Cryptographic Profile

  • Curve: X25519 (Curve25519) for ECDH
  • Key derivation: HKDF-SHA256 (identical to BitChat)
  • Cipher: ChaCha20-Poly1305 (or AES-GCM in some implementations)
  • Critical distinction: Per-message ephemeral DH keys provide forward secrecy

Forward Secrecy: The Decisive Difference

NIP-44 generates a new ephemeral key pair for every message. Even if a recipient's long-term private key is later compromised, past messages remain secure. BitChat's envelope, sealed to static keys, offers no forward secrecy—a compromise of the recipient's key decrypts all historical envelopes.


Direct Comparison: BitChat Private Envelope vs NIP-44

Aspect BitChat Private Envelope NIP-44
Standardization Proprietary, BitChat-only Official Nostr improvement proposal
Key agreement curve secp256k1 X25519
Key derivation HKDF-SHA256 HKDF-SHA256
AEAD cipher XChaCha20-Poly1305 (24-byte nonce) ChaCha20-Poly1305 or AES-GCM
Forward secrecy None — static-key sealed Yes — ephemeral DH per message
Metadata Expiry, copy-budget, version flag Minimal — encrypted blob with optional hints
Transport mechanism gift_wrap events or physical courier drops Direct relay transmission only
Routing Tag-based, offline-capable Online relay-dependent
Interoperability BitChat exclusive Cross-client compatible
Size characteristics ~2 KB limit + TLV overhead Similar limit, no TLV overhead

Practical Usage Examples

Creating a BitChat Private Envelope (Swift)

This example from CourierEnvelope.swift demonstrates envelope construction with copy-budget and expiry settings:

import BitFoundation

// Recipient's static Noise key (secp256k1)
let recipientKey = try NoiseKeyPair.fromHex("02a3…")

// Build plaintext payload
let plaintext = Data("Hello, secret world!".utf8)

// Create envelope: 1 copy, 24-hour expiry
var envelope = try CourierEnvelope(
    recipientKey: recipientKey,
    plaintext: plaintext,
    expiry: UInt64(Date().addingTimeInterval(86_400).timeIntervalSince1970 * 1_000),
    copies: 1)

// Encode to TLV bytes for transmission
let encoded = try envelope.encode()

// Wrap into Nostr gift_wrap event (kind 1059)
// (Use your Nostr client library to complete event creation)

The CourierEnvelope.encode() method handles TLV serialization, while CourierEnvelope.decode() reverses the process. Both are implemented in localPackages/BitFoundation/Sources/BitFoundation/CourierEnvelope.swift.

Decoding a Received Envelope (Swift)

import BitFoundation

// Extract TLV payload from gift_wrap event
guard let envelopeData = event.giftWrap?.envelope else {
    fatalError("No envelope present")
}

let envelope = try CourierEnvelope.decode(envelopeData)

// Verify addressing
guard envelope.isAddressed(to: myNoiseStaticKey) else {
    fatalError("Envelope not for this peer")
}

// Decrypt and obtain original message
let plaintext = try envelope.open(using: myNoiseStaticKey)

NIP-44 Encryption (JavaScript)

Using nostr-tools for comparison:

import { encrypt, decrypt } from "nostr-tools/nip44";

const alice = generateSecretKey();  // sender (X25519)
const bob   = generateSecretKey();  // recipient (X25519)

// Encrypt with ephemeral key (forward secrecy guaranteed)
const ciphertext = await encrypt(alice, bob, "Hello, NIP-44!");

// Decrypt on receipt
const plaintext = await decrypt(bob, alice, ciphertext);

Architectural Trade-offs Explained

When BitChat's Envelope Excels

Offline resilience is the envelope's primary advantage. When Nostr relays are unreachable—whether due to censorship, network partitions, or deliberate air-gapping—the courier system provides an alternative path. The copy-budget mechanism enables redundant, time-bounded storage across untrusted nodes without requiring recipient online presence.

The tag-based routing in docs/BRING_THE_NOISE.md demonstrates how couriers can batch and forward envelopes without cryptographic capability to read contents or identify recipients beyond day-rotated tags.

When NIP-44 Is Preferable

For standard relay-based messaging, NIP-44 offers:

  • Immediate interoperability across Nostr clients
  • Strong forward secrecy guarantees
  • Simpler implementation with audited libraries
  • No additional metadata management overhead

Summary

  • BitChat's private envelope uses secp256k1 ECDH, HKDF-SHA256, and XChaCha20-Poly1305 in a TLV structure supporting expiry timestamps and copy-budget metadata
  • The format enables offline courier delivery through recipient tags computed from static keys and UTC day rotation
  • No forward secrecy: compromise of a static key exposes all past envelopes
  • NIP-44 provides standardized, forward-secure encryption via X25519 and ephemeral keys, but requires online relay access
  • Choose BitChat's envelope for resilient, store-and-forward scenarios; use NIP-44 for standard interoperable messaging

Frequently Asked Questions

What cryptographic primitives does the BitChat private envelope use?

The envelope combines secp256k1 ECDH for key agreement, HKDF-SHA256 for key derivation, and XChaCha20-Poly1305 for authenticated encryption. The XChaCha20 variant uses a 24-byte nonce, providing safer nonce handling than standard ChaCha20's 12-byte nonce. These choices are documented in docs/privacy-assessment.md and implemented in CourierEnvelope.swift.

Why doesn't BitChat use NIP-44 for all messages?

NIP-44 requires online relay connectivity and provides no mechanism for store-and-forward delivery. BitChat's courier architecture needs envelopes that can persist on untrusted nodes with configurable redundancy (copy-budget) and automatic expiration. The proprietary envelope format encodes these requirements directly in the TLV structure.

How does the recipient tag protect privacy?

The 4-byte tag is computed from the recipient's Noise static public key and the current UTC day. Couriers use this tag for routing decisions without possessing the full public key. Tags rotate daily, limiting correlation windows. However, as noted in docs/privacy-assessment.md, relays and couriers still observe timestamps, approximate sizes, and the tags themselves—metadata absent from the encrypted payload but present in the transport layer.

Can other Nostr clients read BitChat private envelopes?

No. The format is intentionally incompatible with NIP-44, NIP-17, and NIP-59. Only BitChat implements the secp256k1-based key agreement and TLV parsing required for decryption. This exclusivity is the cost of the specialized courier functionality; interoperability would require standardization of the copy-budget and expiry metadata fields.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →