156 lines
8.9 KiB
Markdown
156 lines
8.9 KiB
Markdown
# XGRIDS / LixelKity K1 plugin
|
||
|
||
This directory is the package boundary for the first Mission Core device
|
||
plugin. It now owns the canonical v1alpha2 manifest
|
||
[`plugin.manifest.json`](plugin.manifest.json). The statically linked frontend
|
||
contribution lives in [`frontend/`](frontend/) beside the manifest and profiles;
|
||
its provisioning, acquisition/replay, diagnostics and metrics are separate
|
||
plugin-owned components. The
|
||
verified backend implementation is physically isolated in
|
||
`src/k1link/device_plugins/xgrids_k1/`.
|
||
|
||
The v1alpha2 catalog can describe one or more independently profiled models.
|
||
This manifest currently exposes only `xgrids.lixelkity-k1`, and the transitional
|
||
runtime still owns one active model/session at a time. Concurrent model/session
|
||
routing remains a later supervisor milestone.
|
||
|
||
The plugin owns:
|
||
|
||
- the exact firmware/topology compatibility profiles under
|
||
[`profiles/`](profiles/), with strict evidence flags and a fail-closed loader;
|
||
- BLE discovery hints and K1 GATT metadata;
|
||
- Bridge and Direct Connect through the reviewed firmware-3 BLE Wi-Fi
|
||
provisioning frame, plus the reviewed Quick Connect AP-enable frame and
|
||
one-shot host-profile association adapter;
|
||
- read-only refresh of the selected K1's session-scoped DHCP address before a
|
||
new LAN operation; address changes rotate the device session and IP is never
|
||
treated as device identity;
|
||
- subscribe-only data MQTT transport plus a separately bounded canonical
|
||
application-control transport;
|
||
- native `.k1mqtt` capture;
|
||
- the read-only factory-calibration transport: one live `DeviceInfo` binding,
|
||
command-5 reads of exactly `camera.yaml` and
|
||
`extrinsic_camera_lidar.yaml`, and append-only private source snapshots; file
|
||
write and arbitrary paths are unavailable;
|
||
- safe exact-schema normalization of KB4 intrinsics, the main-camera 800x600
|
||
resize and explicit `T_camera_N_from_lidar` matrices, with firmware-verified
|
||
`sensor.camera.left -> camera_0` and `sensor.camera.right -> camera_1` binding;
|
||
- firmware-scoped protobuf/LZ4 and legacy codecs;
|
||
- normalization of K1 point cloud and pose, plus raw-only preservation of the
|
||
still-undecoded status and heartbeat channels;
|
||
- exact-profile left/right RTSP producer selection, H.264 copy-remux preview and
|
||
the handoff of acquisition-owned init/fMP4/index evidence to the host archive;
|
||
- K1-specific operator instructions and compatibility tests.
|
||
- the scoped React `device.connection` contribution and its BLE/Wi-Fi and
|
||
acquisition pipeline UI.
|
||
- the interactive canonical application-control session: one socket owner, one
|
||
operator launch intent, response-gated workspace/project/START stages, a
|
||
separate explicit STOP action, live status gates and no automatic retry.
|
||
|
||
The plugin does not own:
|
||
|
||
- Mission Core navigation, fleet, missions, users, roles, or audit;
|
||
- the generic spatial scene or Rerun Web Viewer;
|
||
- the host observation catalog, RRD preparation/cache, recorded-camera player,
|
||
workspace layout, platform storage, remote transport, or other devices.
|
||
|
||
The `k1link` distribution and CLI names remain compatibility names. All
|
||
device-specific implementation behind them is plugin-owned, and every change
|
||
must preserve replay and real-device acceptance.
|
||
|
||
Mission Core imports this plugin only from
|
||
`apps/control-station/src/composition/devicePlugins.ts`. The generic shell never
|
||
branches on this plugin ID or reads `k1_ip`, BLE candidates, MQTT topics, or
|
||
other vendor state. The frontend contribution imports the host only through
|
||
`@mission-core/plugin-sdk`; its CSS is scoped below `.xgrids-k1-plugin`.
|
||
|
||
Backend startup loads the reviewed factory declared by `backendEntrypoint`, then
|
||
cross-checks the runtime-owned plugin ID, version, supported host API and exact
|
||
action set against this manifest. The runtime must complete the versioned
|
||
control-plane handshake and report `ready` before any action is admitted.
|
||
Actions enter through the host dispatcher and the transitional facade
|
||
`src/k1link/device_plugins/xgrids_k1/facade.py`. The old flat routes live in the plugin's
|
||
legacy router; both paths now cross the same runtime transport and delegate to the proven
|
||
`XgridsK1CompatibilityService` methods. Synchronous capture/runtime operations
|
||
run outside the FastAPI event loop.
|
||
|
||
Model switching calls the plugin deactivation hook. It is rejected while the
|
||
canonical K1 control socket is open, so navigation can never become an implicit
|
||
device STOP. Operator-manual acquisition uses semantic `acquisition.stop` in
|
||
`capture-only` mode; replay and pre-v1alpha2 sessions retain the legacy
|
||
`stream.stop` shim. BLE, MQTT, codec,
|
||
archive discovery and RRD export modules now live under the plugin package
|
||
after replay parity and physical K1 regression. The current transport remains
|
||
in-process and its health is lifecycle-only; process isolation, crash/restart
|
||
containment and signed deployment remain later supervisor gates.
|
||
|
||
The compatibility profile is descriptive and cannot itself authorize a vendor
|
||
write. The UI selects the exact firmware `3.0.2` local-network profile and an
|
||
exact topology for Bridge, Quick Connect or Direct Connect; the
|
||
canonical bootstrap then requires a correlated live `DeviceInfo` match for
|
||
model `LixelKity K1`, platform type `A4`, activation and firmware before START.
|
||
Validate the current exact-match profile without device I/O with:
|
||
|
||
```bash
|
||
uv run python plugins/xgrids-k1/profile_loader.py
|
||
```
|
||
|
||
Plugin v0.6.0 retains the physically accepted v0.5.0 control transport and adds
|
||
the connection matrix behind the existing explicit `network.provision` action.
|
||
Bridge remains the default. Direct Connect sends the same single reviewed
|
||
99-byte frame with credentials for an already-running controller hotspot. Quick
|
||
Connect accepts no browser/API credential: it sends one reviewed fixed 100-byte
|
||
AP-enable frame to the selected K1, waits up to 15 seconds for the canonical
|
||
byte-51 AP-ready flag, and keeps that BLE session alive while the macOS adapter
|
||
performs bounded exact-SSID CoreWLAN discovery and one association. Credentials
|
||
are resolved by a preinstalled exact `3.0.2` firmware provider. Its optional laboratory importer
|
||
validates the reviewed official archive, extracts the single AP declaration and
|
||
installs firmware-scoped material in the OS secure store. The macOS helper then
|
||
materializes the selected device profile entirely inside Keychain before any
|
||
BLE write. The secret never enters the browser, API, argv, logs or evidence;
|
||
the importer's short-lived mutable buffer is zeroized after the stdin handoff.
|
||
There is no automatic BLE-write or association retry. A clean host cannot
|
||
obtain the provider from BLE and the product does not download firmware during
|
||
connection. Windows/Linux Quick Connect adapters are not planned while that
|
||
bootstrap remains unresolved. Mission Core then uses the
|
||
reviewed K1 AP data-plane address. The
|
||
99-byte station-provisioning payload and 100-byte
|
||
AP-enable payload share `7f01` but remain distinct exact reviewed frames; neither
|
||
is a credential-read command.
|
||
|
||
For retained laboratory reproduction only, an operator can install the reviewed
|
||
source on a macOS host without displaying the credential:
|
||
|
||
```bash
|
||
uv run k1link authority import-k1-fw302-ap \
|
||
--firmware /private/path/to/the-official-3.0.2-archive.tar \
|
||
--confirm-reviewed-firmware
|
||
```
|
||
|
||
The 2026-07-20 UI run physically accepted the prepared-host path: one BLE discovery,
|
||
one AP-enable write, AP-ready, one CoreWLAN association, control bootstrap and
|
||
the normal acquisition lifecycle. It does not prove that the current build is
|
||
portable to a clean Mac. Bridge/direct-LAN remains the default and continuing
|
||
product route; Quick Connect stays available as laboratory functionality.
|
||
|
||
Plugin v0.5.0 installed the reviewed application-control transport behind
|
||
explicit plugin actions.
|
||
The normal UI accepts the project name and one explicit launch confirmation.
|
||
It then performs operations 1–6, operation 7 and operations 8–10 only after the
|
||
previous response barrier succeeds, prepares local reception, and emits one
|
||
START carrying the project name. Local state polling controls only when the
|
||
next reviewed action may be requested; it never schedules a device command by
|
||
elapsed time. START waits for bound `SCANNING + project + init_ready` before
|
||
operations 13–14. STOP is separately permitted, never retried, and keeps the same socket until K1
|
||
reports unbound READY. That protocol state automatically completes local sealing;
|
||
stable green is physical corroboration rather than a second UI gate. No
|
||
physical command is emitted merely by loading the plugin, opening the page, navigating, polling
|
||
state or running repository tests. The full v0.5.0 START/live/STOP/save path is
|
||
physically accepted on the reviewed K1/A4/FW 3.0.2 unit.
|
||
|
||
The optional owner-controlled iPhone/LixelGO observation tool lives under
|
||
[`lab/iphone-capture/`](lab/iphone-capture/). It pins `pymobiledevice3` in a
|
||
separate `uv` environment, writes only ignored evidence sessions, and remains a
|
||
lab subprocess rather than a plugin/runtime dependency. The first capture is
|
||
gated by ADR 0004 and ADR 0005.
|