#  The registry, as data.
#
#  Data only. The tables in REGISTRY.md are generated from this file; the prose
#  around them is written by hand and stays there, because a data file carrying
#  three kilobytes of markdown is a document with an awkward syntax.
#
#  Edit here, run `cargo xtask registry`, commit both. A number allocated in two places is a number two people can
#  allocate differently, which is the whole failure this registry exists to stop
#  and which it was itself an instance of: PROTOCOL.md said `Pair` authenticates
#  under a derived key while REGISTRY.md said it did not, for an hour, in a repo
#  with five audit rounds behind it.

[meta]
protocol = "1.0"

# The two spaces where an unrecognised value is surfaced rather than refused.
# Everything else is a closed set: an unknown value there is a message whose
# meaning is not knowable, and the generated bindings make that a parse error.
skip_unknown = ["metric_kind", "event_kind"]

[[messages]]
request = 0x00
response = 0x80
name = "Discover"
auth_request = "none"
auth_response = "none"
status = "live"
since = "1.0"

[[messages]]
request = 0x01
response = 0x81
name = "Hello"
auth_request = "proof"
auth_response = "rsp"
status = "live"
since = "1.0"

[[messages]]
request = 0x02
response = 0x82
name = "Snapshot"
auth_request = "wrq"
auth_response = "rsp"
status = "live"
since = "1.0"

[[messages]]
request = 0x03
response = 0x83
name = "Subscribe"
auth_request = "wrq"
auth_response = "rsp"
status = "live"
since = "1.0"

[[messages]]
response = 0x04
name = "Event"
auth_response = "evt"
status = "live"
since = "1.0"

[[messages]]
request = 0x05
response = 0x85
name = "ReadLog"
auth_request = "wrq"
auth_response = "rsp"
status = "live"
since = "1.0"

[[messages]]
request = 0x06
response = 0x86
name = "GetConfig"
auth_request = "wrq"
auth_response = "rsp"
status = "reserved"
since = "1.0"

[[messages]]
request = 0x07
response = 0x87
name = "SetConfig"
auth_request = "signed"
auth_response = "rsp"
status = "reserved"
since = "1.0"

[[messages]]
request = 0x08
response = 0x88
name = "Command"
auth_request = "signed"
auth_response = "rsp"
status = "reserved"
since = "1.0"

[[messages]]
request = 0x09
response = 0x89
name = "Firmware"
auth_request = "signed"
auth_response = "rsp"
status = "reserved"
since = "1.0"

[[messages]]
request = 0x0A
response = 0x8A
name = "Time"
auth_request = "signed"
auth_response = "rsp"
status = "live"
since = "1.0"

[[messages]]
request = 0x0B
response = 0x8B
name = "Pair"
auth_request = "pair_key"
auth_response = "pair_key"
status = "live"
since = "1.0"

[[messages]]
request = 0x0C
response = 0x8C
name = "Goodbye"
auth_request = "wrq"
auth_response = "rsp"
status = "live"
since = "1.0"

[[message_ranges]]
request_lo = 0x60
request_hi = 0x7E
response_lo = 0xE0
response_hi = 0xFE
name = "link-local"
see = "LINK.md"
since = "1.0"

[[messages]]
response = 0xFF
name = "Error"
auth_response = "rsp_or_bare"
status = "live"
since = "1.0"


[[errors]]
code = 1
meaning = "Malformed frame"
macd = false
status = "live"

[[errors]]
code = 2
meaning = "Unknown message type"
macd = false
status = "live"

[[errors]]
code = 3
meaning = "Protocol major mismatch"
macd = false
status = "live"

[[errors]]
code = 4
meaning = "Hello required first"
macd = false
status = "live"

[[errors]]
code = 5
meaning = "Payload too large"
macd = false
status = "live"

[[errors]]
code = 6
meaning = "Unknown section"
macd = true
status = "live"

[[errors]]
code = 7
meaning = "Busy — retry"
macd = true
status = "live"

[[errors]]
code = 8
meaning = "Session table full"
macd = false
status = "live"

[[errors]]
code = 9
meaning = "Session expired"
macd = false
status = "live"

[[errors]]
code = 10
meaning = "Bad MAC"
macd = false
status = "live"

[[errors]]
code = 11
meaning = "Counter not fresh"
macd = true
status = "live"

[[errors]]
code = 12
meaning = "Unknown client"
macd = false
status = "live"

[[errors]]
code = 13
meaning = "Pairing window closed"
macd = false
status = "withdrawn"

