Data Export Contracts¶
The HTTP surfaces other repos and external tools consume to pull data out of WayPoint. Two distinct families live here, and they must not be conflated:
- Replay export — a recorded operational timeline, streamed as NDJSON.
- Plan export — a current geometry snapshot, rendered as GeoJSON or KML.
This is the HTTP export contract. It is separate from
wire-protocol.md, which is the Zenoh mesh shape — nothing
here rides Zenoh. Terms are defined in ../architecture/glossary.md.
ORK below means operational record-keeping (automated archival of the authoritative operation record). It is one of the intended consumers, not a WayPoint component.
Replay export — recorded timeline (NDJSON)¶
Streams an operation's full recorded replay record for a window [from, to] as a
structured newline-delimited JSON bundle — one JSON object per line. WayPoint
provides the record; it does not fork timelines or run simulation. That is left
to external tools.
Scope and gating¶
- Operation-scoped to the active operation, and role-gated to
adminwithin that operation. A lower op role (signals/operator/observer) gets403; a platformsystem_adminbypasses the role gate. Unauthenticated →401. - Window clamped to the operation lifetime (
startDate→endDate, UTC), identical to every other/api/replay/*endpoint. A window fully outside the op's dates yields an empty bundle (header + summary only). Clamping is the sole op-scope for layers whose rows carry nooperation_id(chat, voice). - Response:
Content-Type: application/x-ndjson,Content-Disposition: attachment; …,Cache-Control: no-store.
Line structure¶
A meta header line, then zero-or-more record lines grouped by layer in a fixed
order, then a summary footer line.
schemaVersion is a constant 1 (EXPORT_SCHEMA_VERSION = 1). Consumers should
pin it. The meta window reflects the clamped window actually exported, not the
raw request.
layer is one of, in stream order:
layer |
Contents |
|---|---|
track |
operator track hits — callsign, SIDC, lat/lon, alt, heading, speed (m/s; converted to knots/km-h for display per SIDC — see glossary track_hits), timestamp |
feed |
external feed hits (AIS / ADS-B) — track-hit shape plus feed layer ids |
chat |
chat messages — channel, message id, sender, content, sent-at |
voice |
voice segment metadata only, plus an audioUrl reference (see below) |
drawing |
drawing shapes — shape id, type, payload, timestamp, deleted-at |
detection |
force-tracking / sightings — observed-at |
target |
targeting board entries — ref, name, priority, affiliation, SIDC, lat/lon |
target_transition |
target state-change rows — from/to state, created-at |
plan |
plan-of-record versions — every published seq per plan id (plan id, scope unit, title, H-hour, tasks, published-at) |
orbat_change |
ORBAT mutation timeline — per-unit snapshot rows (kind, unit fields, member ids, timestamp) |
report_requirement |
report tasking records — kind, target units, cadence / due DTG, seq versions |
report |
submitted reports — RAG, narrative, DTG, location, attachment refs (blob-plane sha256) |
activity |
audit log — kind, payload, timestamp |
State-at-T is reconstructed, not stored
target rows do not carry an explicit current state. A consumer
reconstructs the state at any cursor T by folding target_transition rows with
createdAtMs ≤ T (this mirrors the live replay board). Transitions are exported
up to the clamped to, so any cursor inside the window is reconstructable.
The plan and orbat_change layers follow the same fold-forward pattern:
the plan as HQ believed it at T is the highest seq with
publishedAtMs ≤ T per plan id, and the ORBAT at T is the latest
orbat_change row ≤ T per unit id (a deleted row removes the unit).
Both layers deliberately include rows from before the window start — a plan
published (or a unit formed) before the window is still active inside it.
The summary footer carries per-layer counts, a totalRecords, and a per-layer
truncated map. truncated[layer] === true means that layer hit its internal row
cap and is partial — the server also logs a warning, so truncation is never
silent. Treat any true as "narrow the window and re-export".
Voice audio is by reference only¶
Voice record lines carry segment metadata plus:
The export never inlines audio bytes. To pull a segment's audio, a consumer issues a separate request to that URL, which is itself op-gated (the segment's window must overlap the operation's clamped lifetime) and session-authenticated. This keeps the bundle from becoming an unauthenticated bulk-audio leak — audio always flows through the same op gate.
Streaming / memory¶
The handler pipes the service's exportForOperation async generator. Each layer
is fetched lazily, one at a time, and released before the next is read, so peak
retained memory is one layer's cap — never the whole operation. A multi-day
exercise therefore streams without exhausting memory.
Per-layer endpoints¶
Beyond the all-in-one bundle, each layer has a dedicated read endpoint under
/api/replay/:
hits, drawings, feed-hits, chat, detections, targets, voice,
voice/:id/audio, activity, reports, report-requirements,
plan-records, orbat-changes.
Intended consumers¶
- ORK — archive the bundle as the operation's authoritative record (tracks, comms, drawings, detections, targeting decisions, audit).
- External replay / simulation — third-party tools replay the exercise or seed a simulator from the bundle.
Plan export — current geometry snapshot¶
Exports the operation's current draw-plane geometry — a snapshot, with no time axis. This is distinct from the replay NDJSON timeline above: the source is the draw plane as it stands now, not a recorded sequence of events.
format |
Content-Type |
|---|---|
geojson |
application/geo+json |
kml |
application/vnd.google-earth.kml+xml |
scope=publishedexports the control measures of the current published plan records (the shapes bound to orders — the set that reached devices);scope=allexports every current drawing, clamped to the operation's lifetime window (drawing rows carry nooperation_id; the clamp is the op scope, as on the replay export).- The KML writer XML-escapes operator-supplied text (names, descriptions) as an injection guard.
- The response is streamed.
Stated consumers: QGIS, Google Earth, and ATAK — standard GIS / mapping tools that ingest GeoJSON or KML directly.
Where to read the code¶
| Concern | Location |
|---|---|
| Replay NDJSON export (service + stream) | web/app/domains/replay/replay_export.ts |
| Plan GeoJSON / KML export | web/app/domains/plan/export/plan_export.ts |