feat(k1): recover exact application bootstrap
This commit is contained in:
@@ -21,23 +21,25 @@ 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 |
|
||||
| K1 application control | PAUSE (exact shadow dialogue implemented) — all 10 retained pre-START requests and START/STOP are byte-matched; BLE-selected live DeviceInfo supplies per-scanner identity while one private application-level OpenAPI authority remains outside Git/Ops; 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
|
||||
now-verified network path. Owner-operated LixelGO traffic verifies the MQTT
|
||||
start/stop mapping and RTSP camera transport. The exact start/stop protobuf
|
||||
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. 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.
|
||||
publisher. Retained PCAP plus client static analysis prove that OpenAPI is one
|
||||
private application-level value in this LixelGO build, not a per-scanner
|
||||
credential. The selected BLE peripheral returns its own LAN IP; the initial
|
||||
unbound `DeviceInfoRequest` then returns vendor ID, serial, model, activation and
|
||||
version facts for that live K1. Mission Core's shadow bootstrap reproduces the
|
||||
10 observed pre-START requests byte-for-byte: three unbound discovery reads,
|
||||
one response-bound time/timezone sync and six further reads. A live-only safety
|
||||
layer rejects identity drift or malformed status, and can form a non-executable
|
||||
START only from unbound `READY` or STOP only from bound `SCANNING`. Public output
|
||||
contains only digests and wire metadata. Secure application-authority loading,
|
||||
an authorized response-gated one-shot publisher and durable post-stop save are
|
||||
not yet 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;
|
||||
@@ -256,7 +258,7 @@ useful stream is decoded or structurally identified.
|
||||
- firmware or internal-Linux analysis;
|
||||
- physical end-to-end shared-timeline playback for a newly archived session
|
||||
containing point cloud plus one selected K1 camera;
|
||||
- legitimate K1 OpenAPI credential provisioning, authorized command transport
|
||||
- private application-level OpenAPI authority loading, authorized command transport
|
||||
and stable-artifact proof after the observed stop lifecycle;
|
||||
- long-running large-session WebViewer/WASM memory telemetry;
|
||||
- production retention, replication, encryption and cross-platform packaging.
|
||||
|
||||
@@ -166,14 +166,27 @@ separated four values which must never be substituted for one another:
|
||||
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.
|
||||
- the OpenAPI value is private application-level material embedded by the
|
||||
observed LixelGO build; it is shared across all retained request types and is
|
||||
not derived from the current scanner while the application value is present.
|
||||
|
||||
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.
|
||||
Consequently, support for another K1 never replays captured payload bytes and
|
||||
does not require a manually maintained scanner profile. BLE selection and its
|
||||
provisioning response bind the transport to that unit; an initial
|
||||
`DeviceInfoRequest` without device ID returns the live vendor ID, serial, model,
|
||||
activation and version facts. Those values bind later requests and must agree
|
||||
with the live status stream. Any mismatch, identity drift, malformed response,
|
||||
replay-only evidence or unexpected lifecycle state fails closed.
|
||||
|
||||
The clean LixelGO cycle contains exactly ten requests before START, all QoS 2
|
||||
and retain false: `DeviceInfo`, `ModelingStatus`, `GetRtkAdvance`, a bound
|
||||
`DeviceConfig` time/timezone sync, then `DeviceInfo`, `GetRtkAdvance`,
|
||||
`GetNtripProfile`, `GetCloudServerConfig`, `GetRtkAdvance`, `DeviceInfo`. The
|
||||
special time-sync session is exactly
|
||||
`${device_id}:DeviceConfigRequest:Publish_Proto_DeviceConfig_SetTime`. The
|
||||
repository shadow bootstrap reproduces all ten retained payloads and their
|
||||
topic order byte-for-byte. Time sync is the only mutation before START; the
|
||||
other nine requests are reads.
|
||||
|
||||
The repository now contains an inert bounded encoder/response parser and a
|
||||
fail-closed device-status state machine for this exact profile. A live-only
|
||||
@@ -182,9 +195,9 @@ 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
|
||||
Publishing remains deliberately disabled: the private application authority
|
||||
still needs a secure runtime loader 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.
|
||||
|
||||
@@ -143,14 +143,13 @@ per-message metadata and a hash summary. Repository-level
|
||||
the current writer. The connector subscribes to the fixed report-topic
|
||||
allowlist and does not publish an application request or modeling command.
|
||||
|
||||
The recovered command substrate is intentionally inert. It can encode exact
|
||||
start/stop request bytes, correlate response identity/action/result and classify
|
||||
observed device states without importing MQTT or publishing anything. The header
|
||||
requires explicit ASCII `device_id` and OpenAPI key and derives the session ID
|
||||
exactly as `${device_id}:ModelingRequest`. Legitimate OpenAPI key
|
||||
provenance/provisioning and durable save completion after stop are unresolved,
|
||||
so `vendor_writes_enabled` is false and the physical-button workflow remains
|
||||
canonical.
|
||||
The recovered command substrate is intentionally inert. It byte-matches the ten
|
||||
observed pre-START requests plus START/STOP, correlates DeviceInfo and modeling
|
||||
responses, and classifies live device states without importing MQTT or
|
||||
publishing anything. One private application-level OpenAPI authority is kept
|
||||
separate from the transient vendor ID/serial returned by the BLE-selected K1.
|
||||
Secure authority loading and durable save completion after stop remain open, so
|
||||
`vendor_writes_enabled` is false and the physical-button workflow is canonical.
|
||||
|
||||
## Automatic Rerun source and lifecycle
|
||||
|
||||
@@ -332,8 +331,8 @@ listener and its process memory must be closed unconditionally.
|
||||
separate generic media path. Historical sessions predating that archive have
|
||||
no recoverable video.
|
||||
- Physical double-click remains the K1 scan start/stop control. Any MQTT command
|
||||
publisher needs a separately reviewed state-changing profile with legitimate
|
||||
OpenAPI key provisioning and a proven durable-save completion gate.
|
||||
publisher needs reviewed private application-authority loading, response-gated
|
||||
one-shot behavior and a proven durable-save completion gate.
|
||||
- No terrain map, elevation model, obstacle segmentation, localization fusion,
|
||||
mission planner or vehicle control is implemented by this viewer milestone.
|
||||
- Exact coordinate axes and the scanner-to-vehicle transform remain a mounting
|
||||
|
||||
@@ -142,16 +142,14 @@ transport mutation. Application-command publishing remains disabled; the
|
||||
separately reviewed BLE Wi-Fi provisioning write retains its own explicit
|
||||
operator gate.
|
||||
|
||||
An exact but inert K1 modeling-control substrate now lives inside the vendor
|
||||
plugin. It encodes the recovered start/stop protobuf shapes, strictly correlates
|
||||
response identity/action/numeric result and maps bounded device-status values
|
||||
into an observation-only state machine. Header construction is no longer
|
||||
guessed: callers provide explicit ASCII device ID and ASCII OpenAPI key, and the
|
||||
session ID is derived exactly as `${device_id}:ModelingRequest`. No MQTT import
|
||||
or publisher exists in that substrate. Legitimate OpenAPI key
|
||||
provenance/provisioning and stable-artifact proof after the stop lifecycle are
|
||||
still unresolved, so `vendor_writes_enabled=false` and every state-machine
|
||||
snapshot remains false for durable save completion.
|
||||
An exact but inert K1 application-control substrate now lives inside the vendor
|
||||
plugin. It byte-matches the retained ten-request pre-START dialogue plus
|
||||
START/STOP, correlates live DeviceInfo/modeling responses and maps bounded
|
||||
device-status values into an observation-only state machine. The OpenAPI value
|
||||
is one private application-level authority; vendor ID and serial come from the
|
||||
BLE-selected live K1 and are cross-checked against status. No MQTT publisher
|
||||
exists. Secure authority loading and stable-artifact proof after STOP remain
|
||||
unresolved, so `vendor_writes_enabled=false`.
|
||||
|
||||
## Semantic lifecycle
|
||||
|
||||
@@ -223,8 +221,8 @@ views.
|
||||
4. Physically accept a newly archived left/right K1 session, then package the
|
||||
read-only RTSP/H.264 adapter for each target OS, add disk-backed sealed media
|
||||
caching and evolve same-host MSE delivery toward an authenticated Edge media
|
||||
plane. Keep the modeling-command publisher disabled until legitimate OpenAPI
|
||||
key provisioning and durable-save confirmation close its separate safety
|
||||
plane. Keep the modeling-command publisher disabled until private application
|
||||
authority loading and durable-save confirmation close its separate safety
|
||||
gate.
|
||||
|
||||
Complex equipment will likely be assembled from separately useful component
|
||||
|
||||
@@ -513,5 +513,5 @@ viewer/WASM payload. That is a packaging optimization item, not a failed gate.
|
||||
No retained physical K1 session contains the new canonical camera archive, so a
|
||||
real point-cloud plus one-camera recorded playback remains an explicit hardware
|
||||
acceptance test. Automated protocol tests also do not authorize K1 modeling
|
||||
publishing: legitimate OpenAPI key provisioning and durable save completion
|
||||
publishing: private application-authority loading and durable save completion
|
||||
remain separate physical/security gates.
|
||||
|
||||
@@ -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