[[errors]]
code = 14
meaning = "Stale challenge — reconnect and retry"
macd = false
status = "live"

[[errors]]
code = 15
meaning = "Snapshot exceeds channel cap"
macd = true
status = "withdrawn"

[[errors]]
code = 16
meaning = "Not permitted on this transport"
macd = false
status = "withdrawn"

[[errors]]
code = 17
meaning = "Clock not set"
macd = true
status = "withdrawn"

[[error_ranges]]
lo = 256
hi = 511
name = "link-local"
see = "LINK.md"

# Allocated in LINK.md, listed here so a number cannot be handed out twice and so
# the three that reach a client can be named by a generated constant rather than
# by a literal somebody typed.
[[link_errors]]
code = 256
name = "wrong_side"
meaning = "Unknown link-local opcode, or one sent from the wrong side"
reaches_client = false
status = "live"

[[link_errors]]
code = 257
name = "link_type_on_client_transport"
meaning = "Link-local type on a client transport"
reaches_client = true
status = "live"

[[link_errors]]
code = 258
name = "before_link_up"
meaning = "Client frame before LinkUp completed"
reaches_client = true
status = "live"

[[link_errors]]
code = 259
name = "unknown_handle"
meaning = "Unknown connection handle"
reaches_client = true
status = "live"

[[link_errors]]
code = 260
name = "connection_table_full"
meaning = "Connection table full"
reaches_client = false
status = "live"

[[link_errors]]
code = 261
name = "link_major_mismatch"
meaning = "Link protocol major mismatch"
reaches_client = false
status = "live"

[[link_errors]]
code = 262
name = "too_many_outstanding"
meaning = "Too many outstanding link-local requests"
reaches_client = false
status = "live"

[[link_errors]]
code = 263
name = "non_zero_session"
meaning = "Link-local type with a non-zero session"
reaches_client = false
status = "live"

[[link_errors]]
code = 264
name = "no_authorisation"
meaning = "No authorisation matches this image"
reaches_client = false
status = "live"

# The per-client capability mask. P-105 is the rule; this allocates the bits and
# the row each client_kind is handed.
[[client_capability]]
bit = 0
name = "write_config"
may = "write configuration at all"
status = "live"

[[client_capability]]
bit = 1
name = "write_network_and_cloud"
may = "write the **network** (`0x0020`) and **cloud** (`0x0021`) sections"
denied_to = ["cloud"]
status = "live"

[[client_capability]]
bit = 2
name = "send_command"
may = "send `Command 0x08`"
status = "live"

[[client_capability]]
bit = 3
name = "set_clock"
may = "set the clock with `Time 0x0A`"
denied_to = ["cloud"]
status = "live"

[[client_capability]]
bit = 4
name = "push_firmware"
may = "push firmware with `Firmware 0x09`"
denied_to = ["cloud"]
status = "live"

[[client_capability]]
range = "5–15"
may = "unallocated"


[[outcomes.command]]
value = 1
name = "accepted"
status = "live"
meaning = "Acted on"

[[outcomes.command]]
value = 2
name = "rejected"
status = "live"
meaning = "Refused, `detail` says why"

[[outcomes.command]]
value = 3
name = "duplicate"
status = "live"
meaning = "Already seen this `cmd_id`; not acted on twice"

[[outcomes.command]]
value = 4
name = "inhibited"
status = "live"
meaning = "The selector is at Off, or the generator is not ours"

[[outcomes.command]]
value = 5
name = "unauthorised"
status = "live"
meaning = "This client may not do this"

[[outcomes.command]]
value = 6
name = "shadowed"
status = "live"
meaning = "Would have been accepted; nothing was actuated"


[[outcomes.set_config]]
value = 1
name = "accepted"
status = "live"

[[outcomes.set_config]]
value = 2
name = "stale_version"
status = "live"

[[outcomes.set_config]]
value = 3
name = "invalid"
status = "live"

[[outcomes.set_config]]
value = 4
name = "unauthorised"
status = "live"

[[outcomes.set_config]]
value = 5
name = "exceeds_cap"
status = "live"


[[outcomes.pair]]
value = 1
name = "enrolled"
status = "live"

[[outcomes.pair]]
value = 2
name = "window_closed"
status = "live"

[[outcomes.pair]]
value = 3
name = "bad_proof"
status = "live"

[[outcomes.pair]]
value = 4
name = "table_full"
status = "live"

[[outcomes.pair]]
value = 5
name = "reclaimed"
status = "live"


