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 Directory-authorized plan entry materializes as a live channel (top-down), and a connected field channel activated under Directory authority flows back into the plan (bottom-up), flagged. An offline local draft is not part of this registry until activation succeeds.

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 requests a channel on the hoof → Directory activates it while connected → 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 activated channels from the registry; keeps an offline new-channel draft local and visibly unactivated until Directory activation. 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. A connected device requests channel activation from Directory. Once Directory issues the exact publish/query, membership, and ownership authority, the device gossips the sealed definition (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. An offline device may prepare and edit a visibly unactivated local draft, but it emits no shared definition and cannot present the channel as active. The user explicitly retries activation after reconnecting; no authority-change outbox queues or claims success.

Reconciliation

  • Same activated id: last-writer-wins by seq — safe, it is the same Directory-authorized channel updating. The writer stamps a monotonic seq per id; on conflict the highest seq wins and a stale/lower seq is rejected. Shared lifecycle, membership, or ownership changes require Directory connectivity; an offline local-draft edit has no shared sequence or authority effect.
  • Distinct activated ids never auto-merge. Two connected field devices may activate separate channels called "Net Alpha". Both survive as provisional field entries; the matrix flags the name clash and a signaller reconciles. Two offline drafts remain local and unactivated until each is explicitly retried and authorized after reconnect.
  • Signaller actions (author_comms_plan, with Directory reachable): bless (field → planned, clear provisional, re-own to the operation), retire, or rename.
  • Retire leaves a tombstone. Directory-authorized retirement 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, these operations require Directory connectivity and exact Directory-issued authority:

  • 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 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 — model the local unactivated draft and explicit retry; stamp origin=field only after Directory activation; 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.