Comms-plan ↔ live-channel convergence (registry)¶
Status: Design (proposed) — 2026-06-15. The promoted "comms-net realtime publication" roadmap line. Implemented per-repo later (see Sequencing).
The comms plan (web's "comms matrix") and the live voice/chat channels are two views of one artefact, not two systems sharing the noun "channel". This design makes them converge: a plan entry materializes as a live channel (top-down), and a channel created on the hoof flows back into the plan (bottom-up), flagged.
See also: Authorization model (the capabilities that gate these writes), the channel-definition integrity gate (the receive-side last line this builds on), and the operational-tactical flow (which generalises this registry's dual-store + field-ingest pattern to plans, reports, and ORBAT).
Goal¶
- Planned (top-down): a signaller authors a net in the plan → it gossips → the channel materializes on every device.
- Field (bottom-up): an operator/signaller creates a channel on the hoof → that creation flows back into the plan as a provisional entry a signaller can later formalize.
- One signed, gossiped channel registry per operation; live channels are a projection of it.
Topology (dual store + sync)¶
| Holder | Role |
|---|---|
Web SQL (comms_nets / members) |
The operation comms-plan of record — authoritative for planned entries, plus ingested field entries. The matrix reads/writes it. |
| Gossip plane (sealed channel defs) | The live channel bus — distribution of planned channels + the source of field-origin entries. Server-blind. |
| Android | Gossip-only projection — materializes channels from the registry; stamps origin=field on field creation. No SQL. |
A web-side reconciler bridges the two directions. The forward half already exists (the comms provisioning bridge); the reverse half (field → SQL ingest) is new. The server never sees channel content (sealing preserved).
flowchart LR
subgraph Web
M[Comms matrix] --- S[(comms_nets SQL<br/>plan of record)]
end
S -- "planned: publish sealed def" --> G((gossip<br/>channel bus))
G -- "field: verify + ingest provisional" --> S
G --> AND[Android<br/>projection]
G --> DEV[every device]
Entry shape¶
One shape, mirrored as a SQL row and a sealed gossip channel def:
| Field | Meaning |
|---|---|
id |
Stable VOICE-<uuid> / CHAT-<uuid> — the merge key |
netId |
The comms-net this channel belongs to |
kind |
voice | chat |
name |
Display name |
owner |
Verified principal (field) or the operation (planned) |
origin |
planned | field |
provisional |
true until a signaller blesses a field entry |
classification |
The channel's classification label |
seq |
Monotonic per id — drives last-writer-wins on the same channel |
Flows¶
Forward — planned. Matrix authors/edits a net → SQL → reconciler publishes
the sealed def to gossip (origin=planned, owner = operation, seq+1) → all
devices materialize. This is the existing provisioning bridge plus the new
fields.
Reverse — field. Any device creates a channel → sealed def gossiped
(origin=field, owner = creating principal, signed AuthEnvelope) → web's
registry subscriber verifies the envelope and upserts a provisional
field entry into SQL → it appears in the matrix as provisional. Web is the
sole ingester (the plan of record); Android stays a projection.
Reconciliation¶
- Same
id: last-writer-wins byseq— safe, it's the same channel updating. The writer stamps a monotonicseqperid; on conflict the highestseqwins. An offline device editing the same channel repeatedly increments locally and its latest (highest-seq) state wins on reconnect; a stale/lowerseqis rejected. - Distinct
ids never auto-merge. Two field devices creating "Net Alpha" offline produce two real channels (different UUIDs). Both survive as provisionalfieldentries; the matrix flags the name clash; a signaller reconciles. No silent drops — nothing's members/voice are orphaned. - Signaller actions (
author_comms_plan): bless (field → planned, clearprovisional, re-own to the operation), retire, or rename. - Retire leaves a tombstone. Retiring a channel publishes a gossip tombstone (the existing delete path) and keeps a retired marker in the plan for audit — it does not vanish. Devices drop the live channel; the plan records that it existed and was retired, by whom.
Security¶
The registry is a signed gossip artefact — verified like the existing channel defs, not trusted blindly:
- Every entry write is a signed
AuthEnvelope;owner/originare bound to the verified principal (no unsigned "X created this"). - Updates are sequence-monotonic per
id; a stale/replayedseqis rejected. fieldclassification inherits the operation ceiling and may not exceed the creator's Directory clearance — same invariant as the classification banner / channel-def gate in the security model.- Reuses the receive-side channel-definition integrity gate (owner / global-admin, raise-only classification) as the last line.
Authorization¶
Per the authorization model:
- Create a field channel →
create_voice/create_chat(operator, signals) — this is a registry write (origin=field). - Author / bless / retire planned entries →
author_comms_plan(signals, admin). - Channel ownership / management →
owner-of-entry ormanage_channels. The per-entry channel-admin grant is the same mechanism as the per-operation role override the authz model defers.
Sequencing (multi-repo)¶
Depends on browser/Android realtime transport being live (the
zenoh-plugin-remote-api deployment gate). Each behind its own PR:
common— extend the channel-def schema withorigin,provisional,seq(+ confirmowneris carried); merge/seq rules.- web — registry subscriber (verify + ingest
fieldentries to SQL), reconcile UI (name-clash flag, bless/retire/rename), matrix on the dual store; extend the forward provisioning bridge to stamp the new fields. - Android — stamp
origin=fieldon create; project the registry; verify + merge on the receive side (sibling of the existing membership subscriber).
Not in scope¶
- Per-operation role-definition overrides (tracked in the authorization model); the per-entry channel-admin grant here is the same mechanism and may land together.