[[outcomes.time]]
value = 1
name = "accepted"
status = "live"

[[outcomes.time]]
value = 2
name = "rejected"
status = "live"

[[outcomes.time]]
value = 3
name = "unauthorised"
status = "live"

[[outcomes.time]]
value = 4
name = "needs_button"
status = "live"


[[outcomes.firmware]]
value = 1
name = "accepted"
status = "reserved"

[[outcomes.firmware]]
value = 2
name = "bad_offset"
status = "reserved"

[[outcomes.firmware]]
value = 3
name = "bad_signature"
status = "reserved"

[[outcomes.firmware]]
value = 4
name = "wrong_target"
status = "reserved"

[[outcomes.firmware]]
value = 5
name = "too_large"
status = "reserved"

[[outcomes.firmware]]
value = 6
name = "no_transfer_in_progress"
status = "reserved"

[[outcomes.firmware]]
value = 7
name = "not_owner"
status = "reserved"

[[outcomes.firmware]]
value = 8
name = "unauthorised"
status = "reserved"



[[metrics]]
group = "`0x01xx` — DC electrical"
kind = 257
name = "battery voltage"
unit = "V"
scale = -3
status = "live"

[[metrics]]
group = "`0x01xx` — DC electrical"
kind = 258
name = "battery current (+ charge, − discharge)"
unit = "A"
scale = -3
status = "live"

[[metrics]]
group = "`0x01xx` — DC electrical"
kind = 259
name = "battery power"
unit = "W"
scale = -1
status = "live"

[[metrics]]
group = "`0x01xx` — DC electrical"
kind = 260
name = "state of charge"
unit = "%"
scale = -1
status = "live"

[[metrics]]
group = "`0x01xx` — DC electrical"
kind = 261
name = "battery temperature"
unit = "°C"
scale = -1
status = "live"

[[metrics]]
group = "`0x01xx` — DC electrical"
kind = 272
name = "PV array voltage"
unit = "V"
scale = -3
status = "live"

[[metrics]]
group = "`0x01xx` — DC electrical"
kind = 273
name = "PV array current"
unit = "A"
scale = -3
status = "live"

[[metrics]]
group = "`0x01xx` — DC electrical"
kind = 274
name = "PV array power"
unit = "W"
scale = -1
status = "live"

[[metrics]]
group = "`0x01xx` — DC electrical"
kind = 288
name = "load current"
unit = "A"
scale = -3
status = "live"

[[metrics]]
group = "`0x01xx` — DC electrical"
kind = 289
name = "load power"
unit = "W"
scale = -1
status = "live"

[[metrics]]
group = "`0x01xx` — DC electrical"
kind = 304
name = "start battery voltage"
unit = "V"
scale = -3
status = "live"


[[metrics]]
group = "`0x02xx` — AC electrical"
kind = 513
name = "AC voltage"
unit = "V"
scale = -1
status = "live"

[[metrics]]
group = "`0x02xx` — AC electrical"
kind = 514
name = "AC current"
unit = "A"
scale = -3
status = "live"

[[metrics]]
group = "`0x02xx` — AC electrical"
kind = 515
name = "AC power"
unit = "W"
scale = -1
status = "live"

[[metrics]]
group = "`0x02xx` — AC electrical"
kind = 516
name = "AC frequency"
unit = "Hz"
scale = -2
status = "live"

[[metrics]]
group = "`0x02xx` — AC electrical"
kind = 517
name = "AC energy"
unit = "Wh"
scale = 0
status = "live"


[[metrics]]
group = "`0x03xx` — temperature"
kind = 769
name = "ambient temperature"
unit = "°C"
scale = -1
status = "live"

[[metrics]]
group = "`0x03xx` — temperature"
kind = 770
name = "probe temperature"
unit = "°C"
scale = -1
status = "live"


[[metrics]]
group = "`0x04xx` — level"
kind = 1025
name = "tank level"
unit = "%"
scale = -1
status = "live"

[[metrics]]
group = "`0x04xx` — level"
kind = 1026
name = "tank sender current"
unit = "A"
scale = -6
status = "live"


[[metrics]]
group = "`0x05xx` — generator"
kind = 1281
name = "generator state (enum, see below)"
unit = "—"
scale = 0
status = "live"

[[metrics]]
group = "`0x05xx` — generator"
kind = 1282
name = "generator run hours"
unit = "h"
scale = -2
status = "live"

[[metrics]]
group = "`0x05xx` — generator"
kind = 1283
name = "generator starts since commissioning"
unit = "count"
scale = 0
status = "live"

