Changelog
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.
[0.5.0] — 2026-06-08
Section titled “[0.5.0] — 2026-06-08”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.yamlJOIN (0x03) payload — 6 bytes (proto_role, hw_rev, fw_ver, flags, rsvd), uplink.flagsbit 0 isble_wake_request. Sent as the first uplink after boot, before the STATUS loop.frames.yamlJOIN_ACK (0x04) payload — 7 bytes (flags, hub_time, config_version), downlink.flags: bit 0accepted, bit 1config_pending, bit 2ble_wake_granted. Mirrors the STATUS_ACK shape.frames.yamlANNOUNCE (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 indocs/protocol.md.
Note on semver
Section titled “Note on semver”- 0.4.0 → 0.5.0 (minor). Additive: three previously-
pendingpayloads 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.
Firmware scope at this version
Section titled “Firmware scope at this version”- 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.
Prose / glossary
Section titled “Prose / glossary”docs/protocol.mdalready 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: addedJOIN,JOIN_ACK,ANNOUNCE,proto_role.
[0.4.0] — 2026-05-30
Section titled “[0.4.0] — 2026-05-30”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.yamlCOMMAND (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 inneradmin_micis an AES-CMAC-128 (truncated to 8 B) oversrc||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 explicitprivilegecolumn.frames.yamlCOMMAND_ACK (0x08) payload — 5 bytes (cmd_seq, result, new_config_version), uplink, with the result-code enum.
Note on semver
Section titled “Note on semver”- 0.3.0 → 0.4.0 (minor). Additive: two previously-
pendingpayloads become defined; the envelope, STATUS, and STATUS_ACK are unchanged.
Firmware scope at this version
Section titled “Firmware scope at this version”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 returnresult=unknown_cmd_typeuntil their apply handlers land.cmd_seqandconfig_versionare persisted on the field (NVS) alongside the frameseq.
Prose / glossary
Section titled “Prose / glossary”docs/protocol.mdalready carried the COMMAND / COMMAND_ACK prose and the commands table; this ratifies them into the contract.docs/project/glossary.mdalready had COMMAND / COMMAND_ACK / K_admin / K_field entries. Addedadmin_mic,cmd_seq,config_version.
[0.3.0] — 2026-05-29
Section titled “[0.3.0] — 2026-05-29”First downlink frame. The hub replies to an ack-requested STATUS with a STATUS_ACK; firmware on both sides implements the round-trip.
frames.yamlSTATUS_ACK (0x02) payload schema — 7 bytes, ratified (flags, hub_time, config_version). Direction downlink (dir = 1), so this is the first frame that exercises thedir = 1nonce path.
Note on semver
Section titled “Note on semver”- 0.2.0 → 0.3.0 (minor). Additive: a previously-
pendingpayload becomes defined; nothing about the envelope or the STATUS payload changes, so a 0.2.0 consumer is unaffected.
Prose / glossary
Section titled “Prose / glossary”docs/protocol.mdalready carried the STATUS_ACK payload prose;docs/project/glossary.mdalready had the term. No prose change needed beyond this ratification.
[0.2.0] — 2026-05-29
Section titled “[0.2.0] — 2026-05-29”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.yamlcrypto:block making the AEAD construction explicit: AES-128-CCM underK_group, 7-byte nonce, 4-byte MIC, the 12-byte clear header as AAD.frames.yamlSTATUS (0x01) payload schema — 10 bytes, ratified.
Changed
Section titled “Changed”frames.yamlenvelope 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, anddocs/protocol.md/docs/project/glossary.mdechoed it. The nonce issrc(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 matchesdocs/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.
Note on semver
Section titled “Note on semver”- 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”).
Prose / glossary updated in lockstep
Section titled “Prose / glossary updated in lockstep”docs/protocol.md— envelope diagram corrected (nonce no longer drawn as a wire field; 16-byte overhead noted).docs/project/glossary.md—Envelopeentry corrected to the 12-byte header (ver/type/src/dst/seq); nonce clarified as derived.
Decisions referenced
Section titled “Decisions referenced”- ADR-01 (PHY), ADR-14 (firmware frameworks). Crypto per
docs/crypto.md.
[0.1.0] — pre-release
Section titled “[0.1.0] — pre-release”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.
Decisions referenced
Section titled “Decisions referenced”- 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.mdupdates. - Spec version is bumped to 0.2.0 once the first complete frame definitions land.