# 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 repository is intentionally migrating in stages. The current `src/k1link` package is the compatibility implementation of the first plugin path; vendor 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 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: `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 ``` ## 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 six architectural sections — Center, Fleet, Observation, Missions, Data and System — while the K1 BLE/Wi-Fi/live workflow remains isolated as the first real device adapter. 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, read-only MQTT live capture, native `.k1mqtt` and reviewed-TSV replay, raw-first evidence storage and measured preview metrics. Physical K1 scanning is still started and stopped by the verified double-click; 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 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 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. The complete live runtime path is K1 MQTT → raw-first evidence capture → bounded 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. 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. 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. 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/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//captures/ble.json uv run k1link ble gatt-dump --device \ --out sessions//captures/gatt.json uv run k1link ble wifi-configure --device \ --profile xgrids-k1-fw3-wifi-v1 --write-mode with_response \ --confirm-write --out sessions//captures/wifi.sensitive.json uv run k1link net snapshot --out sessions//captures/network.json uv run k1link net mqtt-capture --host \ --confirm-owned-device --duration 180 \ --out sessions//captures/mqtt-run uv run k1link analyze mqtt-streams \ --capture sessions//captures/mqtt-run/mqtt.raw.k1mqtt \ --out sessions//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) - [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) - [Redacted live lab report](docs/lab/001_K1_LIVE_MQTT_20260715.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. Application command publishing remains disabled: physical double-click is the verified start/stop mechanism. Any future MQTT publisher, router configuration change or new BLE write 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.