[[metrics]]
group = "`0x05xx` — generator"
kind = 1284
name = "run contact commanded (bool)"
unit = "—"
scale = 0
status = "live"


[[metrics]]
group = "`0x06xx` — controller"
kind = 1537
name = "generator selector position (enum, see below)"
unit = "—"
scale = 0
status = "live"

[[metrics]]
group = "`0x06xx` — controller"
kind = 1538
name = "uptime since boot"
unit = "s"
scale = 0
status = "live"

[[metrics]]
group = "`0x06xx` — controller"
kind = 1539
name = "last boot reason (enum)"
unit = "—"
scale = 0
status = "live"

[[metrics]]
group = "`0x06xx` — controller"
kind = 1540
name = "log ring utilisation"
unit = "%"
scale = -1
status = "live"


[[events]]
kind = 257
name = "value changed"
class = "B"
status = "reserved"

[[events]]
kind = 258
name = "value quality changed"
class = "A"
status = "reserved"

[[events]]
kind = 513
name = "generator state changed"
class = "A"
status = "reserved"

[[events]]
kind = 514
name = "generator command withdrawn"
class = "A"
status = "reserved"

[[events]]
kind = 515
name = "generator stop not honoured"
class = "A"
status = "reserved"

[[events]]
kind = 769
name = "behaviour decision (shadow or applied)"
class = "A"
status = "reserved"

[[events]]
kind = 770
name = "behaviour inhibited"
class = "A"
status = "reserved"

[[events]]
kind = 1025
name = "output changed"
class = "A"
status = "reserved"

[[events]]
kind = 1281
name = "alarm raised"
class = "A"
status = "reserved"

[[events]]
kind = 1282
name = "alarm cleared"
class = "A"
status = "reserved"

[[events]]
kind = 1537
name = "boot"
class = "A"
status = "reserved"

[[events]]
kind = 1538
name = "config changed"
class = "A"
status = "reserved"

[[events]]
kind = 1539
name = "client enrolled"
class = "A"
status = "reserved"

[[events]]
kind = 1540
name = "time set"
class = "A"
status = "reserved"

[[events]]
kind = 1793
name = "records dropped"
class = "A"
status = "reserved"

[[events]]
kind = 1794
name = "record failed CRC"
class = "A"
status = "reserved"

[[events]]
kind = 2049
name = "comms link lost"
class = "A"
status = "reserved"

[[events]]
kind = 2050
name = "comms power cycled"
class = "A"
status = "reserved"

[[events]]
kind = 2051
name = "comms unrecoverable"
class = "A"
status = "reserved"

[[events]]
kind = 2052
name = "sessions shed for backpressure"
class = "A"
status = "reserved"


[[codes.config_section]]
number = 1
name = "identity and site"
status = "reserved"

[[codes.config_section]]
number = 2
name = "channels"
status = "reserved"

[[codes.config_section]]
number = 3
name = "buses and devices"
status = "reserved"

[[codes.config_section]]
number = 16
name = "generator behaviour"
status = "reserved"

[[codes.config_section]]
number = 17
name = "frost behaviour"
status = "reserved"

[[codes.config_section]]
number = 18
name = "schedule behaviour"
status = "reserved"

[[codes.config_section]]
number = 19
name = "load-shed behaviour"
status = "reserved"

[[codes.config_section]]
number = 32
name = "network"
status = "reserved"

[[codes.config_section]]
number = 33
name = "cloud"
status = "reserved"


[[codes.command_kind]]
number = 257
name = "start generator"
status = "reserved"

[[codes.command_kind]]
number = 258
name = "stop generator"
status = "reserved"

[[codes.command_kind]]
number = 259
name = "run exercise cycle now"
status = "reserved"

[[codes.command_kind]]
number = 513
name = "set output"
status = "reserved"

[[codes.command_kind]]
number = 514
name = "clear alarm"
status = "reserved"

[[codes.command_kind]]
number = 769
name = "clear override"
status = "reserved"


[[codes.capability_bit]]
number = 0
name = "event log readable"
status = "live"

[[codes.capability_bit]]
number = 1
name = "configuration writable"
status = "live"

[[codes.capability_bit]]
number = 2
name = "commands accepted"
status = "reserved"

[[codes.capability_bit]]
number = 3
name = "firmware update accepted"
status = "reserved"

[[codes.capability_bit]]
number = 4
name = "clock settable by client"
status = "live"

[[codes.capability_bit]]
number = 5
name = "counted state of charge available"
status = "live"

[[codes.capability_bit]]
number = 6
name = "AC metering on the generator"
status = "live"

[[codes.capability_bit]]
number = 7
name = "any behaviour is in shadow mode"
status = "live"

[[codes.capability_bit]]
range = "8–31"
name = "unallocated"


[[enums.quality]]
value = 1
name = "measured"
meaning = "Read directly from an instrument"

[[enums.quality]]
value = 2
name = "counted"
meaning = "Integrated or accumulated, and trustworthy as a total"

[[enums.quality]]
value = 3
name = "estimated"
meaning = "Derived from something else, e.g. SoC from terminal voltage"

[[enums.quality]]
value = 4
name = "stale"
meaning = "Was measured, is no longer current: older than its channel's maximum age, or the same reading for so long that the instrument is presumed hung"

[[enums.quality]]
value = 5
name = "absent"
meaning = "No reading. **The value key is omitted entirely**"


[[enums.time_source]]
value = 1
name = "client"
meaning = "A signed `Time` write from an enrolled client"

[[enums.time_source]]
value = 2
name = "ntp-via-comms"
meaning = "A `TimeOffer` the controller accepted from the comms processor"


[[enums.client_kind]]
value = 1
name = "app"

[[enums.client_kind]]
value = 2
name = "browser"

[[enums.client_kind]]
value = 3
name = "cloud"

[[enums.client_kind]]
value = 4
name = "cli"


# The three spaces P-125 names: a metric whose key 3 is an answer rather than a
# measurement. They were prose under the metric tables until a client had to
# decide what an unrecognised one means, and prose is not something an
# implementation can be generated from.

[[enums.generator_state]]
value = 1
name = "stopped"
meaning = "No AC and our contact open. Autostart may fire"

[[enums.generator_state]]
value = 2
name = "starting"
meaning = "Our contact closed, no AC yet. Gives up after the start window"

[[enums.generator_state]]
value = 3
name = "running"
meaning = "AC present with our contact closed. Ours, and ours to stop"

[[enums.generator_state]]
value = 4
name = "running, not ours"
meaning = "AC present with our contact **open** — somebody started it with the fob. Not a fault, and never rendered as one"

[[enums.generator_state]]
value = 5
name = "stop not honoured"
meaning = "Our command was withdrawn and the AC persists. Notify; never retry"

[[enums.generator_state]]
value = 6
name = "gave up"
meaning = "Commanded to run and it did not, as many times as the starter is worth. The contact stays open and nothing here will try again until a person clears it — out of fuel, an oil-alert shutdown and a flat start battery are indistinguishable from the controller, and hammering a start line into any of them is wrong"


[[enums.generator_selector]]
value = 1
name = "auto"
meaning = "Behaviours may command the generator"

[[enums.generator_selector]]
value = 2
name = "off"
meaning = "Never command it, for any reason. A behaviour that needs it reports itself `inhibited`"

[[enums.generator_selector]]
value = 3
name = "manual"
meaning = "The operator is driving. The controller monitors and logs only"


[[enums.boot_reason]]
value = 1
name = "power-on"

[[enums.boot_reason]]
value = 2
name = "watchdog"

[[enums.boot_reason]]
value = 3
name = "brown-out"

[[enums.boot_reason]]
value = 4
name = "software reset"

[[enums.boot_reason]]
value = 5
name = "panic"

# The link-local enum spaces. Allocated here for the same reason the link error
# codes are: a space allocated somewhere else is a space two people can allocate
# from. LINK.md's body listings stay the documentation — they are where the rule
# and the value sit together — and `every_live_number_is_reachable` checks that
# every value below is named there, so the two cannot drift.
#
# Rendered into no Markdown table on purpose. REGISTRY.md is the client
# protocol's registry, and eleven link-local tables in it would bury the spaces a
# client author actually needs. They reach no client, so they are emitted into
# the Rust bindings both firmwares share and into no TypeScript at all.

[[link_outcomes.client_connected]]
value = 1
name = "accepted"
status = "live"

[[link_outcomes.client_connected]]
value = 2
name = "refused_table_full"
status = "live"

[[link_outcomes.client_connected]]
value = 3
name = "refused_handle_in_use"
status = "live"

[[link_outcomes.client_connected]]
value = 4
name = "refused_link_not_up"
status = "live"

[[link_outcomes.client_disconnected]]
value = 1
name = "released"
status = "live"

[[link_outcomes.client_disconnected]]
value = 2
name = "unknown_handle"
status = "live"

[[link_outcomes.close_connection]]
value = 1
name = "closed"
status = "live"

[[link_outcomes.close_connection]]
value = 2
name = "unknown_handle"
status = "live"

[[link_outcomes.net_config]]
value = 1
name = "stored"
status = "live"

[[link_outcomes.net_config]]
value = 2
name = "rejected_invalid"
status = "live"

[[link_outcomes.net_config]]
value = 3
name = "nvs_write_failed"
status = "live"

[[link_outcomes.time_offer]]
value = 1
name = "accepted"
status = "live"

[[link_outcomes.time_offer]]
value = 2
name = "refused_implausible"
status = "live"

# Held rather than reallocated: L-152 says no rule produces it and no sound rule
# can, and handing the number to whoever allocates next is how a withdrawn
# meaning comes back wearing somebody else's name.
[[link_outcomes.time_offer]]
value = 3
name = "refused_have_better"
status = "withdrawn"

[[link_outcomes.time_offer]]
value = 4
name = "refused_step_too_large"
status = "live"

[[link_outcomes.time_offer]]
value = 5
name = "refused_rate_limited"
status = "live"

[[link_outcomes.comms_release]]
value = 1
name = "authorised"
status = "live"

[[link_outcomes.comms_release]]
value = 2
name = "installed"
status = "live"

[[link_outcomes.comms_release]]
value = 3
name = "activated"
status = "live"

[[link_outcomes.comms_release]]
value = 4
name = "refused_digest_mismatch"
status = "live"

[[link_outcomes.comms_release]]
value = 5
name = "refused_signature"
status = "live"

[[link_outcomes.comms_release]]
value = 6
name = "refused_no_space"
status = "live"

[[link_outcomes.comms_release]]
value = 7
name = "rolled_back"
status = "live"

[[link_enums.disconnect_reason]]
value = 1
name = "closed_by_client"
status = "live"

[[link_enums.disconnect_reason]]
value = 2
name = "transport_error"
status = "live"

[[link_enums.disconnect_reason]]
value = 3
name = "idle_timeout"
status = "live"

[[link_enums.disconnect_reason]]
value = 4
name = "closed_by_comms"
status = "live"

[[link_enums.close_reason]]
value = 1
name = "session_expired"
status = "live"

[[link_enums.close_reason]]
value = 2
name = "shedding"
status = "live"

[[link_enums.close_reason]]
value = 3
name = "authentication_failures"
status = "live"

[[link_enums.close_reason]]
value = 4
name = "resync"
status = "live"

[[link_enums.link_transport]]
value = 1
name = "ble"
status = "live"

[[link_enums.link_transport]]
value = 2
name = "wifi_local"
status = "live"

[[link_enums.link_transport]]
value = 3
name = "cloud"
status = "live"

[[link_enums.link_transport]]
value = 4
name = "usb"
status = "live"

[[link_enums.net_config_op]]
value = 1
name = "set"
status = "live"

[[link_enums.net_config_op]]
value = 2
name = "clear"
status = "live"

[[link_enums.comms_release_op]]
value = 1
name = "authorise"
status = "live"

[[link_enums.comms_release_op]]
value = 2
name = "activate"
status = "live"

[[link_enums.comms_release_op]]
value = 3
name = "revoke"
status = "live"

[[link_enums.comms_release_op]]
value = 4
name = "confirm_healthy"
status = "live"

# The link-local opcodes. LINK.md's own table was the only place these were
# written down, so the reserved range read as fully allocated and nothing could
# tell you 0x68 was free. Same argument as the link errors and the link enums.
[[link_messages]]
request = 0x60
response = 0xE0
name = "LinkUp"
direction = "either"

[[link_messages]]
request = 0x61
response = 0xE1
name = "Heartbeat"
direction = "either"

[[link_messages]]
request = 0x62
response = 0xE2
name = "ClientConnected"
direction = "comms-to-controller"

[[link_messages]]
request = 0x63
response = 0xE3
name = "ClientDisconnected"
direction = "comms-to-controller"

[[link_messages]]
request = 0x64
response = 0xE4
name = "CloseConnection"
direction = "controller-to-comms"

[[link_messages]]
request = 0x65
response = 0xE5
name = "NetConfig"
direction = "controller-to-comms"

[[link_messages]]
request = 0x66
response = 0xE6
name = "TimeOffer"
direction = "comms-to-controller"

[[link_messages]]
request = 0x67
response = 0xE7
name = "CommsRelease"
direction = "controller-to-comms"
