# Differences Between BitChat BLE Packet Formats V1 and V2

> Explore BitChat BLE packet formats V1 vs V2. Discover V2's explicit version byte, TLV routing, and 64-bit capabilities versus V1's implicit typing and flood delivery. Understand the key upgrades.

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

---

**BitChat V2 introduces an explicit version byte, TLV-encoded source routing, and a 64-bit capability field, while V1 uses implicit typing with flood-based delivery and lacks extensible metadata headers.**

BitChat's BLE mesh transport layer evolved significantly between Version 1 (the original flood-based format) and Version 2 (which added deterministic routing and extensible metadata). Understanding these wire-format distinctions is essential for developers building compatible mesh nodes or debugging packet flows in the permissionless network.

## Header Structure and Version Discrimination

The most immediate difference between V1 and V2 appears in the packet header itself, fundamentally changing how nodes parse incoming BLE frames.

### Implicit vs Explicit Versioning

In **V1**, the first byte of every packet represented the **message type** directly, with no explicit version identifier. According to the codebase, legacy parsing logic in [`bitchat/Protocols/Packets.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Protocols/Packets.swift) treats the initial octet as a type discriminator (`currentVersion = 1` context), forcing backward compatibility to be inferred from payload structure alone.

**V2** inserts a dedicated **version byte** at the head of every packet. As documented in [`docs/SOURCE_ROUTING.md`](https://github.com/permissionlesstech/bitchat/blob/main/docs/SOURCE_ROUTING.md), this upgrade allows receivers to inspect `data[0]` and branch to appropriate parsing logic before interpreting subsequent fields.

### Header Size and Layout

The header expansion reflects the new architecture:

- **V1**: 5-byte header (`type` + `TTL` + `flags`)
- **V2**: 6-byte header (`version` + `type` + `TTL` + `flags`)

This additional byte enables the version-gated handling seen in [`bitchat/Services/BLE/BLEService.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/BLE/BLEService.swift) around line 3707, where the service checks `requiringVersion: 2` before processing advanced features.

## Source Routing and Topology Changes

V1 relied on network flooding or simple unicast, while V2 implements explicit pathing through the mesh.

### Flood-Based Delivery in V1

V1 packets contain no routing information beyond destination and TTL. Every intermediate node floods the packet to all neighbors until the hop limit expires, creating redundant traffic and battery drain on dense networks.

### TLV-Encoded Source Routes in V2

V2 introduces a **Type-Length-Value (TLV)** structure for source-route lists. When the **R-S-R** (Request Source Route) flag is set—bit mask `0b0000_0100` as defined in [`bitchat/Services/BLE/BLEService.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/BLE/BLEService.swift) at line 3701—the packet includes a TLV-encoded array of hop IDs that instruct each intermediate node exactly where to forward the frame next.

This eliminates flooding for routed messages and enables power-efficient unicast chains across the mesh.

## Capability Advertisement and Feature Extensibility

V2 adds machine-readable feature negotiation through a capability bitfield, absent in V1's fixed metadata.

### The 64-Bit Capability Bitset

Where V1 announce packets carried only sender ID and basic metadata, V2 announce packets include a **peer-capability bitmask** (`uint64`) and optional **peer-ID rotation** fields. As noted in [`docs/PEER-ID-ROTATION.md`](https://github.com/permissionlesstech/bitchat/blob/main/docs/PEER-ID-ROTATION.md), this bitset reserves specific bits for features—bit 14 handles peer-ID rotation—allowing nodes to advertise support for stickers, Nostr double-ratchet, and other extensions without breaking older clients.

The [`bitchat/Services/BLE/BLEAnnounceHandlingPolicy.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/BLE/BLEAnnounceHandlingPolicy.swift) file (lines 32-35) demonstrates how nodes pre-flight check these capability bits before engaging in protocol upgrades.

## Payload Handling and Fragmentation

The wire format changes directly impact maximum transmission unit (MTU) handling and message fragmentation strategies.

### MTU Constraints in V1

V1 packets were constrained by standard BLE MTU limits (~20 bytes), forcing large messages to split into numerous tiny fragments. The `BLEOutboundFragmentPlanner` treated every fragment as a discrete packet, incurring overhead proportionate to the 5-byte header on each chunk.

### Extended Fragment Sizes in V2

