# BitChat Security Model: How Persistent Identifiers and BLE Header Metadata Protect Privacy

> Discover BitChat's security model for protecting privacy using persistent identifiers and BLE header metadata. Learn how it secures communication.

- Repository: [permissionlesstech/bitchat](https://github.com/permissionlesstech/bitchat)
- Tags: deep-dive
- Published: 2026-08-02

---

**BitChat isolates device identity from transport layer by deriving a persistent identifier from the Noise public key and exposing only a minimal nickname plus two public keys in BLE advertisements, with full payload encryption protecting all subsequent communication.**

The **BitChat security model** treats Bluetooth Low Energy (BLE) as an inherently observable transport while ensuring that no sensitive data leaks beyond what is cryptographically necessary for mesh discovery. This article examines how the open-source messenger balances discoverability with privacy through careful handling of persistent identifiers and BLE header metadata.

---

## Persistent Identifiers Derived from Cryptographic Keys

Each BitChat installation generates a **long-term device identifier** that survives app restarts but remains cryptographically bound to the user's identity. This identifier is not a random UUID—it is derived directly from the **Noise public key**, which serves as the primary encryption and authentication key for all peer-to-peer communication.

According to [`SECURITY.md`](https://github.com/permissionlesstech/bitchat/blob/main/SECURITY.md), this identifier is stored securely in the device keychain and is only erased when a **panic wipe** is triggered. This design guarantees that:

- The same device is recognizable across sessions without requiring centralized account registration
- No stale identity persists after a security reset
- The identifier carries cryptographic meaning rather than being arbitrary metadata

The derivation happens once during initial setup and is reused for the lifetime of the installation:

```swift
import BitFoundation

let keychain = Keychain(service: "com.permissionless.bitchat")
let noiseKeyPair = try NoiseKeyPair.generate()          // Ed25519 + Noise keys
try keychain.set(data: noiseKeyPair.publicKey.rawData,
                forKey: "bitchat-noise-pubkey")      // persistent identifier

// Retrieve on app start
let persistedNoisePubKey = try keychain.data(forKey: "bitchat-noise-pubkey")
let deviceIdentifier = try PublicKey(data: persistedNoisePubKey)

```

This approach, implemented in the BitChat source code, ensures that identity continuity does not depend on any server infrastructure.

---

## BLE Header Metadata: What Gets Broadcast

The **BLE advertisement payload** in BitChat intentionally leaks only three pieces of information. As documented in [`docs/privacy-assessment.md`](https://github.com/permissionlesstech/bitchat/blob/main/docs/privacy-assessment.md) (lines 27–28), these are:

- A human-readable **nickname**
- The persistent **Noise public key**
- The **Ed25519 signing public key**

This metadata is considered **public by design**. The BitChat security model assumes that any adversary within BLE range can observe presence, nicknames, and public keys—but cannot decrypt or forge messages without the corresponding private keys.

The construction of this advertisement follows strict size constraints to fit within standard BLE advertising frames:

```swift
import CoreBluetooth

func makeBLEAdvertisementPayload(nickname: String,
                                 noisePubKey: PublicKey,
                                 signingPubKey: PublicKey) -> Data {
    var payload = Data()
    // 1️⃣ Nickname (UTF‑8, length‑prefixed)
    let nameData = nickname.data(using: .utf8)!
    payload.append(UInt8(nameData.count))
    payload.append(nameData)

    // 2️⃣ Noise public key (32 bytes)
    payload.append(noisePubKey.rawData)

    // 3️⃣ Ed25519 signing public key (32 bytes)
    payload.append(signingPubKey.rawData)

    return payload          // ≤ 65 bytes, fits within BLE advertising frame
}

// Example usage inside BLEService:
let advData = makeBLEAdvertisementPayload(nickname: "Alice",
                                          noisePubKey: deviceIdentifier,
                                          signingPubKey: mySigningKey.publicKey)
peripheralManager.startAdvertising([CBAdvertisementDataServiceDataKey: advData])

```

The resulting payload is capped at **65 bytes**, ensuring compatibility with BLE advertising data limits while leaving no room for accidental information leakage.

---

## Transport Layer Boundary and Encrypted Payloads

The **BLE transport layer**, detailed in [`docs/BLE-ARCHITECTURE-V3.md`](https://github.com/permissionlesstech/bitchat/blob/main/docs/BLE-ARCHITECTURE-V3.md), enforces a strict boundary: the header is **metadata-only**, and all higher-level protocols must rely on the encrypted channel for confidentiality and integrity.

This separation means:

| Layer | Responsibility | Security Property |
|-------|---------------|-------------------|
| **BLE Header** | Device discovery, identity binding | Observable but non-sensitive |
| **Noise Handshake** | Session key establishment | Ephemeral, forward-secret |
| **Encrypted Payload** | Message content, file transfers | Confidential and authenticated |

Any protocol built atop BitChat—whether courier messaging, private media sharing, or future extensions—must assume the BLE layer is untrusted and encrypt accordingly.

---

## Panic Wipe and Identifier Lifecycle

The **persistent identifier** is not permanent in the sense of being unrecoverable. The [`SECURITY.md`](https://github.com/permissionlesstech/bitchat/blob/main/SECURITY.md) documentation specifies that a **panic wipe** operation erases:

- The stored Noise public key from the keychain
- All session state
- Persistent message stores

This creates a clean break: after a panic wipe, the device presents as a completely new identity to the mesh. This property is essential for threat models involving device seizure or coercion.

---

## Summary

- **Persistent identifiers** in BitChat are derived from Noise public keys and stored in the device keychain, enabling recognition across sessions without centralized infrastructure
- **BLE header metadata** is intentionally minimal—only nickname, Noise public key, and Ed25519 signing key are broadcast, as specified in [`docs/privacy-assessment.md`](https://github.com/permissionlesstech/bitchat/blob/main/docs/privacy-assessment.md)
- **Transport layer isolation** ensures that all sensitive data travels through encrypted channels established after the header exchange, per [`docs/BLE-ARCHITECTURE-V3.md`](https://github.com/permissionlesstech/bitchat/blob/main/docs/BLE-ARCHITECTURE-V3.md)
- **Panic wipe functionality** guarantees complete identity reset when needed, leaving no cryptographic breadcrumbs

---

## Frequently Asked Questions

### What information does BitChat leak over BLE?

BitChat's BLE advertisements contain only a **nickname**, the **Noise public key**, and the **Ed25519 signing public key**—a total of approximately 65 bytes. No message content, contact lists, or location data is transmitted. This minimal metadata exposure is a deliberate design choice documented in the privacy assessment.

### Can an attacker track my device using the persistent identifier?

The **Noise-derived identifier** is long-term but not permanent. An adversary within BLE range can correlate advertisements to track presence over time, but the [`SECURITY.md`](https://github.com/permissionlesstech/bitchat/blob/main/SECURITY.md) panic wipe mechanism allows complete identity rotation. Additionally, the identifier alone cannot decrypt any communication without the corresponding private key.

### How does BitChat prevent metadata leakage in group chats?

All payloads beyond the BLE header—including group membership and message content—are encrypted using the **Noise protocol**. The transport layer boundary defined in [`BLE-ARCHITECTURE-V3.md`](https://github.com/permissionlesstech/bitchat/blob/main/BLE-ARCHITECTURE-V3.md) ensures that group metadata never appears in unencrypted BLE frames, regardless of group size.

### What happens to my identifier if I uninstall and reinstall BitChat?

Uninstalling the app typically clears the iOS keychain entry for that specific app, which erases the stored Noise public key. Upon reinstallation, `NoiseKeyPair.generate()` creates a fresh cryptographic identity with no linkage to the previous installation.