Skip to content

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 by seq — safe, it's the same channel updating. The writer stamps a monotonic seq per id; on conflict the highest seq wins. An offline device editing the same channel repeatedly increments locally and its latest (highest-seq) state wins on reconnect; a stale/lower seq is rejected.
  • Distinct ids never auto-merge. Two field devices creating "Net Alpha" offline produce two real channels (different UUIDs). Both survive as provisional field entries; 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, clear provisional, 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 / origin are bound to the verified principal (no unsigned "X created this").
  • Updates are sequence-monotonic per id; a stale/replayed seq is rejected.
  • field classification 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 channelcreate_voice / create_chat (operator, signals) — this is a registry write (origin=field).
  • Author / bless / retire planned entriesauthor_comms_plan (signals, admin).
  • Channel ownership / managementowner-of-entry or manage_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:

  1. common — extend the channel-def schema with origin, provisional, seq (+ confirm owner is carried); merge/seq rules.
  2. web — registry subscriber (verify + ingest field entries 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.
  3. Android — stamp origin=field on 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.