528 lines
30 KiB
Markdown
528 lines
30 KiB
Markdown
# NODEDC MISSION CORE
|
||
|
||
NODEDC MISSION CORE is the vendor-neutral control, observation, mission-planning,
|
||
recording, and integration platform for NODEDC autonomous systems. This
|
||
repository is the canonical early-stage monorepo: it contains the Mission Core
|
||
Control Station, the current local control plane, shared contracts as they are
|
||
extracted, and device plugins.
|
||
|
||
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,
|
||
connects to its MQTT broker, persists each raw frame before preview work, decodes
|
||
point cloud and pose, and renders the real cloud plus trajectory through an
|
||
embedded self-hosted Rerun Web Viewer. Native MQTT persistence uses bounded
|
||
group commit (at most 0.5 seconds, 4 MiB or 32 messages), and camera archives
|
||
commit complete fMP4 segments before their index rows. These are explicit
|
||
crash-RPO bounds, not a zero-loss or disk-replication claim. The former
|
||
Foxglove bridge remains only as a legacy regression module.
|
||
|
||
The backend vendor implementation is physically isolated below
|
||
`src/k1link/device_plugins/xgrids_k1/`; its reviewed manifest and compatibility
|
||
profile remain below `plugins/xgrids-k1/`. Mission Core host code discovers
|
||
archive sources, recovery hooks and recording exporters only through the plugin
|
||
runtime contribution. The verified wire protocol and raw evidence format are
|
||
unchanged.
|
||
|
||
The K1 acquisition now also requires an operator project name. Frontend and
|
||
backend normalize it with NFKC plus surrounding-whitespace trimming, reject
|
||
control/surrogate characters and values above 96 Unicode characters, and
|
||
preserve it as local session/catalog display metadata. It is never used as a
|
||
filesystem path. The physically accepted interactive control path carries the
|
||
same validated value in its single canonical START; there is no separate
|
||
project-name write.
|
||
|
||
Plugin SDK v0alpha2 now provides executable, vendor-neutral identity, session,
|
||
operation, runtime-action, stream, evidence and compatibility contracts. Every
|
||
backend runtime must now pass a versioned descriptor/handshake against its
|
||
manifest before actions are admitted. Every action then crosses immutable SDK
|
||
`RuntimeActionInvocation` and `RuntimeActionResult` validation through a
|
||
replaceable runtime transport seam. The meanings remain a local experimental
|
||
vocabulary rather than a mutation of NODE.DC Platform Ontology. The current
|
||
transport is deliberately 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 application-control bootstrap reads and correlates live `DeviceInfo` before
|
||
START. The exact profile requires model `LixelKity K1`, observed platform type
|
||
`A4`, activated state, firmware `3.0.2`, and direct-LAN topology. The UI selects
|
||
that profile but does not ask the operator to attest firmware manually.
|
||
|
||
The repository now contains one narrowly gated state-changing command:
|
||
`ble wifi-configure`. It accepts only the reviewed firmware-3 provisioning
|
||
profile and requires explicit `--confirm-write`; the Wi-Fi password is collected
|
||
through a hidden local macOS dialog. MQTT capture and decoding are read-only.
|
||
Nothing changes router settings, firmware or global Python packages.
|
||
|
||
## Verified XGRIDS K1 stand
|
||
|
||
- one XGRIDS/LixelKity K1;
|
||
- one Apple Silicon MacBook running macOS;
|
||
- one ordinary TP-Link Deco/mesh network used by other devices;
|
||
- 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
|
||
existing LAN without changing it. A Guest/IoT SSID is optional and may be
|
||
counterproductive if Deco isolates clients; Mac and K1 must ultimately be able
|
||
to reach each other.
|
||
|
||
## What is actually being proved
|
||
|
||
The project has three independent gates:
|
||
|
||
1. K1 is operational and can record a project autonomously.
|
||
2. Mac can discover and inspect the K1 BLE/GATT surface safely.
|
||
3. Without LixelGO, K1 can be associated with Wi-Fi and a proprietary data
|
||
session can be opened.
|
||
|
||
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
|
||
block, and `lio_pose` is an uncompressed protobuf. Owner-operated LixelGO capture
|
||
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
|
||
|
||
The project uses Python 3.12 in a repository-local `.venv` managed by `uv`.
|
||
This does not install Python packages globally and does not modify neighboring
|
||
repositories.
|
||
|
||
```bash
|
||
cd /Users/dcconstructions/Downloads/mnt/NODEDC/NODEDC_MISSION_CORE
|
||
uv sync --frozen --group dev
|
||
uv run k1link doctor
|
||
uv run pytest
|
||
```
|
||
|
||
## External perception worker
|
||
|
||
Mission Core can now package one sealed camera epoch as an immutable,
|
||
content-addressed `missioncore.compute-job/v1` without changing the source
|
||
evidence. The first recorded TEST007 job was digest-verified and decoded on a
|
||
separate Windows RTX 4090 worker, executed through pinned Triton/YOLOX-S and
|
||
returned a content-addressed `missioncore.compute-result/v1` containing 56
|
||
session-timestamped generic COCO detections. The worker never connects to K1 and
|
||
does not own the raw archive.
|
||
|
||
Mission Core now validates and projects that optional result into the same saved
|
||
Rerun recording. Open TEST007 and use **Распознавание** to see the recorded
|
||
left-camera frames and boxes; **Облако точек** returns to 3D. This remains a
|
||
recorded camera-only acceptance, not a navigation stack: large-epoch
|
||
performance, direct worker transport and bounded live fan-out remain open. See
|
||
[ADR 0014](docs/adr/0014-bounded-external-perception-worker.md)
|
||
and the [external worker contract](docs/10_EXTERNAL_PERCEPTION_WORKER.md).
|
||
|
||
## Simulation Polygon
|
||
|
||
Mission Core now has a parallel Polygon product branch for reproducible
|
||
autonomy qualification. Mission Core owns scenarios, qualification-run
|
||
lifecycle, authority, canonical contracts, provenance and reports; Gazebo, PX4
|
||
SITL, ROS 2, Nav2 and viewers remain replaceable providers. Simulation, replay,
|
||
digital twin, HIL and physical shadow keep distinct causal run kinds.
|
||
|
||
SIM S0 is accepted on the reviewed D-only worker. Its strict profile fixes
|
||
loopback-only process isolation, exact provider pins, a 2 ms physics step,
|
||
1×/2× RTF/resource budgets and disabled real/direct actuator authority. Two
|
||
accepted-profile stock-rover runs and nine digest-bound evidence claims passed;
|
||
the target doctor returned `GO`. The doctor remains read-only and a development
|
||
host without the private evidence pack still returns `INCOMPLETE`:
|
||
|
||
```bash
|
||
uv run missioncore-sim s0 doctor \
|
||
--evidence /path/to/private/evidence.yaml \
|
||
--json
|
||
```
|
||
|
||
S1A now adds the first server-owned domain boundary: typed immutable
|
||
`QualificationRun` identities, legal lifecycle transitions, canonical
|
||
Ackermann/Differential commands, authority generation and TTL validation, and
|
||
an append-only crash-safe run/event/command/artifact repository. Active runs
|
||
recover as failed/interrupted rather than silently resuming, and reset creates
|
||
a new linked run/episode. This is repository-level lifecycle acceptance only;
|
||
PX4 command delivery, watchdog/failsafe behavior, navigation behavior and real
|
||
actuator control remain unaccepted.
|
||
|
||
The S1B repository increment adds the transport-neutral
|
||
`SimulationApplicationService`, an exact-S0/D-only worker admission guard and a
|
||
POSIX process supervisor. Start is idempotent, a second active owner is
|
||
rejected, provider processes receive dedicated PGIDs and run-scoped logs, and
|
||
shutdown follows the S0 dependency order with residue reported as failure.
|
||
Pause/resume/step/reset/stop are owned by the application service through a
|
||
worker port.
|
||
|
||
Commit `6cb1495` has now passed the real D-only `MissionCore-Sim` S1B lifecycle:
|
||
the exact immutable source generation started Micro XRCE-DDS Agent and the PX4
|
||
v1.17.0/Gazebo Harmonic stock Ackermann rover in a fresh loopback-only
|
||
namespace, admitted the run only after world/startup/DDS-writer health markers,
|
||
and stopped both owned PGIDs with zero residue. The persisted run reached
|
||
`completed`, contains eight lifecycle events and zero control commands. This
|
||
run is now visible through UI-0: a direct internal read-only Control Station
|
||
surface backed by the same append-only qualification repository. It exposes
|
||
identity, terminal reason, provider pins, events, command count, artifact
|
||
metadata and the virtual-only authority boundary. Live pause/resume/step/reset,
|
||
canonical telemetry, PX4 command mapping, watchdog/failsafe evidence and the
|
||
accepted command path remain gated.
|
||
|
||
S1C commit `b7ccca3` adds the first registered live-worker vertical without
|
||
collapsing that boundary. An unprivileged worker agent owns the existing
|
||
application service and providers inside a fresh loopback-only network
|
||
namespace. Mission Core reaches it through a mode-`0600` Unix socket; React
|
||
reaches only same-origin Polygon API routes. The live browser panel can
|
||
start/stop the stock rover and show worker/run/provider status. UI-2 commit
|
||
`60e7916` adds a worker-gated `Полигон` header root and a browser-native
|
||
Three.js Ackermann Rover scene with orbit, zoom, pan, follow and reset. The
|
||
deep-link remains for exact-run review; it is no longer the primary operator
|
||
entry. Gazebo dynamic-pose ground truth remains explicitly `diagnostic`, not
|
||
accepted PX4/ROS 2 telemetry.
|
||
|
||
The exact D-only UI-2 generation passed 35 target tests and a real UI-driven run
|
||
`s1c-60e7916-20260724t182006z-2f164e`: both providers reached readiness, the
|
||
browser received live `VehicleState`, the run reached `completed` after
|
||
27.74 seconds of Gazebo time, and shutdown left no PX4/Gazebo/XRCE process
|
||
residue. The run contained zero commands, so this result does not yet prove
|
||
rover motion, PX4 command delivery, watchdog/failsafe behavior, navigation or
|
||
safety acceptance.
|
||
|
||
S1D exact code generation `49b0f47` closes the first virtual Ackermann command
|
||
and motion slice. React sends bounded speed/steering intents through the
|
||
same-origin Mission Core API and private worker socket. The worker journals each
|
||
accepted command before mapping it to PX4 rover-level Offboard setpoints:
|
||
`RoverThrottleSetpoint` is calibrated against the stock model's
|
||
`RO_MAX_THR_SPEED=3.1`, `RoverSteeringSetpoint` carries normalized steering,
|
||
and PX4 retains arming, Offboard loss handling and control allocation. No
|
||
`ActuatorMotors` or `ActuatorServos` topic is exposed or published by Mission
|
||
Core.
|
||
|
||
The immutable D-only generation passed 40 focused target tests. Its first cold
|
||
start completed in one request after 32.08 seconds. Run
|
||
`s1c-49b0f47-20260724t195259z-62edd2` proved 3.74 m straight motion, a curved
|
||
turn, reverse motion, 44.6 ms sampled command admission, a 250 ms TTL transition
|
||
(`ttl_expired_count` 4 -> 5), stable stopped pose and clean provider shutdown
|
||
with no PX4/Gazebo/XRCE residue. This does not accept calibrated closed-loop
|
||
speed tracking, Differential Rover, canonical PX4/ROS 2 telemetry,
|
||
pause/step/reset, heartbeat/offboard/link-loss cases, Nav2, perception or safety
|
||
behavior. Real actuator authority remains false.
|
||
|
||
Start the worker from the exact immutable D source generation:
|
||
|
||
```bash
|
||
sudo simulation/s1/worker-agent.sh <exact-40-character-mission-core-commit>
|
||
```
|
||
|
||
Then configure the Mission Core backend. When the worker status is available,
|
||
open `Полигон` from the Control Station header:
|
||
|
||
```bash
|
||
export MISSIONCORE_POLYGON_RUNS_ROOT=/mnt/d/NDC_MISSIONCORE/simulation/artifacts/s1/runs
|
||
export MISSIONCORE_POLYGON_WORKER_SOCKET=/run/missioncore-sim/worker.sock
|
||
export MISSIONCORE_POLYGON_WORKER_CONTROL=internal-virtual-only
|
||
export MISSIONCORE_COMMIT=<exact-40-character-mission-core-commit>
|
||
uv run uvicorn k1link.web.app:app --host 127.0.0.1 --port 8765
|
||
```
|
||
|
||
```text
|
||
http://127.0.0.1:8765/
|
||
```
|
||
|
||
The direct `?workspace=polygon-run&run=<qualification-run-id>` URL remains a
|
||
compatible evidence deep-link. The archive API remains GET-only. The worker API
|
||
adds status/live GETs and explicit start, command and stop POSTs; POSTs remain
|
||
`403` unless the backend has the `internal-virtual-only` gate and an exact
|
||
commit. Missing configuration, transport drift and corrupt evidence fail
|
||
closed. The browser receives no Unix socket, D root, artifact bytes, PX4
|
||
endpoint or direct provider command.
|
||
|
||
See the [Polygon product/SRS](docs/12_SIMULATION_POLYGON_PRODUCT_AND_SRS.md),
|
||
[ADR 0015](docs/adr/0015-simulation-polygon-qualification-boundary.md),
|
||
[ADR 0016](docs/adr/0016-distributed-product-edge-and-worker-topology.md) and the
|
||
[SIM S0 worker runbook](docs/runbooks/SIM_S0_AI_WORKER.md). The live-worker
|
||
launch and acceptance procedure is in
|
||
[SIM S1C live-worker runbook](docs/runbooks/SIM_S1C_LIVE_WORKER.md).
|
||
|
||
Mission Core remains a distributed browser product rather than a
|
||
platform-specific desktop executable. React runs in the operator browser;
|
||
Gateway/API and Control Plane run as long-lived services; Gazebo/PX4/ROS 2/Nav2
|
||
run headless on a registered simulation worker; and a future field vehicle runs
|
||
a headless onboard Linux Edge Agent with local-first evidence and
|
||
store-and-forward synchronization. Native Gazebo GUI is worker-local
|
||
diagnostics. The accepted live rover scene uses canonical state in Three.js;
|
||
Rerun remains the recorded spatial-evidence path. Field execution never depends
|
||
on streaming a simulator desktop.
|
||
|
||
## Mission Core Control Station and visualization adapters
|
||
|
||
The browser application is the universal Mission Core Control Station rather than
|
||
a K1-specific Foxglove launcher. Its fixed shell contains Center, Fleet,
|
||
Observation, Missions, Data and System; a seventh Polygon section is inserted
|
||
only while an admitted Simulation Worker is available. The K1
|
||
BLE/Wi-Fi/live workflow remains isolated as the first real device adapter. Its
|
||
React provisioning, acquisition/replay and diagnostic blocks live beside the
|
||
plugin manifest under `plugins/xgrids-k1/frontend`; the generic application
|
||
mounts them through one reviewed composition import.
|
||
|
||
Install, type-check, build and serve the complete local application from the
|
||
repository root:
|
||
|
||
```bash
|
||
uv sync --frozen --group dev
|
||
cd apps/control-station
|
||
npm ci
|
||
npm run test:unit
|
||
npm run typecheck
|
||
npm run build
|
||
cd ../..
|
||
uv run k1link serve
|
||
```
|
||
|
||
Open `http://127.0.0.1:8000`. The static application, REST/WebSocket control
|
||
plane and credential endpoint bind to loopback only. The current K1 adapter
|
||
still provides real CoreBluetooth discovery, one operator-triggered reviewed
|
||
BLE Wi-Fi provisioning write, a one-shot CoreWLAN K1-AP association path,
|
||
read-only MQTT live capture, native `.k1mqtt` and
|
||
reviewed-TSV replay, raw-first evidence storage, device-reported scan
|
||
time/distance/speed and measured preview metrics. Its frontend contribution now
|
||
also owns an optional spatial-scene control block, including acquisition phase,
|
||
telemetry and the stop action. Operator-manual acquisition still finalizes only
|
||
local reception; plugin-commanded v0.5.0 acquisition uses the separately gated
|
||
canonical K1 START/STOP dialogue.
|
||
|
||
Plugin v0.6.0 makes the local connection direction explicit. Bridge remains the
|
||
default and accepted product path; Direct Connect sends the reviewed station
|
||
provisioning frame for an already-running controller hotspot. Quick Connect
|
||
sends one separately reviewed AP-enable frame and can associate a prepared Mac
|
||
through CoreWLAN. Its device activation and prepared-host association were
|
||
physically accepted, but credential bootstrap is not portable: a clean host
|
||
cannot acquire the firmware-defined AP material from the reviewed BLE protocol.
|
||
Quick Connect therefore remains laboratory functionality, with no automatic
|
||
firmware download, iPhone extraction or hard-coded fallback. Each mode has a
|
||
distinct topology attestation and no automatic retry; see
|
||
[ADR 0013](docs/adr/0013-k1-local-connection-matrix.md).
|
||
|
||
The exact recovered `ModelingRequest` start/stop encoder, response correlator
|
||
and device-status state machine are installed in the operator-gated interactive
|
||
control path.
|
||
The plugin also contains the exact ten-request pre-START bootstrap recovered
|
||
from retained LixelGO traffic. Its first `DeviceInfoRequest` has no device ID;
|
||
the response supplies live vendor identity, serial, model, activation and
|
||
version facts for the BLE-selected K1. Static and wire evidence identify OpenAPI
|
||
as one private application-level value in the observed client, not a manual
|
||
per-scanner profile. The whole pre-START order and START/STOP payloads are
|
||
byte-matched offline. A fixed macOS Keychain loader now supplies the exact
|
||
application authority without env/file/browser fallback, and a five-batch
|
||
orchestrator passes all retained response barriers. The legacy shadow publisher
|
||
boundary remains structurally write-disabled and cannot call its sink. Its
|
||
dormant coordinator can hold authority
|
||
for at most 300 seconds and expiry/disarm/reprovision/acquisition/shutdown revoke
|
||
it together with the orchestrator. Plugin v0.5.0 installs a separate
|
||
physical-acceptance transport reproducing MQTT 3.1.1 `clean_session=false`,
|
||
keepalive 60, exact
|
||
response subscriptions, QoS2 completion and the five response-gated batches.
|
||
It consumes operation keys before publish and poisons unknown outcomes without
|
||
retry. One background owner services that socket through response-gated
|
||
connection, workspace, project, START, STOP and device-standby stages. The normal
|
||
UI collapses only the pre-START controls into one explicit operator intent;
|
||
navigation cannot emit a command. The first operator-present physical attempt on 2026-07-18 emitted only
|
||
the first six bootstrap requests, then failed closed while correlating the third
|
||
response batch; START was not emitted. A second one-shot attempt correlated all
|
||
ten pre-START requests and the START acknowledgement, but incorrectly collapsed
|
||
the whole exchange into 365 ms and ended the control lifecycle before the
|
||
retained post-START reads. K1 stayed in `SCAN_STARTING` for about 24 seconds,
|
||
reported `ALGORITHM_ERROR` twice, emitted no point/pose data and showed steady
|
||
red until an operator power cycle restored steady green. The collapsed executor
|
||
is now disabled. The offline acceptance executor maps connection requests 1–6,
|
||
workspace-entry request 7, project-prompt reads 8–10, user-confirmed START 11,
|
||
immediate status read 12 and state-gated reads 13–14 onto one continuously owned
|
||
MQTT session. No captured operator delay is a protocol timer: the final reads
|
||
wait for live `SCANNING`, project binding and `init_ready`, while STOP/save waits
|
||
for live unbound `READY`. Standalone START and STOP
|
||
are both rejected. The same socket is serviced throughout launch and STOP
|
||
confirmation, and no captured human delay is replayed as a protocol timer.
|
||
Plugin v0.5.0 wires this executor to the facade and plugin UI. The full staged
|
||
START/live/STOP/save cycle is physically accepted on the reviewed unit. START
|
||
and STOP each retain an explicit operator action; final local sealing now follows
|
||
the scanner's protocol-reported unbound READY without a redundant second click.
|
||
|
||
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.
|
||
|
||
Polygon is implemented as a capability-gated seventh section. When Mission Core
|
||
confirms an admitted Simulation Worker, its header button opens one direct
|
||
workspace with live 3D inspection and the server-owned qualification
|
||
repository. `?workspace=polygon-run[&run=<id>]` remains a compatibility
|
||
deep-link. Command and safety acceptance remain separate gates.
|
||
|
||
The Observation spatial workspace embeds the open-source Rerun Web Viewer
|
||
inside the Mission Core shell. It can open a compatible RRD over same-origin
|
||
HTTP or a Rerun gRPC/proxy source such as
|
||
`rerun+http://127.0.0.1:9876/proxy`; no external hosted viewer UI is used.
|
||
Dynamic point-cloud/camera composition and the live source contract are fixed in
|
||
[`ADR 0006`](docs/adr/0006-vendor-neutral-observation-sources-and-live-only-timeline.md).
|
||
The durable session catalog, recorded `session_time` scrubber and versioned
|
||
workspace-layout profile are fixed in
|
||
[`ADR 0008`](docs/adr/0008-durable-observation-sessions-and-workspace-layout.md).
|
||
|
||
The first K1 live session or adapter file-replay (`.k1mqtt`/reviewed TSV) in a
|
||
`k1link serve` process creates one local Rerun `RecordingStream`, starts its
|
||
gRPC/proxy server on TCP 9876 and publishes the resulting URL through
|
||
control-plane state. Later live/file-replay sessions reset their session-local
|
||
scene and metrics and reuse that process-wide stream. Saved observation sessions
|
||
do not reuse this listener: they open a private digest-bound cache-v9 RRD
|
||
generation over same-origin HTTP through Rerun's native incremental receiver.
|
||
Unless an operator has entered a manual source, the React application assigns
|
||
the applicable source to the embedded viewer. A generic host action clears a
|
||
previous manual/archive selection only after a plugin-owned live or file-replay
|
||
start succeeds; a failed start leaves the current scene mounted. A fail-closed
|
||
guard blocks operator source switches for every nonterminal acquisition. The
|
||
complete visual live runtime path is K1 MQTT → raw-first evidence capture →
|
||
bounded four-message latest-wins preview queue → explicitly injected K1
|
||
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. K1 `ModelingReport` status is consumed by an
|
||
explicitly injected pre-preview observer, so scan telemetry cannot evict point
|
||
or pose frames from that four-message queue.
|
||
|
||
The default Rerun blueprint shows a 12-second sliding accumulation of real point
|
||
frames. Product controls are connected for point size, intensity/height/distance
|
||
or available RGB coloring, Turbo/Viridis/Plasma/grayscale/custom palettes,
|
||
point and trajectory visibility, and the scene grid. The first disk action saves
|
||
and restores the versioned spatial layout without mutating sensor evidence.
|
||
Saved native point/pose sessions are materialized losslessly into private,
|
||
digest-bound RRD recordings and can be played, paused and scrubbed on a
|
||
zero-based `session_time` timeline. New captures durably publish a clock origin
|
||
before camera production, retain a transport-scoped provisional envelope and
|
||
atomically point their summary to a content-addressed session envelope after all
|
||
producers stop. That session envelope becomes the real RRD origin/end range;
|
||
archived `ModelingReport` distance, speed and scan time are logged as scalar
|
||
time series. No synthetic point cloud, trajectory, camera frame or latency
|
||
value is generated.
|
||
|
||
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
|
||
cloud and zero decode errors. The later RTSP camera preview is available through
|
||
the generic floating observation windows and new acquisitions archive its fMP4
|
||
segments independently of browser delivery. Historical sessions recorded before
|
||
that archive contract contain no video. A real archived session containing one
|
||
camera plus point cloud has not yet passed physical shared-timeline playback;
|
||
the implementation and test contract do not close that hardware gate. Rerun
|
||
`capture_time` and the camera index use Mac receive/arrival timestamps, not
|
||
proven K1 sensor timestamps or a photon-to-screen measurement.
|
||
|
||
The old Foxglove implementation is retained only in
|
||
`src/k1link/device_plugins/xgrids_k1/viewer/foxglove_bridge.py` and its regression tests. The current
|
||
live/replay runtime does not start it or use TCP 8765. The
|
||
[live viewer runbook](docs/06_K1_LIVE_VIEWER.md) records the active Rerun path and
|
||
its timing/security boundaries; the frontend contract is documented in
|
||
[`apps/control-station/README.md`](apps/control-station/README.md).
|
||
The host storage layout, recovery rules, replay API and operator path are in
|
||
[`docs/09_OBSERVATION_SESSIONS.md`](docs/09_OBSERVATION_SESSIONS.md).
|
||
|
||
The FastAPI application and credential endpoint bind to loopback, but the Rerun
|
||
gRPC server currently binds TCP 9876 on all network interfaces even though its
|
||
reported source URL contains `127.0.0.1`. It has no connector-level
|
||
authentication or TLS. Use it only on a trusted laboratory LAN, do not expose
|
||
9876 to the public Internet or a cellular WAN, and add an authenticated secure
|
||
proxy before any remote deployment. Stopping acquisition keeps the local scene
|
||
server and its URL available for the next session. Stop `k1link serve` to close
|
||
the listener and release its retained memory.
|
||
|
||
`doctor` is intentionally non-invasive. It checks the local Python environment
|
||
and reports external tools; it does not request Bluetooth permission, scan the
|
||
LAN, touch the K1, alter Homebrew, or change capture permissions.
|
||
|
||
The implemented laboratory commands include:
|
||
|
||
```bash
|
||
uv run k1link ble scan --duration 30 --out sessions/<id>/captures/ble.json
|
||
uv run k1link ble gatt-dump --device <corebluetooth-uuid> \
|
||
--out sessions/<id>/captures/gatt.json
|
||
uv run k1link ble wifi-configure --device <corebluetooth-uuid> \
|
||
--profile xgrids-k1-fw3-wifi-v1 --write-mode with_response \
|
||
--confirm-write --out sessions/<id>/captures/wifi.sensitive.json
|
||
uv run k1link net snapshot --out sessions/<id>/captures/network.json
|
||
uv run k1link net mqtt-capture --host <confirmed-private-k1-ip> \
|
||
--confirm-owned-device --duration 180 \
|
||
--out sessions/<id>/captures/mqtt-run
|
||
uv run k1link analyze mqtt-streams \
|
||
--capture sessions/<id>/captures/mqtt-run/mqtt.raw.k1mqtt \
|
||
--out sessions/<id>/analysis/mqtt-streams.summary.json
|
||
```
|
||
|
||
On macOS the BLE scan is active CoreBluetooth discovery, but it does not connect
|
||
to or modify devices. `gatt-dump` connects and performs service discovery only.
|
||
`mqtt-capture` accepts only a literal RFC1918 target, uses a fixed report-topic
|
||
allowlist, never publishes and never reconnects. It writes a length-framed raw
|
||
file, JSONL metadata and an integrity summary with mode `0600`.
|
||
|
||
Session output is sensitive and ignored by Git. It can contain device identity,
|
||
trajectory, mapped interiors and local addressing even when no credentials are
|
||
present.
|
||
|
||
## Documentation
|
||
|
||
- [Technical audit](docs/00_TECHNICAL_AUDIT.md)
|
||
- [Implementation gates](docs/01_IMPLEMENTATION_PLAN.md)
|
||
- [First lab runbook](docs/02_FIRST_LAB_RUNBOOK.md)
|
||
- [Artifact and secret policy](docs/03_ARTIFACT_POLICY.md)
|
||
- [Reviewed BLE Wi-Fi profile](docs/04_K1_WIFI_PROVISIONING_PROFILE.md)
|
||
- [Verified MQTT stream profile](docs/05_K1_MQTT_STREAM_PROFILE.md)
|
||
- [Live console and embedded Rerun runbook](docs/06_K1_LIVE_VIEWER.md)
|
||
- [Mission Core monorepo and plugin boundary](docs/07_MISSION_CORE_MONOREPO.md)
|
||
- [Owner-controlled LixelGO/iPhone observation](docs/08_LIXELGO_IPHONE_OBSERVATION.md)
|
||
- [K1 application-control physical acceptance](docs/lab/003_K1_CONTROL_ACCEPTANCE_20260718.redacted.md)
|
||
- [Observation sessions, playback and workspace layout](docs/09_OBSERVATION_SESSIONS.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)
|
||
- [Plugin SDK v0alpha2 and experimental device lifecycle](docs/adr/0004-plugin-sdk-v0alpha2-and-experimental-device-lifecycle.md)
|
||
- [Owner-controlled LixelGO observation decision](docs/adr/0005-owner-controlled-lixelgo-iphone-observation.md)
|
||
- [Vendor-neutral live observation sources](docs/adr/0006-vendor-neutral-observation-sources-and-live-only-timeline.md)
|
||
- [K1 camera preview copy-remux gateway](docs/adr/0007-k1-camera-preview-copy-remux-gateway.md)
|
||
- [Durable observation sessions and workspace layout](docs/adr/0008-durable-observation-sessions-and-workspace-layout.md)
|
||
- [Device-plugin observation runtime and K1 extraction](docs/adr/0009-device-plugin-observation-runtime-and-k1-extraction.md)
|
||
- [Plugin-owned frontend device workflows](docs/adr/0010-plugin-owned-frontend-device-workflows.md)
|
||
- [Laboratory plugin runtime handshake and transport seam](docs/adr/0011-laboratory-plugin-runtime-handshake-and-transport-seam.md)
|
||
- [Device-bound K1 command authority](docs/adr/0012-device-bound-k1-command-authority.md)
|
||
- [Simulation Polygon qualification boundary](docs/adr/0015-simulation-polygon-qualification-boundary.md)
|
||
- [Distributed product, edge and worker topology](docs/adr/0016-distributed-product-edge-and-worker-topology.md)
|
||
- [Redacted live lab report](docs/lab/001_K1_LIVE_MQTT_20260715.redacted.md)
|
||
- [Canonical control and durable archive milestone](docs/lab/004_K1_CANONICAL_CONTROL_ARCHIVE_20260719.redacted.md)
|
||
- [Session manifest schema](schemas/session-manifest.schema.json)
|
||
- [Reference input provenance](docs/reference/README.md)
|
||
|
||
The two supplied source documents are retained unchanged under
|
||
`docs/reference/`. Corrections and decisions are recorded separately so their
|
||
provenance remains clear.
|
||
|
||
## Safety boundary
|
||
|
||
Allowed initial work is non-mutating discovery, standard device-information reads,
|
||
controlled notification listening, autonomous button operation, targeted
|
||
capture of traffic to or from the confirmed K1 address, and offline analysis of
|
||
owned artifacts.
|
||
|
||
The reviewed provisioning write requires its named profile and explicit operator
|
||
confirmation. The interactive application-control publisher is enabled only for
|
||
the exact K1/A4/FW 3.0.2 direct-LAN profile and only inside one operator-opened,
|
||
response-gated session; physical double-click remains the independent fallback.
|
||
The legacy shadow publisher remains structurally disabled. Any broader firmware,
|
||
topology, scanner-family or command surface requires its own evidence and
|
||
reviewed step. Random writes, fuzzing, brute force, firmware operations,
|
||
destructive file access and credential guessing remain out of scope.
|
||
|
||
Real captures, projects, router metadata, serials, credentials, maps, images,
|
||
and logs under `.runtime/`, canonical evidence roots and legacy `sessions/` are
|
||
ignored by normal Git. Redacted manifests and SHA-256 inventories are committed;
|
||
encrypted/replicated artifact storage remains a deployment decision.
|