release: v0.1.0
First cut release. Snapshots ./src/ as ./versions/v0.1.0/ via `just cut-version v0.1.0` (22 files: 21 chapter MDs + SUMMARY.md). Deployed alongside `latest` at: https://swaits.github.io/meshcore-spec/v0.1.0/ Also: - CHANGELOG.md adopts Keep a Changelog-style version sections. Future entries land under [Unreleased] and graduate to a version heading when cut. The existing date-stamped pass entries stay put as a historical record of the work captured in v0.1.0. - README.md fixes a stale "per vX.Y.Z tag" mention left over from the tag-based versioning model, and links the current release explicitly. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
24 files changed
+2478
-2
No files matched your search
@@ -0,0 +1,140 @@
|
||||
# MeshCore Protocol Specification
|
||||
|
||||
## Section 1: Wire Format
|
||||
|
||||
### Overview
|
||||
|
||||
A MeshCore packet is the fundamental transmission unit. It consists of a 1-byte
|
||||
header, an optional 4-byte transport codes field, a 1-byte path length, a
|
||||
variable-length path, and a variable-length payload. The maximum packet size on
|
||||
the wire is 255 bytes (MAX_TRANS_UNIT).
|
||||
|
||||
### Packet Layout
|
||||
|
||||
```
|
||||
0 1 2
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 ...
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Header | Transport Codes (opt, 4B) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Path Length | Path (variable) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Payload (variable) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
```
|
||||
|
||||
The fields appear in this order on the wire:
|
||||
|
||||
```
|
||||
[header(1)][transport_codes(4)?][path_len(1)][path(0-64)][payload(1-184)]
|
||||
```
|
||||
|
||||
### Field Summary
|
||||
|
||||
| Field | Size | Required | Description |
|
||||
|-------|------|----------|-------------|
|
||||
| Header | 1 byte | Yes | Route type, payload type, protocol version (see [Section 2](02-header.md)) |
|
||||
| Transport Codes | 4 bytes | Conditional | Two uint16_le values; present only when route type is `TRANSPORT_FLOOD` (0x00) or `TRANSPORT_DIRECT` (0x03) |
|
||||
| Path Length | 1 byte | Yes | Encoded hash count and hash size (see [Section 3](03-path.md)) |
|
||||
| Path | 0-64 bytes | Yes | Sequence of node hashes; length = hash_count × hash_size |
|
||||
| Payload | 1-184 bytes | Yes | Packet data; structure depends on payload type |
|
||||
|
||||
### Transport Codes
|
||||
|
||||
Transport codes are present if and only if the route type (header bits 0-1) is
|
||||
`TRANSPORT_FLOOD` (0x00) or `TRANSPORT_DIRECT` (0x03).
|
||||
|
||||
When present, transport codes consist of two little-endian 16-bit unsigned
|
||||
integers, for a total of 4 bytes:
|
||||
|
||||
```
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Code 1 (uint16_le) | Code 2 (uint16_le) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
```
|
||||
|
||||
- **Code 1**: Calculated from regional scope parameters
|
||||
- **Code 2**: Reserved (typically 0)
|
||||
|
||||
When transport codes are not present (route types `FLOOD` and `DIRECT`), the
|
||||
implementation MUST set both transport code values to zero internally.
|
||||
|
||||
### Encoding Algorithm
|
||||
|
||||
To encode a packet to wire format:
|
||||
|
||||
1. Write the header byte.
|
||||
2. If the route type is `TRANSPORT_FLOOD` or `TRANSPORT_DIRECT`:
|
||||
a. Write transport_codes[0] as uint16_le (2 bytes).
|
||||
b. Write transport_codes[1] as uint16_le (2 bytes).
|
||||
3. Write the path_len byte.
|
||||
4. Calculate path byte length as `hash_count × hash_size` (see [Section 3](03-path.md)).
|
||||
5. Write `path_byte_length` bytes from the path array.
|
||||
6. Write `payload_len` bytes from the payload array.
|
||||
|
||||
The total wire length is:
|
||||
```
|
||||
1 + (hasTransportCodes ? 4 : 0) + 1 + path_byte_length + payload_len
|
||||
```
|
||||
|
||||
This MUST NOT exceed MAX_TRANS_UNIT (255) bytes.
|
||||
|
||||
### Decoding Algorithm
|
||||
|
||||
To decode a packet from wire format:
|
||||
|
||||
1. Read 1 byte as the header. Extract route type from bits 0-1.
|
||||
2. If route type is `TRANSPORT_FLOOD` (0x00) or `TRANSPORT_DIRECT` (0x03):
|
||||
a. Read 2 bytes as transport_codes[0] (uint16_le).
|
||||
b. Read 2 bytes as transport_codes[1] (uint16_le).
|
||||
Otherwise, set both transport codes to zero.
|
||||
3. Read 1 byte as path_len.
|
||||
4. Validate path_len (see [Section 3](03-path.md)):
|
||||
a. Extract hash_size = (path_len >> 6) + 1. If hash_size is 4, the packet
|
||||
is INVALID.
|
||||
b. Extract hash_count = path_len & 63.
|
||||
c. If hash_count × hash_size > MAX_PATH_SIZE (64), the packet is INVALID.
|
||||
5. Calculate path_byte_length = hash_count × hash_size.
|
||||
6. Read path_byte_length bytes as the path.
|
||||
7. Let `i` be the current read position. If `i >= total_length`, the packet is
|
||||
INVALID (payload MUST contain at least 1 byte).
|
||||
8. Calculate payload_len = total_length - i.
|
||||
9. If payload_len > MAX_PACKET_PAYLOAD (184), the packet is INVALID.
|
||||
10. Read payload_len bytes as the payload.
|
||||
|
||||
### Size Constraints
|
||||
|
||||
| Constraint | Value | Enforcement |
|
||||
|-----------|-------|-------------|
|
||||
| Maximum wire length | 255 bytes | Encoder MUST NOT produce packets exceeding this |
|
||||
| Maximum payload | 184 bytes | Decoder MUST reject payloads exceeding this |
|
||||
| Maximum path | 64 bytes | Decoder MUST reject paths exceeding this |
|
||||
| Minimum payload | 1 byte | Decoder MUST reject packets with zero-length payload |
|
||||
| Minimum packet | 3 bytes | Header (1) + path_len (1) + payload (1 minimum) |
|
||||
|
||||
### Error Conditions
|
||||
|
||||
A conforming decoder MUST reject packets with any of the following:
|
||||
|
||||
1. Total wire length less than 3 bytes (no room for header + path_len + payload)
|
||||
2. Total wire length less than minimum required for the route type:
|
||||
- 3 bytes for FLOOD or DIRECT
|
||||
- 7 bytes for TRANSPORT_FLOOD or TRANSPORT_DIRECT (adds 4 transport code bytes)
|
||||
3. Invalid path_len encoding (see [Section 3](03-path.md))
|
||||
4. Path byte length exceeds remaining bytes
|
||||
5. Zero-length payload after header, transport codes, path_len, and path
|
||||
6. Payload length exceeding MAX_PACKET_PAYLOAD (184)
|
||||
|
||||
### Cross-References
|
||||
|
||||
- [Section 2: Header](02-header.md) — Header byte encoding details
|
||||
- [Section 3: Path](03-path.md) — Path length encoding and path field
|
||||
- Test vectors: `corpus/wire-format/framing/`, `corpus/wire-format/invalid/`
|
||||
|
||||
### Reference Implementation
|
||||
|
||||
- `Packet::writeTo()` in `src/Packet.cpp` — Encoding
|
||||
- `Packet::readFrom()` in `src/Packet.cpp` — Decoding
|
||||
- `Packet::getRawLength()` in `src/Packet.cpp` — Wire length calculation
|
||||
Reference in new issue
Block a user