Skip to content

Protocol map

KM43 / generated overview

The protocol in three views: how authentication changes through a connection, which numeric ranges remain free, and where a new metric or event kind belongs.

Status
Generated and non-normative
Source
protocol.toml
Shows
Authentication, allocation, and families
Regenerate
cargo xtask registry

Generated from protocol.toml. The rules themselves remain in PROTOCOL.md and LINK.md; where a picture and a requirement disagree, the requirement wins.

Grouped by the authentication rule the registry gives it, which is also roughly the order a connection meets them. Each node carries its request opcode and rule, then its response opcode and rule.

flowchart TD
  subgraph Beforeanykeyexists["Before any key exists"]
    Discover["Discover<br/>0x00 none → 0x80 none"]
  end
  subgraph Enrolmentprintedsecretandsomebodyatthepanel["Enrolment — printed secret, and somebody at the panel"]
    Pair["Pair<br/>0x0B pair_key → 0x8B pair_key"]
  end
  subgraph Handshakeprovingaclientkey["Handshake — proving a client key"]
    Hello["Hello<br/>0x01 proof → 0x81 rsp"]
  end
  subgraph Insession["In session"]
    Snapshot["Snapshot<br/>0x02 wrq → 0x82 rsp"]
    Subscribe["Subscribe<br/>0x03 wrq → 0x83 rsp"]
    Event["Event<br/>unsolicited · 0x04 evt"]
    ReadLog["ReadLog<br/>0x05 wrq → 0x85 rsp"]
    GetConfig["GetConfig<br/>0x06 wrq → 0x86 rsp"]
    SetConfig["SetConfig<br/>0x07 signed → 0x87 rsp"]
    Command["Command<br/>0x08 signed → 0x88 rsp"]
    Firmware["Firmware<br/>0x09 signed → 0x89 rsp"]
    Time["Time<br/>0x0A signed → 0x8A rsp"]
    Goodbye["Goodbye<br/>0x0C wrq → 0x8C rsp"]
    Error["Error<br/>unsolicited · 0xFF rsp_or_bare"]
  end
  subgraph TheinternalUART["The internal UART"]
    LinkUp["LinkUp<br/>0x60 → 0xE0 · either side"]
    Heartbeat["Heartbeat<br/>0x61 → 0xE1 · either side"]
    ClientConnected["ClientConnected<br/>0x62 → 0xE2 · comms → controller"]
    ClientDisconnected["ClientDisconnected<br/>0x63 → 0xE3 · comms → controller"]
    CloseConnection["CloseConnection<br/>0x64 → 0xE4 · controller → comms"]
    NetConfig["NetConfig<br/>0x65 → 0xE5 · controller → comms"]
    TimeOffer["TimeOffer<br/>0x66 → 0xE6 · comms → controller"]
    CommsRelease["CommsRelease<br/>0x67 → 0xE7 · controller → comms"]
  end
  Beforeanykeyexists --> Enrolmentprintedsecretandsomebodyatthepanel
  Enrolmentprintedsecretandsomebodyatthepanel --> Handshakeprovingaclientkey
  Handshakeprovingaclientkey --> Insession

A table of allocated numbers cannot show a gap, and a gap is the only thing somebody allocating the next number needs to see.

client requests — 0x00 to 0x5F, 12 allocated, 84 free
0x00 ####.### #####... ........ ........
0x20 ........ ........ ........ ........
0x40 ........ ........ ........ ........
link-local requests — 0x60 to 0x7E, 8 allocated, 23 free
0x60 ######## ........ ........ .......
client responses — 0x80 to 0xDF, 12 allocated, 84 free
0x80 ####.### #####... ........ ........
0xA0 ........ ........ ........ ........
0xC0 ........ ........ ........ ........
link-local responses — 0xE0 to 0xFE, 8 allocated, 23 free
0xE0 ######## ........ ........ .......
client errors — 1 to 32, 17 allocated, 15 free
1 ######## ######## #....... ........
link-local errors — 256 to 287, 9 allocated, 23 free
256 ######## #....... ........ ........

The two error spaces run further than shown — a client code is a u16 and the link-local range ends at 511. The window is where the next one would go.

0x7F is absent from the link-local range on purpose: 0x7F with the high bit set is 0xFF, which is Error.

The high byte is the family. A kind allocated in the wrong one is not wrong on the wire and is wrong for everybody reading a log.

flowchart LR
  metrics["metric kinds"]
  events["event kinds"]
  metrics --> m1["0x01xx · DC electrical · 11 allocated"]
  metrics --> m2["0x02xx · AC electrical · 5 allocated"]
  metrics --> m3["0x03xx · temperature · 2 allocated"]
  metrics --> m4["0x04xx · level · 2 allocated"]
  metrics --> m5["0x05xx · generator · 4 allocated"]
  metrics --> m6["0x06xx · controller · 4 allocated"]
  events --> e1["0x01xx · 2 allocated · 1 class A"]
  events --> e2["0x02xx · 3 allocated · 3 class A"]
  events --> e3["0x03xx · 2 allocated · 2 class A"]
  events --> e4["0x04xx · 1 allocated · 1 class A"]
  events --> e5["0x05xx · 2 allocated · 2 class A"]
  events --> e6["0x06xx · 4 allocated · 4 class A"]
  events --> e7["0x07xx · 2 allocated · 2 class A"]
  events --> e8["0x08xx · 4 allocated · 4 class A"]

0xF0000xFFFF is vendor and experimental in both spaces and is never allocated here.