docs: record Mission Core K1 architecture milestone
This commit is contained in:
parent
e6f7648b84
commit
05bdab24a5
72
README.md
72
README.md
|
|
@ -8,16 +8,30 @@ extracted, and device plugins.
|
||||||
|
|
||||||
The first proven hardware vertical is the XGRIDS/LixelKity K1 plugin. On firmware
|
The first proven hardware vertical is the XGRIDS/LixelKity K1 plugin. On firmware
|
||||||
3.0.2 the host provisions the scanner onto an existing LAN without LixelGO,
|
3.0.2 the host provisions the scanner onto an existing LAN without LixelGO,
|
||||||
connects to its MQTT broker, durably captures the raw stream, decodes point cloud
|
connects to its MQTT broker, persists each raw frame before preview work, decodes
|
||||||
and pose, and renders the real cloud plus trajectory through an embedded
|
point cloud and pose, and renders the real cloud plus trajectory through an
|
||||||
self-hosted Rerun Web Viewer. The former Foxglove bridge remains only as a
|
embedded self-hosted Rerun Web Viewer. Capture files are `fsync`ed on clean
|
||||||
legacy regression module.
|
close; per-frame power-loss durability is not claimed. The former Foxglove
|
||||||
|
bridge remains only as a legacy regression module.
|
||||||
|
|
||||||
The repository is intentionally migrating in stages. The current `src/k1link`
|
The repository is intentionally migrating in stages. The current `src/k1link`
|
||||||
package is the compatibility implementation of the first plugin path; vendor
|
package is the compatibility implementation of the first plugin path; vendor
|
||||||
transport and codecs will move behind `plugins/xgrids-k1` and the Mission Core
|
transport and codecs will move behind `plugins/xgrids-k1` and the Mission Core
|
||||||
Plugin SDK without changing the verified wire protocol or raw evidence format.
|
Plugin SDK without changing the verified wire protocol or raw evidence format.
|
||||||
|
|
||||||
|
Plugin SDK v0alpha2 now provides executable, vendor-neutral identity, session,
|
||||||
|
operation, stream, evidence and compatibility contracts. The meanings remain a
|
||||||
|
local experimental vocabulary rather than a mutation of NODE.DC Platform
|
||||||
|
Ontology. The current runtime is still transitional and in process: it uses an
|
||||||
|
explicitly injected K1 normalizer to produce transport-neutral local consumer
|
||||||
|
views; portable SDK stream envelopes, process isolation, durable operations,
|
||||||
|
multi-device routing and the remote Edge split remain later gates.
|
||||||
|
|
||||||
|
The current runtime cannot read K1 firmware automatically. It keeps the exact
|
||||||
|
profile inactive until the operator explicitly attests firmware `3.0.2` and
|
||||||
|
direct-LAN topology; state records that basis as `operator-attested`, not as
|
||||||
|
device-derived evidence.
|
||||||
|
|
||||||
The repository now contains one narrowly gated state-changing command:
|
The repository now contains one narrowly gated state-changing command:
|
||||||
`ble wifi-configure`. It accepts only the reviewed firmware-3 provisioning
|
`ble wifi-configure`. It accepts only the reviewed firmware-3 provisioning
|
||||||
profile and requires explicit `--confirm-write`; the Wi-Fi password is collected
|
profile and requires explicit `--confirm-write`; the Wi-Fi password is collected
|
||||||
|
|
@ -29,7 +43,16 @@ Nothing changes router settings, firmware or global Python packages.
|
||||||
- one XGRIDS/LixelKity K1;
|
- one XGRIDS/LixelKity K1;
|
||||||
- one Apple Silicon MacBook running macOS;
|
- one Apple Silicon MacBook running macOS;
|
||||||
- one ordinary TP-Link Deco/mesh network used by other devices;
|
- one ordinary TP-Link Deco/mesh network used by other devices;
|
||||||
- no LixelGO, phone, Linux host, dedicated AP, OpenWrt, or vendor SDK.
|
- the proven baseline used no LixelGO, phone, Linux host, dedicated AP,
|
||||||
|
OpenWrt, or vendor SDK.
|
||||||
|
|
||||||
|
An owner-controlled iPhone with LixelGO is now available for a separate,
|
||||||
|
evidence-only observation stage. It does not invalidate the no-phone baseline
|
||||||
|
and is not a runtime dependency. The decision and gated runbook are
|
||||||
|
[`ADR 0005`](docs/adr/0005-owner-controlled-lixelgo-iphone-observation.md) and
|
||||||
|
[`docs/08_LIXELGO_IPHONE_OBSERVATION.md`](docs/08_LIXELGO_IPHONE_OBSERVATION.md).
|
||||||
|
The Mac capture environment is isolated under
|
||||||
|
`plugins/xgrids-k1/lab/iphone-capture/` and does not require full Xcode.
|
||||||
|
|
||||||
The ordinary router is sufficient for the first gates. We first observe the
|
The ordinary router is sufficient for the first gates. We first observe the
|
||||||
existing LAN without changing it. A Guest/IoT SSID is optional and may be
|
existing LAN without changing it. A Guest/IoT SSID is optional and may be
|
||||||
|
|
@ -45,10 +68,11 @@ The project has three independent gates:
|
||||||
3. Without LixelGO, K1 can be associated with Wi-Fi and a proprietary data
|
3. Without LixelGO, K1 can be associated with Wi-Fi and a proprietary data
|
||||||
session can be opened.
|
session can be opened.
|
||||||
|
|
||||||
All three gates are now proven on the tested unit. The external stream is plain
|
All three gates are now proven on the tested unit. The spatial stream is plain
|
||||||
MQTT 3.1.1 on TCP 1883. Firmware-3 `lio_pcl` is protobuf wrapped in a raw LZ4
|
MQTT 3.1.1 on TCP 1883. Firmware-3 `lio_pcl` is protobuf wrapped in a raw LZ4
|
||||||
block, and `lio_pose` is an uncompressed protobuf. Raw panoramic camera access is
|
block, and `lio_pose` is an uncompressed protobuf. Owner-operated LixelGO capture
|
||||||
still unproven and is not implied by point-cloud success.
|
also proved separate left/right RTSP/H.264 camera previews on TCP 8554. This is a
|
||||||
|
compressed preview contract, not proof of full-resolution raw camera access.
|
||||||
|
|
||||||
## Local environment
|
## Local environment
|
||||||
|
|
||||||
|
|
@ -58,7 +82,7 @@ repositories.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd /Users/dcconstructions/Downloads/mnt/NODEDC/NODEDC_MISSION_CORE
|
cd /Users/dcconstructions/Downloads/mnt/NODEDC/NODEDC_MISSION_CORE
|
||||||
uv sync --group dev
|
uv sync --frozen --group dev
|
||||||
uv run k1link doctor
|
uv run k1link doctor
|
||||||
uv run pytest
|
uv run pytest
|
||||||
```
|
```
|
||||||
|
|
@ -74,9 +98,10 @@ Install, type-check, build and serve the complete local application from the
|
||||||
repository root:
|
repository root:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
uv sync --group dev
|
uv sync --frozen --group dev
|
||||||
cd apps/control-station
|
cd apps/control-station
|
||||||
npm install
|
npm ci
|
||||||
|
npm run test:unit
|
||||||
npm run typecheck
|
npm run typecheck
|
||||||
npm run build
|
npm run build
|
||||||
cd ../..
|
cd ../..
|
||||||
|
|
@ -89,7 +114,14 @@ still provides real CoreBluetooth discovery, one operator-triggered reviewed
|
||||||
BLE Wi-Fi provisioning write, read-only MQTT live capture, native `.k1mqtt` and
|
BLE Wi-Fi provisioning write, read-only MQTT live capture, native `.k1mqtt` and
|
||||||
reviewed-TSV replay, raw-first evidence storage and measured preview metrics.
|
reviewed-TSV replay, raw-first evidence storage and measured preview metrics.
|
||||||
Physical K1 scanning is still started and stopped by the verified double-click;
|
Physical K1 scanning is still started and stopped by the verified double-click;
|
||||||
the connector publishes no modeling command.
|
the connector publishes no modeling command. The observed LixelGO action mapping
|
||||||
|
remains descriptive and write-disabled.
|
||||||
|
|
||||||
|
This locked bootstrap is repeatable in the current workspace, not yet a
|
||||||
|
standalone release install. The frontend consumes sibling `file:` packages from
|
||||||
|
`NODEDC_DESIGN_GUIDELINE`; `package-lock.json` does not pin that checkout's Git
|
||||||
|
revision or content hash. Publishing/vendoring those packages or enforcing an
|
||||||
|
immutable donor revision remains a packaging and CI prerequisite.
|
||||||
|
|
||||||
The Observation spatial workspace embeds the open-source Rerun Web Viewer
|
The Observation spatial workspace embeds the open-source Rerun Web Viewer
|
||||||
inside the Mission Core shell. It can open a compatible RRD file over HTTP(S) or a
|
inside the Mission Core shell. It can open a compatible RRD file over HTTP(S) or a
|
||||||
|
|
@ -102,9 +134,12 @@ the resulting URL through control-plane state. Later sessions reset their
|
||||||
session-local scene and metrics and reuse that process-wide stream; this avoids
|
session-local scene and metrics and reuse that process-wide stream; this avoids
|
||||||
restarting the native listener while the embedded browser remains connected.
|
restarting the native listener while the embedded browser remains connected.
|
||||||
Unless an operator has entered a manual source, the React application assigns
|
Unless an operator has entered a manual source, the React application assigns
|
||||||
that URL to the embedded viewer. The complete runtime path is K1 MQTT → raw-first evidence
|
that URL to the embedded viewer. The complete runtime path is K1 MQTT → raw-first
|
||||||
capture → bounded latest-wins preview queue → reviewed protobuf/LZ4 decoders →
|
evidence capture → bounded latest-wins preview queue → explicitly injected K1
|
||||||
Rerun `Points3D`, `Transform3D` and `LineStrips3D` → embedded Web Viewer.
|
protobuf/LZ4 normalizer → transport-neutral decoded local views → Rerun
|
||||||
|
`Points3D`, `Transform3D` and `LineStrips3D` → embedded Web Viewer. Rerun does
|
||||||
|
not inspect K1 topics or raw payloads. These local decoded views are not yet the
|
||||||
|
portable Plugin SDK wire envelopes.
|
||||||
|
|
||||||
The default Rerun blueprint shows a 12-second sliding accumulation of real point
|
The default Rerun blueprint shows a 12-second sliding accumulation of real point
|
||||||
frames. Product controls are connected for point size, intensity/height/distance
|
frames. Product controls are connected for point size, intensity/height/distance
|
||||||
|
|
@ -115,9 +150,9 @@ point cloud, trajectory, camera frame or latency value is generated.
|
||||||
|
|
||||||
A powered-device checkpoint passed 80 real MQTT messages through the current
|
A powered-device checkpoint passed 80 real MQTT messages through the current
|
||||||
Rerun runtime: 38 point-cloud frames, 42 pose frames, 2,775 points in the last
|
Rerun runtime: 38 point-cloud frames, 42 pose frames, 2,775 points in the last
|
||||||
cloud and zero decode errors. Raw panoramic camera frames remain absent. Rerun
|
cloud and zero decode errors. The later RTSP camera preview is not yet wired into
|
||||||
`capture_time` is the Mac receive timestamp, not a proven K1 sensor timestamp or
|
the Rerun/runtime path. Rerun `capture_time` is the Mac receive timestamp, not a
|
||||||
photon-to-screen measurement.
|
proven K1 sensor timestamp or photon-to-screen measurement.
|
||||||
|
|
||||||
The old Foxglove implementation is retained only in
|
The old Foxglove implementation is retained only in
|
||||||
`src/k1link/viewer/foxglove_bridge.py` and its regression tests. The current
|
`src/k1link/viewer/foxglove_bridge.py` and its regression tests. The current
|
||||||
|
|
@ -179,6 +214,7 @@ present.
|
||||||
- [Mission Core monorepo and plugin boundary](docs/07_MISSION_CORE_MONOREPO.md)
|
- [Mission Core monorepo and plugin boundary](docs/07_MISSION_CORE_MONOREPO.md)
|
||||||
- [Monorepo architecture decision](docs/adr/0002-mission-core-monorepo.md)
|
- [Monorepo architecture decision](docs/adr/0002-mission-core-monorepo.md)
|
||||||
- [Device plugin UI and runtime boundary](docs/adr/0003-device-plugin-ui-and-runtime-boundary.md)
|
- [Device plugin UI and runtime boundary](docs/adr/0003-device-plugin-ui-and-runtime-boundary.md)
|
||||||
|
- [Plugin SDK v0alpha2 and experimental device lifecycle](docs/adr/0004-plugin-sdk-v0alpha2-and-experimental-device-lifecycle.md)
|
||||||
- [Redacted live lab report](docs/lab/001_K1_LIVE_MQTT_20260715.redacted.md)
|
- [Redacted live lab report](docs/lab/001_K1_LIVE_MQTT_20260715.redacted.md)
|
||||||
- [Session manifest schema](schemas/session-manifest.schema.json)
|
- [Session manifest schema](schemas/session-manifest.schema.json)
|
||||||
- [Reference input provenance](docs/reference/README.md)
|
- [Reference input provenance](docs/reference/README.md)
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ device adapter, но структура интерфейса от него не
|
||||||
| Контур | Состояние | Что это означает |
|
| Контур | Состояние | Что это означает |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| Mission Core fixed shell | Реализован | Header, навигация по разделам, рабочая поверхность, окна и инспекторы работают в одном приложении. |
|
| Mission Core fixed shell | Реализован | Header, навигация по разделам, рабочая поверхность, окна и инспекторы работают в одном приложении. |
|
||||||
| Device plugin registry | Реализован, v1alpha1 | До выбора модели provider остаётся inert и не делает I/O; каждый manifest объявляет ровно одну модель, custom `device.connection` UI key и backend factory. Frontend component подключается одним reviewed import в composition root. |
|
| Device plugin registry | Реализован, v1alpha1 + v1alpha2 | До выбора модели provider остаётся inert и не делает I/O. v1alpha1 сохраняет одну модель; v1alpha2 допускает одну или несколько моделей и требует profile coverage каждой. Custom `device.connection` UI key и backend factory подключаются одним reviewed import в composition root. |
|
||||||
| Локальный control plane | Реализован | React получает состояние и выполняет операции через FastAPI REST и WebSocket на loopback. |
|
| Локальный control plane | Реализован | React получает состояние и выполняет операции через FastAPI REST и WebSocket на loopback. |
|
||||||
| K1 BLE → Wi-Fi | Реализован | Реальный BLE-поиск всех видимых устройств и одна подтверждённая provisioning-запись выбранному устройству. |
|
| K1 BLE → Wi-Fi | Реализован | Реальный BLE-поиск всех видимых устройств и одна подтверждённая provisioning-запись выбранному устройству. |
|
||||||
| K1 live/replay MQTT | Реализован | Read-only приём, raw-first сохранение, декодирование облака точек и позы, реальные метрики. |
|
| K1 live/replay MQTT | Реализован | Read-only приём, raw-first сохранение, декодирование облака точек и позы, реальные метрики. |
|
||||||
|
|
@ -36,12 +36,13 @@ device adapter, но структура интерфейса от него не
|
||||||
K1 MQTT :1883, read-only
|
K1 MQTT :1883, read-only
|
||||||
└── raw .k1mqtt + metadata + SHA-256 сохраняются первыми
|
└── raw .k1mqtt + metadata + SHA-256 сохраняются первыми
|
||||||
└── bounded latest-wins preview queue (32 сообщения)
|
└── bounded latest-wins preview queue (32 сообщения)
|
||||||
└── проверенный protobuf/LZ4 decoder
|
└── явно внедрённый K1 protobuf/LZ4 normalizer
|
||||||
└── Rerun Points3D + Transform3D + LineStrips3D
|
└── transport-neutral DecodedPointCloudView / DecodedPoseView
|
||||||
└── gRPC/proxy TCP 9876
|
└── Rerun Points3D + Transform3D + LineStrips3D
|
||||||
└── rerun_grpc_url в REST/WebSocket state
|
└── gRPC/proxy TCP 9876
|
||||||
└── встроенный @rerun-io/web-viewer
|
└── rerun_grpc_url в REST/WebSocket state
|
||||||
└── пространственная сцена Mission Core
|
└── встроенный @rerun-io/web-viewer
|
||||||
|
└── пространственная сцена Mission Core
|
||||||
|
|
||||||
Mission Core Control Station ←→ REST /api/v1/device-plugins/*
|
Mission Core Control Station ←→ REST /api/v1/device-plugins/*
|
||||||
+ plugin-scoped WebSocket events
|
+ plugin-scoped WebSocket events
|
||||||
|
|
@ -108,10 +109,11 @@ layout пока остаются интерфейсным контрактом.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd /Users/dcconstructions/Downloads/mnt/NODEDC/NODEDC_MISSION_CORE
|
cd /Users/dcconstructions/Downloads/mnt/NODEDC/NODEDC_MISSION_CORE
|
||||||
uv sync --group dev
|
uv sync --frozen --group dev
|
||||||
|
|
||||||
cd apps/control-station
|
cd apps/control-station
|
||||||
npm install
|
npm ci
|
||||||
|
npm run test:unit
|
||||||
npm run typecheck
|
npm run typecheck
|
||||||
npm run build
|
npm run build
|
||||||
|
|
||||||
|
|
@ -124,6 +126,12 @@ uv run k1link serve
|
||||||
предусмотрен, потому что endpoint подключения кратковременно принимает пароль
|
предусмотрен, потому что endpoint подключения кратковременно принимает пароль
|
||||||
Wi-Fi.
|
Wi-Fi.
|
||||||
|
|
||||||
|
Этот locked bootstrap повторяем в текущем workspace, но он ещё не является
|
||||||
|
standalone release install. Локальные `file:` зависимости берутся из соседнего
|
||||||
|
`NODEDC_DESIGN_GUIDELINE`, а lockfile не фиксирует Git revision или content hash
|
||||||
|
этого checkout. До CI/portable packaging эти пакеты нужно опубликовать,
|
||||||
|
завендорить либо проверять по неизменяемой donor revision.
|
||||||
|
|
||||||
Для разработки интерфейса после запуска backend:
|
Для разработки интерфейса после запуска backend:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|
@ -142,7 +150,10 @@ Vite слушает `http://127.0.0.1:5173` и проксирует весь `/a
|
||||||
2. Выбрать **Парк → Локальное устройство**.
|
2. Выбрать **Парк → Локальное устройство**.
|
||||||
3. В каталоге моделей выбрать **XGRIDS LixelKity K1**. Только после этого
|
3. В каталоге моделей выбрать **XGRIDS LixelKity K1**. Только после этого
|
||||||
активируется runtime и монтируется custom UI XGRIDS-плагина.
|
активируется runtime и монтируется custom UI XGRIDS-плагина.
|
||||||
4. Включить K1, дождаться стабильного индикатора и подтвердить это в форме.
|
4. Включить K1, дождаться стабильного индикатора, вручную сверить firmware
|
||||||
|
`3.0.2` и direct-LAN топологию, затем подтвердить это в форме. Mission Core
|
||||||
|
не читает firmware с устройства; такое подтверждение остаётся
|
||||||
|
`operator-attested`, а не device-derived evidence.
|
||||||
5. Нажать **Показать все BLE-устройства**. Интерфейс показывает полный результат
|
5. Нажать **Показать все BLE-устройства**. Интерфейс показывает полный результат
|
||||||
шестисекундного поиска; метка совместимости является подсказкой, выбор делает
|
шестисекундного поиска; метка совместимости является подсказкой, выбор делает
|
||||||
оператор.
|
оператор.
|
||||||
|
|
@ -211,7 +222,7 @@ Frontend XGRIDS-плагина использует только v1alpha namespa
|
||||||
`/api/viewer/settings` сохранены как deprecated compatibility shims внутри
|
`/api/viewer/settings` сохранены как deprecated compatibility shims внутри
|
||||||
XGRIDS backend contribution.
|
XGRIDS backend contribution.
|
||||||
|
|
||||||
В v1alpha1 backend composition загружается из manifests, а frontend plugins
|
В v1alpha1/v1alpha2 backend composition загружается из manifests, а frontend plugins
|
||||||
статически включаются в сборку через `src/composition/devicePlugins.ts`.
|
статически включаются в сборку через `src/composition/devicePlugins.ts`.
|
||||||
Автоматической runtime-сверки двух installed sets пока нет: их соответствие —
|
Автоматической runtime-сверки двух installed sets пока нет: их соответствие —
|
||||||
проверяемое требование сборки до появления подписанных plugin bundles и startup
|
проверяемое требование сборки до появления подписанных plugin bundles и startup
|
||||||
|
|
@ -219,9 +230,17 @@ compatibility handshake. Поля manifest `permissions`, `mutating` и
|
||||||
`secretFields` пока являются декларативными метаданными; generic host валидирует
|
`secretFields` пока являются декларативными метаданными; generic host валидирует
|
||||||
их форму и action allowlist, но ещё не реализует на их основе RBAC, подтверждения
|
их форму и action allowlist, но ещё не реализует на их основе RBAC, подтверждения
|
||||||
оператора или secret-vault substitution.
|
оператора или secret-vault substitution.
|
||||||
|
Точный K1 compatibility profile остаётся неактивным, пока оператор явно не
|
||||||
|
подтвердит firmware `3.0.2` и direct-LAN топологию. Runtime не получает версию
|
||||||
|
firmware с устройства и явно помечает основание как `operator-attested`.
|
||||||
|
Ошибки валидации namespaced action endpoint и deprecated `/api/connect`
|
||||||
|
возвращаются как общий `422` без исходных значений запроса, поэтому отклонённое
|
||||||
|
значение credential-поля не отражается клиенту.
|
||||||
|
|
||||||
Legacy `/api/state` и изменяющие состояние ответы могут вернуть snapshot напрямую или
|
Legacy `/api/state` и изменяющие состояние ответы могут вернуть snapshot напрямую или
|
||||||
как `{ "state": { ... } }`. Текущие поля включают `phase`, `message`, `devices`,
|
как `{ "state": { ... } }`. v1alpha2 snapshot отдельно содержит `device_ref`,
|
||||||
|
`device_session`, `acquisition`, `operations`, compatibility/calibration/sensor
|
||||||
|
facts и старые presentation-поля `phase`, `message`, `devices`,
|
||||||
`selected_device_id`, `k1_ip`, `source_mode`, `metrics`, `rerun_grpc_url` и
|
`selected_device_id`, `k1_ip`, `source_mode`, `metrics`, `rerun_grpc_url` и
|
||||||
`viewer_settings`. Legacy-поля `foxglove_ws_url`/`foxglove_viewer_url` остаются
|
`viewer_settings`. Legacy-поля `foxglove_ws_url`/`foxglove_viewer_url` остаются
|
||||||
пустыми. OpenAPI доступен по `/api/docs`.
|
пустыми. OpenAPI доступен по `/api/docs`.
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
# Technical audit
|
# Technical audit
|
||||||
|
|
||||||
Status: live feasibility validated, 2026-07-15.
|
Status: live feasibility and camera-preview transport validated, 2026-07-16.
|
||||||
|
|
||||||
## Executive finding
|
## Executive finding
|
||||||
|
|
||||||
|
|
@ -11,9 +11,10 @@ physical-button scan and decoded both point-cloud and pose reports.
|
||||||
|
|
||||||
All 1,140 captured `lio_pcl` frames decoded as raw-LZ4 protobuf blocks, yielding
|
All 1,140 captured `lio_pcl` frames decoded as raw-LZ4 protobuf blocks, yielding
|
||||||
4,165,862 points. All 1,215 `lio_pose` messages decoded, and the resulting
|
4,165,862 points. All 1,215 `lio_pose` messages decoded, and the resulting
|
||||||
approximately 1.566 m displacement matched the controlled movement. The
|
approximately 1.566 m displacement matched the controlled movement. A later
|
||||||
remaining feasibility question is the panoramic camera branch, not the core
|
owner-operated LixelGO/iPhone capture also observed left/right RTSP/H.264 camera
|
||||||
realtime LiDAR/pose path.
|
preview and the remote start/stop wire mapping. The remaining camera work is a
|
||||||
|
bounded Mission Core receiver, not transport discovery.
|
||||||
|
|
||||||
The supplied Bible is useful as an OSINT dossier. It is not an executable plan
|
The supplied Bible is useful as an OSINT dossier. It is not an executable plan
|
||||||
for the actual stand because many experiments assume a phone and LixelGO. The
|
for the actual stand because many experiments assume a phone and LixelGO. The
|
||||||
|
|
@ -52,12 +53,14 @@ the local LAS alone: current LixelStudio materials describe K1 remote control
|
||||||
and realtime point-cloud streaming over USB/Wi-Fi. LixelStudio is Windows-only
|
and realtime point-cloud streaming over USB/Wi-Fi. LixelStudio is Windows-only
|
||||||
and this does not disclose or guarantee access to the protocol from macOS. It
|
and this does not disclose or guarantee access to the protocol from macOS. It
|
||||||
does, however, justify keeping realtime point cloud as the primary stream target.
|
does, however, justify keeping realtime point cloud as the primary stream target.
|
||||||
No comparable official evidence proves an exportable raw panorama/camera stream.
|
The physical Lab 002 capture now provides stronger evidence than the prior
|
||||||
|
official-material inference: a compressed left/right preview leaves the device.
|
||||||
|
It still does not prove a full-resolution raw panorama stream.
|
||||||
|
|
||||||
### Still unsupported until measured
|
### Still unsupported until measured
|
||||||
|
|
||||||
- Local `map.las` layout resembles the network stream.
|
- Local `map.las` layout resembles the network stream.
|
||||||
- Raw or stitched panoramic camera frames leave the device.
|
- Full-resolution raw or pre-stitched panoramic camera frames leave the device.
|
||||||
- The upper 24 bits of point `rgbi` are usable packed RGB.
|
- The upper 24 bits of point `rgbi` are usable packed RGB.
|
||||||
- MQTT application commands can be published safely without first reproducing
|
- MQTT application commands can be published safely without first reproducing
|
||||||
device/session header state and response handling.
|
device/session header state and response handling.
|
||||||
|
|
|
||||||
|
|
@ -15,12 +15,14 @@ Each gate produces evidence and an explicit GO, PAUSE or BLOCKED result.
|
||||||
| Stage 4 artifacts/flows | GO — bounded capture, hashes and negative control |
|
| Stage 4 artifacts/flows | GO — bounded capture, hashes and negative control |
|
||||||
| Stage 5 point cloud | GO — raw-LZ4 protobuf, 1,140 live frames decoded |
|
| Stage 5 point cloud | GO — raw-LZ4 protobuf, 1,140 live frames decoded |
|
||||||
| Stage 5 pose | GO — 1,215 live frames decoded and motion-correlated |
|
| Stage 5 pose | GO — 1,215 live frames decoded and motion-correlated |
|
||||||
| Stage 5 camera | PAUSE — no independent frame/video stream observed |
|
| Stage 5 camera | GO (discovery) — left/right RTSP/H.264 preview observed; runtime adapter pending |
|
||||||
| Stage 6 live viewer | GO — React console, Foxglove cloud/path and Mac latency metrics |
|
| Stage 6 live viewer | GO — React console, Foxglove cloud/path and Mac latency metrics |
|
||||||
|
|
||||||
USB project copying remains optional ground truth rather than a blocker for the
|
USB project copying remains optional ground truth rather than a blocker for the
|
||||||
now-verified network path. MQTT control publishing remains deliberately deferred
|
now-verified network path. Owner-operated LixelGO traffic verifies the MQTT
|
||||||
because the physical button is a known-safe start/stop mechanism.
|
start/stop mapping and RTSP camera transport. MQTT control publishing remains
|
||||||
|
deliberately deferred because complete request/save/rollback semantics are not
|
||||||
|
yet modeled and the physical button remains a known-safe fallback.
|
||||||
|
|
||||||
The Stage 6 alpha uses a bounded raw-first bridge: loss in the visualization
|
The Stage 6 alpha uses a bounded raw-first bridge: loss in the visualization
|
||||||
queue cannot discard MQTT evidence. Acceptance is replay of the full captured
|
queue cannot discard MQTT evidence. Acceptance is replay of the full captured
|
||||||
|
|
|
||||||
|
|
@ -131,12 +131,17 @@ orientation is exposed as `(x,y,z,w)`. Unknown tail fields are retained.
|
||||||
|
|
||||||
## Application control boundary
|
## Application control boundary
|
||||||
|
|
||||||
Static analysis identified the application start/stop topic as
|
Static analysis first identified the application start/stop topic as
|
||||||
`lixel/application/request/modeling`, QoS 2, with a protobuf
|
`lixel/application/request/modeling`, QoS 2, with a protobuf
|
||||||
`ModelingRequest`. Start action is 1 and stop action is 2. A valid request also
|
`ModelingRequest`. Start action is 1 and stop action is 2. A valid request also
|
||||||
contains a device/session/OpenAPI header and, for start, project/record/scan/mount
|
contains a device/session/OpenAPI header and, for start, project/record/scan/mount
|
||||||
settings.
|
settings.
|
||||||
|
|
||||||
|
Lab 002 then observed both mappings in owner-operated LixelGO traffic and
|
||||||
|
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.
|
||||||
|
|
||||||
Publishing is deliberately not implemented. The physical double-click provides
|
Publishing is deliberately not implemented. The physical double-click provides
|
||||||
a verified autonomous start/stop path and avoids inventing session headers or
|
a verified autonomous start/stop path and avoids inventing session headers or
|
||||||
changing scan settings. A future publisher requires a separate reviewed profile,
|
changing scan settings. A future publisher requires a separate reviewed profile,
|
||||||
|
|
@ -156,9 +161,10 @@ Default defensive limits are applied before allocation or iteration:
|
||||||
Per-frame failures do not justify firmware writes or speculative recovery.
|
Per-frame failures do not justify firmware writes or speculative recovery.
|
||||||
Always preserve the raw MQTT record and report the decoder error separately.
|
Always preserve the raw MQTT record and report the decoder error separately.
|
||||||
|
|
||||||
## Current camera result
|
## Camera result outside MQTT
|
||||||
|
|
||||||
No independent camera, panorama, JPEG, H.264 or RTSP topic was observed in this
|
No camera payload was observed in the report-topic MQTT run documented above.
|
||||||
run. `lio_pcl` may carry intensity or packed color information, but that does not
|
The later LixelGO/iPhone experiment found the preview on a separate transport:
|
||||||
prove access to the two raw panoramic camera streams. Camera discovery remains a
|
left/right RTSP sessions on TCP `8554` with interleaved RTP/TCP and H.264. This
|
||||||
separate evidence gate.
|
proves compressed preview availability, not full-resolution raw frames or
|
||||||
|
camera calibration. See Lab 002.
|
||||||
|
|
|
||||||
|
|
@ -1,42 +1,193 @@
|
||||||
# NODEDC MISSION CORE monorepo
|
# NODEDC MISSION CORE monorepo
|
||||||
|
|
||||||
This document maps the renamed repository to the target Mission Core
|
This document maps the current repository to the target Mission Core
|
||||||
architecture without claiming that unfinished package boundaries already
|
architecture without claiming that deferred package and process boundaries
|
||||||
exist.
|
already exist. The accepted device-lifecycle decision is recorded in
|
||||||
|
[`ADR 0004`](adr/0004-plugin-sdk-v0alpha2-and-experimental-device-lifecycle.md).
|
||||||
|
|
||||||
## Current layout
|
## Current layout
|
||||||
|
|
||||||
| Path | Current responsibility | Target responsibility |
|
| Path | Current responsibility | Target responsibility |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `apps/control-station/` | React shell, device workflow, spatial scene | Vendor-neutral operator application |
|
| `apps/control-station/` | React shell, device workflow, spatial scene | Vendor-neutral operator application |
|
||||||
| `src/k1link/web/` | Local FastAPI control plane and K1 session orchestration | Generic host APIs plus plugin supervisor |
|
| `packages/plugin-sdk/` | Installable v0alpha2 Pydantic contracts and JSON Schema export | Portable host/plugin identity, lifecycle, stream and evidence boundary |
|
||||||
| `src/k1link/viewer/` | Replay runtime and Rerun adapter | Canonical scene sink and presentation adapter |
|
| `plugins/xgrids-k1/` | v1alpha2 manifest, exact compatibility profile, fail-closed loader and extraction documentation | Independently versioned XGRIDS device plugin |
|
||||||
| `src/k1link/ble/` | K1 BLE discovery and reviewed provisioning | `plugins/xgrids-k1` |
|
| `src/k1link/web/` | Local FastAPI host, in-memory operation/acquisition lifecycle and transitional XGRIDS facade | Generic host APIs plus isolated plugin supervisor |
|
||||||
| `src/k1link/mqtt/` | Raw-first K1 MQTT capture | `plugins/xgrids-k1` behind EvidenceStore |
|
| `src/k1link/mqtt/` | Raw-first K1 MQTT capture | Plugin-owned transport behind an EvidenceStore |
|
||||||
| `src/k1link/protocol/` | Firmware-scoped K1 codecs | `plugins/xgrids-k1` |
|
| `src/k1link/protocol/` | Firmware-scoped K1 codecs and explicit vendor normalizer | Plugin-owned protocol adapter |
|
||||||
| `packages/plugin-sdk/` | Contract placeholder | Versioned manifests, capabilities, lifecycle, schemas |
|
| `src/k1link/data_plane/` | Transport-neutral decoded in-process consumer views | Local projections hydrated from portable SDK envelopes |
|
||||||
| `plugins/xgrids-k1/` | Extraction boundary and documentation | Independently versioned device plugin |
|
| `src/k1link/viewer/` | Rerun consumer, visualization runtime and legacy Foxglove regression module | Replaceable canonical scene sink and presentation adapters |
|
||||||
|
| `docs/domain-model/` | Versioned experimental Mission Core vocabulary | Promotion source for meanings proven across devices/services |
|
||||||
|
|
||||||
## Extraction order
|
## Implemented boundary
|
||||||
|
|
||||||
1. Define canonical PointCloud, Pose, DeviceStatus, RawEnvelope, and metrics
|
The current K1 live/replay data path is:
|
||||||
contracts in the Plugin SDK.
|
|
||||||
2. Make Rerun consume canonical frames rather than K1 MQTT topics.
|
```text
|
||||||
3. Extract codecs and replay parity fixtures.
|
raw K1 transport
|
||||||
4. Extract raw capture with fault isolation between evidence and preview.
|
-> raw-first evidence capture
|
||||||
5. Extract BLE/Wi-Fi provisioning and MQTT into a least-privilege edge worker.
|
-> bounded preview queue
|
||||||
6. Replace K1-specific host routes and state with plugin/device-session APIs.
|
-> XGRIDS normalizer injected by the plugin facade
|
||||||
7. Drive the connection wizard from plugin capabilities and schemas.
|
-> DecodedPointCloudView / DecodedPoseView
|
||||||
8. Re-run native replay and the physical BLE → Wi-Fi → MQTT → scene acceptance.
|
-> Rerun bridge
|
||||||
|
-> embedded local viewer
|
||||||
|
```
|
||||||
|
|
||||||
|
Rerun does not import K1 protocol code, inspect MQTT topics or receive raw
|
||||||
|
payloads. `VisualizationRuntime` cannot choose a vendor decoder implicitly; its
|
||||||
|
composition owner injects one. The local `Decoded*View` classes are in-process
|
||||||
|
consumer projections, not the portable Plugin SDK wire envelopes.
|
||||||
|
|
||||||
|
The Plugin SDK v0alpha2 separately defines model/device/session identity,
|
||||||
|
operation events, acquisition-related session state, canonical stream
|
||||||
|
envelopes, payload handles, evidence lineage and compatibility assessments. It
|
||||||
|
is importable through the root editable path dependency and independently
|
||||||
|
buildable from `packages/plugin-sdk`. The hot K1 path does not yet instantiate
|
||||||
|
all SDK envelopes or use the SDK `EvidenceStore` protocol.
|
||||||
|
|
||||||
|
## Experimental vocabulary, not Platform Ontology
|
||||||
|
|
||||||
|
The local vocabulary distinguishes:
|
||||||
|
|
||||||
|
```text
|
||||||
|
model != device != transport alias != device session
|
||||||
|
acquisition != operation != source/channel != evidence
|
||||||
|
acknowledgement != completion
|
||||||
|
raw evidence != decoded data != viewer state
|
||||||
|
```
|
||||||
|
|
||||||
|
These meanings remain versioned under `docs/domain-model/` and do not mutate or
|
||||||
|
create a runtime dependency on NODE.DC Platform Ontology. Promotion requires a
|
||||||
|
second device family or another platform service using the same stable meaning,
|
||||||
|
an owner, lifecycle, compatibility policy and migration tests.
|
||||||
|
|
||||||
|
## Plugin manifest and compatibility
|
||||||
|
|
||||||
|
The catalog accepts existing v1alpha1 manifests and additive v1alpha2 manifests.
|
||||||
|
v1alpha1 remains a one-model contract; v1alpha2 accepts one or more models and
|
||||||
|
requires every model to be covered by a reviewed, plugin-local, path-confined
|
||||||
|
compatibility profile. The current XGRIDS profile matches exactly:
|
||||||
|
|
||||||
|
- XGRIDS LixelKity K1;
|
||||||
|
- firmware `3.0.2`;
|
||||||
|
- direct-LAN topology;
|
||||||
|
- one retained physical laboratory evidence scope.
|
||||||
|
|
||||||
|
Unknown or unattested firmware fails closed. The current runtime does not read
|
||||||
|
firmware from the device: it activates this limited, read-only profile only
|
||||||
|
after explicit operator attestation of firmware `3.0.2` and direct-LAN topology,
|
||||||
|
and records the basis as `operator-attested`. That claim is not device-derived
|
||||||
|
proof. The profile independently records observation, decode, replay,
|
||||||
|
physical-verification and write evidence. Loading it cannot authorize a
|
||||||
|
transport mutation. Application-command publishing remains disabled; the
|
||||||
|
separately reviewed BLE Wi-Fi provisioning write retains its own explicit
|
||||||
|
operator gate.
|
||||||
|
|
||||||
|
## Semantic lifecycle
|
||||||
|
|
||||||
|
The transitional facade now creates separate provisional device,
|
||||||
|
device-session, acquisition and operation IDs. Acquisition can be prepared,
|
||||||
|
wait for receiver readiness, then wait for an external physical start, and become acquiring only after real point
|
||||||
|
data, wait for an externally confirmed stop, finalize and complete. Capture-only
|
||||||
|
stop reports the K1 physical state as unknown.
|
||||||
|
|
||||||
|
The operation journal is bounded and in memory. It records IDs, idempotency,
|
||||||
|
declared deadlines, progress and terminal results without action parameters or secrets.
|
||||||
|
It is not durable, distributed or recoverable after process restart.
|
||||||
|
|
||||||
|
Semantic acquisition actions coexist with legacy `stream.*` actions for
|
||||||
|
v1alpha1/UI compatibility. On this profile, start and stop operate the local
|
||||||
|
receiver and operator workflow only. K1 scanning is still started and stopped
|
||||||
|
by physical double-click; no modeling request is published.
|
||||||
|
|
||||||
|
Left/right camera preview transport, endpoint paths and H.264 framing are now
|
||||||
|
observed under the exact compatibility profile. The read-only runtime adapter is
|
||||||
|
not implemented yet. Device status and heartbeat remain raw observed channels
|
||||||
|
without semantic decoders. Device calibration command and sensor-to-vehicle
|
||||||
|
extrinsics are unavailable.
|
||||||
|
|
||||||
|
## Remaining extraction order
|
||||||
|
|
||||||
|
1. Preserve native replay parity and repeat the exact-profile physical
|
||||||
|
point/pose regression through the explicit normalizer boundary.
|
||||||
|
2. Map portable SDK stream/evidence contracts onto the hot path without
|
||||||
|
replacing raw evidence or changing consumer output.
|
||||||
|
3. Extract codecs, normalizer and raw capture physically behind the XGRIDS
|
||||||
|
plugin package boundary.
|
||||||
|
4. Add process isolation and a durable operation/evidence supervisor.
|
||||||
|
5. Replace compatibility routes and singleton state with multi-device session
|
||||||
|
routing.
|
||||||
|
6. Split Edge execution from the Control Station behind authenticated transport.
|
||||||
|
7. Implement the now-observed read-only RTSP/H.264 camera adapter; keep the
|
||||||
|
modeling-command publisher disabled until its separate safety gate closes.
|
||||||
|
|
||||||
## Invariants
|
## Invariants
|
||||||
|
|
||||||
- Raw evidence is durable before preview work.
|
- Raw evidence is persisted before preview work and is never overwritten by a
|
||||||
- Mission Core starts when the XGRIDS manifest is absent. A formal
|
decode result.
|
||||||
disabled/quarantine state for an installed plugin is a future supervisor feature.
|
- Mission Core starts when the XGRIDS manifest is absent. Formal installed
|
||||||
- The plugin cannot add global navigation or arbitrary CSS.
|
plugin quarantine remains a future supervisor feature.
|
||||||
- Vendor writes and commands must ultimately require declared capabilities and
|
- Core code does not branch on XGRIDS IDs, fields, topics or firmware.
|
||||||
operator gates. In v1alpha1 `permissions`, `mutating`, and `secretFields` are
|
- A plugin cannot add global navigation or arbitrary CSS.
|
||||||
validated metadata only; concrete adapters still enforce request safety.
|
- A transport alias never becomes a stable device identity merely because it
|
||||||
- The visualization engine can be replaced without changing device plugins.
|
appears in a vendor header.
|
||||||
- Real captures and credentials remain outside Git.
|
- Acknowledgement, first data, operator confirmation and physical completion
|
||||||
|
remain distinct evidence levels.
|
||||||
|
- Vendor writes require a declared capability, exact compatibility evidence,
|
||||||
|
host authorization and an operator gate. Manifest metadata and profiles alone
|
||||||
|
grant none of these.
|
||||||
|
- The visualization engine can be replaced without changing vendor transports
|
||||||
|
or codecs.
|
||||||
|
- Real captures, credentials, device identities, maps and router metadata remain
|
||||||
|
outside Git.
|
||||||
|
|
||||||
|
## Deferred, not implemented
|
||||||
|
|
||||||
|
- isolated/signed plugin processes and crash containment;
|
||||||
|
- durable operation journal, restart recovery, vault and RBAC;
|
||||||
|
- persistent device identity and concurrent multi-device routing;
|
||||||
|
- complete SDK-envelope/EvidenceStore hot-path integration;
|
||||||
|
- remote Edge split, authenticated WAN relay and fleet orchestration;
|
||||||
|
- automatic K1 start/stop/calibration commands;
|
||||||
|
- camera discovery, decoding, synchronization and visualization.
|
||||||
|
|
||||||
|
## Workspace-repeatable locked architecture gate
|
||||||
|
|
||||||
|
From the repository root:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
uv sync --frozen --group dev
|
||||||
|
uv run pytest \
|
||||||
|
tests/test_plugin_sdk_v0alpha2_contracts.py \
|
||||||
|
tests/test_plugin_catalog.py \
|
||||||
|
tests/test_xgrids_compatibility_profile.py \
|
||||||
|
tests/test_device_lifecycle.py \
|
||||||
|
tests/test_xgrids_acquisition_lifecycle.py \
|
||||||
|
tests/test_canonical_pipeline.py \
|
||||||
|
tests/test_rerun_bridge.py \
|
||||||
|
tests/test_web_validation_security.py
|
||||||
|
uv run ruff check .
|
||||||
|
uv run mypy
|
||||||
|
uv run mypy --strict packages/plugin-sdk/python/missioncore_plugin_sdk
|
||||||
|
uv run python plugins/xgrids-k1/profile_loader.py
|
||||||
|
uv run python -m missioncore_plugin_sdk.v0alpha2 \
|
||||||
|
> /tmp/missioncore-plugin-sdk-v0alpha2.schemas.json
|
||||||
|
|
||||||
|
cd apps/control-station
|
||||||
|
npm ci
|
||||||
|
npm run test:unit
|
||||||
|
npm run typecheck
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
These checks perform no BLE scan, network capture, router mutation or K1
|
||||||
|
application write. The next physical and communication gates are specified in
|
||||||
|
ADR 0004.
|
||||||
|
|
||||||
|
This gate is repeatable in the current workspace, not standalone reproducible.
|
||||||
|
The frontend consumes mutable sibling `file:` dependencies from
|
||||||
|
`NODEDC_DESIGN_GUIDELINE`; its lockfile does not pin the donor checkout's Git
|
||||||
|
revision or content hash. CI and portable packaging remain blocked until those
|
||||||
|
packages are published or vendored, or an immutable donor revision and content
|
||||||
|
verification are enforced. The current root Python wheel also omits the plugin
|
||||||
|
manifests/profile loader and built frontend, so it is not a deployment bundle.
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,338 @@
|
||||||
|
# ADR 0004: Plugin SDK v0alpha2 and experimental device lifecycle
|
||||||
|
|
||||||
|
- Status: accepted
|
||||||
|
- Date: 2026-07-16
|
||||||
|
- Supersedes: the next-milestone assumptions in ADR 0003; it does not replace
|
||||||
|
the v1alpha1 compatibility boundary
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
ADR 0003 isolated the first concrete device integration from the Mission Core
|
||||||
|
shell, but the backend still exposed one aggregate runtime and the repository
|
||||||
|
had no executable, vendor-neutral definitions for identity, sessions,
|
||||||
|
operations, acquisitions, streams, or evidence. The verified K1 path also fed
|
||||||
|
vendor MQTT topics and decoder results directly into visualization code.
|
||||||
|
|
||||||
|
The next research stage will use an owner-controlled iPhone and LixelGO to
|
||||||
|
observe additional K1 communication. Before that experiment, Mission Core needs
|
||||||
|
stable local meanings and dependency boundaries so that discovered camera or
|
||||||
|
command mechanics do not spread vendor assumptions through the host.
|
||||||
|
|
||||||
|
The domain is still experimental. Only one K1, firmware `3.0.2`, one direct-LAN
|
||||||
|
topology, point cloud, pose, raw report capture, and operator-manual acquisition
|
||||||
|
have physical evidence. Mutating NODE.DC Platform Ontology now would promote
|
||||||
|
single-device hypotheses into platform-wide meanings before a second device or
|
||||||
|
service proves them.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
Mission Core adopts an independently installable Plugin SDK v0alpha2 and a
|
||||||
|
repository-local experimental vocabulary. It does not modify or depend on
|
||||||
|
NODE.DC Platform Ontology at this stage.
|
||||||
|
|
||||||
|
The executable package lives in:
|
||||||
|
|
||||||
|
```text
|
||||||
|
packages/plugin-sdk/
|
||||||
|
pyproject.toml
|
||||||
|
python/missioncore_plugin_sdk/v0alpha2/
|
||||||
|
```
|
||||||
|
|
||||||
|
It contains closed, deeply immutable Pydantic contracts and deterministic JSON
|
||||||
|
Schema export. Nested mappings and collections are frozen after validation;
|
||||||
|
`model_copy(update=...)` revalidates its update before returning a new contract.
|
||||||
|
The contracts cover:
|
||||||
|
|
||||||
|
- device model and device-instance identity;
|
||||||
|
- transport aliases and execution-node bindings;
|
||||||
|
- device-session state;
|
||||||
|
- operation policy, request, acknowledgement, progress, completion, failure,
|
||||||
|
cancellation and timeout;
|
||||||
|
- point-cloud, pose, image, encoded-video and device-status stream envelopes;
|
||||||
|
- payload and evidence handles, evidence records and raw transport records;
|
||||||
|
- compatibility assessments.
|
||||||
|
|
||||||
|
The experimental semantic definitions and promotion rules live in
|
||||||
|
`docs/domain-model/mission-core-experimental-vocabulary-v0alpha2.md`. Breaking
|
||||||
|
wire or semantic changes create a new explicit SDK module. Existing v0alpha2
|
||||||
|
documents and retained evidence are never silently reinterpreted.
|
||||||
|
|
||||||
|
### Domain distinctions
|
||||||
|
|
||||||
|
These terms are deliberately not aliases:
|
||||||
|
|
||||||
|
| Term | Meaning in this milestone |
|
||||||
|
| --- | --- |
|
||||||
|
| Model | A plugin-owned definition of a supported hardware/product model. It is not physical hardware. |
|
||||||
|
| Device | One enrolled or provisionally identified physical instance. Its identity outlives a connection when a stable basis is available. |
|
||||||
|
| Transport alias | A scoped BLE UUID, IP address, SSID, MQTT identity or USB path. It can locate a device but is not automatically a stable `device_id`. |
|
||||||
|
| Device session | One bounded association between one plugin execution agent and one device instance. Restarting or reconnecting may create a new session. |
|
||||||
|
| Acquisition | One bounded production of requested sensor data, potentially containing multiple channels and sources. |
|
||||||
|
| Operation | One requested action with its own ID, deadline, idempotency boundary, progress and terminal outcome. An acknowledgement is not completion. |
|
||||||
|
| Source/channel | A logical producer or consumable data channel within an acquisition or replay. It is neither the physical device nor the viewer URL. |
|
||||||
|
| Evidence | Immutable raw or derived bytes plus content identity, lineage, retention and redaction metadata. Evidence is not decoded state or presentation state. |
|
||||||
|
|
||||||
|
Enrollment, connectivity and acquisition remain independent state machines. A
|
||||||
|
device can be enrolled while offline, connected while idle, or retain identity
|
||||||
|
after an acquisition failure.
|
||||||
|
|
||||||
|
### Manifest and compatibility profiles
|
||||||
|
|
||||||
|
The manifest catalog accepts both v1alpha1 and v1alpha2 device-plugin manifests.
|
||||||
|
v1alpha1 remains valid for existing plugins and does not gain profile semantics
|
||||||
|
implicitly. A v1alpha2 manifest must link every declared model to at least one
|
||||||
|
plugin-local compatibility profile through `profileId`, `modelId`, and a path
|
||||||
|
constrained to that plugin's directory. v1alpha1 remains exactly one model;
|
||||||
|
v1alpha2 can describe more than one independently profiled model.
|
||||||
|
|
||||||
|
The XGRIDS manifest is now `missioncore.nodedc/v1alpha2`, plugin version `0.2.0`,
|
||||||
|
and links exactly one profile:
|
||||||
|
|
||||||
|
```text
|
||||||
|
xgrids.lixelkity-k1.fw-3.0.2.direct-lan.v1
|
||||||
|
```
|
||||||
|
|
||||||
|
The profile is evidence-scoped to exact firmware `3.0.2`, one direct-LAN
|
||||||
|
topology, and the retained laboratory sources. Its independent flags distinguish
|
||||||
|
`observed`, `decoded`, `replay_verified`, `physical_verified`, and
|
||||||
|
`write_enabled`. Unknown or unattested firmware fails closed. The current
|
||||||
|
runtime cannot read firmware from the device; it activates the limited,
|
||||||
|
read-only profile only after explicit operator attestation of firmware `3.0.2`
|
||||||
|
and direct-LAN topology, records an `operator-attested` basis, and does not
|
||||||
|
present that claim as device-derived proof.
|
||||||
|
|
||||||
|
A profile is descriptive compatibility evidence. Loading a manifest or profile
|
||||||
|
does not grant transport permissions, authorization, or device-write authority.
|
||||||
|
The XGRIDS v1 profile loader rejects every attempted `write_enabled: true` and
|
||||||
|
is executed as a fail-closed plugin-activation gate, not only as a test helper.
|
||||||
|
The separately reviewed BLE Wi-Fi provisioning path remains an explicit legacy
|
||||||
|
operator-confirmed mutation and is not authorized by this profile.
|
||||||
|
|
||||||
|
### Semantic acquisition lifecycle
|
||||||
|
|
||||||
|
The transitional backend now separates operation metadata from acquisition
|
||||||
|
state. It assigns provisional device, device-session, acquisition and operation
|
||||||
|
identifiers and records bounded in-process lifecycle snapshots.
|
||||||
|
|
||||||
|
The current XGRIDS acquisition lifecycle is:
|
||||||
|
|
||||||
|
```text
|
||||||
|
prepare
|
||||||
|
-> prepared
|
||||||
|
-> starting (host receiver is not ready yet)
|
||||||
|
-> awaiting_external_start
|
||||||
|
-> acquiring after the first real point frame
|
||||||
|
-> awaiting_external_stop when graceful stop is requested
|
||||||
|
-> finalizing
|
||||||
|
-> completed
|
||||||
|
```
|
||||||
|
|
||||||
|
Failure, abort and interruption are distinct terminal states. `capture-only`
|
||||||
|
stop may terminate the receiver but must report the physical K1 stop as
|
||||||
|
`unknown`. Graceful stop does not claim success until the operator explicitly
|
||||||
|
confirms the physical action.
|
||||||
|
|
||||||
|
The bounded `OperationJournal` records lifecycle metadata, sequence, revision,
|
||||||
|
declared deadline, idempotency key, terminal result/error and evidence references. It is
|
||||||
|
in memory only, limited to one process and intentionally excludes action inputs
|
||||||
|
and secrets. Deadline persistence and distributed enforcement are not implied.
|
||||||
|
It is not a durable audit log, distributed command journal or recovery mechanism.
|
||||||
|
|
||||||
|
HTTP validation failures on the plugin-scoped action endpoint and deprecated
|
||||||
|
`/api/connect` endpoint return a generic, input-free `422` response. Pydantic
|
||||||
|
diagnostics and rejected request values are not reflected to the client. This
|
||||||
|
is defense in depth for the current in-memory credential path, not a secret
|
||||||
|
vault implementation.
|
||||||
|
|
||||||
|
The v1alpha2 manifest declares semantic acquisition actions alongside legacy
|
||||||
|
`stream.*` compatibility actions. `acquisition.start` and `acquisition.stop`
|
||||||
|
currently mutate host-side lifecycle and capture state only. They prompt and
|
||||||
|
observe the verified physical double-click workflow; they do not publish an
|
||||||
|
MQTT modeling request to the K1.
|
||||||
|
|
||||||
|
### Data-plane boundary
|
||||||
|
|
||||||
|
The implemented local visualization path is:
|
||||||
|
|
||||||
|
```text
|
||||||
|
K1 transport message
|
||||||
|
-> raw-first .k1mqtt evidence capture
|
||||||
|
-> bounded latest-wins preview queue
|
||||||
|
-> explicitly injected K1 vendor normalizer
|
||||||
|
-> transport-neutral DecodedPointCloudView / DecodedPoseView
|
||||||
|
-> Rerun bridge
|
||||||
|
-> local embedded Rerun Web Viewer
|
||||||
|
```
|
||||||
|
|
||||||
|
`VisualizationRuntime` has no default vendor decoder. Its composition owner must
|
||||||
|
inject a `CanonicalNormalizer`; the XGRIDS facade explicitly injects
|
||||||
|
`normalize_k1_message`. The Rerun bridge consumes only decoded local views and
|
||||||
|
contains no MQTT topics, raw payload access, protobuf/LZ4 decoder imports, or
|
||||||
|
K1 branches.
|
||||||
|
|
||||||
|
The `src/k1link/data_plane/Decoded*View` classes are allocation-conscious,
|
||||||
|
in-process consumer projections. They are intentionally not the portable
|
||||||
|
Plugin SDK `PointCloudFrame`, `PoseFrame`, or `DeviceStatusFrame` wire
|
||||||
|
contracts. The current hot path does not yet instantiate SDK stream envelopes,
|
||||||
|
nor does the existing raw capture use the SDK `EvidenceStore` protocol. A later
|
||||||
|
process boundary will map portable SDK envelopes into local consumer views.
|
||||||
|
|
||||||
|
This transitional distinction avoids both vendor/viewer coupling and the false
|
||||||
|
claim that cross-process extraction is already complete.
|
||||||
|
|
||||||
|
### Current K1 safety and evidence status
|
||||||
|
|
||||||
|
The current profile and implementation assert only the following:
|
||||||
|
|
||||||
|
- point cloud and pose are observed, bounded-decoded, replay-verified and
|
||||||
|
physically verified for the exact profile;
|
||||||
|
- device status and heartbeat are observed raw channels but have no semantic
|
||||||
|
decoder;
|
||||||
|
- physical scan start and stop use the verified K1 double-click;
|
||||||
|
- application-level K1 command publishing is disabled;
|
||||||
|
- the statically identified modeling topic and action values remain research
|
||||||
|
evidence only because headers, settings, acknowledgement, error, timeout,
|
||||||
|
rollback and save-completion contracts are unresolved;
|
||||||
|
- device calibration command and sensor-to-vehicle extrinsics are unavailable;
|
||||||
|
- camera transport, endpoint, codec, framing and independent camera channels
|
||||||
|
remain unknown. One negative MQTT observation does not prove absence.
|
||||||
|
|
||||||
|
## Compatibility with v1alpha1
|
||||||
|
|
||||||
|
This milestone is additive:
|
||||||
|
|
||||||
|
- the catalog continues to validate v1alpha1 manifests;
|
||||||
|
- the v1alpha1 `state.read` safety requirement remains;
|
||||||
|
- the established composition root, plugin-scoped dispatcher/events, inactive
|
||||||
|
provider behavior and teardown gate remain in force;
|
||||||
|
- legacy `stream.start-live`, `stream.start-replay`, `stream.stop`, and viewer
|
||||||
|
settings actions remain compatibility shims for the proven UI and runtime;
|
||||||
|
- the XGRIDS backend still runs through the reviewed
|
||||||
|
`transitional-in-process` facade in `src/k1link`.
|
||||||
|
|
||||||
|
SDK v0alpha2 contracts do not imply that every current host endpoint, frontend
|
||||||
|
state object, or compatibility shim already emits those contracts.
|
||||||
|
|
||||||
|
## Deliberately deferred
|
||||||
|
|
||||||
|
The following are not complete and must not be inferred from this ADR:
|
||||||
|
|
||||||
|
- process isolation, crash containment or signed independently deployed plugin
|
||||||
|
bundles;
|
||||||
|
- durable operation journal, restart recovery or distributed idempotency;
|
||||||
|
- secret vault implementation, RBAC, host policy enforcement or command
|
||||||
|
approval workflow;
|
||||||
|
- persistent stable device identity and multi-device/multi-session routing;
|
||||||
|
- multiple concurrent acquisitions or multiple plugin models per runtime;
|
||||||
|
- portable SDK-envelope transport in the live hot path;
|
||||||
|
- SDK `EvidenceStore` integration with the existing `.k1mqtt` recorder;
|
||||||
|
- remote Edge/Control Station split, authenticated WAN relay or fleet
|
||||||
|
orchestration;
|
||||||
|
- automatic K1 acquisition/calibration commands;
|
||||||
|
- camera discovery, decode, synchronization or visualization;
|
||||||
|
- verified sensor timestamps, clock synchronization, coordinate convention,
|
||||||
|
sensor-to-vehicle extrinsics or georeferencing.
|
||||||
|
|
||||||
|
Four contract-compatibility questions are explicitly deferred before portable
|
||||||
|
SDK envelopes may enter the live hot path:
|
||||||
|
|
||||||
|
- `ClockDomain` names UTC, device-monotonic and host-monotonic domains, while
|
||||||
|
the current header stores aware datetimes. A later revision must define a
|
||||||
|
language-neutral monotonic tick/nanosecond representation plus its measured
|
||||||
|
relationship to UTC; an aware datetime must not be used as native monotonic
|
||||||
|
time.
|
||||||
|
- `ReferencedPayload` must distinguish total referenced-object length, segment
|
||||||
|
offset and segment length. Until that invariant is fixed, a non-zero-offset
|
||||||
|
shared-memory segment cannot be assumed to satisfy `PointCloudFrame`'s exact
|
||||||
|
layout-length check.
|
||||||
|
- the coarse SDK `AcquisitionState` vocabulary and the richer transitional
|
||||||
|
facade states require an explicit, tested mapping. Matching `v1alpha2` labels
|
||||||
|
alone do not make their state machines interchangeable.
|
||||||
|
- the generic manifest catalog currently validates profile link integrity,
|
||||||
|
model coverage, path confinement and profile identity only. Evidence and
|
||||||
|
device-specific profile semantics remain the responsibility of the plugin's
|
||||||
|
fail-closed loader. A future generic profile schema must not be claimed until
|
||||||
|
that validation boundary is versioned and tested.
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
New K1 protocol discoveries must enter through the compatibility profile,
|
||||||
|
plugin-owned transport/normalizer code and retained raw evidence. They cannot be
|
||||||
|
added directly to Rerun, Mission Core navigation, generic lifecycle code, or the
|
||||||
|
platform ontology.
|
||||||
|
|
||||||
|
The repository can continue laboratory work without prematurely freezing the
|
||||||
|
platform ontology. A future Edge split can preserve SDK identities, operations,
|
||||||
|
stream envelopes and evidence lineage while replacing the in-process facade and
|
||||||
|
local consumer projection.
|
||||||
|
|
||||||
|
The cost is an explicit temporary adapter boundary: the portable SDK contracts,
|
||||||
|
the in-process lifecycle implementation and the decoded consumer views coexist
|
||||||
|
until extraction and replay parity are proven.
|
||||||
|
|
||||||
|
## Workspace-repeatable locked acceptance checks
|
||||||
|
|
||||||
|
The architecture gate is hardware-free and must pass from the repository root:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
uv sync --frozen --group dev
|
||||||
|
uv run pytest \
|
||||||
|
tests/test_plugin_sdk_v0alpha2_contracts.py \
|
||||||
|
tests/test_plugin_catalog.py \
|
||||||
|
tests/test_xgrids_compatibility_profile.py \
|
||||||
|
tests/test_device_lifecycle.py \
|
||||||
|
tests/test_xgrids_acquisition_lifecycle.py \
|
||||||
|
tests/test_canonical_pipeline.py \
|
||||||
|
tests/test_rerun_bridge.py \
|
||||||
|
tests/test_web_validation_security.py
|
||||||
|
uv run ruff check .
|
||||||
|
uv run mypy
|
||||||
|
uv run mypy --strict packages/plugin-sdk/python/missioncore_plugin_sdk
|
||||||
|
uv run python plugins/xgrids-k1/profile_loader.py
|
||||||
|
uv run python -m missioncore_plugin_sdk.v0alpha2 \
|
||||||
|
> /tmp/missioncore-plugin-sdk-v0alpha2.schemas.json
|
||||||
|
```
|
||||||
|
|
||||||
|
The frontend compatibility gate remains:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd apps/control-station
|
||||||
|
npm ci
|
||||||
|
npm run test:unit
|
||||||
|
npm run typecheck
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
No command above scans Bluetooth, contacts the K1, changes the router, or emits
|
||||||
|
a K1 application command.
|
||||||
|
|
||||||
|
These commands are repeatable in the current workspace, not a standalone
|
||||||
|
release installation. The frontend uses sibling `file:` dependencies from
|
||||||
|
`NODEDC_DESIGN_GUIDELINE`, and `package-lock.json` does not pin that checkout's
|
||||||
|
Git revision or content hash. CI and portable packaging remain blocked until
|
||||||
|
those packages are published or vendored, or the build enforces an immutable
|
||||||
|
donor revision plus content verification. The root Python wheel does not yet
|
||||||
|
bundle plugin manifests, the profile loader, or the built frontend.
|
||||||
|
|
||||||
|
## Next evidence gate
|
||||||
|
|
||||||
|
Communication research begins only after the architecture checks pass and the
|
||||||
|
existing physical regression is repeated on the exact profile:
|
||||||
|
|
||||||
|
1. verify firmware `3.0.2` and the owner-controlled direct-LAN topology;
|
||||||
|
2. prepare one operator-manual acquisition;
|
||||||
|
3. start the K1 by physical double-click;
|
||||||
|
4. retain raw `.k1mqtt` evidence outside Git;
|
||||||
|
5. confirm at least one real point frame and pose data reach the viewer through
|
||||||
|
the injected normalizer with no vendor dependency in Rerun;
|
||||||
|
6. stop by the operator-confirmed physical workflow and record whether device
|
||||||
|
stop was confirmed or remains unknown;
|
||||||
|
7. verify that no MQTT request topic was subscribed to or published and no
|
||||||
|
application command was emitted.
|
||||||
|
|
||||||
|
Only then may a separate read-only LixelGO/iPhone communication-observation
|
||||||
|
experiment begin. Its first output is evidence: endpoints, flows, topics,
|
||||||
|
payload signatures, timing and command/response correlation. It does not enable
|
||||||
|
camera or command execution. Any state-changing publisher or new BLE write
|
||||||
|
requires its own profile revision, exact request/acknowledgement/timeout/error
|
||||||
|
contract, replay fixture, operator gate and physical acceptance.
|
||||||
|
|
@ -1,25 +1,29 @@
|
||||||
# XGRIDS / LixelKity K1 plugin
|
# XGRIDS / LixelKity K1 plugin
|
||||||
|
|
||||||
This directory is the package boundary for the first Mission Core device
|
This directory is the package boundary for the first Mission Core device
|
||||||
plugin. It now owns the canonical v1alpha manifest
|
plugin. It now owns the canonical v1alpha2 manifest
|
||||||
[`plugin.manifest.json`](plugin.manifest.json). The statically linked frontend
|
[`plugin.manifest.json`](plugin.manifest.json). The statically linked frontend
|
||||||
contribution lives in `apps/control-station/src/device-plugins/xgrids-k1/`; the
|
contribution lives in `apps/control-station/src/device-plugins/xgrids-k1/`; the
|
||||||
verified backend implementation remains temporarily in `src/k1link` so the
|
verified backend implementation remains temporarily in `src/k1link` so the
|
||||||
real-device path stays operational during extraction.
|
real-device path stays operational during extraction.
|
||||||
|
|
||||||
The v1alpha1 host deliberately assigns one model to one plugin runtime, so this
|
The v1alpha2 catalog can describe one or more independently profiled models.
|
||||||
manifest exposes only `xgrids.lixelkity-k1`. Adding another device model means a
|
This manifest currently exposes only `xgrids.lixelkity-k1`, and the transitional
|
||||||
new plugin contribution until session/model routing lands in the next SDK.
|
runtime still owns one active model/session at a time. Concurrent model/session
|
||||||
|
routing remains a later supervisor milestone.
|
||||||
|
|
||||||
The plugin owns:
|
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;
|
- BLE discovery hints and K1 GATT metadata;
|
||||||
- the reviewed firmware-3 Wi-Fi provisioning profile;
|
- the reviewed firmware-3 Wi-Fi provisioning profile;
|
||||||
- K1 LAN status and private-address validation;
|
- K1 LAN status and private-address validation;
|
||||||
- subscribe-only MQTT transport and report-topic allowlist;
|
- subscribe-only MQTT transport and report-topic allowlist;
|
||||||
- native `.k1mqtt` capture;
|
- native `.k1mqtt` capture;
|
||||||
- firmware-scoped protobuf/LZ4 and legacy codecs;
|
- firmware-scoped protobuf/LZ4 and legacy codecs;
|
||||||
- normalization of K1 point cloud, pose, status, health, and artifacts;
|
- normalization of K1 point cloud and pose, plus raw-only preservation of the
|
||||||
|
still-undecoded status and heartbeat channels;
|
||||||
- K1-specific operator instructions and compatibility tests.
|
- K1-specific operator instructions and compatibility tests.
|
||||||
|
|
||||||
The plugin does not own:
|
The plugin does not own:
|
||||||
|
|
@ -45,7 +49,26 @@ legacy router; both paths delegate to the same proven
|
||||||
`XgridsK1CompatibilityService` methods. Synchronous capture/runtime operations
|
`XgridsK1CompatibilityService` methods. Synchronous capture/runtime operations
|
||||||
run outside the FastAPI event loop.
|
run outside the FastAPI event loop.
|
||||||
|
|
||||||
Model switching calls the plugin deactivation hook and must receive a successful
|
Model switching calls the plugin deactivation hook. An active acquisition uses
|
||||||
`stream.stop` before Mission Core removes this custom UI. BLE, MQTT, codec and
|
semantic `acquisition.stop` in `capture-only` mode; replay and pre-v1alpha2
|
||||||
|
sessions retain the legacy `stream.stop` shim. Neither path claims that the
|
||||||
|
physical K1 stopped without separate operator evidence. BLE, MQTT, codec and
|
||||||
evidence modules remain in `src/k1link` until replay parity and another physical
|
evidence modules remain in `src/k1link` until replay parity and another physical
|
||||||
K1 regression are complete.
|
K1 regression are complete.
|
||||||
|
|
||||||
|
The compatibility profile is descriptive and cannot itself authorize a vendor
|
||||||
|
write. The current runtime cannot inspect K1 firmware: it keeps the profile
|
||||||
|
inactive until the operator explicitly attests firmware `3.0.2` and direct-LAN
|
||||||
|
topology, and records that basis as `operator-attested` rather than
|
||||||
|
device-derived evidence. Validate the current exact-match profile without
|
||||||
|
device I/O with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
uv run python plugins/xgrids-k1/profile_loader.py
|
||||||
|
```
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue