Skip to content

Operational ↔ tactical information flow

Status: Design (proposed) — 2026-07-03. Covers the operational-content distribution model (artifact store + manifest) and the flow of plans, reports, and ORBAT between the operational layer (web — PJHQ/TJHQ) and the tactical edge (Android devices).

See also: Comms-plan registry (the pattern this generalises), Authorization model (roles and capabilities), Security model (envelope verification and classification), and the UX flows (agreed screen-level journeys for this design).

Goal

  • Down + out: an operational HQ builds an operation (ORBAT, comms matrix, plans) on web and pushes it to every device it tasks. A team leader at the edge builds a plan on Android and one publish serves both his troop's bubble and the HQ above him.
  • Up: devices submit structured reports (sitreps) that roll up per unit on web; commanders can task reports and see compliance.
  • Rhythm: both layers plan and report multiple times a day — records are cheap, versioned, and offline-first.

The two-plane rule

Content splits by weight, not by direction:

Plane Carries Transport Properties
Record plane Plans, reports, report requirements, ORBAT entries, channel defs — small structured signed records Zenoh gossip (AuthEnvelope-signed) Instant, offline-first, bubble-native, web ingests as store of record
Blob plane Map packs, KMZ overlays, imagery, order documents — large static artifacts HTTPS pull from web's artifact store Hash-addressed, deduped, resolved via per-principal manifest

Records reference blobs by sha256; devices resolve the bytes over HTTPS when they have reach. Web SQL is the store of record for all structured content; Android holds gossip-fed projections (no SQL). Field-origin records flow up the same plane and are ingested by web as provisional — the comms-plan registry reverse flow, generalised to every record kind.

flowchart TB
  subgraph Web["Web — operational layer (PJHQ/TJHQ)"]
    SQL[(store of record<br/>operations, ORBAT, plans, reports)]
    ART[(artifact store<br/>sha256-deduped blobs)]
    MAN[per-principal manifest]
    SQL --- MAN
    ART --- MAN
  end
  subgraph Edge["Tactical edge (Android)"]
    TL[team leader device]
    TR[troop devices]
    TL <-->|peer bubble| TR
  end
  SQL -->|"publish signed records<br/>(plans, ORBAT, requirements)"| TL
  SQL -->|records| TR
  TL -->|"field records up<br/>(plans, reports) — ingested provisional"| SQL
  MAN -->|"HTTPS pull: blobs by sha256<br/>(IdentityToken bearer)"| TL

Blob plane: artifact store + manifest

  • Flat artifact store on web: content-addressed by sha256, deduped — the same KMZ uploaded twice is one artifact.
  • Attachments bind artifacts to a scope: operation | plan | phase (phase is an optional label for now). One artifact can attach to several plans.
  • Manifest: web serves one per-principal operational content manifest — entries {kind: overlay | map-pack | plan | comms-matrix, scope, name, artifact URL, sha256, classification label}. It resolves everything visible to that principal: their operation, their plans (per the visibility rule below), the active phase.
  • Delivery is pull-only over HTTPS. Blobs never ride Zenoh — no broadcast, gossip, or republish of artifact bytes.
  • Auth: the device presents its Directory-issued IdentityToken as a bearer; web verifies the Directory signature and expiry. Devices can also upload artifacts (e.g. a report photo) over the same authenticated channel; the record referencing the blob rides the record plane.
  • The Directory stays user management only — no content, no operation concept.

ORBAT is the spine

The operation's ORBAT — operation → units (squadron/troop/section) → callsigns (principals) — is the single grouping structure everything else keys off:

ORBAT drives How
Publish scopes Each unit gets a key-space subtree; plans and reports address unit scopes
Comms matrix Auto-suggested nets per unit + a command net; a signaller edits from there
Plan targeting A plan's tasked units define its distribution set
Report rollup Reports aggregate up the unit tree
  • Commander = appointment, not a token role. Token roles (authorization model) stay global capability grants; the ORBAT records position. Team-leader powers — author a plan for the unit, task subordinates, submit the unit report — derive from the appointment. Appointments change mid-operation with an ORBAT edit; the Directory is never involved and never learns unit structure.
  • Edge can form provisional teams. A field callsign creates an ad-hoc team (an ORBAT record, origin=field, provisional) — rendered immediately in the bubble, ingested by web, blessed or retired by an admin exactly like field channels in the comms-plan registry.

Callsign — assigned, not self-declared

A callsign is never entered by the operator. It is assigned when web adds a principal to an ORBAT unit as a member (orbat_unit_members.callsign, edited in the ORBAT builder) — uppercased, max 32 chars, unique case-insensitive per operation among planned (non-provisional) memberships. It rides the existing OrbatRecord.member_callsigns field on the wire; there is no separate callsign message or self-service entry point.

Every device resolves its own tactical labels, keyed by principal_id, from its gossip-fed ORBAT projection — a fallback chain, never a stored label:

  1. The member's ORBAT callsign, if the projection has one for that principal.
  2. Otherwise the principal's shortId, uppercased 8-hex — never the Directory display_name (deliberately excluded from tactical labels — OPSEC; it still appears in the separate "CALLSIGN · Operator Name" chat suffix, which is not a tactical label).

