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.
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
- No playback → F06 already owns this (HLS player + snapshot hero + metadata + VLC/projector buttons). We build on it, not around it.
- 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. - 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:
ffprobea 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, nosrtprotocol). All SRT ingest runs in a Dockerffmpeg+libsrtsidecar (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_HOSTpattern 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
- F08 discovery —
ffprobea real feed's source: does it carry in-band KLV? Pick the primary provider from the answer. - F08 decoder —
misb0601.mjs+ unit tests vs ST 0601 conformance vectors. - F06 — HLS player detail view (verified on-mesh against a real feed).
- F08 pipeline —
klv-agent(in-band demux or side channel) →/api/telemetrySSE → telemetry HUD in the F06 theater. - F09 per-feed map — mini-COP in the theater from F08 telemetry.
- F09 global COP — all live platforms on one map; polish, export, a11y, layout.
- 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.