V2 supports **up to 512-byte fragments**, selectable via the packet version field. The fragment planner in [`bitchat/Services/BLE/BLEOutboundFragmentPlanner.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/BLE/BLEOutboundFragmentPlanner.swift) (lines 6-91) uses `fragmentVersion = 2` to determine chunk sizing, significantly reducing header overhead for bulk data like voice frames or encrypted attachments.

## Security and Privacy Enhancements

V2 hardens the protocol against size-based fingerprinting and improves forward privacy.

### Constant-Width Signing and Padding

V1 packets contained **no padding**, meaning every byte contributed to the signed hash and packet lengths varied with payload size. This created a side-channel for traffic analysis.

V2 mandates **signed, constant-width padding**. As stated in [`docs/BLE-ARCHITECTURE-V3.md`](https://github.com/permissionlesstech/bitchat/blob/main/docs/BLE-ARCHITECTURE-V3.md), "padding is signed" and fixed-width, simplifying verification and preventing adversaries from inferring message types from packet dimensions.

### Peer-ID Rotation Support

V2's capability bits enable dynamic peer-ID rotation, whereas V1 used static identifiers for the duration of the session. This privacy feature requires the new announce format introduced in V2 to distribute rotated keys without disrupting the mesh graph.

## Code Examples

### Constructing a V2 Packet in Swift

The following pattern from [`BLEOutboundFragmentPlanner.swift`](https://github.com/permissionlesstech/bitchat/blob/main/BLEOutboundFragmentPlanner.swift) demonstrates assembling a V2 packet with source routing:

```swift
import Bitchat

let payload = Data("Hello, mesh!".utf8)
let version: UInt8 = 2                     // V2 format identifier
let type = MessageType.chatMessage.rawValue // 0x01
let ttl: UInt8 = 5
let rsrFlag: UInt8 = 0b0000_0100           // Request Source Route flag

var packet = Data()
packet.append(version)                     // Explicit version byte
packet.append(type)
packet.append(ttl)
packet.append(rsrFlag)

// Optional source-route TLV (hop list)
let sourceRoute: [Data] = [peerA, peerB]
packet.append(contentsOf: TLV.encode(id: .sourceRoute, value: sourceRoute))

packet.append(payload)
BLEService.shared.send(packet)

```

### Parsing Version-Tagged Packets

Receivers use the version byte to branch between legacy and modern parsers:

```swift
func parseBLEPacket(_ data: Data) throws -> BLEPacket {
    guard let version = data.first, version == 2 else {
        // Fallback to V1 parser: first byte is message type
        return try BLEPacketV1(data)
    }
    
    let type   = data[1]
    let ttl    = data[2]
    let flags  = data[3]
    
    var cursor = 4
    var sourceRoute: [Data] = []
    
    if flags & 0b0000_0100 != 0 {               // R-S-R flag check
        let tlv = try TLV.decode(from: data, at: &cursor)
        if tlv.id == .sourceRoute { 
            sourceRoute = tlv.value 
        }
    }
    
    let payload = data.suffix(from: cursor)
    return BLEPacket(
        version: version,
        type: type,
        ttl: ttl,
        flags: flags,
        sourceRoute: sourceRoute,
        payload: payload
    )
}

```

## Summary

- **V1** uses a 5-byte header with no version byte, supports only flooding/unicast, lacks capability advertisement, and is limited to ~20-byte BLE MTU payloads.
- **V2** adds a 6-byte header with explicit versioning, TLV-encoded source routing, a 64-bit capability bitfield (enabling peer-ID rotation), R-S-R flags, and 512-byte fragment support.
- **Interoperability**: V2 nodes can parse V1 packets by detecting the absence of the version byte (value ≤ 1), but V1 nodes cannot interpret V2's extended headers or source-route TLVs.
- **Implementation**: Key logic resides in [`BLEService.swift`](https://github.com/permissionlesstech/bitchat/blob/main/BLEService.swift) (version gating), [`BLEOutboundFragmentPlanner.swift`](https://github.com/permissionlesstech/bitchat/blob/main/BLEOutboundFragmentPlanner.swift) (fragment sizing), and [`BLEAnnounceHandlingPolicy.swift`](https://github.com/permissionlesstech/bitchat/blob/main/BLEAnnounceHandlingPolicy.swift) (capability checks).

## Frequently Asked Questions

### Can Version 1 nodes communicate with Version 2 nodes in BitChat?

Yes, but unidirectionally. V2 implementations inspect the first byte of every received packet; if the value is 1 or less, they fall back to legacy V1 parsing logic. However, V1 nodes receiving V2 packets will misinterpret the version byte as a message type, leading to parsing failures unless they ignore unknown types.

### Why did BitChat move from flooding to source routing in V2?

Flooding creates excessive battery drain and airtime contention in dense meshes. The V2 TLV-encoded source-route list (triggered by the R-S-R flag) allows senders to specify exact forwarding paths, eliminating redundant transmissions and enabling efficient multi-hop unicast for large payloads like voice frames.

### How does the capability bitfield affect packet compatibility?

The 64-bit capability field in V2 announce packets (located in [`BLEAnnounceHandlingPolicy.swift`](https://github.com/permissionlesstech/bitchat/blob/main/BLEAnnounceHandlingPolicy.swift)) uses reserved bits to advertise features like peer-ID rotation and Nostr support. Older V1 nodes ignore these bytes as trailing padding, preserving backward compatibility while allowing V2 nodes to negotiate advanced features before exchanging sensitive data.

### What is the maximum practical payload size difference between V1 and V2?

V1 fragments are constrained by the standard BLE MTU of approximately 20 bytes per packet. V2 supports fragments up to 512 bytes when the fragment planner detects `version = 2`, reducing the number of BLE transactions required for large messages by roughly 96% compared to V1 fragmentation.