feat(k1): add device-bound shadow control gate

This commit is contained in:
DCCONSTRUCTIONS
2026-07-18 12:09:23 +03:00
parent 0ac6424c46
commit af9319a33b
7 changed files with 617 additions and 11 deletions
+9 -4
View File
@@ -3,7 +3,7 @@
This plan supersedes the app-dependent experiment order in the reference Bible.
Each gate produces evidence and an explicit GO, PAUSE or BLOCKED result.
## Current checkpoint — 2026-07-17
## Current checkpoint — 2026-07-18
| Stage | Result |
| --- | --- |
@@ -21,6 +21,7 @@ Each gate produces evidence and an explicit GO, PAUSE or BLOCKED result.
| Stage 7 observation archive | GO (point/pose/telemetry contract) — durable catalog, recovery, capture-clock-bounded RRD preparation, archived metric time series, saved-session timeline and atomic playback are implemented |
| Stage 7 recorded cameras | GO (contract), acceptance pending — acquisition-owned fMP4 archive and player are implemented/tested; one real archived K1 camera plus point-cloud session has not passed playback yet |
| Plugin isolation | GO (laboratory control plane) — vendor backend/frontend and optional scene controls are plugin-owned; manifest/runtime descriptor parity, versioned handshake, lifecycle health and transport correlation fail closed while execution remains in-process |
| K1 application control | PAUSE (shadow gate implemented) — the exact captured codec is byte-matched, live status binds vendor identity plus serial, and START/STOP can be planned without publish authority; device-bound OpenAPI enrollment, one-shot publisher, physical save acceptance and recovery policy remain closed gates |
| Stage 8 product storage | PAUSE — retention, replication, encryption, capacity monitoring and long-run browser/WASM stress remain deployment gates |
USB project copying remains optional ground truth rather than a blocker for the
@@ -30,9 +31,13 @@ encoder, response correlator and device-status state machine now exist without a
publisher. Header construction is known: an explicit ASCII device ID and ASCII
OpenAPI key are required, while the session ID is derived exactly as
`${device_id}:ModelingRequest`. MQTT control publishing remains deliberately
disabled because legitimate OpenAPI credential provenance/provisioning and a
durable post-stop save gate are not proven. The physical button remains the
known-safe fallback.
disabled. A live-only shadow safety layer now observes the device-reported
vendor identity and serial, rejects identity drift or malformed status, and can
form a non-executable START only from unbound `READY` or STOP only from a bound
`SCANNING` state. Its output contains a digest and wire metadata, never the
device identity, serial, OpenAPI value or payload. Legitimate per-device OpenAPI
provisioning, an authorized one-shot publisher and a durable post-stop save gate
are not proven. The physical button remains the known-safe fallback.
The Stage 6 live path uses a bounded raw-first bridge: loss in the visualization
queue cannot discard MQTT evidence. The queue holds four preview messages;
+30 -6
View File
@@ -157,13 +157,37 @@ correlated them with device success responses and physical start/stop of the
high-rate streams. This promotes the mapping from a static hint to descriptive
wire evidence; it does not authorize replay or publishing.
An offline bounded audit of the retained owner-controlled captures additionally
proved that the repository encoder reproduces the observed START and STOP
payloads byte-for-byte for this one K1 and exact firmware profile. It also
separated four values which must never be substituted for one another:
- Mission Core's provisional device UUID is local inventory identity and never
belongs in a vendor request;
- the vendor device ID comes from live K1 status and occupies the request header;
- the K1 serial is a separate live device-binding value;
- the OpenAPI value is a private, device-bound command credential and is not
present in the status report.
Consequently, support for another K1 cannot replay this scanner's captured
payload or OpenAPI value. It requires a separate owner-authorized enrollment
whose vendor device ID and serial are attested against live status under the
same exact firmware/topology profile. Any mismatch, identity drift, malformed
status, replay-only evidence or unexpected lifecycle state fails closed.
The repository now contains an inert bounded encoder/response parser and a
fail-closed device-status state machine for this exact profile. They have no
MQTT publish dependency. Publishing remains deliberately disabled: the OpenAPI
credential's provenance and secure provisioning are unresolved, and the
complete `ScanOver`/ready/stable-artifact sequence has not physically proved a
durable save result. The verified physical double-click remains the acquisition
control until a separate reviewed write gate closes.
fail-closed device-status state machine for this exact profile. A live-only
shadow planner accepts START only from `READY` with no project and STOP only
from `SCANNING` with a bound project. It emits only a non-executable digest and
wire metadata, uses QoS 2 with retain false, and declares automatic retry
forbidden. It has no MQTT publish dependency and cannot send a command.
Publishing remains deliberately disabled: secure per-device OpenAPI enrollment
is unresolved, and the complete stop/save sequence has not physically proved a
durable vendor project. A correlated STOP success, stream quiescence, return to
`READY`/steady-green hardware state and appearance of a reusable native project
are separate gates. The verified physical double-click remains the acquisition
control until a separately reviewed one-shot write gate closes.
## Decoder and raw-capture bounds
@@ -0,0 +1,94 @@
# ADR 0012: device-bound K1 command authority
- Status: accepted
- Date: 2026-07-18
- Extends: ADR 0004, ADR 0005, ADR 0009 and ADR 0010
## Context
Owner-controlled LixelGO traffic proves the K1 modeling START/STOP protobuf
shape, MQTT topic, QoS and correlated success response for one K1 running the
exact `xgrids.lixelkity-k1.fw-3.0.2.direct-lan.v1` profile. A bounded offline
audit also proves that Mission Core's encoder reproduces both retained requests
byte-for-byte. This is sufficient to describe the current device dialogue, but
not to replay a captured request or authorize writes to any K1.
The protocol exposes several identities with different authority. Mission
Core's provisional device UUID is local inventory identity. The vendor device
ID is reported by the live K1 and occupies the request header. The K1 serial is
a separate device-binding value. The OpenAPI value is private command material
which is absent from live status. LixelGO derives the command session relation
from the vendor device ID and request type; it is not a reusable caller session.
Native project durability is also not equivalent to command acceptance. STOP
success, stream quiescence, return to READY/steady green and a reusable project
on the scanner are separate observations.
## Decision
K1 application control is exact-profile, device-bound and fail-closed.
1. Mission Core never sends its local inventory UUID, BLE identifier or a K1
serial in place of the vendor request identity.
2. Each K1 requires an owner-authorized enrollment containing its vendor device
ID, serial and private OpenAPI value. Enrollment secrets are not committed,
placed in Ops, returned by APIs or written to logs.
3. Before a command can be considered, live MQTT status must attest the same
vendor device ID and serial under the exact firmware/topology profile.
Replay evidence cannot establish current command authority.
4. Identity drift, missing identity, malformed status, profile mismatch or an
unexpected lifecycle state blocks command planning.
5. START retains the observed LixelGO parameters: record-and-calculate mode 2,
LCC scan mode 1, handheld mount mode 0 and the operator's validated project
name. STOP contains only the same bound header and action.
6. MQTT command semantics remain QoS 2, retain false. Automatic retry is
forbidden: an unknown outcome must be reconciled from live status and
physical evidence before another command.
7. No firmware, activation, account, update or vendor-cloud endpoint is part of
the control path. Local direct-LAN MQTT is the only reviewed transport.
8. STOP completion has four gates: correlated command result, local stream
quiescence and evidence sealing, READY/steady-green device state, then native
project verification. Earlier gates must not claim the later ones.
## Current implementation boundary
`LiveModelingControlSafety` observes only live device-status messages. It binds
vendor identity and serial, tracks lifecycle state and creates non-executable
shadow START/STOP plans. Public state and plan output contain only booleans,
counts, action, topic, QoS, retain flag, payload length and digest. The encoded
payload and enrolled values remain private in memory.
The shadow plan is blocked by both `vendor-writes-disabled` and
`publisher-not-installed`. There is no MQTT publisher, enrollment endpoint or
automatic retry path. The physical K1 control remains the production fallback.
## Promotion gate
A future publisher requires a separate review and an operator-present physical
acceptance on the enrolled K1:
1. confirm exact firmware/profile, steady-green state, battery and storage;
2. close LixelGO and arm raw evidence plus the intended camera receiver;
3. attest live READY identity against the device enrollment;
4. review the shadow command metadata;
5. send one START, without retry, and correlate its exact response;
6. observe calibration and first point/pose/camera data;
7. send one STOP, without retry, and correlate its exact response;
8. seal local evidence while accepting the bounded stream tail;
9. wait for READY and steady green before power-off or another scan;
10. verify the native project independently through the vendor-supported
workflow.
Any unknown response, identity change, activation state, fault, low battery or
unexpected lifecycle transition pauses the test. It does not trigger a guessed
recovery command.
## Consequences
Adding another K1 means creating another private enrollment and proving its
exact compatibility profile; it never means reusing captured bytes from the
first scanner. The design preserves native record-and-calculate behavior while
keeping command authority inside the XGRIDS plugin and out of generic Mission
Core. It intentionally postpones convenience automation until identity,
credential provenance, one-shot transport behavior and durable save semantics
have all passed physical acceptance.