feat(k1): recover exact application bootstrap
This commit is contained in:
@@ -112,9 +112,9 @@ plugin-owned.
|
||||
|
||||
The operator project name is normalized and validated by the K1 contribution,
|
||||
stored as display/catalog metadata and never used as a path component. This ADR
|
||||
does not claim that it reaches the scanner: the inert modeling-control codec has
|
||||
no publisher, and automatic K1 writes remain disabled pending legitimate OpenAPI
|
||||
credential provisioning and durable post-stop save evidence.
|
||||
does not claim that it reaches the scanner: the inert application-control codec
|
||||
has no publisher, and automatic K1 writes remain disabled pending reviewed
|
||||
private application-authority loading and durable post-stop save evidence.
|
||||
|
||||
ADR 0011 subsequently places the action control plane behind a versioned
|
||||
descriptor/handshake/health transport seam. Observation discovery and export
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# ADR 0012: device-bound K1 command authority
|
||||
# ADR 0012: live-bound K1 control with application-level authority
|
||||
|
||||
- Status: accepted
|
||||
- Date: 2026-07-18
|
||||
@@ -6,89 +6,113 @@
|
||||
|
||||
## 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.
|
||||
Owner-controlled LixelGO traffic proves the K1 preparation and modeling
|
||||
dialogue for one activated K1 running the exact
|
||||
`xgrids.lixelkity-k1.fw-3.0.2.direct-lan.v1` profile. Offline regression proves
|
||||
that Mission Core reproduces all ten requests before START, plus START and STOP,
|
||||
byte-for-byte. This describes the current device dialogue but does not 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.
|
||||
The protocol exposes identities with different ownership. Mission Core's
|
||||
inventory UUID and the macOS CoreBluetooth UUID are local transport identities.
|
||||
The vendor device ID and serial identify the live scanner. The OpenAPI value is
|
||||
private application material: retained traffic uses one value across every
|
||||
request type, and decompiled LixelGO uses its embedded application value before
|
||||
the serial-derived fallback. It is therefore not a manual per-scanner profile.
|
||||
|
||||
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.
|
||||
The selected BLE peripheral returns the LAN address of that same unit during
|
||||
the reviewed Wi-Fi provisioning exchange. On MQTT, LixelGO first sends
|
||||
`DeviceInfoRequest` without a device ID. `DeviceInfoResponse` supplies vendor ID,
|
||||
serial, model, activation and version facts; subsequent headers bind to that
|
||||
identity. Native project durability remains separate from command acceptance.
|
||||
|
||||
## Decision
|
||||
|
||||
K1 application control is exact-profile, device-bound and fail-closed.
|
||||
K1 application control is exact-profile, live-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.
|
||||
1. Mission Core never substitutes its inventory UUID, BLE UUID or serial for
|
||||
the vendor request identity.
|
||||
2. One private application-level OpenAPI authority is loaded outside Git, Ops,
|
||||
browser state, APIs and logs. It is not copied into scanner profiles.
|
||||
3. The BLE-selected transport is followed by an unbound DeviceInfo exchange.
|
||||
Its live vendor ID, serial, activation and FW 3.0.2 facts form the transient
|
||||
device binding; a saved profile is optional metadata, not protocol authority.
|
||||
4. The ten retained pre-START requests keep their exact order and response
|
||||
boundaries. The only mutation is the observed time/timezone sync; the other
|
||||
nine requests are reads. No request is batched, skipped or automatically
|
||||
retried.
|
||||
5. Live DeviceInfo binding must agree with the live DeviceStatus stream before
|
||||
START/STOP planning. Identity drift, malformed data, inactive equipment,
|
||||
profile mismatch or an unexpected lifecycle state fails closed.
|
||||
6. START retains record-and-calculate mode 2, LCC mode 1, handheld mount 0 and
|
||||
the validated project name. STOP contains only bound header and action.
|
||||
7. MQTT application semantics remain QoS 2, retain false. An unknown outcome is
|
||||
reconciled from correlated response, status and physical evidence before any
|
||||
new operator-authorized attempt.
|
||||
8. No firmware, activation, account, update or vendor-cloud mutation belongs to
|
||||
this path. `GetCloudServerConfig` is a local K1 read.
|
||||
9. STOP completion has separate gates: correlated result, local stream
|
||||
quiescence/evidence sealing, READY plus steady green, then independent native
|
||||
project verification.
|
||||
|
||||
## Recovered pre-START sequence
|
||||
|
||||
The clean cycle contains:
|
||||
|
||||
1. unbound `DeviceInfoRequest`;
|
||||
2. unbound `ModelingStatusRequest`;
|
||||
3. unbound `GetRtkAdvanceRequest`;
|
||||
4. bound `DeviceConfigRequest` time/timezone sync;
|
||||
5. bound `DeviceInfoRequest`;
|
||||
6. bound `GetRtkAdvanceRequest`;
|
||||
7. bound `GetNtripProfileRequest`;
|
||||
8. bound `GetCloudServerConfigRequest`;
|
||||
9. bound `GetRtkAdvanceRequest`;
|
||||
10. bound `DeviceInfoRequest`;
|
||||
11. bound `ModelingRequest` START.
|
||||
|
||||
Normal sessions are `${device_id_or_empty}:${MessageType}`. Time sync uses the
|
||||
captured special relation
|
||||
`${device_id}:DeviceConfigRequest:Publish_Proto_DeviceConfig_SetTime`.
|
||||
|
||||
## 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.
|
||||
`application_bootstrap.py` provides the bounded application authority type,
|
||||
DeviceInfo response correlation/decoding and the exact non-executable ten-step
|
||||
shadow plan. Its retained clean-cycle regression is 10/10 payloads and topic
|
||||
order. `LiveModelingControlSafety` independently binds that DeviceInfo identity
|
||||
to live status and creates non-executable START/STOP plans.
|
||||
|
||||
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.
|
||||
Public state contains only booleans, counts and wire metadata. Private identity,
|
||||
authority and payload bytes stay out of repr/API output. Both shadow paths are
|
||||
blocked by `vendor-writes-disabled` and `publisher-not-installed`; no MQTT
|
||||
publisher or automatic retry path exists.
|
||||
|
||||
## Promotion gate
|
||||
|
||||
A future publisher requires a separate review and an operator-present physical
|
||||
acceptance on the enrolled K1:
|
||||
A future publisher requires separate review and an operator-present physical
|
||||
acceptance:
|
||||
|
||||
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;
|
||||
1. load the private application authority through a reviewed local secret
|
||||
mechanism;
|
||||
2. confirm battery/storage and select one K1 over BLE;
|
||||
3. run the exact response-gated bootstrap and attest activated FW 3.0.2;
|
||||
4. confirm READY identity against DeviceInfo and review shadow metadata;
|
||||
5. send one START without retry and correlate its response;
|
||||
6. observe calibration and first point/pose/camera data;
|
||||
7. send one STOP, without retry, and correlate its exact response;
|
||||
7. send one STOP without retry and correlate its 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.
|
||||
9. wait for READY and steady green;
|
||||
10. verify the native project 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.
|
||||
Any unknown response, identity change, fault, low battery/storage or unexpected
|
||||
transition pauses the test. It never triggers 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.
|
||||
Adding another identical K1 means selecting it over BLE and deriving a fresh
|
||||
live DeviceInfo binding, not creating or copying a command profile. A second
|
||||
physical K1 remains a useful portability acceptance, but another packet capture
|
||||
is not required before implementing the reviewed current profile. The design
|
||||
preserves native record-and-calculate behavior while keeping vendor control in
|
||||
the XGRIDS plugin and out of generic Mission Core.
|
||||
|
||||
Reference in New Issue
Block a user