Data Export Contracts¶
The HTTP surface other repos and external tools consume to pull recorded data out of WayPoint: replay export, an operational timeline streamed as NDJSON.
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 |
Membership activity provenance¶
PETRA 1.0 does not reconstruct membership history from a Zenoh invite/membership recorder. That legacy recorder, its startup subscription, and its compatibility catch-up path are retired. The sole authoritative history inputs are the verified connected Directory mutation receipt and Directory's audit-ledger event for each invitation, revocation, member upsert/removal, ownership transfer, channel activation, and retirement.
Web persists those inputs into the activity layer after verifying the Directory receipt
and matching its request/result digests. The replay/AAR view retains the mutation kind,
exact aggregate, committed version/time, result commitment, actor principal, and distinct
forwarding Web machine where present. It does not store an actor private key, capability,
opaque-addressing salt, or treat the activity row as reusable authority. A failed,
conflicting, or connectivity-required request may be represented as an audit attempt but
must not appear as a committed membership transition.
Removing or tombstoning a channel removes its current data-plane grants by complete-set omission; it does not erase earlier activity or already verified chat, voice, and position archive rows. Membership changes therefore remain visible in the operational record even though Web no longer subscribes to invite or membership transport streams.
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.
Where to read the code¶
| Concern | Location |
|---|---|
| Replay NDJSON export (service + stream) | web/app/domains/replay/replay_export.ts |