How BitChat Encodes Peer Capabilities in Mesh Announcement TLV Packets
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 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 appends three components sequentially:
- The type byte (
0x03fromTLVType.capabilities.rawValue) - A single length byte (
UInt8(bytes.count)) - 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. The PeerCapabilities type conforms to OptionSet, backing feature flags with a UInt64 rawValue.
The encoded() method converts this integer into a minimal byte sequence:
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
0x00byte, preventing ambiguity with a missing TLV.
Embedding Capabilities in AnnounceV2Packet
During serialization, AnnounceV2Packet.encode() validates and embeds the capabilities TLV:
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:
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:
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:
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:
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
UInt64bitfield inside TLV type0x03of mesh announcement packets. - The
encoded()method inPeerCapabilities.swiftproduces minimal-length byte sequences by omitting trailing zeros. - The 255-byte length limit enforced in
AnnounceV2Packet.swiftaccommodates 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 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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →