Skip to content

IEEE register generated

CBOR keys#

Control-plane payloads are CBOR maps with integer keys. The key space is global: a key means the same thing in every message that uses it.

Keys 1 to 63 are core. Keys 64 to 127 are reserved. Keys 128 and above are experimental and never appear in a tagged release.

A receiver ignores an unknown key. It never NACKs one.

Global key space#

Key Name Type Notes
1 proto_ver uint HELLO/WELCOME: protocol major version
2 client_kind tstr e.g. webui, c5-remote, mobile, sim, tcode-bridge
3 client_name tstr human-readable, ≤32 UTF-8 bytes
4 instance_id bstr 8-byte stable client identity (§6.1)
5 token bstr 16-byte pairing token (§12.2); absent = viewer
6 session_id uint u32, hub-assigned (§6.1)
7 boot_id uint u32 random per hub boot (§7.2)
8 catalog_etag bstr 8-byte truncated SHA-256 (§8.3)
9 cfg_gen uint u16 config generation (§4.2)
10 subscriptions array of
11 publishes array of {15:channel,12:rate,42:burst}: HELLO wishes and PUBLISH (0x18) renegotiation (RFC-013)
12 rate_hz float requested rate; 0 = on-change only
13 priority uint priority class 0-3
14 granted_rate_hz float GRANT: applied rate after clamp (§10.2)
15 channel_id uint u16
16 code uint NACK/GOODBYE reason code (§16.1)
17 detail tstr optional human-readable diagnostic
18 intent_id uint u16 idempotency id, session-scoped (§9.3)
19 applied map ECHO: post-clamp applied values
20 value any INTENT payload value(s) per catalog schema
21 timestamp uint hub-ms (control plane events)
22 limits map WELCOME: hub limits (max_frame, max_subs, max_clients...)
23 roles uint granted access level (max of session)
24 deadman_ms uint WELCOME: applied deadman timeout for this session
25 deadman_policy uint 0=stop(decel) 1=hold 2=none: per active-source rules §11.3
26 probe_result map PROBE_REPORT: {bytes, span_ms, loss_pct, rtt_ms}: sub-keys in probe_result_keys
27 chunks array BLOB_REQ repair: missing chunk indices. (Was 'CATALOG_REQ repair' pre-v1.0; the key is REUSED rather than replaced because catalog transfer is now blob namespace 0: same meaning, generalized carrier.) Present WITH a full request = MALFORMED (RFC-022.6).
28 pin_proof bstr PAIR_REQ: HMAC-SHA256(PIN, hello-nonce) truncated 16B (§12.2)
29 nonce bstr WELCOME: 8-byte pairing nonce
30 precondition uint INTENT: expected cfg_gen (CAS guard, §9.3)
31 retry_after_ms uint NACK BUSY: earliest reconnect time
32 takeover bool safety-intent: forcible source takeover flag (§11.4)
33 event_kind uint EVENT: kind discriminator per catalog entry
34 seq_of_state uint EVENT: seq of the STATE twin frame it corresponds to (§9.4)
35 grants array WELCOME: batch grant results: array of {13:priority, 14:granted_rate_hz, 15:channel_id} (§6.3, §10.2)
36 granted_publishes array WELCOME / PUBLISH result: granted STREAM-ingress publishes: array of {14:granted_rate_hz, 15:channel_id, 42:burst} (§6.3, §10.5). Omitted when empty.
37 identity map WELCOME: hub identity (RFC-016): sub-keys in identity_keys. Capability discovery is CATALOG introspection, not a list here: a feature exists iff its channels exist.
38 blob map BLOB_REQ / BLOB_CHUNK / store CRUD intents: which blob, and its item fields (RFC-021): sub-keys in blob_keys.
39 trust map HELLO + WELCOME + AUTH: identity proof, signature material, token presentation, pairing modes (RFC-029/027): sub-keys in trust_keys. Absent = the potato path: bearer token, no crypto, unchanged v1-draft handshake cost.
40 body map EVENT: the kind-specific fields. Integer keys come from the CHANNEL'S CATALOG schema, exactly as INTENT's value (20) does, NOT from this global space. Grammar fix from the feasibility pass: with kind-specific fields at the top level, every device-authored EVENT channel (the motion-anomaly channel!) would have needed a registry PR to name its own fields: the precise coupling the self-describing catalog exists to prevent.
41 intent_seq uint NACK: seq of the frame being rejected (RFC-001). Hubs SHOULD populate it whenever a specific inbound frame provoked the NACK; clients MUST tolerate its absence. Without it a client with two intents in flight on ONE channel cannot tell which was refused.
42 burst float publishes / granted_publishes ENTRY maps: token-bucket capacity in samples, decoupled from rate (RFC-013). Default = granted rate (today's behavior). Clamped to rate x max_burst_multiple and echoed like every wish. Exists because §10.5 made rate double as bucket depth, so a 2-4/s segment sender with a 25/s peak had to declare 30 Hz: lying to admission control to buy burst.
43 reboot_in_ms uint ECHO applied (19): this accepted intent commits by rebooting, in about this many ms (RFC-020). The hub then GOODBYEs every session with REBOOTING; boot_id change handles the rest.
44 deadman_wish_ms uint HELLO: requested deadman window (RFC-038). The hub clamps into [deadman_min_ms, deadman_max_ms] (a hub MAY clamp tighter) and echoes the APPLIED value via the existing key 24, post-clamp echo, zero new response plumbing. Exists because a client that KNOWS its liveness cadence is coarse (a browser whose background-tab timers are throttled, a BLE client on a slow connection interval) could not ask for a window it can actually honor; §11.3's loss policy is untouched: this negotiates WHEN the deadman fires, never WHAT it does.
45 curve_family uint publishes / granted_publishes ENTRY maps: which curve_families smoothness class the segment stream describes (RFC-030). Wish rides HELLO or PUBLISH (0x18) like burst, so a sender switching interpolators mid-session renegotiates without a reconnect; the GRANT echo carries the EFFECTIVE family, post curve_policy override, so a client can tell 'honored' from 'downgraded'. Absent = unspecified = today's behavior. RFC-049b: requested_curve_family (48) rides the SAME entry map echoing the original wish verbatim, so the honored-vs-downgraded read is a direct comparison of two present keys, not an inference from what the client remembers sending.
46 ws_port uint WELCOME: the hub's own WebSocket listening port (RFC-046 §3). Present on every binding but load-bearing over BLE: it is the in-band endpoint disclosure a BLE-connected client needs to hop to WS (RFC-043's auto-upgrade). Also closes the sim's hub-identity gap for WS-side clients: the same key tells a WS client what the hub believes its own endpoint is. 0 = none (no WS listener right now).
47 ipv4 uint WELCOME: the hub's own IPv4 address (RFC-046 §3), packed big-endian into one u32 (e.g. 192.168.1.229 = 0xC0A801E5): there is no bstr(4) here because a plain integer makes '0 = none' the same natural sentinel 0.0.0.0 already is. Read alongside ws_port for the BLE→WS upgrade hop. 0 = none.
48 requested_curve_family uint publishes / granted_publishes ENTRY maps (RFC-049b): echoes the client's curve_family (45) WISH verbatim, unmodified by curve_policy. Exists alongside the existing effective value at key 45 so a downgrade is a visible FACT (both numbers present, compare them) rather than an inference a client has to reconstruct from what it originally sent. Present only when a curve_family wish was made; a hub with no curve-family opinion omits both keys exactly as before this RFC. Implementation: Phase D (RFC-049).
49 schedule_latency_us uint granted_publishes ENTRY maps (RFC-059): the hub's declared fixed delay, in µs, between a sample's time (segments: t_base + t_off; samples: its arrival stamp, RFC-084) and the start of its execution, inclusive of every hub-internal hop. On a samples-kind grant it is the chase-planning budget. A commitment, constant for the life of the grant; a change is an unsolicited GRANT. Absent or 0 = unspecified. No client wish exists (§9.6).
50 schedule_horizon_ms uint granted_publishes ENTRY maps (RFC-087), segments-kind grants only: the schedule horizon, how far ahead of hub time a segment's START may be stamped, and the span cap of a c2h segments bundle. One of 250 (the default, max_future_schedule_ms), 500 or 1000 (schedule_horizon_max_ms); the larger steps exist for lookahead players on poor WiFi. A cap, never a delay. Absent = 250. A commitment for the life of the grant; a change is an unsolicited GRANT. The hub picks it; no client wish exists.
51 trial bool INTENT (RFC-099, §9.3): true = a TRIAL write: applied, clamped, echoed and published like any write, bumps cfg_gen like any change, but never persisted. The hub keeps the key's pre-trial value in the sender's trial set until settings-trial (0x0016) commit or revert, or the session's end, which reverts. Absent or false = a durable write. Send only to a hub whose catalog declares 0x0016.

