# BitChat Mesh Protocol Binary Packet Format: Structure and Implementation

> Explore the BitChat mesh protocol binary packet format. Discover how BitchatPacket struct and encode/decode methods handle compression, signatures, and routing for versions 1 and 2.

- Repository: [permissionlesstech/bitchat](https://github.com/permissionlesstech/bitchat)
- Tags: api-reference
- Published: 2026-07-27

---

**The BitChat mesh protocol uses a compact, versioned binary wire format defined in [`BinaryProtocol.swift`](https://github.com/permissionlesstech/bitchat/blob/main/BinaryProtocol.swift), where packets are represented by the `BitchatPacket` struct and serialized through static `encode`/`decode` methods, supporting optional compression, cryptographic signatures, and source routing across v1 and v2 specifications.**

The BitChat mesh network, maintained in the `permissionlesstech/bitchat` repository, implements a lightweight communication layer optimized for Bluetooth Low Energy (BLE) constraints. Understanding the **BitChat mesh protocol binary packet format** is essential for developers building compatible clients or analyzing network traffic. The format prioritizes minimal overhead while remaining extensible through a flags-based variable header system.

## Binary Packet Structure Overview

The binary format defines a fixed header followed by optional variable-length fields. According to the implementation in [`BinaryProtocol.swift`](https://github.com/permissionlesstech/bitchat/blob/main/BinaryProtocol.swift), the header occupies **14 bytes for v1 packets** and **16 bytes for v2 packets**, with the specific layout documented in lines 23-30 of the source file.

### Header Components

Every packet begins with a standardized header containing routing and metadata information:

- **Version** (1 byte): Protocol version identifier, currently `1` or `2`
- **Type** (1 byte): Application-level message type (e.g., chat messages, file transfers)
- **TTL** (1 byte): Hop-limit for mesh routing; decremented on each relay to prevent infinite loops
- **Timestamp** (8 bytes): Unix epoch time in seconds, encoded as big-endian unsigned 64-bit integer
- **Flags** (1 byte): Bit-field indicating optional sections:
  - `0x01` – Recipient ID present
  - `0x02` – Signature present (Ed25519)
  - `0x04` – Payload compressed
  - `0x08` – Route present (v2+ only)
  - `0x10` – RSR (Return Source Route) flag
- **Payload Length** (2 bytes in v1, 4 bytes in v2): Size of the payload including optional original-size field when compressed

### Variable-Length Fields

Following the header, the parser reads optional fields based on the flags byte, as implemented in lines 52-57 of [`BinaryProtocol.swift`](https://github.com/permissionlesstech/bitchat/blob/main/BinaryProtocol.swift):

- **Sender ID** (8 bytes): Fixed-size identifier for the packet origin, padded or truncated as needed
- **Recipient ID** (8 bytes, optional): Target recipient identifier; omitted for broadcast messages when `hasRecipient` flag is unset
- **Route** (variable, v2+): Source-routing information consisting of a 1-byte hop count followed by `count × 8` bytes of hop identifiers
- **Original Size** (2 bytes v1 / 4 bytes v2, conditional): Uncompressed payload size present when compression flag is set
- **Payload** (variable): Raw or compressed message data
- **Signature** (64 bytes, optional): Ed25519 cryptographic signature present when `hasSignature` flag is set
- **Padding** (variable): Zero-padding to nearest block size for BLE-MTU alignment, handled by `MessagePadding` post-serialization

## Protocol Versions and Feature Differences

The BitChat mesh protocol maintains backward compatibility while extending capabilities through versioned specifications.

### Version 1 Specification

Version 1 provides the foundational mesh communication format with specific constraints:

- **Fixed 14-byte header** with 16-bit payload length field
- **Maximum packet size** of 65,535 bytes, limited by the 16-bit length encoding
- **No route support**; packets rely on flood-based or table-driven routing without source-route embedding
- Compression support with 2-byte original size fields

### Version 2 Enhancements

Version 2 expands the protocol to support larger payloads and advanced routing, as detailed in lines 99-115 of [`BinaryProtocol.swift`](https://github.com/permissionlesstech/bitchat/blob/main/BinaryProtocol.swift):

- **Expanded 16-byte header** with 32-bit payload length field, enabling packets exceeding 65KB
- **Source routing capabilities** via the route field (flag `0x08`), allowing explicit hop-by-hop path specification
- **32-bit original size fields** for compression metadata
- Support for up to 255 explicit routing hops in the source-route list

## Encoding and Decoding Implementation

The `BinaryProtocol` class in [`BinaryProtocol.swift`](https://github.com/permissionlesstech/bitchat/blob/main/BinaryProtocol.swift) provides static methods for serialization and deserialization, interacting with the `BitchatPacket` struct defined in [`BitchatPacket.swift`](https://github.com/permissionlesstech/bitchat/blob/main/BitchatPacket.swift).

### Serialization Flow

The encoding process follows a structured pipeline:

1. **Header Assembly**: Write version, type, TTL, timestamp, and calculate initial flags
2. **Variable Field Writing**: Append sender ID, conditionally write recipient ID and route based on flags
3. **Compression Evaluation**: If payload exceeds 256 bytes, `CompressionUtil.shouldCompress` determines whether to compress; if compressed, set `isCompressed` flag and prepend original size
4. **Payload Appending**: Write compressed or raw payload data
5. **Signature Injection**: Append 64-byte Ed25519 signature if `hasSignature` flag is set
6. **Padding Application**: Apply `MessagePadding` to align to BLE-MTU block sizes

```swift
import Foundation
import BitFoundation

let senderID = Data([0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08])
let recipientID = Data([0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0x10])
let payload = "Hello, mesh!".data(using: .utf8)!

let packet = BitchatPacket(
    type: 0x01,
    senderID: senderID,
    recipientID: recipientID,
    timestamp: UInt64(Date().timeIntervalSince1970),
    payload: payload,
    signature: nil,
    ttl: 8,
    version: 2,
    route: nil,
    isRSR: false
)

guard let binary = BinaryProtocol.encode(packet) else {
    fatalError("Encoding failed")
}
print("Encoded packet size:", binary.count)

```

### Deserialization Flow

Decoding reverses the encoding process with validation:

1. **Header Parsing**: Extract version, type, TTL, timestamp, flags, and payload length from the fixed header
2. **Flag Evaluation**: Determine which optional fields exist based on the flags byte
3. **Field Extraction**: Read sender ID, conditional recipient ID, and route (if v2+ with route flag)
4. **Decompression**: If `isCompressed` flag is set, read original size and decompress payload using the inverse of `CompressionUtil`
5. **Signature Verification**: Validate 64-byte Ed25519 signature if present
6. **Object Construction**: Instantiate `BitchatPacket` with extracted values

```swift
if let decoded = BinaryProtocol.decode(binary) {
    print("Version:", decoded.version)
    print("Sender:", decoded.senderID.hexString)
    print("Payload:", String(data: decoded.payload, encoding: .utf8) ?? "<binary>")
}

```

## Optional Features and Extensions

The binary format supports several optional capabilities controlled through the flags byte, enabling efficient bandwidth utilization and security.

### Payload Compression

When payload data exceeds **256 bytes**, the encoder evaluates compression via `CompressionUtil.shouldCompress`. If compression reduces size, the encoder:

- Sets the `0x04` compression flag
- Prepends the uncompressed original size (2 bytes for v1, 4 bytes for v2) before the compressed payload
- Automatically decompresses during decoding when the flag is detected, as implemented in lines 36-48 and 60-71 of [`BinaryProtocol.swift`](https://github.com/permissionlesstech/bitchat/blob/main/BinaryProtocol.swift)

### Message Padding for BLE MTU

To accommodate Bluetooth Low Energy maximum transmission unit constraints, the protocol supports optional padding via `MessagePadding`. This adds zero-padding after serialization to align packets to specific block sizes, ensuring efficient radio utilization without fragmenting frames.

## Practical Implementation Examples

### Adding Source Routes (Version 2)

For directed routing through specific mesh nodes, v2 packets support explicit route embedding:

```swift
let hop1 = Data([0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA])
let hop2 = Data([0xBB,0xBB,0xBB,0xBB,0xBB,0xBB,0xBB,0xBB])

var routedPacket = packet
routedPacket.route = [hop1, hop2]   // Up to 255 hops supported
routedPacket.version = 2            // Required for route support

let routedBinary = BinaryProtocol.encode(routedPacket)!

```

### Working with Signed Packets

To create authenticated packets, populate the signature field with a 64-byte Ed25519 signature and set the `hasSignature` flag (0x02):

```swift
var signedPacket = packet
signedPacket.signature = ed25519SignatureData  // 64 bytes
// BinaryProtocol.encode automatically sets flag 0x02 when signature is non-nil
let signedBinary = BinaryProtocol.encode(signedPacket)

```

## Summary

- The **BitChat mesh protocol binary packet format** uses a fixed 14-byte (v1) or 16-byte (v2) header with variable-length optional fields controlled by a flags byte.
- **Version 2** introduces 32-bit payload lengths and source routing capabilities not available in Version 1.
- **Compression** automatically activates for payloads exceeding 256 bytes, with original size metadata prepended to the payload.
- **Cryptographic signatures** use Ed25519 (64 bytes) and are conditionally included based on the flags field.
- All encoding/decoding logic resides in [`BinaryProtocol.swift`](https://github.com/permissionlesstech/bitchat/blob/main/BinaryProtocol.swift), while packet structure is defined in [`BitchatPacket.swift`](https://github.com/permissionlesstech/bitchat/blob/main/BitchatPacket.swift) within the `BitFoundation` local package.

## Frequently Asked Questions

### What is the maximum payload size supported by the BitChat binary format?

Version 1 supports payloads up to 65,535 bytes due to its 16-bit length field, while Version 2 expands this to approximately 4 GB using a 32-bit length field. However, practical limits are constrained by BLE-MTU sizes and mesh network bandwidth, with compression recommended for payloads exceeding 256 bytes.

### How does the protocol handle packet routing in mesh networks?

The protocol supports two routing modes: broadcast (omitting recipient ID) and unicast (including 8-byte recipient ID). Version 2 adds explicit source routing through an optional route field containing up to 255 hop identifiers, enabling predetermined path traversal through specific mesh nodes.

### Where is the binary packet format defined in the source code?

The wire format is implemented in [`localPackages/BitFoundation/Sources/BitFoundation/BinaryProtocol.swift`](https://github.com/permissionlesstech/bitchat/blob/main/localPackages/BitFoundation/Sources/BitFoundation/BinaryProtocol.swift), which provides static `encode` and `decode` methods. The data model is defined in [`BitchatPacket.swift`](https://github.com/permissionlesstech/bitchat/blob/main/BitchatPacket.swift) within the same directory, while unit tests covering version handling, compression, and padding reside in [`BinaryProtocolTests.swift`](https://github.com/permissionlesstech/bitchat/blob/main/BinaryProtocolTests.swift) and [`BinaryProtocolPaddingTests.swift`](https://github.com/permissionlesstech/bitchat/blob/main/BinaryProtocolPaddingTests.swift).

### What compression algorithm does BitChat use for large payloads?

The source code references `CompressionUtil.shouldCompress` for determining compression eligibility, with the `isCompressed` flag (0x04) indicating compressed payloads. The implementation automatically handles compression for payloads exceeding 256 bytes and stores the original uncompressed size before the payload data to facilitate verification during decompression.