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

> Explore the BitChat private envelope format built on secp256k1 ECDH and compare it to NIP-44. Understand its store-and-forward resilience and trade-offs for offline delivery.

- Repository: [permissionlesstech/bitchat](https://github.com/permissionlesstech/bitchat)
- Tags: architecture
- Published: 2026-08-04

---

**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`](https://github.com/permissionlesstech/bitchat/blob/main/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`](https://github.com/permissionlesstech/bitchat/blob/main/CourierEnvelope.swift) demonstrates envelope construction with copy-budget and expiry settings:

```swift
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`](https://github.com/permissionlesstech/bitchat/blob/main/localPackages/BitFoundation/Sources/BitFoundation/CourierEnvelope.swift).

### Decoding a Received Envelope (Swift)

```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:

```javascript
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`](https://github.com/permissionlesstech/bitchat/blob/main/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`](https://github.com/permissionlesstech/bitchat/blob/main/docs/privacy-assessment.md) and implemented in [`CourierEnvelope.swift`](https://github.com/permissionlesstech/bitchat/blob/main/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`](https://github.com/permissionlesstech/bitchat/blob/main/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.