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
IdentityTokenas 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:
- The member's ORBAT callsign, if the projection has one for that principal.
- 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
parentset. 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
idwithseq+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_GRANTverify pattern in the security model — they must verify after the author's token expires). Catch-up returns the latestseqversion 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_CONTENTpattern). 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 newkinds without schema change. - Per-operation role-definition overrides — tracked in the authorization model.
- Phase as a first-class object —
phasestays 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):
common— record schemas: plan, report, report requirement, ORBAT entry/appointment; scope-key layout per unit.- 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.
- 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.