# 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.