Scoped sub-map key spaces#

A feature that needs several keys takes one global key and numbers its interior in its own tiny space. Those spaces are listed below. A number here has no meaning in the global space above.

limits (key 22)#

Sizing caps the hub advertises in WELCOME.

Sub-key Name Notes
1 max_frame largest frame this hub accepts, bytes
2 max_subscriptions per-session subscription cap
3 retained_pending count of retained STATE pushes that will follow WELCOME
4 max_subscriptions_per_frame RFC-033.3: most subscription wishes one SUBSCRIBE (or HELLO) frame may carry. Before this was advertised, a client could only find the reference hub's 16-wish decode cap by binary-searching against a live machine. Two clients did this, one night each.
5 max_sessions RFC-055 (§6.3): concurrent sessions this hub admits. 0 = unknown.
6 sessions_in_use RFC-055 (§6.3): sessions occupying a slot, the admitted one included (STALE sessions count). 0 = unknown.

probe_result (key 26)#

The client's measurement of the optional post-WELCOME network probe.

Sub-key Name Notes
1 bytes_received bytes received during the probe burst
2 span_ms wall time of the burst as observed by the client
3 loss_pct_x100 loss percentage x100 (2 decimal fixed-point)
4 rtt_ms measured round-trip time, ms

identity (key 37)#

