spec: add BLE transport chapter (v0.1.0)

Drafts spec/02-ble-transport.md: GATT service/characteristic UUIDs,
MTU-driven fragment sizing, the 13-byte fragment header, fragment
cap/lifetime, route-aware fragmentation, and advertising/scanning
behavior (service-UUID-only advertisements, no peer-identifying
bytes). Trims WHITEPAPER.md §4.4 to a cross-link now that the
byte-exact detail lives in the spec chapter.
This commit is contained in:
Yashodhan Singh 2026-08-02 00:11:04 +05:30
parent fd22df6639
commit 7882839e01
2 changed files with 70 additions and 1 deletions

View File

@ -64,7 +64,7 @@ Announcements carry up to 10 direct-neighbor IDs, giving each node a shallow top
### 4.4 Fragmentation
Packets exceeding the link MTU split into ~469-byte fragments (8-byte fragment ID, index/total header) that relay independently and reassemble at each receiving node (128 concurrent assemblies, 30 s timeout, 1 MiB cap).
The GATT service/characteristic, MTU-driven chunk sizing, fragment header layout, and advertising behavior are specified byte-exactly in [BLE Transport](spec/02-ble-transport.md).
### 4.5 Presence

69
spec/02-ble-transport.md Normal file
View File

@ -0,0 +1,69 @@
# BLE Transport
This chapter defines bitchat's Bluetooth Low Energy transport: the GATT service and characteristic peers connect over, the MTU-driven fragmentation scheme that lets a `bitchat packet` exceed a single BLE write, and the advertising/scanning behavior peers use to find each other. It does not define the packet header or payload encodings themselves; see the Wire Format chapter for those.
## 1. GATT Service and Characteristic
A bitchat node runs both the GATT peripheral and central roles simultaneously, so it can be discovered by, and discover, other nodes without a fixed client/server split.
| Item | Value |
|---|---|
| Service UUID | `F47B5E2D-4A9E-4C5A-9B3F-8E1D2C3A4B5C` |
| Characteristic UUID | `A1B2C3D4-E5F6-4A5B-8C9D-0E1F2A3B4C5D` |
| Characteristic properties | notify, write, write-without-response, read |
There is a single characteristic on the service; it is the sole data channel, carrying every `bitchat packet` in both directions. There is no separate read/write characteristic pair.
## 2. MTU and Fragment Sizing
BLE links in this specification are sized to a 512-byte MTU ceiling. A sender's default per-fragment chunk size is 469 bytes, leaving headroom for the fragment header (see [Fragment Header](#31-fragment-header)), the enclosing packet's own header and ID sections, and encryption overhead when the fragment rides inside a `noiseEncrypted` packet.
## 3. Fragmentation
A packet whose encoded size exceeds the link MTU MUST be split into `fragment` (`0x20`) packets (see the Wire Format chapter's [Message Types](01-wire-format.md#7-message-types)) and reassembled at the receiving node. Each fragment carries one slice of the original packet's encoded bytes as its payload; the original packet's own header and sections are not re-transmitted per fragment beyond what is captured by the fragment header below.
### 3.1 Fragment Header
```
+----------------+-------------+-------------+------------+----------------+
| Fragment ID | Index | Total | Orig. Type | Fragment Data |
| 8 bytes | 2 bytes | 2 bytes | 1 byte | variable |
+----------------+-------------+-------------+------------+----------------+
offset 0 8 10 12 13
```
| Offset | Length (bytes) | Field | Description |
|---|---|---|---|
| 0 | 8 | `fragmentID` | A random identifier generated per fragmented stream, shared by every fragment in that stream. |
| 8 | 2 | `index` | This fragment's zero-based position within the stream, big-endian. |
| 10 | 2 | `total` | The total number of fragments in the stream, big-endian. |
| 12 | 1 | `originalType` | The `type` byte of the packet being fragmented (see [Message Types](01-wire-format.md#7-message-types)), so the receiver knows how to interpret the reassembled bytes. |
| 13 | variable | `fragmentData` | One contiguous slice of the original packet's encoded bytes. |
### 3.2 Fragment Cap and Lifetime
A sender MUST NOT split a packet into more than 256 fragments; a receiver MAY reject a fragment stream whose `total` exceeds this cap. This ceiling exists because it is a cross-platform contract, not an arbitrary internal limit — implementations that accept larger fragment counts still MUST NOT rely on peers doing the same.
A receiving node reassembles fragments for a stream by collecting them keyed by `(sender, fragmentID)` until the number received equals `total`, then concatenates them in `index` order to recover the original packet's encoded bytes, which are then decoded per the Wire Format chapter using `originalType`. A stream with no new fragment arriving for 30 seconds is considered stalled; a node SHOULD request the missing fragments (see the Store and Forward chapter's sync mechanism) rather than discarding the stream outright.
Per-node limits on concurrent in-flight reassemblies and their buffered byte size are resource guards, not part of the wire protocol — they are implementation-defined and MAY differ between nodes without affecting interop.
### 3.3 Route-Aware Fragmentation (v2)
When the packet being fragmented carries a v2 [source route](README.md#glossary), the fragment-carrying packets MAY themselves carry that same source route so each fragment follows the same explicit path. Doing so adds the route section's bytes to every fragment's overhead, so a sender MAY shrink its per-fragment chunk size below the 469-byte default to keep the outer packet within the MTU, floored at a 64-byte minimum chunk size.
## 4. Advertising and Scanning
### 4.1 Advertisement Contents
A node's BLE advertisement (and any scan-response packet) MUST carry only the service UUID from [§1](#1-gatt-service-and-characteristic). It MUST NOT carry the device's local name, TX power level, peer ID, or any other peer-identifying bytes — advertisement contents are the only signal visible to a passive scanner before a connection and handshake establish identity, and leaking a stable identifier there undermines that.
A peer's `peer ID` is learned only after connecting and exchanging an `announce` packet over the characteristic, never from the advertisement itself.
### 4.2 Scanning
A central-role node scans filtered to the service UUID from [§1](#1-gatt-service-and-characteristic) and, on connecting, (re)discovers that same service and its characteristic before use.
### 4.3 Advertising and Scan Interval
The physical advertising interval and scan duty cycle are not part of the wire protocol — they are implementation-defined, since the platform BLE stack, not the application, ultimately governs them. Implementations MAY duty-cycle scanning or vary advertising parameters for power management without affecting interop, provided the contents in [§4.1](#41-advertisement-contents) and the service/characteristic in [§1](#1-gatt-service-and-characteristic) are unchanged.