# BitChat Packet Types: Complete Guide to Message Encoding and Processing

> Explore BitChat packet types from announce to announceV2. Learn how this binary protocol handles compression, routing, encryption, and signature verification for secure messaging.

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

---

**BitChat uses 17 distinct packet types—from `0x01` (announce) to `0x2C` (announceV2)—each with single-byte identifiers processed through a binary protocol that handles compression, routing, encryption, and signature verification.**

The BitChat mesh messaging protocol defines a compact wire-level format for decentralized peer-to-peer communication. This article examines the packet type system implemented in the `permissionlesstech/bitchat` repository, covering how messages are constructed, encoded, routed, and processed across the network.

## Core Packet Types in BitChat

All packet type identifiers are defined in **[`MessageType.swift`](https://github.com/permissionlesstech/bitchat/blob/main/MessageType.swift)** as a Swift enumeration. Each case maps to a single byte value used by the binary encoder.

### Public and Presence Messages

These packet types handle basic mesh participation and public chat:

- **`announce` (0x01)** — Peer advertises presence with a nickname. Encoded as plain-text; processed by the announce coordinator to build the local peer directory.

- **`message` (0x02)** — Standard public chat message. Sent unencrypted; payload passed directly to the chat view model for display.

- **`leave` (0x03)** — Peer departure notification. Triggers cleanup of peer state in the local mesh table.

- **`announceV2` (0x2C)** — Identity-free presence for rotating peer IDs. Contains epoch, recognition tags, and capability flags; parsed according to [[`docs/PEER-ID-ROTATION.md`](https://github.com/permissionlesstech/bitchat/blob/main/docs/PEER-ID-ROTATION.md)](https://github.com/permissionlesstech/bitchat/blob/main/docs/PEER-ID-ROTATION.md).

### Encrypted and Key-Management Packets

These types implement the Noise protocol and cryptographic key exchange:

- **`noiseHandshake` (0x10)** — Noise protocol handshake (initiator or responder). Processed by the Noise handshake state machine to establish ephemeral session keys.

- **`noiseEncrypted` (0x11)** — All encrypted payloads including messages and receipts. Decrypted using the established Noise session key before further processing.

- **`prekeyBundle` (0x24)** — Batch of one-time pre-keys gossiped between peers. Added to the pre-key store for future session establishment.

### Synchronization and Routing Packets

Mesh maintenance and state synchronization use these types:

- **`courierEnvelope` (0x04)** — Store-and-forward envelope for trusted peers. Decoded by the courier service, then forwarded toward its destination.

- **`requestSync` (0x21)** — Local filter-based sync request. Handled by the sync manager; not routed through the mesh.

- **`fragment` (0x20)** — Fragment of a large message. Reassembled by the fragmentation buffer before higher-level processing.

- **`ping` (0x26)** — Echo request containing nonce and origin TTL. Target node returns a `pong` response.

- **`pong` (0x27)** — Echo reply with matching nonce and TTL. Used to measure round-trip latency between peers.

### Content and Application Packets

Specialized payload types for file transfer, voice, and external protocols:

- **`fileTransfer` (0x22)** — Binary file, audio, or image payload. Decoded by the file-transfer service; may be zlib-compressed.

- **`boardPost` (0x23)** — Signed geohash bulletin-board post or tombstone. Verified with sender's public key, then stored locally.

- **`groupMessage` (0x25)** — Group-encrypted broadcast with cleartext group ID and ChaChaPoly body. Decrypted with group key after group ID lookup.

- **`nostrCarrier` (0x28)** — Signed Nostr event ferried between mesh-only and internet-gateway peers. Decoded by the Nostr bridge and relayed to appropriate Nostr relays.

- **`voiceFrame` (0x29)** — Push-to-talk burst packet for ephemeral broadcast. Handled by the live-voice subsystem; **not gossip-synced or stored**.

## How BitChat Packets Are Processed

The packet lifecycle follows four stages: construction, binary encoding, network transmission, and decoding with type-specific handling.

### 1. Packet Construction

A `BitchatPacket` is instantiated through its initializer in **[`BitchatPacket.swift`](https://github.com/permissionlesstech/bitchat/blob/main/BitchatPacket.swift)** ([source](https://github.com/permissionlesstech/bitchat/blob/main/localPackages/BitFoundation/Sources/BitFoundation/BitchatPacket.swift#L27-L38)):

```swift
let packet = BitchatPacket(
    type: MessageType.message.rawValue,  // 0x02
    senderID: myPeerID,
    recipientID: nil,                     // nil for broadcast
    timestamp: UInt64(Date().timeIntervalSince1970 * 1_000_000),
    payload: Data("Hello, mesh!".utf8),
    signature: nil,
    ttl: 8
)

```

**Key fields:**
- `version` — Protocol version (1 or 2)
- `type` — MessageType raw value (single byte)
- `senderID` / `recipientID` — 8-byte peer identifiers
- `timestamp` — Microseconds since Unix epoch
- `payload` — Variable-length message data
- `signature` — Optional 64-byte Ed25519 signature
- `ttl` — Time-to-live hop counter
- `route` — Optional path array (v2 only)
- `isRSR` — Re-send-request flag (excluded from signing)

### 2. Binary Encoding

The **`BinaryProtocol.encode(_:padding:)`** method ([source](https://github.com/permissionlesstech/bitchat/blob/main/localPackages/BitFoundation/Sources/BitFoundation/BinaryProtocol.swift#L31-L52)) builds the wire format through these steps:

1. Validate version (1 or 2)
2. Compress payload if `CompressionUtil.shouldCompress` returns true
3. Assemble header: `version`, `type`, `ttl`, 8-byte timestamp, flags
4. Set flag bits for: recipient present, signature present, compressed, route present, RSR
5. Write length field (16-bit for v1, 32-bit for v2)
6. Append fixed fields: `senderID`, optional `recipientID`
7. If routed (v2): write hop count and each 8-byte hop
8. If compressed: prepend original payload size
9. Append payload and optional signature
10. Optionally pad to optimal block size via `MessagePadding`

```swift
// Encode with padding for optimal transmission
guard let binary = packet.toBinaryData(padding: true) else {
    fatalError("Encoding failed")
}
// Send `binary` via BLE, LoRa, or other transport

```

### 3. Binary Decoding

The **`BinaryProtocol.decode(_:)`** method ([source](https://github.com/permissionlesstech/bitchat/blob/main/localPackages/BitFoundation/Sources/BitFoundation/BinaryProtocol.swift#L55-L66)) reverses encoding:

- Attempts raw decode; on failure strips padding and retries
- Reads header and extracts flag bits
- Determines which optional sections are present
- Reads route array if indicated
- Decompresses payload when compression flag is set
- Reconstructs `BitchatPacket` instance

```swift
// On receiving side
guard let received = BitchatPacket.from(binaryData) else {
    fatalError("Decoding failed")
}

```

### 4. Type-Specific Processing

After decoding, the packet type determines handling:

```swift
switch MessageType(rawValue: received.type) {
case .message:
    let text = String(data: received.payload, encoding: .utf8)!
    print("Received: \(text)")
    
case .noiseEncrypted:
    // Decrypt with established Noise session
    let plaintext = noiseSession.decrypt(received.payload)
    
case .voiceFrame:
    // Ephemeral: play immediately, do not store
    audioEngine.play(received.payload)
    
case .nostrCarrier:
    // Bridge to Nostr relay
    nostrBridge.relay(received.payload)
    
default:
    break
}

```

## Routing, Security, and Special Handling

### TTL and Mesh Routing

Each hop decrements the `ttl` field. Packets with `ttl == 0` are discarded. In protocol v2, the `route` array tracks the exact path; routers strip the first hop before forwarding.

### Signature Verification

When `hasSignature` flag is set, the payload is verified against the embedded 64-byte Ed25519 signature. The `isRSR` flag is excluded from signature generation—use `toBinaryDataForSigning()` to produce the signed representation.

### Compression and Padding

`CompressionUtil` applies zlib compression when beneficial. `MessagePadding` aligns packets to optimal block sizes for the underlying transport, improving throughput on BLE and LoRa links.

### Fragmentation

Large messages exceeding transport MTU are split into `fragment` (0x20) packets. The fragmentation buffer reassembles them before higher-level processing.

## Complete Example: Sending and Receiving

```swift
import BitFoundation

// Build and send a public message
func sendChatMessage(text: String, to mesh: MeshManager) {
    let packet = BitchatPacket(
        type: MessageType.message.rawValue,
        senderID: myPeerID,
        recipientID: nil,
        timestamp: UInt64(Date().timeIntervalSince1970 * 1_000_000),
        payload: Data(text.utf8),
        signature: nil,
        ttl: 8
    )
    
    guard let binary = packet.toBinaryData(padding: true) else { return }
    mesh.broadcast(binary)
}

// Process incoming packets
func handlePacket(_ data: Data) {
    guard let packet = BitchatPacket.from(data),
          let type = MessageType(rawValue: packet.type) else { return }
    
    switch type {
    case .announce:
        peerDirectory.update(from: packet)
    case .message:
        display(ChatMessage(packet))
    case .ping:
        respondWithPong(to: packet)
    default:
        routeOrProcess(packet)
    }
}

```

## Key Implementation Files

| File | Purpose | Link |
|:---|:---|:---|
| [`MessageType.swift`](https://github.com/permissionlesstech/bitchat/blob/main/MessageType.swift) | Packet type enumeration with all 17 identifiers | [View source](https://github.com/permissionlesstech/bitchat/blob/main/localPackages/BitFoundation/Sources/BitFoundation/MessageType.swift) |
| [`BitchatPacket.swift`](https://github.com/permissionlesstech/bitchat/blob/main/BitchatPacket.swift) | Core data structure with `toBinaryData` and `from` helpers | [View source](https://github.com/permissionlesstech/bitchat/blob/main/localPackages/BitFoundation/Sources/BitFoundation/BitchatPacket.swift) |
| [`BinaryProtocol.swift`](https://github.com/permissionlesstech/bitchat/blob/main/BinaryProtocol.swift) | Low-level encoder/decoder, flag handling, compression, routing | [View source](https://github.com/permissionlesstech/bitchat/blob/main/localPackages/BitFoundation/Sources/BitFoundation/BinaryProtocol.swift) |
| [`docs/PEER-ID-ROTATION.md`](https://github.com/permissionlesstech/bitchat/blob/main/docs/PEER-ID-ROTATION.md) | Specification for `announceV2` rotating-ID format | [View docs](https://github.com/permissionlesstech/bitchat/blob/main/docs/PEER-ID-ROTATION.md) |

## Summary

- **17 packet types** cover all BitChat functionality, from basic presence (`0x01`) to Nostr bridging (`0x28`) and rotating peer IDs (`0x2C`).
- **Single-byte identifiers** enable compact binary encoding with minimal overhead.
- **[`BinaryProtocol.swift`](https://github.com/permissionlesstech/bitchat/blob/main/BinaryProtocol.swift)** handles version negotiation, compression, routing, padding, and flag management transparently.
- **Noise protocol integration** provides encrypted channels via `noiseHandshake` (0x10) and `noiseEncrypted` (0x11).
- **Type-specific processing** occurs after decode: voice frames are ephemeral, fragments are reassembled, and Nostr events are bridged to external relays.

## Frequently Asked Questions

### How does BitChat handle packet versioning?

BitChat supports protocol versions 1 and 2. The version field in the header determines field sizes—v1 uses 16-bit length fields while v2 uses 32-bit lengths and adds optional routing arrays. The `BinaryProtocol` encoder validates the version and sets flags accordingly.

### What is the difference between `announce` and `announceV2`?

The original `announce` (0x01) includes a persistent nickname tied to a stable peer ID. `announceV2` (0x2C) enables privacy-preserving presence without fixed identity, using epoch-based rotation, recognition tags, and capability flags—documented in [`docs/PEER-ID-ROTATION.md`](https://github.com/permissionlesstech/bitchat/blob/main/docs/PEER-ID-ROTATION.md).

### Are BitChat packets always signed?

No. Signatures are optional and controlled by the `hasSignature` flag. When present, the 64-byte Ed25519 signature covers all packet fields except the `isRSR` flag. Plain public messages and voice frames typically omit signatures for lower overhead, while bulletin-board posts and pre-key bundles require authentication.

### How does the fragmentation system work?

Messages exceeding the transport MTU are automatically split by the sender into multiple `fragment` (0x20) packets. Each fragment carries sequence information for reassembly. The receiving peer buffers fragments until the complete message is reconstructed, then processes the reassembled payload according to the original message type.