Skip to content

Changelog

spec v0.5.0

All notable changes to the meshtrap spec.

This file follows Keep a Changelog and semver. The version applies to the entire spec/ directory as a unit.

The discovery / join handshake. A node now announces itself on boot, the hub welcomes it, and the node sends a full descriptor — the foundation for the router tier (Phase 3) and provisioning (Phase 4).

  • frames.yaml JOIN (0x03) payload — 6 bytes (proto_role, hw_rev, fw_ver, flags, rsvd), uplink. flags bit 0 is ble_wake_request. Sent as the first uplink after boot, before the STATUS loop.
  • frames.yaml JOIN_ACK (0x04) payload — 7 bytes (flags, hub_time, config_version), downlink. flags: bit 0 accepted, bit 1 config_pending, bit 2 ble_wake_granted. Mirrors the STATUS_ACK shape.
  • frames.yaml ANNOUNCE (0x05) payload — variable-length full node descriptor (location, hw/fw, role, router preference list, audit fields, autonomous-reorder flag, name), transcribed from the layout already in docs/protocol.md.
  • 0.4.0 → 0.5.0 (minor). Additive: three previously-pending payloads become defined in the already-reserved app message-type range (0x03–0x05). The envelope and all prior payloads are unchanged, so a 0.4.0 consumer is unaffected.
  • Full vertical slice on both field (PSA) and hub (mbedTLS): endpoint boots → JOIN → hub JOIN_ACK (accepted) → endpoint ANNOUNCE → hub logs the descriptor → normal STATUS loop. Runs endpoint↔hub direct (no router tier yet — Phase 3). ANNOUNCE carries real audit fields from NVS; location/name/router-list are bench placeholders until provisioning (Phase 4). WHO_ARE_YOU (0x06) and BLE actually toggling are deferred.
  • docs/protocol.md already carried the JOIN sequence and the ANNOUNCE byte layout; this fills in the JOIN / JOIN_ACK payload boxes and ratifies all three into the contract.
  • docs/project/glossary.md: added JOIN, JOIN_ACK, ANNOUNCE, proto_role.

COMMAND downlink and its endpoint reply. The hub can now push an authenticated, replay-protected instruction to an endpoint and receive a confirmation; firmware implements set_ack_interval end to end.

  • frames.yaml COMMAND (0x07) payload — variable length: cmd_type(1) || cmd_seq(2) || cmd_payload || admin_mic(8), carried inside the normal AES-128-CCM/K_group envelope. The inner admin_mic is an AES-CMAC-128 (truncated to 8 B) over src||dst||cmd_type||cmd_seq||cmd_payload, keyed by K_admin or K_field per the command’s privilege class. Commands table moved into the contract with an explicit privilege column.
  • frames.yaml COMMAND_ACK (0x08) payload — 5 bytes (cmd_seq, result, new_config_version), uplink, with the result-code enum.
  • 0.3.0 → 0.4.0 (minor). Additive: two previously-pending payloads become defined; the envelope, STATUS, and STATUS_ACK are unchanged.
  • set_ack_interval (0x06) is fully implemented (decode, inner CMAC verify, apply, COMMAND_ACK) on both field and hub. Other cmd_types decode + verify but return result=unknown_cmd_type until their apply handlers land. cmd_seq and config_version are persisted on the field (NVS) alongside the frame seq.
  • docs/protocol.md already carried the COMMAND / COMMAND_ACK prose and the commands table; this ratifies them into the contract.
  • docs/project/glossary.md already had COMMAND / COMMAND_ACK / K_admin / K_field entries. Added admin_mic, cmd_seq, config_version.

First downlink frame. The hub replies to an ack-requested STATUS with a STATUS_ACK; firmware on both sides implements the round-trip.

  • frames.yaml STATUS_ACK (0x02) payload schema — 7 bytes, ratified (flags, hub_time, config_version). Direction downlink (dir = 1), so this is the first frame that exercises the dir = 1 nonce path.
  • 0.2.0 → 0.3.0 (minor). Additive: a previously-pending payload becomes defined; nothing about the envelope or the STATUS payload changes, so a 0.2.0 consumer is unaffected.
  • docs/protocol.md already carried the STATUS_ACK payload prose; docs/project/glossary.md already had the term. No prose change needed beyond this ratification.

First ratified frame definitions, landed alongside firmware bring-up. Field firmware (Zephyr, firmware/field/) transmits and hub firmware (ESP-IDF, firmware/hub/) receives an encrypted STATUS frame on real hardware.

  • frames.yaml crypto: block making the AEAD construction explicit: AES-128-CCM under K_group, 7-byte nonce, 4-byte MIC, the 12-byte clear header as AAD.
  • frames.yaml STATUS (0x01) payload schema — 10 bytes, ratified.
  • frames.yaml envelope is now described as an explicit on-wire layout: [clear header 12 B][ciphertext N][mic 4 B], 16-byte fixed overhead.
  • Clarified that the AES-CCM nonce is derived, not transmitted. The 0.1.0 skeleton listed nonce(7) as an envelope field, and docs/protocol.md / docs/project/glossary.md echoed it. The nonce is src(4) || seq(2) || dir(1) — every input is already in the clear header (src, seq) or derivable from the message type (dir) — so it is reconstructed at the receiver and never sent. This removes 7 bytes of redundant airtime per frame and matches docs/crypto.md, which always treated the nonce as a derived AEAD input. No firmware ever shipped against the 0.1.0 skeleton, so nothing breaks.
  • Bumped 0.1.0 → 0.2.0 (minor). 0.1.0 was an un-implemented skeleton explicitly reserving the namespace; these are additive first definitions, as the 0.1.0 changelog anticipated (“Spec version is bumped to 0.2.0 once the first complete frame definitions land”).
  • docs/protocol.md — envelope diagram corrected (nonce no longer drawn as a wire field; 16-byte overhead noted).
  • docs/project/glossary.mdEnvelope entry corrected to the 12-byte header (ver/type/src/dst/seq); nonce clarified as derived.
  • ADR-01 (PHY), ADR-14 (firmware frameworks). Crypto per docs/crypto.md.

Skeleton structure laid down. No implementations yet.

  • frames.yaml — initial skeleton listing frame envelope and reserved message-type ranges.
  • mqtt.yaml — initial skeleton listing topic structure.
  • api.yaml — initial skeleton OpenAPI placeholder for central service.
  • ADRs 1, 2, 3, 4, 5, 6 in docs/project-management/decisions.md.
  • Concrete field definitions land alongside firmware bring-up (Phase 1) and are accompanied by docs/protocol.md updates.
  • Spec version is bumped to 0.2.0 once the first complete frame definitions land.