Snapshot vs live. Most surfaces — COP labels, chat, voice speaker badges, the command-and-control issuer panel, device tasking dialogs — resolve live off the current ORBAT projection, so a re-assignment or a callsign edit is visible immediately everywhere. Two surfaces snapshot instead, because they are records of a moment rather than a live view:

  • SitReps stamp the author's resolved callsign at compose time (ReportRecord.callsign) — durable, kept even if the ORBAT changes later.
  • Web replay rows snapshot at ingest time (a resolver backed by a 30 s TTL cache), so a replay reads as it did at the time it was recorded.

Provisional-team merge. If, on bless/merge, a field-formed provisional unit's member callsign collides with an existing planned callsign, the colliding callsign drops to null on merge (first-merged-wins) rather than failing the merge or silently overwriting the planned assignment.

Deprecated on the wire. IdentityToken.callsign is still issued by the Directory but unread by clients for tactical labelling — see pki.md. TacNetMessage.device_callsign and VoiceDatagram.sender_callsign are reserved. Directory login/extend now send device_callsign empty (Android) or "web" (web) — the field's fate (repurpose as a device-admin name, or remove) is an open follow-up.

Plans — down and out

Web authoring flow (operational): create operation → build ORBAT + appoint commanders → accept/adjust the auto-suggested comms matrix → author a plan → publish. Publishing writes the plan record to the record plane (tasked units' scopes) and registers its attachments so tasked principals' manifests list the blobs.

Android authoring flow (tactical) — the 7 Questions estimate. A guided flow, one card per question, whose answers are mostly references to things already on the device:

Question Captured as
Q1–2 — situation, mission Text + reference to the received parent plan
Q3–5 — effects, actions, resources Tasks assigned to callsigns / sub-units picked from the ORBAT
Q6 — synchronisation Ordered timeline of those tasks
Q7 — control measures Drawings on the map — boundaries, report lines, FUPs are ordinary draw-plane content; the plan record binds their ids

Plan record shape:

Field Meaning
id Stable plan id — the merge key
parent Parent plan id (empty for a top-level operational plan)
scope The authoring unit — defines the distribution subtree
mission / situation Text
tasks[] {unit or callsign, task text, timing}
controlMeasures[] Draw-plane drawing ids
attachments[] {name, sha256} blob references
classification Label; operation ceiling applies
seq Monotonic per id — latest-wins versioning
  • One publish, two audiences. The team leader's publish lands in his troop's bubble immediately (peer mode — no HQ reach needed) and, whenever any path to web exists, web ingests it as a field-origin plan under the operation. The author never chooses "send up" vs "send out".
  • Plans form a tree mirroring the ORBAT. A commander receives a parent plan, extracts his unit's task, runs his own estimate, publishes a child plan with parent set. HQ can walk the tree: intent at the top, execution detail at the leaves.
  • Versioned, not append-only. Re-planning during the day republishes the same id with seq+1; latest wins on every device.
  • Visibility: own subtree + chain of command. A plan flows down to the units it tasks and up the command chain (superiors and web always see it). Flanking units do not receive it by default; a per-plan additive "also share to unit X" is deferred (see Not in scope).

Reports — up

A report is a small append-only record:

Field Meaning
unit / callsign Reporting unit and author
dtg Date-time group (auto-stamped)
location Auto-stamped from the device position
kind sitrep at v1; new kinds (LOCSTAT, CASEVAC 9-liner) are additive later on the same shape
status Green / amber / red
narrative Free text
attachments[] Blob references (e.g. a photo, uploaded via the blob plane)
inResponseTo Report-requirement id, when answering a tasked report
  • Published to the unit scope + the echelon above: bubble peers get free situational awareness; web ingests into the operation log and renders a per-unit feed plus a rollup (which units are green, which silent).
  • Append-only — a correction is a new report, never an edit.
  • Offline, the existing outbox holds it and forwards on reconnect.

Tasked reports. A commander publishes a report requirement record down his subtree: {kind, one-shot due DTG or cadence, scope}. The expected reporters are the commander-appointed units of that subtree, root included — a unit with nobody appointed is never asked, and is surfaced as an explicit ORBAT gap beside the matrix rather than dropped from view. The commander's device prompts when a report is due; web and the commander's device render a compliance matrix (reported / late / silent) per battle-rhythm period.

Security and enforcement

  • Every record is an AuthEnvelope-signed publish, verified per the security model — envelope crypto proves who.
  • The ORBAT projection proves may: receivers (devices and web ingest) check the verified author's appointment against the current ORBAT before treating a plan or report requirement as authoritative for a scope — the same receive-side pattern as the channel-definition integrity gate. A report needs the command appointment on the reporting unit itself — membership alone does not qualify, and a member-authored report is dropped. Authority is judged at ingest against the CURRENT ORBAT, not at the report's dtg_ms: a return authored by the then-commander but delivered after a re-appointment is refused ("the latest ORBAT always takes precedence"). A refused field return is an accountability event — someone in contact believed they had reported — so it lands on the operation's activity timeline (and therefore in replay and the export), never only in a server log.
  • The appointment rule itself is pinned across platforms by common's testdata/report_appointment_vectors.json: web and Android keep their own implementations, and the shared corpus is what stops the two ingest gates drifting apart.
  • Every record and artifact carries a classification label; the operation ceiling and the author's clearance bound it, as for field channels.
  • Web ingests field-origin records as provisional and never partially trusts a failed verification.

Replay and catch-up

"Replay" means two things here; the design serves both.

Device catch-up (record plane). A late joiner or reconnecting device queries catch-up per scope subtree — the same machinery channel defs use today:

  • Plans, ORBAT entries, appointments, report requirements are authority-bearing durable records (the DURABLE_GRANT verify pattern in the security model — they must verify after the author's token expires). Catch-up returns the latest seq version only — a device joining mid-operation receives the current orders and the current ORBAT, never the day's edit history.
  • Reports are append-only content (the DURABLE_CONTENT pattern). Catch-up is bounded to the current battle-rhythm window; deeper report history lives on web and is pulled over HTTPS, never gossip.

Operation replay (AAR / export). Web keeps every plan seq version and the full report log, and exposes them as new NDJSON layers in the replay export alongside the existing ones (sync_task is the precedent): plan, report, orbat_change, report_requirement. ORBAT state at any cursor T reconstructs by folding orbat_change rows — the same pattern as target_transition. Attachments export by reference, as voice audio does. An AAR consumer can then answer: which orders existed at 1400, who commanded a callsign then, which units had reported that period. The plan layer replaces web's sync_task layer outright — the synchronisation-matrix task store retires into plan records (see the UX flows, Journey 7) with no dual-export compatibility window.

Degraded operations

The bubble is fully functional with no HQ reach: the 7 Questions flow, plan publication to the troop, drawings, and report authoring all work peer-to-peer. Records queue in the outbox toward web; on reconnect they flow up, web ingests provisional field content, and manifests refresh for any new blob references. There is no separate offline mode — the same publish serves both states.

Not in scope (deferred)

  • Per-plan additive share ("also share to unit X") for flanking coordination — visibility stays subtree + chain of command until a real need appears.
  • Report template kinds beyond sitrep — the record shape admits new kinds without schema change.
  • Per-operation role-definition overrides — tracked in the authorization model.
  • Phase as a first-class objectphase stays an optional attachment label until phase transitions need machinery.

Sequencing (multi-repo)

Each behind its own PR set; the record plane reuses the comms-plan-registry machinery (signed defs, seq-monotonic merge, web ingest of field-origin records):

  1. common — record schemas: plan, report, report requirement, ORBAT entry/appointment; scope-key layout per unit.
  2. web — operation setup flow (ORBAT builder, appointments, matrix auto-suggest), artifact store + manifest endpoint, record-plane ingesters (plans, reports, ORBAT), report-compliance and plan-tree views.
  3. Android — manifest client + blob cache, 7 Questions flow, plan projection + ORBAT projection, report form + due prompts, appointment check on the receive side.

Appendix — prior art

How the established systems handle the same problem, and what this design takes from each:

System Mechanism Takeaway
TAK / ATAK CoT events are the ephemeral live plane; the Mission API / Data Sync keeps a server-side mission store clients subscribe to, with late joiners syncing missed changes on reconnect; Data Packages are blobs downloaded on demand Validates the two-plane split (mission feed ≈ record plane, data package ≈ blob plane, mission DB ≈ web store of record). Gaps this design closes: no ORBAT spine (flat server groups), plans are unstructured shared content, and mission sync requires the server — a pure peer mesh loses it, where the bubble here keeps working
SitaWare (Systematic) Headquarters does staff planning and plans/orders dissemination down to Frontline/Edge; SitaWare Tactical Communication replicates typed data over DDIL links, resynchronising latest-first on reconnect and skipping outdated data; plans share as MIP4 ZIP files when disconnected Closest echelon model. Latest-first resync is this design's latest-seq-only catch-up; MIP4 files ≈ the blob plane. Worth stealing later: per-information-type bandwidth priority (tracks > reports > plans > blobs). Its planning is staff-driven top-down — edge-authored plans flowing up are not its strength
Delta (Ukraine) Cloud-native backend, thin clients on any device, deployed across all echelons; situational-awareness centres fuse reports flowing up from units, drones, and vetted civilians Validates the web-native operational layer and the reports-up-then-vet flow (their hub vetting ≈ web ingest-provisional-then-bless). Weakness to avoid: cloud dependence — the offline tactical bubble is not its story, which is exactly what the record plane's peer mode covers

The combination this design targets — structured planning authored at the edge, one publish serving both the local bubble and HQ, fully functional disconnected — none of the three provides end-to-end.