R2D2-OPS-Kuma/.settings/ops/PLAN.md
Joshua Belke 8af48d5c08 feat(ops): media detail views, live recording, and KLV probe
Ops console upgrade — F06/F07/F08 land together because they wire through
the same serve.mjs routes and index.html render path:
- F06 native detail: expandable Kuma-style row detail (facts + heartbeat
  history + ping sparkline), /api/monitor, HLS player via vendored
  hls.min.js served at /vendor/hls.min.js (offline, no CDN)
- F07 recording: recorder.mjs per-feed state machine (idle->live->dead->cut),
  /api/record/{start,stop,status}, storage gauge + recordings panel,
  writes to tuf8tb
- F08 KLV: klv-probe.sh discovery helper for MISB ST 0601 in the source TS
- PLAN.md: umbrella C2-FMV console arc

ops-server.log (runtime) gitignored.
2026-07-07 10:19:15 -04:00

7.1 KiB

Ops Feed → C2-grade FMV console — umbrella plan

Status: proposed · This is the north-star doc that ties the per-feature plans (../features/NN-*.md) into one arc: turn the read-only status wall into an industry-standard Full-Motion-Video + telemetry + map console for drone/video operations, on the closed NetBird/Tailscale mesh.

It does not renumber or restate the existing plans. It sequences them and adds the two new features that close the "C2" gap:

# Feature Doc State
01 Ops Feed (search/filter companion) features/01-search-and-filter.md done
02 holovids CSV enrichment features/02-holovids-enrichment.md done
03 Alerting & escalation features/03-alerting-and-escalation.md planned
04 SLO rollups & mesh-aware muting features/04-rollups-and-mesh-muting.md planned
05 Deep stream probes & liveness features/05-deep-probes-and-export.md partial
06 Native media detail + HLS player features/06-native-media-detail-views.md planned
07 Live-triggered recording to disk features/07-live-stream-recording.md building
08 KLV/MISB telemetry pipeline features/08-klv-telemetry.md new
09 C2 map & global common operating picture features/09-c2-map-cop.md new

The three gaps to "C2 FMV board" — and who closes them

  1. No playback → F06 already owns this (HLS player + snapshot hero + metadata + VLC/projector buttons). We build on it, not around it.
  2. No telemetry → F08 (new). Decode KLV / MISB ST 0601 (platform lat/lon/alt, heading/pitch/roll, sensor az/el, slant range, frame-centre, footprint corners) from the source TS and publish it to the existing nanomq broker (reporting-bridge-1), so the F06 HUD, the F09 map, and the reporting historian (questdb/superset) all subscribe independently. We ingest the KLV — we do not strip it.
  3. No map → F09 (new). MapLibre GL over offline vendored tiles — a per-feed mini-COP in the F06 theater and a global common operating picture view beside table/wall.

Testing posture — real feeds, no synthetic fixture

Verification is done against the real assets on the mesh (operator-directed — no demo drone / synthetic stream). Practical consequences:

  • Dev + verify runs on-mesh (or wherever the real feeds are reachable), because off-mesh every projector HLS 404s and the srt://100.85.x… sources are mesh-only (measured 2026-07-07, F06/F07). There is no off-mesh video/telemetry to test against, and we are not manufacturing one.
  • The first task is discovery, not code: ffprobe a live feed's SRT/TS source to confirm whether it carries an in-band KLV / MISB 0601 data stream at all. That answer decides F08's primary provider (in-band demux vs a side channel such as the existing NodeRed fleet).
  • The decoder is still unit-tested against static MISB ST 0601 conformance vectors — those are standard reference byte-arrays from the spec, not a demo feed; they're how you prove a KLV decoder is correct before trusting a live one.

Constraints that shape the build (measured)

  • KLV rides in the source TS, not the HLS. The SRT→HLS transcode drops the KLV data PID, so F08 decodes from the source (srt://… / origin TS/FLV), never from the memfs .m3u8.
  • Host ffmpeg has no libsrt (ffmpeg 8.0.1, no srt protocol). All SRT ingest runs in a Docker ffmpeg+libsrt sidecar (klv-agent), never on the host.
  • Where telemetry lives is mixed/unknown → F08's pipeline is provider-pluggable and auto-probes: in-band demux (srt-klv/ts-klv) if present, else a configured side channel (http-poll/mqtt/nodered).
  • Basemap = offline vendored PMTiles, served by the ops server with HTTP range support; MapLibre GL reads it via range requests. Fully air-gap-safe, no CDN.
  • Invariant kept: no credential (and no raw SRT) ever reaches the browser; every new proxy is host-allowlisted (extends the SNAP_HOST pattern from F05).

Target architecture

                 ┌──────────────────────── ops server (serve.mjs) ────────────────────────┐
  Kuma  ◀socket▶ │ Kuma model · snapshot proxy+liveness (F05) · MQTT→SSE bridge (F08)      │
                 │ static: index.html + app/*.js + vendor/{hls,maplibre,pmtiles}           │
  browser ◀HTTP▶ │ /api/events · /api/telemetry/:id (SSE) · /api/telemetry (all) · /map/*   │
                 └───────────────▲──────────────────────────────────────────────────────────┘
                                 │ subscribe r2d2/telemetry/#          (browser never speaks MQTT)
                 ┌───────────────┴───────────────┐
                 │ nanomq  (reporting-bridge-1)   │──▶ reporting ingester → questdb → superset
                 │ MQTT bus  r2d2/telemetry/…      │        (history / analytics, free)
                 └───────────────▲───────────────┘
                                 │ publish state/raw/health (retained, QoS1, LWT)
                 ┌───────────────┴──────────────┐        real assets on the mesh
                 │ klv-agent (Docker,libsrt,F08) │◀── srt://100.85.x… / origin TS ── drones
                 │ ffmpeg demux data PID →        │        (in-band KLV) — or —
                 │ MISB 0601 decode → normalize   │◀── NodeRed / MQTT / HTTP ──────── side channel
                 └───────────────────────────────┘

Frontend stops being one monolithic HTML file: index.html shells in app/*.js modules and locally-vendored hls.js (F06), maplibre-gl + pmtiles (F09). serve.mjs gains a small static handler for app/ + vendor/ + range-served /map/basemap.pmtiles.

Sequence

  1. F08 discovery — ffprobe a real feed's source: does it carry in-band KLV? Pick the primary provider from the answer.
  2. F08 decoder — misb0601.mjs + unit tests vs ST 0601 conformance vectors.
  3. F06 — HLS player detail view (verified on-mesh against a real feed).
  4. F08 pipeline — klv-agent (in-band demux or side channel) → /api/telemetry SSE → telemetry HUD in the F06 theater.
  5. F09 per-feed map — mini-COP in the theater from F08 telemetry.
  6. F09 global COP — all live platforms on one map; polish, export, a11y, layout.
  7. QA/E2E hardening — Playwright against real live feeds (plausibility + region/coordinate cross-checks); load; /security-review.

Details, data models, verify steps and open questions live in each feature doc. This file is the map; the feature docs are the territory.