Who this hub is. There is exactly one home for hub identity.

Sub-key Name Notes
1 product tstr: product/model identifier, e.g. 'nucleus' (<=32 B)
2 fw_version tstr: hub firmware version, e.g. '2.1.47' (<=24 B). Retires the mDNS-TXT-only exposure that made the MFP plugin label devices 'boot 0x...'. A change here SHOULD be surfaced to the user (RFC-029.3).
3 hub_name tstr: operator-assigned machine name (<=32 B). Writable as a str16/str32 setting (RFC-026) where the hub offers one.
4 info map: OPTIONAL device-defined extras (hardware rev, build date...). Keys are device-defined tstr; the protocol never interprets them. Depth: WELCOME map -> identity map -> info map = 3, one under the §5.3 cap.
5 hub_instance_id uint (u64): RFC-048, operator veto of an RFC-046 decision. DURABLE hub identity: generated once and NVS-persisted, survives every reboot and firmware update (only a factory reset regenerates it), as opposed to boot_id (cbor_keys 7, §6.1/§7.2), which is a FRESH random value EVERY boot and exists only to fence stale per-boot state. Distinguishes THIS PHYSICAL HUB from any other, across time. Present in WELCOME identity (37) for any hub that persists one; absent = the hub has no durable identity yet (a fresh dev build, a non-persisting simulator) and a client MUST tolerate its absence exactly as it tolerates the rest of identity (§6.3). Also the value DISCOVER_REPLY (0x1F, §13.8) now carries as hub_instance_id, replacing that frame's original boot_id-based disambiguator (see the frame_types 0x1F note): a boot-scoped id could not deduplicate 'two hubs sharing a name' across a reboot, which was the field's whole job.
6 estop_cuts_power bool: RFC-085 (§11.2). Whether this hub's ESTOP cuts motor power (true: category 0, the machine goes limp and the hub is unhomed after release) or is a maximum-deceleration halt with power and home kept (false). REQUIRED of a hardware-profile hub (the declaration, never the mechanism); absent reads as false. Drives the client label: true renders E-Stop, false or absent renders Halt.

blob (key 38)#

Which blob, and its item fields. Shared by BLOB_REQ, BLOB_CHUNK and store intents.

Sub-key Name Notes
1 ns uint: which blob space: see blob_namespaces. (Named ns, not namespace: the generated C++ constant would otherwise be a keyword. Prose and CDDL may say 'namespace'.)
2 store_id uint u8: which store within blob_namespaces.store; absent/0 for the catalog namespace. Declared by the store's STORE-class catalog entry.
3 slot uint u8: item index within the store, 0..capacity-1
4 generation uint u16: the roster generation this request/response is consistent with. Lets a client notice its enumeration went stale mid-transfer.
5 name tstr: item name, <= the store's declared name_max
6 kind tstr: namespaced payload kind, e.g. 'pattern.frayd'. The hub validates kind + size on import and NACKs INVALID_VALUE; it never inspects the payload itself.
7 payload bstr: the opaque item document (<= preset_item_max_bytes / the store's per_item_max). Present on save-with-payload (= import); absent on save means 'capture CURRENT live state'.
8 chunk_index uint: 0-based index of this chunk
9 chunk_count uint: total chunks in this transfer
10 total_bytes uint: total encoded byte length being transferred (lets a receiver size/reject before assembling: RFC-028 no-unbounded-allocation)
11 digest bstr: RFC-073, OPTIONAL on a store item map: SHA-256 (32 B) over payload alone. The receiver's local check for BLOB_DONE status 0 vs 1 on a store transfer (§8.4/§8.7). A store item IS a CBOR map of these keys: slot 3, name 5, kind 6, payload 7 required, digest 11 optional.

trust (key 39)#

Identity proof, signature material, token presentation, pairing modes. Every key is optional.

Sub-key Name Notes
1 client_ver tstr: HELLO, client software version (<=24 B). The CHANGE TRIPWIRE: an observed version change drops a paired device to RECOGNIZED-PENDING (admitted at watch, granted role suspended, re-approval surfaced to configure sessions). HONESTY CLAUSE, normative: self-reported, therefore a tripwire and NOT attestation: a deliberately malicious update lies and keeps its token. The real bounds are role scoping, instant revocation, roster visibility and the role-exempt safety ops.
2 client_nonce bstr: HELLO: 8 bytes of CLIENT entropy. The hub signs client_nonce || session_id || boot_id. This was a feasibility-pass BLOCKER: without client entropy the WELCOME signature is replayable from one captured handshake and an evil twin passes verification.
3 sig_request bool: HELLO, 'please sign my nonce'. Signing is ON REQUEST because the S3 has no ECC accelerator (mbedtls software ECDSA: sign ~30-80 ms, one uninterruptible call, never inline in a WS handler), so potato handshakes must stay instant. Absent/false = no signature, no cost. A request is NOT a promise: a hub with no keypair answers with silence, and silence is a conformant answer. Only a client that PINNED a key (which it can only have received from that machine's own PAIR_GRANT) is entitled to read silence as failure, after hub_sig_timeout_ms.
4 hub_pubkey bstr: PAIR_GRANT, SEC1-compressed P-256 public key (33 B). Delivered AT THE PAIRING CEREMONY, i.e. TOFU anchored at the moment physical presence was proven. P-256 because WebCrypto can verify it: the browser participates.
5 welcome_sig bstr: WELCOME or HUB_SIG (0x1D): deterministic ECDSA-P256 (RFC 6979) signature over the 16-byte string client_nonce(8) || session_id(u32 LE) || boot_id(u32 LE). A clone machine copies every identity string and fails this; the client MUST surface 'not your machine' and withhold intents. Potato clients paired by physical ceremony MAY skip verification. TWO DELIVERY POINTS, ONE MEANING: inline in WELCOME where a hub can sign without stalling its own tick, otherwise deferred in HUB_SIG once the hub's own low-priority worker has produced it. Signature material and client handling are identical either way; a client accepts whichever arrives first and ignores a second.
6 token_proof bstr: AUTH: HMAC-SHA256(token, WELCOME nonce) truncated to 16 B. Costs ONE extra round trip per connect; that is the honest price. The 'previous-session nonce' shortcut was DROPPED by the feasibility pass as replay-unsafe (undefined rotation point, and §6.3 makes a successful replay EVICT the real client).
7 presentation_mode uint: how the client presents its token: 0 = bearer (raw 16 B in HELLO; legal, the potato floor stays a memcpy), 1 = proof (AUTH, RECOMMENDED for anything with SHA-256, i.e. everyone but coin cells). v1 transports are cleartext, so bearer is sniffable by a passive LAN observer; the roster records which mode a device uses, making security posture visible.
8 pairing_modes uint: WELCOME: BITMASK of pairing_modes this hub currently offers, RE-EVALUATED PER SESSION so a mode that is only transiently available (RFC-027(c)'s push-to-pair window) is advertised only while it is actually open. RFC-027.3 said 'WELCOME limits/identity map'; it lands here instead because it is session-security capability, and limits is sized caps while identity is who-you-are. Window state is thus observable in-band by any watch session.

Blob namespaces#

One transfer verb serves the whole protocol. The namespace names what a transfer carries. Values 0 to 127 are spec-governed. Values 128 to 255 are device-defined.

Value Namespace Notes
0 catalog the hub's channel catalog (§8.4). store_id/slot absent. This is the ONLY namespace with a READY concept (CATALOG_READY 0x19): you cannot decode STATE without the catalog, but nothing gates on a preset.
1 store items in a catalog-declared STORE-class channel: store_id picks the store, slot picks the item. Presets, saved positions, limit profiles, recordings, the trust ledger: all the same machinery, for free. Unused is unproblem.

DEMO-CANDIDATE: decode one captured HELLO or WELCOME frame live, key by key, against this table.