Skip to content

Getting data into the COP

There are two ways external data lands in the web app, and which one you use depends on what the data is — not on who produces it. Pick the path first, then follow its runbook.

You have… It is… Path Mechanism Runbook
Tracks (track_hits) high-volume ambient position feeds — AIS, ADS-B, raw radar tracks. Mostly neutral, no per-contact operational judgement. Feed ingestion NiFi writes directly to Postgres under a least-privilege role Provision feed ingestion
Detections / targets operational observations — a sensor/AI/human says "this specific thing, here, now, with this confidence/affiliation". Nominatable to the target board. Ingest API producer POSTs to the HTTP /api/feed/* API with a JWT Push detections & targets

The rule of thumb

Ambient background picture → tracks. Operational judgement → detections.

track_hits is the dumb backdrop: where is everything. It carries no affiliation, confidence, or provenance per row — the kind (ais/adsb/radar/geojson) lives on the feed's layer, and that's it. The moment a producer is asserting something operationally meaningful about a contact — an affiliation, a confidence, a classification, a signature match — that is a detection, and it belongs on the operational path (where it gets source_kind, confidence, affiliation, and can be nominated to a target).

The two paths are also technically distinct: feed ingestion is a direct-DB write (no HTTP, no token — a Postgres role); the ingest API is an authenticated HTTP POST (a signed JWT, scopes, revocation). They share no code.

Worked distinction — same sensor, different path

  • AIS vessel traffic → a track. It's ambient — thousands of neutral merchant vessels reporting position. Feed ingestion → track_hits, layer kind ais. No per-contact judgement.
  • Nightingale radar contactsdetections. Nightingale emits classified ContactResults (type match, signature, confidence) — that is operational judgement, so each contact is a detection (source_kind=sensor, an affiliation, a confidence), not a flat track. Ingest API → /api/feed/detections. See the push runbook for the Nightingale example end-to-end.

!!! note "Nightingale is mid-migration to the detections path" Today the NiFi ParseNightingaleToTrackHit processor still routes Nightingale to track_hits (a flat radar track with a fixed SIDC, discarding the type/signature richness). The target is ParseNightingaleToDetectionPOST /api/feed/detections. The NiFi-side change is outstanding; the web /api/feed/detections endpoint already exists.

See also