# How BitChat Encodes Peer Capabilities in Mesh Announcement TLV Packets

> Discover how BitChat encodes peer capabilities in mesh announcement TLV packets using a compact bitfield. Learn about feature flag preservation and efficient TLV type 0x03 implementation for enhanced mesh networking.

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

---

**BitChat encodes peer capabilities as a compact, little-endian bitfield within TLV type `0x03` of the announce-v2 mesh packet, using variable-length truncation that omits trailing zeros while preserving all 64 possible feature flags.**

BitChat advertises supported peer features inside mesh announcement packets using a Type-Length-Value (TLV) encoding scheme. According to the permissionlesstech/bitchat source code, these **peer capabilities** are packed into a space-efficient 64-bit structure defined in [`PeerCapabilities.swift`](https://github.com/permissionlesstech/bitchat/blob/main/PeerCapabilities.swift) that balances protocol extensibility with bandwidth constraints in decentralized mesh networks.

## TLV Type 0x03: The Capabilities Container

Inside the `AnnounceV2Packet` structure, capabilities are transmitted as a TLV entry with type identifier `0x03`. The packet construction logic in [`AnnounceV2Packet.swift`](https://github.com/permissionlesstech/bitchat/blob/main/AnnounceV2Packet.swift) appends three components sequentially:

1. The type byte (`0x03` from `TLVType.capabilities.rawValue`)
2. A single length byte (`UInt8(bytes.count)`)
3. The value bytes (the encoded bitfield)

The length byte limits the payload to 255 bytes maximum, though the `PeerCapabilities` implementation uses only 8 bytes for a `UInt64` bitfield.

## Bitfield Encoding in PeerCapabilities.swift

The encoding logic resides in [`localPackages/BitFoundation/Sources/BitFoundation/PeerCapabilities.swift`](https://github.com/permissionlesstech/bitchat/blob/main/localPackages/BitFoundation/Sources/BitFoundation/PeerCapabilities.swift). The `PeerCapabilities` type conforms to `OptionSet`, backing feature flags with a `UInt64 rawValue`.

The `encoded()` method converts this integer into a minimal byte sequence:

```swift
public func encoded() -> Data {
    var value = rawValue
    var bytes = Data()
    repeat {
        bytes.append(UInt8(truncatingIfNeeded: value))
        value >>= 8
    } while value != 0          // stops when all higher‑order bytes are zero
    return bytes                // little‑endian, no trailing zeros
}

```

### Canonical Representation Rules

The encoding follows three strict rules to ensure deterministic, minimal representations:

- **Little-endian byte order**: The least-significant byte is emitted first via the bitwise right-shift (`>>= 8`) loop.
- **Trailing zero omission**: The loop terminates once remaining bits are zero, producing the shortest valid encoding.
- **Non-zero length guarantee**: An empty capability set emits a single `0x00` byte, preventing ambiguity with a missing TLV.

## Embedding Capabilities in AnnounceV2Packet

During serialization, `AnnounceV2Packet.encode()` validates and embeds the capabilities TLV:

```swift
if let capabilities {
    let bytes = capabilities.encoded()
    guard bytes.count <= 255 else { return nil }   // length fits in a single byte
    data.append(TLVType.capabilities.rawValue)    // → 0x03
    data.append(UInt8(bytes.count))               // length
    data.append(bytes)                            // value (little‑endian bitfield)
}

```

The guard ensures the variable-length encoding does not exceed the maximum representable length in one byte.

## Decoding and Forward Compatibility

Reconstruction occurs in `AnnounceV2Packet.decode()` when encountering the `0x03` TLV type. The decoder passes raw bytes to `PeerCapabilities(encoded:)`, which rebuilds the `UInt64` by OR-ing each byte at its respective offset:

```swift
case .capabilities:
    let decoded = PeerCapabilities(encoded: value)
    // Verify canonical representation – the encoded form must match the raw bytes.
    guard decoded.encoded() == value else { return nil }
    capabilities = decoded

```

The canonical verification ensures that peers cannot transmit non-minimal encodings with unnecessary trailing zeros. Additionally, bytes beyond the 8th position are ignored during decoding, ensuring forward compatibility with future 64-bit extensions.

## Practical Code Examples

**Creating a capabilities TLV:**

```swift
import BitFoundation

// Example: a peer that supports prekeys, groups, and mesh diagnostics
let caps = PeerCapabilities.prekeys
            .union(.groups)
            .union(.meshDiagnostics)

let tlvData = caps.encoded()          // → Data([0x0D])   // binary: 00001101
print(tlvData.hexString)             // "0d"

```

**Constructing an announcement packet:**

```swift
let packet = AnnounceV2Packet(
    epoch:   42,
    tagBlock: Data(repeating: 0, count: AnnounceV2Packet.tagBlockLength),
    capabilities: caps,
    bridgeGeohash: nil
)

guard let encoded = packet.encode() else {
    fatalError("Failed to encode packet")
}
print(encoded.hexString)   // TLV sequence includes: 03 <len> <caps-bytes>

```

**Decoding received capabilities:**

```swift
if let decoded = AnnounceV2Packet.decode(from: encoded) {
    if let caps = decoded.capabilities {
        print("Peer supports: \(caps)")
    } else {
        print("No capabilities advertised")
    }
}

```

## Summary

- BitChat **encodes peer capabilities** as a little-endian `UInt64` bitfield inside TLV type `0x03` of mesh announcement packets.
- The `encoded()` method in [`PeerCapabilities.swift`](https://github.com/permissionlesstech/bitchat/blob/main/PeerCapabilities.swift) produces minimal-length byte sequences by omitting trailing zeros.
- The 255-byte length limit enforced in [`AnnounceV2Packet.swift`](https://github.com/permissionlesstech/bitchat/blob/main/AnnounceV2Packet.swift) accommodates future extensibility while maintaining TLV compactness.
- Canonical encoding verification prevents malformed packets with non-minimal zero padding.
- Forward compatibility is ensured by ignoring bytes beyond the 64th bit during decoding.

## Frequently Asked Questions

### What is the maximum size of the encoded capabilities bitfield?

The bitfield itself is a 64-bit unsigned integer, requiring up to 8 bytes. However, the TLV length field in `AnnounceV2Packet` uses a single `UInt8`, limiting the total TLV value to 255 bytes. This provides substantial headroom for future protocol extensions while maintaining packet compactness.

### Why does the encoding omit trailing zero bytes?

The `encoded()` implementation strips trailing zeros to minimize mesh bandwidth usage. By terminating the byte sequence once the remaining value equals zero, the protocol transmits only significant bytes. An empty capability set still emits one zero byte (`0x00`) to distinguish it from a missing TLV entry.

### How does BitChat handle unknown capability bits from newer protocol versions?

The decoder ignores bytes beyond the 8th position during reconstruction, ensuring older clients remain compatible with newer feature advertisements. Additionally, the canonical verification (`decoded.encoded() == value`) rejects non-minimal encodings that could indicate malformed or malicious packets.

### What happens if a peer sends a capabilities TLV exceeding 255 bytes?

The encoder in [`AnnounceV2Packet.swift`](https://github.com/permissionlesstech/bitchat/blob/main/AnnounceV2Packet.swift) contains an explicit guard (`bytes.count <= 255`) that returns `nil` if the encoded length exceeds the single-byte length field capacity. This causes packet construction to fail safely rather than truncating or overflowing the TLV structure.