Proposal 070: Room — Generic Subject-Addressed Room Primitive¶
Based on:
doc/project/40-proposals/003-question-envelope-and-answer-channel.mddoc/project/40-proposals/005-operator-participation-room-policy-profiles.mddoc/project/40-proposals/009-communication-exposure-modes.mddoc/project/40-proposals/013-whisper-social-signal-exchange.mddoc/project/40-proposals/014-node-transport-and-discovery-mvp.mddoc/project/40-proposals/035-agora-topic-addressed-record-relay.mddoc/project/40-proposals/069-corpus.mddoc/project/40-proposals/072-capability-registry.mddoc/project/40-proposals/082-sensorium-interfaces.mddoc/project/60-solutions/008-agora/008-agora.mddoc/project/60-solutions/000-node/000-node.mddoc/project/60-solutions/011-whisper/011-whisper.mddoc/project/60-solutions/017-inter-node-artifact-channel/017-inter-node-artifact-channel.mddoc/project/60-solutions/023-artifact-delivery/023-artifact-delivery.mddoc/project/30-stories/story-002-federated-peer-learning.md
Status¶
promoted
Promoted to: doc/project/60-solutions/036-room/036-room.md
Date¶
2026-06-23
Executive Summary¶
The swarm has two divergent "room" notions today and no shared primitive:
- the answer room (P003, story-002) is currently a derived read-model
(
answer-room-metadata.v1) over a daemon execution record; its live many-participant form is deferred; - the association-room (P013) is an Agora proposal meta-signal
(
association-room-proposal.v1); its membership/message/event family is documented but unbuilt.
They share no opening artifact, no substrate, and no generic contract. Proposal 069 (Corpus) needs a real live multi-participant room and is the forcing function to fix this divergence rather than mint a third room family.
This proposal defines one generic, subject-addressed room primitive with two
explicit planes:
- a durable record skeleton — signed
room.v1,room-membership.v1,room-event.v1carrying identity, access list, and lifecycle, candidate substrate Agora (P035, the existing "shared record substrate"); - an ephemeral live message plane —
room-live-message.v1frames for synchronous, low-latency exchange that the protocol does not persist and does not treat as facts.
answer-room and association-room become projections of room.v1. Corpus and other
consumers ride the same primitive. A deliberately contracted transport layer (auth,
membership revocation, presence, retry, expiry, cleanup, sequence/replay, retention)
replaces the under-specified "Matrix/WSS as options" hand-wave. The firewall-proof
baseline is one relocatable WSS relay endpoint per active relay epoch, reached through
outbound TLS connections. Matrix remains an optional bridge profile and is never
imported as Room ordering, membership, or durable-history semantics.
Context and Problem Statement¶
Room is referenced by P069 as a prerequisite and implied by P003/story-002, but:
- there is no
room.v1/room-membership.v1/room-event.v1contract; - there is no agreed live transport, and the candidates (Matrix, WSS pub/sub) have very different retention, ordering, and presence semantics;
- the durable plane (if Agora-backed) has no defined topic namespace, signer authority, record kinds, ordering/high-water, replay, membership projection, or query attestation;
- without this, every room consumer (answer-room, association-room, Corpus) reinvents membership, access, and lifecycle.
Goals¶
- One subject-addressed room identity and signed membership/lifecycle records.
- A clear two-plane split: durable signed skeleton vs ephemeral live reasoning.
- A contracted live transport with auth, revocation, presence, retry, expiry, cleanup, sequence/replay, and retention rules.
- An Agora-backed durable plane with explicit topic namespace and authority.
- Re-express
answer-room(P003) andassociation-room(P013) as projections.
Non-Goals¶
- This proposal does not define Corpus reasoning, procurement, or settlement (P069).
- It does not make every participant publicly reachable and does not place STUN/ICE, NAT hole punching, UDP, or QUIC on the Room liveness path. One reachable WSS relay endpoint per active relay epoch is sufficient.
- It does not make Matrix a Room dependency. The implemented Matrix adapter is an optional bridge profile under the same authority boundary; Matrix DAG, room-state, and history semantics do not enter Room Core.
- It does not make the live chat durable; per-member capture stays a private option.
Decision¶
1. Two Planes¶
A room has exactly two planes:
- Durable record skeleton (signed, append-only): who the room is, who may join, what rights they hold, and the ordered lifecycle. This is the source of truth for membership and authority.
- Ephemeral live message plane: synchronous frames for reasoning/coordination. The protocol does not durably store them. One relay epoch MAY keep a bounded in-memory replay window for reconnect, but it creates no cross-epoch history. Any member MAY locally capture frames under its own classification and retention policy; capture is never a protocol obligation and never a shared fact.
2. Durable Plane over Agora¶
When the durable plane is Agora-backed (the recommended default; P035 is the existing
shared record substrate that already carries association-room-proposal.v1):
- Topic namespace:
orbiplex/room/v1/<authority>/<room-id>— the single canonical subject-key form (also used by record-kind consumers and the Implementation Recommendations), derived from the opener authority and the room id. - Signer authority: only the opener (or a delegated room authority) may sign
room.v1open/close and grant-issuingroom-membership.v1; members sign their own join/leave/acks. Authority changes (e.g. arbiter election in P069) are themselves signedroom-event.v1records. - Record kinds:
room.v1,room-membership.v1,room-event.v1, and, after Phase 6A,room-relay-endpoint.v1as Agorarecord/kindvalues. - Ordering / high-water: each room carries a monotonic
seq/no; consumers track a per-room high-water mark; out-of-order or gapped records are buffered or refused per policy, not silently merged. - Replay: the durable skeleton is fully reconstructable from the Agora topic; a lost local projection rebuilds from replay.
- Membership projection: a deterministic read-model folds membership/event records into "current members + rights + lifecycle"; it is a projection, never authority.
- Grant vocabulary: MVP grants are the closed set
speak,vote,answer,observe,actuate,moderate, anddelegate. A Room implementation MUST treat any other grant as an extension requiring an explicit schema/policy extension; arbitrary strings are not accepted at the security gate. P083-009 enactedactuateas the explicit collaborative Sensorium actuation extension; it never replaces exact Sensorium Interface authority or a current control lease. - Query attestation: membership/authority queries return a signed
room-membership-attestation.v1(subject, grants, lifecycle, signer, high-waterseq/no, source record refs), not a bare boolean, so a relying party can audit why a subject is considered a member. This follows the same attested-result pattern as Seed Directory bindings and Agora ingest attestations rather than inventing a new trust model: the attestation is a projection signed by the room authority over the durable records, never a new source of truth.
3. Live Transport Contract¶
The live plane MUST contract the following, regardless of substrate (Matrix or WSS pub/sub):
- Room creation: the opener creates the room (durable
room.v1) first; the live channel is bound to the room id and is meaningless without a durable skeleton. - Auth / join: a node joins only by presenting a room-scoped capability/invitation passport (reuse INAC invitation/capability passports, Solution 017/014). Ambient join is denied.
- Membership revocation: a signed
room-membership.v1revoke removes a member; the transport MUST drop the revoked member's live access promptly (bounded lag) and the revoke is durable. - Presence: presence (joined/ready/left/timed-out) is derived and ephemeral on the
live plane but anchored by durable
ready/leftroom-event.v1where it changes authority or budget accounting. - Retry / expiry / cleanup: live frames are best-effort and idempotent by
(room/id, from, seq/no); the room has a durableexpires-at; on expiry or close the transport channel is torn down through a bounded P055-style cleanup operation and any transport-side artifacts (e.g. Matrix room, WSS topic) are cleaned up. Cleanup timeout is degraded state, not silent success; the recommended MVP cleanup deadline is 30 seconds unless deployment policy tightens or extends it. - Sequence / replay: live frames carry a per-sender
seq/nofor local ordering and duplicate suppression. The protocol does not provide an authority-bearing cross-member total order on the live plane. P070 Phase 6A adds only a per-relay-epoch carrier order for bounded delivery and reconnect; durable and domain-specific signed sequences remain decisive. - Retention: live content is non-retained by contract. Matrix is not ephemeral by default: a Matrix-backed live plane MUST configure non-retention/redaction (server retention policy, message redaction on close, no public history) and MUST treat the Matrix room as a transient carrier, never as the durable skeleton. A WSS pub/sub fanout is naturally non-retained and is the simpler default for closed deliberations.
3A. Relocatable WSS Relay Epochs¶
The production WSS topology uses a simple firewall posture: every participant opens
an outbound wss:// connection, normally over TCP 443, to exactly one active relay
endpoint for the current relay epoch. Room does not require every node to expose a
listener. It also does not require UDP reachability, QUIC, STUN, ICE, or peer-to-peer
hole punching for liveness.
Relay placement is selected in this order unless room policy explicitly narrows it:
- the requester/opener node, when it advertises a currently reachable endpoint;
- another current room member with relay-capable readiness evidence;
- a federation relay service for the all-behind-CGNAT case.
For a node candidate, relay-capable readiness reuses a current signed
node-advertisement.v1 endpoint with endpoint/transport = wss and
endpoint/role = relay, optionally strengthened by node-address-attestation.v1 and
a bounded current reachability probe. It is not a new capability id. A federation
service candidate must supply equivalent operator-admitted endpoint/readiness evidence
under the same evaluator.
Candidate readiness is evidence, not authority. The active placement is established
only by a signed, append-only room-relay-endpoint.v1 fact issued by the current Room
authority or a delegated authority whose durable delegation includes relay/manage.
relay/manage is a closed Room authority-delegation scope carried by
room-event.v1; it is not a membership grant, a relay readiness flag, or a host
capability. Phase 6A provides typed delegation scopes, expiry, and revocation linkage
in the Room event contract and refuses endpoint facts outside that authority. The
first local activation surface is an operator-session action: it derives the signer
from the current local Room authority and does not accept caller-selected authority.
If relay management later becomes callable by a module or remote principal, its
capability id and ledger row must be registered before that delegated surface is
implemented.
The endpoint fact is necessary but not sufficient to authorize a dial. Every client still applies host-owned egress policy and Node Transport endpoint trust. The selected URL, relay subject, and placement must match the referenced advertisement, address attestation, or operator-admitted service evidence exactly. Room authority cannot turn an arbitrary URL into permitted host egress. The endpoint schema deliberately permits private, loopback, and link-local hosts because local and same-host relays are valid deployments; the dial-time egress and transport-trust gates, not schema shape, decide whether a concrete target is reachable from a given host.
The fact binds at least:
room/idand durable Roomseq/no;- positive monotonic
relay/epoch; relay/subjectandrelay/placement = requester | room-member | federation-service;- canonical
endpoint/url, restricted towss://for this profile; crypto/profile,ordering/profile, and issue/expiry; the signed Agora envelope owns optionalrecord/supersedeslineage;- bounded
selection/evidence-refsfor the advertisement, address attestation, service readiness, or probe result used during selection; these refs explain selection but grant no Room authority; - authority subject; the enclosing Agora record binds author/delegation evidence and the canonical-JSON signature.
At most one endpoint fact is active for an epoch. A conflicting fact for the same
relay/epoch is an authority conflict and fails closed. A newer valid epoch supersedes
the old carrier binding and resolves that relay-specific conflict. Generic durable
record conflicts remain pending until their existing operator/policy path acts; only a
strictly newer valid relay epoch may clear a same-epoch relay conflict. Supersession does
not reopen, replace, or fork the durable Room.
The relay assigns a positive monotonic relay/seq-no to every accepted ephemeral
delivery in one epoch. Client resume cursors are the tuple
(relay/epoch, relay/seq-no). This is a presentation and bounded-replay order only:
- sender-owned
seq/no, signed turn numbers, P083 source generations, lease epochs, and operation sequences remain the authority-bearing order; - a relay cannot mint membership, grants, turns, leases, or effects;
- failover starts a new epoch and never merges carrier histories through a DAG, CRDT, or cross-epoch total order;
- after failover, clients rebuild authority from Agora facts and domain read-models, then resume the new live epoch from its current snapshot or bounded buffer.
Carrier sequence detects gaps and supports bounded replay, but does not prove complete delivery. A malicious or failed relay can drop, delay, reorder before acceptance, or observe connection metadata. Domain operations that require an effect or receipt use their own acknowledgements, idempotency, durable facts, and retry policy. Once a newer endpoint fact is accepted, clients refuse frames and resume cursors from every older relay epoch.
Endpoint discovery cannot depend on the failed relay. The endpoint fact is a durable Room/Agora record and may also be delivered through Artifact Delivery mailbox paths to known members. The mailbox accepts endpoint facts only for an already open local Room; unknown or unopened Rooms are rejected synchronously and are not buffered by the acceptor. Artifact Delivery carries the control-plane fact, not live Room frames.
The relay's permitted decisions are deliberately narrow: validate the presented membership attestation, bind the authenticated subject to the session, enforce frame, connection, and buffer caps, assign carrier sequence, and drop sessions after current revocation or expiry becomes visible. It does not evaluate domain policy or issue grants. Connected presence is an ephemeral hint and never membership evidence.
Operational defaults for this profile are:
- WSS over TLS/TCP 443; QUIC may be a measured optimization but never a dependency;
- ping/keepalive approximately every 25 seconds, configurable within a bounded local policy so common NAT idle timers do not silently remove sessions;
- reconnect with exponential backoff and jitter;
- resubscription with the last
(relay/epoch, relay/seq-no)cursor and a closedresume/reasonofrecoveryorreconnect; only the latter increments reconnect diagnostics; - one bounded in-memory replay window, with typed
cursor-expiredfollowed by current snapshot/read-model refresh when the requested frame is no longer retained. A client commits the replacement checkpoint only after that refresh succeeds; failed refresh leaves the prior checkpoint current and retryable.
Room liveness belongs to the relay epoch, not to the Room. A Room remains open and its durable facts remain replayable while no relay endpoint is reachable.
3B. Crypto Profiles Follow Relay Placement¶
The endpoint fact carries an explicit crypto profile from the first implementation:
member-visible-tls-v1is the baseline. The relay is a current Room member whose grants and Room policy permit receiving the live content; TLS protects each participant-to-relay hop. This profile does not claim end-to-end secrecy from the relay, forward secrecy, or post-compromise security. A federation relay service may use this profile only when it is admitted as such a content-visible member.sealed-sender-key-v1is the implemented profile for a federation relay that is not a Room member. Each active sender distributes sender-key material for the relay and membership epoch through recipient-private pairwise X25519/HKDF/XChaCha20-Poly1305 packages signed with Ed25519 domain separation. Encrypted frames bind room, relay epoch, membership epoch, sender, sender sequence, ciphertext digest, and signer, so possession of another member's decryption material does not grant impersonation. Every membership fact advances a monotonic membership high-water; join, leave, grant, or revoke therefore fences prior ciphertext and rotates active sender-key material with bounded O(n) fan-out per active sender. Runtime relay transitions serialize projection refresh, publication, resume, and lifecycle changes so a frame cannot be admitted through an interleaving between membership authorization and carrier-state rotation. The accepted durable membership projection is the rotation trigger: its high-water refreshes and fences host relay state, while each active sender consumes the same projection to generate and pairwise-distribute fresh sender-key material. The host relay and federation relay never perform that redistribution because neither is allowed to possess the sender key. The relay sees outer connection, sender, epoch, sequence, timing, and size metadata, but not plaintext payload class, schema, content, or key material. Receivers advance sender high-water and their durable relay checkpoint only after the signed frame has been authenticated, its payload has been opened successfully, and the plaintext class/schema contract has passed admission.
MLS or another tree-based group protocol is not implicit in either profile. It may be specified later as a new profile only if long-lived, larger rooms make O(n) fan-out per active sender a measured problem.
Matrix is an optional bridge profile over the Room live contract. The adapter exists; production homeserver deployment remains profile work. It may translate Room frames to Matrix events, but Matrix DAG merge, state events, history, and homeserver membership never become Room semantics or a fallback authority source.
3C. Sensorium Control Uses the Same Carrier, Not Its Authority¶
With Phase 6A implemented, P083 status, claim, control, invoke, and receipt messages use the active Room relay as their default firewall-proof carrier. Their exact interface grant, source generation, lease id, lease epoch, operation sequence, and host policy travel in the request and remain decisive. The relay is therefore only a pipe.
An authenticated direct chair-to-Workbench connection may replace the relay path as a latency optimization, especially for human terminal echo. It is never required for correctness and never changes the lease. Hole punching, if ever attempted for that one pair, belongs to the optional direct-carrier adapter rather than Room.
Initial latency budgets guide measurement rather than changing semantics:
| Path | Operational target | Consequence |
|---|---|---|
| deliberation turns | seconds | model generation dominates; relay overhead is not a blocker |
| P082 terminal latest-state | 100-300 ms at 4-10 updates/s | bounded WSS relay is the normal path |
| human terminal input-to-echo | below roughly 150-250 ms | try direct peer when the relay path exceeds the acceptable local policy budget |
An Agent chair does not require the human terminal-echo target. Operators should measure end-to-end latency before enabling a direct upgrade rather than infer it from hop count alone.
4. Access List and Authority¶
room.v1 references a policy (room-policy.v1) carrying the access list and exposure:
access/list— subjects (nyms/participants/nodes) permitted to join;access/closed— if true, only listed subjects may join;exposure— the room-level exposure vocabulary used by P005 and P070;policy/profile— the P005 operator-participation profile interpreted through the mapping table below.
Exposure Vocabulary Mapping¶
P009 remains the user-facing request exposure vocabulary. Room policy uses the more operational room-level vocabulary because rooms need to distinguish bounded cross-federation reach from truly global reach.
| P009 request exposure | P070 room-policy.v1.exposure |
Rule |
|---|---|---|
private-to-swarm |
private-to-swarm |
Direct mapping. |
federation-local |
federation-local |
Direct mapping. |
public-call-for-help |
cross-federation or global |
Call-site policy chooses bounded cross-federation by default; global requires explicit user/operator consent or a federation policy that says global publication is intended. |
This closes the P009 "split public-call-for-help" question for Room policy without
removing the simpler P009 user-facing label.
P005 Profile Mapping¶
The P005 profile is not a free-form label. Once a runtime interprets it, it maps to specific room-policy fields:
| P005 profile | human/linked-messages |
human/live-participation |
Default use |
|---|---|---|---|
none |
denied |
denied |
Global or high-risk rooms where human live presence would create moderation or retention risk. |
mediated-only |
allowed-via-node-mediation |
denied |
Default for private, federation-local, and bounded cross-federation rooms. |
direct-live-allowed |
allowed |
allowed |
Explicit opt-in rooms with stronger membership, moderation, and retention controls. |
Future policy fields may refine those projections, but they must not silently advertise one P005 profile and behave as another.
5. Projection of Existing Rooms¶
- answer-room (P003): re-expressed as a
room.v1opened by aquestion-envelope, withanswer-room-metadata.v1becoming a projection overroom.v1+room-event.v1instead of an execution-derived read-model. - association-room (P013):
association-room-proposal.v1becomes aroom.v1opener variant; the documented membership/message/event family maps ontoroom-membership.v1/room-event.v1/room-live-message.v1.
Migration is eager. There is no backward-compatibility requirement at this stage, so new implementation work SHOULD move answer-room and association-room onto the Room primitive instead of maintaining a compatibility projection first.
Data Contracts¶
| Schema | Status | Purpose |
|---|---|---|
room.v1 |
new | Subject-addressed room identity, opener, policy ref, lifecycle status. Signed. |
room-membership.v1 |
new | Signed join/leave/grant/revoke with rights. Durable. |
room-event.v1 |
new | Signed lifecycle and authority-delegation events. Phase 6A adds closed delegation scopes, bounded expiry, and explicit revocation linkage for relay/manage. Durable. |
room-policy.v1 |
new | Access list, exposure, expiry; reuses P009/P005 vocabularies. |
room-live-message.v1 |
new (ephemeral wire) | Validated live frame: carrier-authenticated session boundary outside the payload, seq/no, max size, replay handling. The bearer session ref is not part of the frame. Validated but not persisted; not a fact. |
room-membership-attestation.v1 |
new | Signed projection answering membership/authority queries (subject, grants, signer, high-water). |
room-membership-attestation-request.v1 |
new | Explicit request contract for runtime attestation issuance: request mode, requester, subject, requested grants, TTL, and room-scoped authorization. |
room-attestation-audit.v1 |
new | Metadata-only operator-visible audit fact for issued, refused, deduplicated, and rate-limited attestation requests. |
room-relay-endpoint.v1 |
implemented Phase 6A | Durable Room payload inside a signed Agora record, selecting one active WSS endpoint, relay epoch, placement, crypto profile, and ordering profile. Agora owns supersession lineage. |
room-relay-delivery.v1 |
implemented Phase 6A | Ephemeral member-visible carrier header with required delivery/kind: member-visible, adding relay/epoch and relay/seq-no around an already validated Room live or projection payload; it is not a durable fact. |
room-relay-sender-key-distribution.v1 |
implemented Phase 6B | Signed recipient-private sender-key package bound to Room, relay epoch, membership epoch, sender, and recipient. It is delivered through an existing pairwise sealed channel and is never relay fan-out content. |
room-relay-sealed-delivery.v1 |
implemented Phase 6B | Ephemeral relay delivery with required delivery/kind: sealed, carrying an authenticated encrypted sender frame plus relay ordering metadata. Plaintext payload metadata remains inside the ciphertext. |
Relationship to Existing Mechanisms¶
- Agora (008/P035): durable record substrate for the skeleton.
- INAC (017) / Key Delegation (014): room-scoped invitation/capability passports gate join and enforce the access list.
- Artifact Delivery (023): delivers room invites, durable skeleton records, and out-of-band relay-endpoint updates when the prior relay is unreachable; it does not carry the live plane.
- Node Transport (P014 / Solution 000): owns public WSS/TLS endpoint mechanics, WebPKI hostname validation, TLS trust configuration, and carrier reachability. Room owns relay selection and admission semantics, not a second TLS identity protocol.
- P003 / P013 / story-002: consumers re-expressed as projections.
- P069 (Corpus): first consumer that needs the live plane; forces this proposal.
Failure Modes and Mitigations¶
| Failure mode | Risk | Mitigation |
|---|---|---|
| Matrix retains "ephemeral" chat | Privacy leak; chat reified as record | Contractual non-retention + redaction on close; Matrix room is a carrier only. |
| Live transport down | No deliberation | Durable skeleton survives; degrade/retry; never promote live frames to facts. |
| Revoked member keeps live access | Authority bypass | Bounded-lag transport drop on durable revoke; relying parties recheck membership projection. |
| Out-of-order durable records | Wrong membership view | Per-room seq/no + high-water; refuse/buffer gaps, never silent-merge. |
| Ambient join | Unauthorized presence | Join requires room-scoped passport; closed-room access list. |
| Orphaned transport rooms | Resource leak | expires-at + cleanup on close. |
| Relay outage is reported as Room death | Durable collaboration is treated as lost | Model liveness on relay/epoch; Room facts and authority survive with no active relay. |
| Relay becomes policy authority | A carrier can mint membership or effects | Relay validates evidence and caps only; durable Room projection and domain-specific fencing remain decisive. |
| Two endpoints claim one epoch | Split live view | Fail closed on same-epoch conflict; only a newer authority-signed epoch performs failover. |
| Failover tries to merge ephemeral histories | DAG/CRDT complexity enters Room Core | Start a new relay epoch and refresh from durable facts/read-models; never merge epochs. |
| Federation relay reads private content | Confidentiality breach | Require sealed-sender-key-v1; disclose metadata leakage and rotate active sender keys after membership change. |
| Idle NAT binding disappears | Silent live disconnect | Bounded approximately 25-second keepalive plus jittered reconnect and cursor resubscription. |
| Room authority selects an arbitrary URL | SSRF-like egress or trust bypass | Require an exact match to referenced endpoint evidence plus host egress and Node Transport trust policy before dialing. |
| Malicious relay drops or delays frames | False assumption of complete delivery | Treat relay sequence as gap detection only; domain acknowledgements, idempotency, facts, and retry remain authoritative. Fail over on bounded liveness policy. |
| Superseded relay continues sending | Split live view or rollback | Bind sessions and cursors to the accepted active epoch and refuse every lower-epoch frame after supersession. |
Resolved Decisions¶
- Live substrate support (revised by Decision 17). Matrix and WSS pub/sub were implemented under one behavior contract. The production baseline is now the relocatable WSS relay; Matrix remains a bridge adapter and is not required for Room liveness or imported into Room semantics.
- Migration scope. Answer-room (P003) and association-room (P013) are eagerly
re-expressed on
room.v1; no compatibility projection is required at this stage. - Presence semantics. Only authority- or budget-relevant transitions are durable. Ordinary joined/typing/ready-ish liveness is live-plane state unless it changes authority, budget accounting, or lifecycle.
- Retention defaults. Closed/private live content is non-retained by default; the durable skeleton is retained. Public rooms may define policy-specific retention, but live frames are never promoted to durable facts by default.
- Authority delegation. The opener may delegate room authority through a signed delegation with explicit scope, expiry, and revocation. Delegation records are durable room events and must be reflected in the membership/authority projection.
- Grant vocabulary. MVP room grants are a closed vocabulary:
speak,vote,answer,observe,actuate,moderate, anddelegate. Federations may introduce extension grants later only through an explicit schema/policy extension, not by silently accepting arbitrary strings at the security gate. P083-009 extends the schema, runtime vocabulary, and conformance tests withactuate; current Room membership remains collaboration policy rather than interface or lease authority. - Exposure vocabulary.
room-policy.v1uses the Room-level exposure vocabulary:private-to-swarm,federation-local,cross-federation, andglobal. P009public-call-for-helpis a user-facing request label mapped at the P009->P070 boundary. More specific room behavior, such as observer-only versus participatory public access, belongs in room policy/profile fields rather than a competing exposure enum. - Derived answer-room projection. A compatibility projection derived from existing
question execution state is not a replayed Room fact stream. Its
high-water/seq-nomust therefore stay0; durable Room projections use the actual Roomseq/nohigh-water. - Live adapter ownership (revised by Decision 17). WSS pub/sub and Matrix are concrete implementations behind one Room live-plane contract. Neither adapter owns membership or authority. Matrix conformance remains useful, but Matrix is an optional bridge rather than a required co-equal liveness substrate.
- Signer-backed attestation service. The first signer-backed
attest_membershipsurface is a daemon query endpoint backed by the local signer. Middleware/live adapters may consume the attestation, but they do not receive a broader host capability to mint it directly in the first implementation. - Room policy transport.
room-policy.v1may be published as an Agora record when a federation wants a public room-policy fact. Local-only policies remain allowed, but Agora-visible policies require schema-gate ingress/export coverage and must still be referenced fromroom.v1throughpolicy/ref. - Attestation transport.
room-membership-attestation.v1remains a direct runtime query response and may also be published through Agora when a durable/public proof of the query result is needed. It is not an Artifact Delivery payload by default. - Policy profile mapping. P005 room policy profiles map through an explicit table
into
room-policy.v1fields. Profiles are not free-form labels once a runtime interprets them. - Implementation layering. Room starts as canonical contracts plus a pure
projection over Agora/host-owned facts, not as a separate monolithic
room-servicestack. Runtime surfaces may later be split intoroom-core, daemon host APIs, and optional transport-adapter middleware, but membership, authority, expiry, and retention semantics stay in the Room projection layer. - Capability registration. The reserved capability ids are
room.open,room.join, androom.membership-query. They are registered in the Capability Registry and Node implementation ledger before runtime work so that passports and host grants do not invent informal room authority. - Clock and expiry evaluation.
seq/nois the only ordering authority. Wall-clock fields such ascreated-at,expires-at, delegation expiry, and membership-attestation expiry are evaluated by the verifier's local trusted clock with an explicit policy-configured skew tolerance. Records whose validity window is impossible under that tolerance are refused or quarantined; near-boundary records may be accepted only with degraded diagnostics and a refresh requirement. The recommended MVP default is a 5 second skew tolerance. A relay endpoint'sissued-atmust not exceed the trusted evaluation clock plus that tolerance. Scoped delegation authority is evaluated at the admitted fact's issue time, preserving deterministic replay of facts that were valid when issued. Old but otherwise valid durable records are not rejected merely for age; retention policy and high-water replay rules decide whether they remain usable. - Firewall-proof relay baseline. Room requires one reachable WSS/TLS endpoint per active relay epoch, not public reachability for every member. The protocol does not depend on hole punching, UDP, QUIC, STUN, or ICE.
- Relay epoch ordering. One relay epoch has one monotonic carrier order. Failover creates a new epoch and clients resynchronize from durable facts and current read-models; no cross-epoch merge is defined.
- Relay placement and crypto. Placement is authority-signed and follows requester,
relay-capable member, then federation-service preference. A member relay uses
member-visible-tls-v1; a non-member relay requires the latersealed-sender-key-v1profile. Neither profile claims MLS, forward secrecy, or PCS. - Presence and relay authority. Connected presence is an ephemeral hint. A relay may validate admission evidence and enforce bounds, but it cannot mint policy, membership, grants, or domain effects.
- Interactive control carrier. The active relay is the default firewall-proof P083 carrier. Direct peer is an optional latency upgrade and cannot create or transfer a lease.
- Relay management authority.
relay/manageis a closed scope of durable Room authority delegation. It is not a membership grant, readiness claim, or host capability, and it expires or is revoked through the Room authority projection. - Relay fact signing.
room-relay-endpoint.v1is signed through itsagora-record.v1envelope. It does not introduce an inner signature or a second content-addressing and supersession protocol. - Local relay activation. The first daemon endpoint is registered as an operator-only API action. The authenticated local operator may request activation, but the runtime derives and verifies the current local Room authority and signs the append-only endpoint fact with that authority. The request cannot supply a caller subject, signer, delegation, or capability. A module-callable or remote variant is a separate delegated host surface and requires prior Capability Registry and ledger registration.
- Member-visible audience disclosure. A targeted
member-visible-tls-v1delivery carries the complete boundedaudienceevidence array. Every authorized recipient of that delivery and the content-visible relay can therefore inspect the other recipient refs, admission refs, membership source sequences, and expiries. This metadata disclosure is an explicit property of the member-visible profile, not authority.sealed-sender-key-v1does not expose a full recipient array in its relay delivery; deployments requiring recipient-set confidentiality must use that profile and still account for traffic-analysis leakage.
Implementation Contract¶
This section defines the minimum implementable shape of the Room primitive. The durable plane and the live plane must remain separate in code: durable records form authority, while live transports only carry ephemeral coordination frames under the authority of the durable projection.
Substrate and Canonicalization¶
Room domain records are payloads carried by the existing Agora envelope when they become durable/federated facts:
room.v1,room-membership.v1,room-event.v1, androom-relay-endpoint.v1arecontent/schemavalues insideagora-record.v1, not replacements for the Agora envelope;record/id,record/kind,topic/key,author/participant-id,author/nym-proof,record/parent,record/supersedes, relay metadata, and envelope signatures remain Agora concerns;- the Room content schema owns only room-domain fields such as
room/id,seq/no, grants, lifecycle event, policy ref, and room-specific authority semantics; - every durable Room signature, digest, idempotency key, and high-water calculation uses
the Orbiplex canonical JSON profile implemented today by
agora-core::canonical::canonical_json_string.
The first schema pass must make this carrier split explicit in examples: one example of
the domain payload and one example of the same payload embedded in agora-record.v1.
Host Runtime Reuse¶
Room must reuse host-owned primitives instead of building a second control plane:
- Bounded Local Server Runtime (016) owns connection bounds, overload behavior,
deadlines, metrics, and shutdown for the synchronous listener implementation. Its
reuse does not make loopback binding or cleartext
ws://a production Room policy. - Node Transport (P014 / Solution 000) owns public WSS/TLS ingress, WebPKI server authentication, and carrier reachability. Room supplies a bounded handler and authority contract; it does not mint Room-specific transport trust roots.
- Artifact Delivery (023) delivers durable room records and room invites; it does not carry live frames.
- Replay Scheduler (020) owns cleanup/replay wakeups for expired rooms and stale projections.
- Bounded Deferred Operations (029) owns long-running cleanup and bounded-lag
revoke -> drop_member/close -> cleanupoperations. - Temporal Storage Convention (028) owns local projection retention, compaction, and replay diagnostics.
- Middleware (019) may host external transport adapters, but adapter processes do not own membership or authority semantics.
- Capability Registry (P072) is not extended merely because a node advertises relay readiness or appears in an endpoint fact. A new capability id is required only if a later slice introduces a callable host-management surface. Phase 6A does not add one; the data-plane relay role remains governed by Room authority facts and admission evidence.
- TLS/WSS termination belongs to the listener/transport layer. A Room WebSocket
adapter may be tested as local
ws://while still satisfying the Room live-plane contract; deployments that expose it off-host must place it behind the existing WSS/TLS listener policy rather than minting Room-specific trust roots.
Implementation Entry Criteria¶
Runtime work MUST NOT begin until the first contract gate exists:
- canonical schemas and examples for
room.v1,room-membership.v1,room-event.v1,room-policy.v1,room-live-message.v1,room-membership-attestation.v1,room-membership-attestation-request.v1, androom-attestation-audit.v1; - positive and negative schema-gate/admission tests for invalid signatures, old
canonicalization, clock skew, missing
author/nym-proof, sequence gaps, duplicateseq/nowith different digest, invalid authority, revoked membership, expired rooms, room-id reuse after close, projection gaps, signer-unavailable, oversized live frames, and missing live-message digest/nonce; - an Agora topic namespace fixture for
orbiplex/room/v1/<authority>/<room-id>; - an explicit policy fixture for a closed private room and one public/exposed room;
- transport conformance tests that can run against both Matrix and WSS adapters using the same behavior contract;
- a positive deterministic projection golden vector proving that two nodes given the same accepted durable records produce byte-identical canonical membership views and the same projection digest.
Durable Store and Projection Contract¶
The durable room plane is append-only. Implementations should separate:
- Fact store: accepted
room.v1,room-membership.v1, androom-event.v1records, keyed byroom/id,seq/no, record digest, and signer. - Projection: deterministic read-model folded from facts into lifecycle, current
authority, current members, grants, room policy, and high-water
seq/no. - Attestation layer: signed answers over the projection, never direct authority.
Projection rules:
- duplicate records with the same digest are idempotent;
- duplicate
seq/nowith different digest is a conflict and must not be silently merged; - conflicts enter a
pending-conflictprojection status until an operator/policy path chooses quarantine, rollback, or authoritative replay; - a same-epoch relay endpoint conflict is the narrow exception resolved by a strictly
newer valid relay epoch; its conflicting fact still consumes its durable
seq/no, and no unrelated next fact clears the conflict; the projection records the typedrelay/conflict-epochdiscriminator instead of deriving state from diagnostic text; - gaps are buffered or refused according to local policy, never skipped;
- expired rooms cannot accept new live joins or live frames;
- revoked grants take effect in the projection before live transport authorization;
- delegated authority applies only inside its scope and before its expiry.
- every projection decision that depends on time must name the evaluation clock and
skew tolerance used; ordering still comes from
seq/no, never from timestamps.
Durability rules for lifecycle-like events are machine-checkable:
| Event class | Durable? | Rule |
|---|---|---|
opened, closed, expired |
always | They define the room lifecycle. |
delegated, delegation-revoked |
always | They change authority and must carry explicit delegation scope and expiry where applicable. |
member-granted, member-revoked |
always | They change access rights and invalidate membership attestations. |
ready, left, timed-out |
conditional | Durable only when the room policy marks readiness/presence as authority-, budget-, quorum-, or lifecycle-relevant. Otherwise they remain live-plane presence. |
| typing, cursor, ephemeral joined state | never by default | Live-plane state unless a future policy extension explicitly promotes it. |
Lifecycle, retention, and cleanup:
- owner: the local Room runtime projection store;
- primary key:
(room/id, seq/no, record/id); - retention: durable facts follow the room retention policy; projections may be compacted after close/expiry only when replay can rebuild the same high-water state;
- cleanup trigger: room close, room expiry, retention window expiry, or operator maintenance task scheduled through Replay Scheduler;
- indexes:
room/id,seq/no,record/id, lifecycle status,expires-at, and projection conflict status; - safety rule: cleanup must not remove active rooms, unresolved conflicts, pending transport cleanup, or facts needed to verify a still-valid membership attestation.
Authority State Machine¶
Room authority evolves through durable records:
| Current state | Event | Next state | Rule |
|---|---|---|---|
| none | opened |
opened |
Creates the room and initial authority. |
opened |
delegated |
delegated |
Allowed only by current authority and within explicit scope/expiry. |
delegated |
delegation-revoked |
opened |
Revokes scoped authority; prior delegated actions remain auditable facts. |
opened or delegated |
member-granted |
unchanged | Adds or extends grants for one subject. |
opened or delegated |
member-revoked |
unchanged | Removes grants for one subject and triggers live drop. |
opened or delegated |
ready |
unchanged | Durable only when it affects authority, budget, or lifecycle. |
opened or delegated |
expired |
expired |
Terminal for live transport; projection remains queryable. |
opened or delegated |
closed |
closed |
Terminal for live transport; projection remains queryable. |
closed or expired |
any mutating event | refused | No reopen or grant mutation after terminal state. |
opened creates the initial authority. delegated grants scoped authority. closed
and expired are terminal for live transport, but the durable skeleton remains
queryable and replayable.
closed means explicit authority/operator closure. expired means the configured
validity window elapsed. Both are terminal: no mutating record may be appended after
either state. If both conditions are observed, the first terminal record in seq/no
order fixes the terminal reason; later terminal records are audit material only or are
refused according to local conflict policy.
Coupled state changes must be represented by one durable record or by an explicit
journaled sequence with monotonic seq/no. Projection trusts durable ordering only; it
must not merge concurrent state changes by "best effort" heuristics.
Live Transport Adapter Interface¶
Matrix and WSS adapters must expose the same host-side behavior:
open(room, policy) -> transport_binding
join(room_id, subject, passport_or_attestation) -> live_session
send(room_id, live_session, room-live-message.v1) -> accepted|rejected
drop_member(room_id, subject, reason) -> dropped|not-present
close(room_id, reason) -> closed
cleanup(room_id) -> cleaned|deferred
health(room_id) -> status
Each operation has mandatory bounds: timeout, maximum response bytes, retry policy,
backoff with jitter, concurrency limit per room, and failure class
(retryable | terminal | degraded). Defaults live in local policy and may be tightened
per deployment; unbounded adapter calls are invalid. The reference runtime uses a
5 second default clock-skew tolerance for live join attestations/passports, a 1 MiB
pre-parse frame/event ceiling for WSS and Matrix carrier inputs, a 1024 open-room
ceiling, and a 256 subscribers-per-room ceiling. Session refs are host-generated from
256 bits of cryptographically secure randomness and act as bearer secrets. A session
ref is returned only to the joining client and may be repeated only in that client's
join-adjacent subscription or admission requests. It MUST NOT appear in live-message
payloads, subscription acknowledgements, fan-out deliveries, member-visible status,
durable Room facts, or shared Corpus observations.
Adapters must not decide membership or authority themselves. They receive an already validated projection/attestation from the Room runtime and enforce it at the live transport boundary.
Retention Enforcement Contract¶
The default closed/private room live plane is non-retained:
- WSS pub/sub MUST avoid durable storage of live frames and MUST clear in-memory session buffers on close/expiry.
- Matrix MUST be configured as a transient carrier: no public history, bounded retention, redaction on close/expiry, and cleanup diagnostics when server-side deletion is delayed or partial. Redaction is a single close/expiry responsibility; cleanup must be idempotent and must not repeatedly redact the same carrier events.
- Any member-local capture is outside the shared protocol and must be classified under that member's own retention policy.
- Durable room records MAY mention high-water lifecycle facts, but MUST NOT mention live session refs or embed live message content.
Membership Attestation API¶
The Room runtime should expose one query surface:
attest_membership(room_id, subject, requested_grants) -> room-membership-attestation.v1
Failure modes are explicit: room-not-found, room-expired, not-member,
grant-missing, projection-gap, authority-conflict, signer-unavailable. A failed
attestation is not a live join denial reason by itself; it is the evidence the live
adapter uses to deny.
Attestations are short-lived. The default cache TTL is one minute for closed/private rooms and MAY be shorter when revocation sensitivity is high. Requested TTL is capped per exposure mode (see Attestation Endpoint Policy), and any membership-changing record invalidates cached attestations for the affected room (revocation freshness beats TTL).
Attestation Endpoint Policy (exposure-governed)¶
The attestation produced by attest_membership is the credential a live adapter
consumes to admit a join — it is the live-join evidence, not a mere read-model. The
endpoint therefore signs, on demand, room-authority assertions that other nodes trust
for access; it is an authorization surface, governed default-deny by the room's
exposure mode, not an open query. This one rule resolves CR-87/CR-88 findings P1-I
(TTL inconsistency), P1-J (endpoint auth), P2-9 (rate-limit/dedup), and P2-10 (audit).
-
Authentication (default-deny), three request modes. Unauthenticated requests are denied, and the endpoint never reveals whether a room or subject exists beyond what the requester is entitled to learn. The request MUST present a room-scoped authorization claim whose
kind, subject, room id, grants, and expiry are validated by this surface; deployments that expose the endpoint beyond the local trusted service boundary MUST back that claim with the sharedcapability.passport.verify/ capability-binding verifier. The authorization kind bounds what it may obtain: -
invitee first-join — an invitee presenting the room-scoped invitation passport the authority already issued obtains a self-scoped attestation sufficient for its first join. This breaks the chicken-and-egg loop: you need not already be a member to attest the membership the invitation grants.
- member self-attestation — an existing member, with its membership/capability passport, attests only its own membership and grants.
- authority roster attestation — the room authority (or a delegated role) attests another subject or the roster, within the disclosure scope below.
(Same INAC material as join, Solution 017/014; the invitation mode is what keeps the
first legal join reachable.)
- Wire surface. Runtime issuance is a POST with an explicit request body:
POST /v1/agora/projections/rooms/{room_id}/membership-attestation carrying
room-membership-attestation-request.v1. The path room id and request room id must
match. The old GET /v1/agora/projections/rooms/{room_id}/membership-attestation
query surface is deprecated and may return 410 Gone; no compatibility guarantee is
required at this stage.
- Disclosure scope by role. A member MAY attest only its own membership/grants; the
room authority (or a delegated role) MAY attest the roster. For private-to-swarm
and federation-local rooms, other members' presence is not disclosed to a
non-authority requester, and a closed-room roster is never enumerable by a non-member.
- TTL cap by exposure (replaces any flat cap; the endpoint MUST reject a requested
TTL above the cap):
| Exposure | Max attestation TTL |
|---|---|
private-to-swarm |
60 s |
federation-local |
300 s |
cross-federation |
900 s |
global |
per federation policy, default 900 s |
Any membership-changing record invalidates cached attestations for the affected room
(revocation freshness beats TTL). These are policy caps applied by the endpoint; the
pure room-core enforces one absolute technical max that MUST be ≥ the largest
per-exposure cap above. Decision: raise ROOM_ATTESTATION_MAX_TTL_SECONDS to 900 s
when implementing this phase, so the core can carry the documented
cross-federation / global rows while endpoint policy still applies lower caps for
private and federation-local rooms.
- Rate-limit and dedup. Requests are rate-limited per requester and per room, and
identical (room, subject, requested-grants, high-water) requests are deduplicated,
so the endpoint cannot be used as a signing-work amplifier.
- Audit. Every issued attestation and every refusal is an operator-visible audit
fact. room-attestation-audit.v1 carries requester/subject, room/id,
subject/ref, requested/grants, decision, reason/code, exposure,
ttl/requested, ttl/granted, projection/high-water, and attestation/ref when
issued. It never carries payload or passport bodies. agora-service persists these
facts in its local attestation audit store and exposes the recent read model through
GET /v1/agora/operator/room-attestation-audit for operator inspection.
The same policy applies wherever the endpoint is hosted, including agora-service:
that surface MUST adopt the per-exposure TTL cap (resolving the room-core 60 s vs
agora-service 3600 s divergence), the passport auth gate, the rate-limit, and the
audit trail before it is reachable outside fixtures.
Migration Plan¶
Because backward compatibility is not required at this stage, migration is eager:
answer-room(P003) opens aroom.v1from the question envelope and derives answer metadata from the Room projection.association-room(P013) maps proposals, membership, and lifecycle events onto the Room fact family.- Existing
answer-room-metadata.v1andassociation-room-proposal.v1records are replayed into compatibility projections during the transition, but new authority is written only as Room facts. - If a node sees both legacy and Room records for the same subject, Room records win for authority and legacy records remain read-only provenance.
- Legacy write paths should be deprecated for one release window and then removed once replay/recovery proves equivalent projections.
Acceptance Slice¶
The first Room acceptance pack should prove:
- open a closed private room and persist the durable skeleton;
- grant two members and reject one ambient join;
- join over WSS and Matrix using the same membership attestation contract;
- revoke one member and observe bounded-lag live drop on both adapters;
- close or expire the room and verify transport cleanup;
- replay durable facts into an equivalent projection after restart;
- verify that no live message content appears in the durable store or attestation;
- reject a revoked passport rejoin attempt;
- reject a join attempt after room expiry;
- reject duplicate
seq/nowith a different digest; - reject room-id reuse after close;
- replay
delegate -> revoke -> grantand assert the same authority projection; - run the same WSS and Matrix adapter scenario independently and assert equal admission/revocation/cleanup outcomes without merging their carrier histories;
- restart the authority node mid-deliberation and assert durable projection recovery without live-message recovery;
- measure non-functional bounds: revoke latency, cleanup time, and projection rebuild time.
Implementation Recommendations¶
The durable snippets below are Room content payloads embedded in a signed
agora-record.v1. The Agora envelope owns author/participant-id, optional nym or key
delegation proof, record/id, and signature; Room payloads do not duplicate those
fields.
room.v1 (durable Room payload; signed by the enclosing Agora record):
{
"schema/v": 1,
"room/id": "room:<authority>:<id>",
"opener/subject": { "kind": "nym", "id": "nym:did:key:..." },
"authority/subject": { "kind": "nym", "id": "nym:did:key:..." },
"policy/ref": "room-policy:<digest>",
"seq/no": 0,
"created-at": "2026-06-23T10:00:00Z",
"expires-at": "2026-06-23T10:30:00Z"
}
room-membership.v1:
{
"schema/v": 1,
"room/id": "room:<authority>:<id>",
"subject": { "kind": "nym", "id": "nym:did:key:P1" },
"op": "join",
"grants": ["speak", "vote"],
"authority/subject": { "kind": "nym", "id": "nym:did:key:..." },
"seq/no": 3,
"created-at": "2026-06-23T10:01:00Z"
}
room-event.v1:
{
"schema/v": 1,
"room/id": "room:<authority>:<id>",
"event": "ready",
"authority/subject": { "kind": "nym", "id": "nym:did:key:..." },
"subject": { "kind": "nym", "id": "nym:did:key:P1" },
"seq/no": 4,
"created-at": "2026-06-23T10:01:05Z"
}
room-live-message.v1 (ephemeral wire schema: validated on the live plane, but not
signed-as-fact and not persisted by the protocol; a member may locally capture it):
{
"schema/v": 1,
"room/id": "room:<authority>:<id>",
"from/subject": { "kind": "nym", "id": "nym:did:key:P1" },
"nonce": "uYV3foRz3LeLwz5N5Jp3ew",
"seq/no": 14,
"size/bytes": 3,
"content/type": "text/markdown",
"content": "..."
}
The live plane validates each frame (schema, size/bytes against a configured max,
monotonic per-sender seq/no for ordering and replay suppression) and binds
from/subject to the authenticated WSS session outside the payload. Validation is not
persistence: accepted frames are never written to the durable skeleton.
room-relay-endpoint.v1 (durable Room payload; signed by the enclosing Agora
record):
{
"schema/v": 1,
"room/id": "room:<authority>:<id>",
"seq/no": 18,
"relay/epoch": 3,
"relay/subject": { "kind": "node", "id": "node:<id>" },
"relay/placement": "room-member",
"endpoint/url": "wss://relay.example.org/room-live",
"crypto/profile": "member-visible-tls-v1",
"ordering/profile": "relay-total-order-v1",
"selection/evidence-refs": [
"node-advertisement:sha256:<digest>",
"node-address-attestation:sha256:<digest>"
],
"issued-at": "2026-07-17T10:00:00Z",
"expires-at": "2026-07-17T10:30:00Z",
"authority/subject": { "kind": "nym", "id": "nym:did:key:..." }
}
seq/no orders the durable endpoint fact with other Room facts. relay/epoch selects
the carrier generation, while replacement lineage uses the enclosing Agora record's
record/supersedes. The WSS delivery header separately adds relay/seq-no; it wraps
the already validated payload and is never signed or persisted as a Room fact. The
exact room-relay-delivery.v1 schema covers ordinary live
messages, P082 latest-state snapshots, and P083 control/receipt payloads without
turning one payload family into another. A receiver recomputes payload/digest from
the canonical payload and validates the exact payload schema before advancing its
relay checkpoint or exposing the delivery to a consumer.
P082's sensorium-operational-context.v1 value and its adjacent
source/generation-ref remain part of the validated
sensorium-interface-read-result.v1 payload. The relay preserves them byte-for-byte
under the delivery digest and exact payload schema. Room does not rank, aggregate,
default, authorize from, or decide the freshness of the impact context; those
operations belong to the source publication and the consuming P082 resolver. A relay
that strips or rewrites either value produces a digest or schema mismatch and the
receiver refuses the delivery. Supersession and source-generation changes are P082
lifecycle facts, not Room relay epochs or Room-owned TTLs.
The authenticated subscription acknowledgement also reports only the bounded count
of immediately following replay or recovery-control frames visible to that subscriber,
so a consumer can drain the exact initial window without guessing from Room-wide
sequence numbers or dropping cursor-expired / epoch-changed. Authoritative
sensorium-interface-status.v1 and sensorium-interface-read-result.v1 publication
is host-only: network members cannot forge either family. When an active source is
withdrawn or replaced, the source-host projection publishes a recipient-filtered
terminal non-published status; consumers compare its relay sequence with candidate
results and reject any older publication. If source-generation or operational-context
validation fails before replacement commits, the source host instead projects an
equally recipient-filtered transient suspended / degraded status carrying the
typed P082 reason. Room still transports and validates the complete payload but does
not interpret impact class, source freshness, or whether the status is durable or
transient. Removing the last authorized recipient clears the projection audience
before close, so bounded relay replay cannot retain an authorized latest-state view.
Agora topic key: orbiplex/room/v1/<authority>/<room-id>; record kinds room.v1,
room-membership.v1, room-event.v1, and room-relay-endpoint.v1; consumers fold
them into a membership/lifecycle and carrier
endpoint projection keyed by room/id with a per-room high-water seq/no.
room-membership-attestation.v1 (signed query result):
{
"schema/v": 1,
"room/id": "room:<authority>:<id>",
"subject": { "kind": "nym", "id": "nym:did:key:P1" },
"member": true,
"grants": ["speak", "vote"],
"high-water/seq-no": 17,
"source/refs": ["room-membership.v1:...", "room-event.v1:..."],
"attested-at": "2026-06-23T10:05:00Z",
"expires-at": "2026-06-23T10:06:00Z",
"signer/ref": "nym:did:key:...",
"signature": { "alg": "ed25519", "value": "..." }
}
The signature payload is the canonical JSON serialization of the full attestation
object with the top-level signature field removed. Verifiers therefore check the
room id, subject, granted rights, high-water sequence, source refs, signer ref, and
validity window as one signed value; the signature never signs itself.
Implementation Tracker¶
Status legend: [ ] not started · [~] in progress · [x] done (with code
evidence) · [!] blocked/needs decision.
Phase 0 — Durable skeleton contracts¶
- [x] Complete the Implementation Entry Criteria: canonical schemas, examples,
schema-gate tests, Agora topic namespace fixture, policy fixtures, and live transport
conformance test harness. Schemas, examples, schema-gate ingress/export coverage,
canonical topic-key fixtures, deterministic projection golden vectors, and shared
live transport conformance are implemented across
room-core,schema-gate, the bounded WebSocket adapter, and the Matrix adapter. - [x] Define
room.v1,room-membership.v1,room-event.v1,room-policy.v1(subject-addressed, signed,seq/no, access list). Implemented as canonical schemas plusorbiplex-node-room-coreDTOs. - [x] Define the Agora topic namespace, record kinds, signer authority, and high-water
ordering. Topic/record-kind constants, persisted fact replay in
agora-projections, and high-water projection are implemented;room-corevalidates record signer authority against the projected authority subject. Cryptographic verification remains a carrier/host boundary responsibility. - [x] Add the positive projection golden vector: identical durable record sets on two
nodes must produce byte-identical canonical membership views and equal projection
digests, including expiry evaluation under an explicit skew tolerance. The golden
vector is shared between
room-coreandagora-projections. - [x] Add schema-gate ingress/export coverage for Agora-visible Room records.
room.v1,room-membership.v1, androom-event.v1have Agora content ingress coverage, androom-policy.v1,room-membership-attestation.v1,room-membership-attestation-request.v1, androom-attestation-audit.v1schemas are mirrored into node schema-gate contracts with positive/negative examples. Dedicated helper APIs now validateroom-policy.v1import/export,room-live-message.v1ingress/egress,room-membership-attestation.v1export,room-membership-attestation-request.v1ingress, androom-attestation-audit.v1export, while Agora content ingress accepts durable policy and attestation publication.
Phase 1 — Membership projection and query attestation¶
- [x] Deterministic membership/lifecycle projection from durable records. Implemented
in
orbiplex-node-room-corewith duplicate, gap, conflict, close, and expiry tests;orbiplex-node-agora-projectionspersistsroom.v1,room-membership.v1, androom-event.v1facts and rebuilds the runtime Room read-model from ordered Agora records. - [x] Attested membership/authority query results (signer, high-water, grants).
room-membership-attestation.v1is defined,room-coreprovides a pureattest_membershipfactory with explicit failure modes, andagora-serviceexposes authenticated projection queries:GET /v1/agora/projections/rooms/{room_id}andPOST /v1/agora/projections/rooms/{room_id}/membership-attestation. The deprecatedGETattestation surface returns a deprecation response instead of minting a credential. The POST path validatesroom-membership-attestation-request.v1, applies the three request modes, signs canonical attestation bytes through the host signer underroom-membership-attestation.v1, and recordsroom-attestation-audit.v1facts. The sharedroom-corecontract bounds subject identifiers, signer refs, signature values, durable digest inputs, local clock-skew tolerance, and attestation TTL; it also normalizes requested grants before membership checks and signing. - [x] Consume P081 scoped nym claims at one live admission boundary without making
claim verification membership authority.
room-serviceschema-gates and verifies a paired scoped request/presentation through the shared durable runtime, binds the evidence toroom-membership-admission, the exact room id, the joining nym, and a current-certificate claim, then calls independent local policy and the existing live transport authorization. Tests prove a valid proof can still be denied by Room policy.
Phase 2 — Live transport contract¶
- [x] Define
room-live-message.v1and the transport contract (auth via room-scoped passport, revocation drop, presence, retry, expiry, cleanup, sequence). The schema exists androom-corenow owns the typed live-frame DTO, room-scoped passport projection gate, join/send/drop/close/cleanup contract, per-senderseq/noduplicate suppression, 256-bit CSPRNG bearer sessions, bearer-free frame/fan-out projection, and shared conformance tests. - [x] Implement the WSS baseline and Matrix bridge with enforced
non-retention/redaction. Both adapters satisfy the same auth, revocation, sequencing,
expiry, cleanup, and retention boundary without sharing carrier-history semantics.
room-wssprovides the bounded WebSocket pub/sub adapter over the shared Room live contract, including pre-parse frame limits.agora-matrix-clientprovides the optional Matrix bridge overMatrixEventSink, including bounded subscriptions, oversized event refusal before JSON projection, and close/expiry redaction with idempotent cleanup. - [x] Expose the P082 WSS Room
latest-stateprojection as a dedicated read-only carrier session. The adapter intersects current Roomobserverights with current Sensorium Interface grantees, coalesces to one latest snapshot, omits source cursors, refuses ordered-event interfaces, and closes only the projection session on source or authority termination. - [x] Expose P083 collaborative actuation through the explicit
actuategrant. The daemon derives the canonical caller and current Room membership atomically from one live-transport snapshot, and then intersects them with the exact actuation grant, method, generation, lease, epoch, sequence, and host policy. Grouping remains process-local and bounded to 64 entries; terminal input never becomes a Room message. Withdrawal releases the active slot immediately, closed or terminal Rooms are reaped before group access or inspection, and a single member-session disconnect does not destroy the Room-scoped group.
Phase 3 — Access, exposure, lifecycle¶
- [x] Enforce access list / closed rooms via INAC room-scoped passports. The live
contract accepts a normalized
RoomScopedPassportor a signed membership attestation, but durable Room projection membership remains decisive so stale or revoked passports cannot rejoin. - [x] Wire exposure modes (P009) and room policy profiles (P005).
room-policy.v1now uses the room-level P009/P070 exposure mapping and documents the explicit P005 profile-to-policy table; schema androom-coreruntime validation reject profile field drift. - [x] Implement open/ready/close/expiry lifecycle with transport cleanup. Durable
projection supports open/close/expiry;
room-serviceowns open/join/send/drop/close and cleanup over live transports, and Matrix close/expiry redacts live frames while cleanup remains idempotent.
Phase 4 — Consolidation¶
- [x] Eagerly re-express answer-room (P003) as a
room.v1projection. Execution inspection exposes aRoomProjectionrather than a bespoke room type. Historical selected-responder executions remain a compatibility-derived projection withhigh-water/seq-no = 0; future collaborative answer-room writes should use Room facts directly. - [x] Re-express association-room (P013) onto the membership/event family. The
association-room lifecycle FSM is now owned by
orbiplex-node-room-coreand reused by Whisper/Agora projections. Legacy association-room proposal records remain provenance; new live/durable implementations should use the Room family directly. - [x] Provide the live plane needed by Corpus (P069) deliberation. The P070 live-plane runtime contract, bounded WebSocket pub/sub adapter, and Matrix adapter now exist; Corpus can build deliberation on Room without inventing a third room model.
Phase 5 — Attestation Endpoint Policy enforcement¶
The previous service-authenticated GET query is deprecated. Runtime issuance now
uses room-membership-attestation-request.v1 over POST and treats attestation minting
as an authorization surface.
- [x] Passport-shaped room authorization with the three request modes (invitee
first-join, member self-attestation, authority roster attestation); default-deny
otherwise. The current
agora-serviceendpoint validates the room-scoped authorization claim at the local trusted service boundary; deployments exposing this outside that boundary must bind it to capability-passport verification. - [x] Disclosure-scope enforcement by role and exposure (closed-room roster not enumerable by a non-authority requester).
- [x] Raise
ROOM_ATTESTATION_MAX_TTL_SECONDSto900 sand enforce the per-exposure endpoint caps from the table above. - [x] Per-requester / per-room rate-limit and
(room, request-mode, subject, grants, high-water, ttl)dedup. - [x] Define and emit
room-attestation-audit.v1fact on issue, refusal, deduplication, and rate-limit decisions (no payload/passport bodies). - [x]
agora-serviceadopts the same auth gate, TTL caps, rate-limit, and audit before the endpoint is reachable outside fixtures.
Phase 6A — Relocatable member-visible WSS relay¶
This phase is post-MVP carrier work. It does not reopen the completed durable Room, membership, attestation, or node-local live-plane contracts.
- [x] Freeze
room-relay-endpoint.v1, its positive/negative fixtures, Agora-envelope signing profile, monotonic epoch and supersession rules, and the ephemeral relay delivery header carrying(relay/epoch, relay/seq-no). Reusenode-advertisement.v1relay endpoints andnode-address-attestation.v1as bounded selection evidence rather than registering an ambient relay capability. Extendroom-event.v1with closed authority-delegation scopes includingrelay/manage, bounded expiry, and explicit revocation linkage; synchronize schema-gate, Node mirrors, implementation ledger, and generated schema documentation. Implemented by the canonical endpoint/delivery schemas, closedrelay/managedelegation fields, synchronized Node mirrors, and schema-gate ingress/egress checks. - [x] Add a pure endpoint projection and selection evaluator. It must prefer requester,
relay-capable member, then federation service; reject same-epoch conflicts; and never
treat reachability or presence as authority. It must also reject endpoint facts from
an ordinary relay-capable member, an expired delegation, or a revoked delegation.
Before dialing, it must require exact endpoint/subject/evidence matching plus
host-owned egress and Node Transport trust admission. Within one candidate class it
uses advertisement priority, canonical subject id, and canonical endpoint URL as
deterministic tie-breaks.
room-corenow owns this evaluator, same-epoch conflict refusal and strictly-newer-epoch recovery, explicit trusted-clock evaluation with the five-second default skew bound, canonical Room topic binding, and exact dial admission. Durable Agora replay proves that a same-epoch conflict remains fail-closed until a newer endpoint epoch restores an active projection. - [x] Add host-owned configuration and operator diagnostics for enabled placement
classes, advertised endpoint, active epoch, crypto profile, connected-session count,
bounded replay occupancy, reconnects, cursor expiry, last failover, and degraded
reason. Diagnostics contain no live payload or sealed key material. Daemon
room_relayconfiguration materializes these bounded controls and Corpus status exposes per-Room metadata-only relay diagnostics. The WSS adapter enforces and reports a hard per-Room relay-connection cap even when one Room bearer is reused; degraded reasons come from a closed runtime vocabulary rather than caller strings. - [x] Make the existing
room-wssserver deployable behind the host-owned TLS/WSS listener on TCP 443, with bounded approximately 25-second keepalive, jittered reconnect, epoch-aware cursor resubscription, replay-window caps, and current-state refresh aftercursor-expired. The adapter advertises the host-ownedwss://endpoint while retaining a bounded local handler behind TLS termination; its persistent client implements keepalive, jittered reconnect, and typed resume. Client admission rejects Room rebinding, cursor rollback, and every non-increasingepoch-changedcheckpoint before it can replace local resume state. - [x] Deliver newer endpoint facts independently of the old relay through Agora replay
and Artifact Delivery mailbox fallback, then recover the selected epoch and relay
sequence checkpoint without restoring live payload content. Agora replay and the
dedicated Artifact Delivery
agora-record.v1mailbox acceptor converge on one pure projection; the acceptor verifies exact kind/schema, signature, and canonical topic, rejects unknown or unopened Rooms without buffering, and has pure boundary tests. Checkpoint storage is metadata-only and restored gaps force refresh. Room-service rechecks close and monotonic projection state after transport I/O before committing the refreshed projection and endpoint source. - [x] Add the P083 relay carrier for status, claim, control, invoke, and receipt messages
without changing grant, source-generation, lease, epoch, sequence, idempotency, or
host-policy checks. Keep authenticated direct peer as an optional latency upgrade.
room-relay-delivery.v1binds each class to its exact Sensorium schema and filters observation versus actuation visibility from current Room grants. The sharedsensorium-statuscarrier class additionally distinguishes observation status from actuation/control status by its exact schema ref, soobservecannot disclose P083 control state. P082 pumps emit a validated resource-boundsensorium-interface-read-result.v1with one inline latest-state snapshot into an active epoch without exposing source cursors. - [x] Add an outbound-only three-node acceptance profile proving initial relay
placement on the requester or another content-visible member, relay failure, endpoint
update through AD, new-epoch recovery without cross-epoch merge, membership
revocation, and P082 latest-state plus P083 fenced invocation over the same relay. It
must also refuse an endpoint fact authored by an ordinary relay-capable member and by
an expired or revoked
relay/managedelegate, a mismatched evidence ref, a disallowed egress target, and every old-epoch frame after failover. Thephase6a_relayacceptance target covers all listed positive and refusal paths over requester, relay-member, and observer nodes. - [x] Keep composed Story 011/012 evidence below the deployment boundary explicit:
their A/B/C daemons use distinct
127.0.0.1/2/3endpoints and exact IP SANs, but remain a multi-address single-host profile. This proves process separation and real TCP/WSS routing without claiming public reachability, NAT behavior, host independence, or replacing the dedicated Phase 6A/6B deployment evidence. The same pack may explicitly select a port-isolated single-address fallback for unattended runs; it is a weaker evidence class and never an implicit degradation of the default profile. - [x] Collect executable POSIX Phase 6A deployment evidence through an actual host-owned TLS
terminator and separate relay, publisher, and observer processes. The feature-gated
Node profile supplies its private trust root explicitly at the host boundary, carries
P082 latest-state and P083 invoke payloads over one WSS relay, and binds the crypto
provider per TLS config without changing process-global rustls state. The terminator
reaps completed connection tasks and aborts outstanding tasks at shutdown. The profile
forces bounded replay expiry followed by current-state refresh, distinguishes recovery
from a real reconnect in diagnostics, restores metadata-only checkpoints after an
unclean stop, separately proves graceful final-checkpoint persistence, performs
strictly-newer-epoch failover, revokes an active member session, and refuses a payload
whose canonical digest no longer matches. The networked publisher validates the
host-minted 256-bit bearer shape and refuses any delivered frame containing its
session/ref; the runner also rejects retained evidence containing a bearer key or value. Its local report contains only bounded timings, counters, and outcomes; fixture keys, attestations, session refs, and payloads are deleted or omitted. The loopback smoke uses an ephemeral TLS port, while the reference deployment terminates public WSS at the host listener on TCP 443.
Phase 6B — Non-member federation relay profile¶
This phase enables the all-members-behind-CGNAT case without admitting the relay service to Room content. It is intentionally sequenced after Phase 6A proves endpoint selection and failover independently of group encryption.
- [x] Implement
sealed-sender-key-v1only for non-member federation relays, with pairwise sealed sender-key distribution, sender-authenticated encrypted frames, O(n) membership-change fan-out per active sender, metadata-leakage disclosure, and no forward-secrecy or PCS claim.room-corenow owns the pure distribution, frame, replay, rotation, and fencing contracts; schema-gate validates both Phase 6B wire families. Member-visible and sealed deliveries carry distinct requireddelivery/kindvalues, soroom-wssdispatches the opaque union by an explicit discriminator rather than trial deserialization, without receiving keys. A between-crate test also requires every payload-class schema ref accepted byroom-coreto remain registered in schema-gate. A dedicated transition lock linearizes projection refresh, relay rotation, publication, resume, close, and cleanup across the membership/carrier state boundary. The common activation boundary rejectssealed-sender-key-v1with an absent or zero membership epoch, and rejects a membership epoch onmember-visible-tls-v1, before it creates or mutates relay state. A membership projection refresh is the host-side rotation trigger; sender clients then own fresh key generation and recipient-private redistribution from that same projection. - [x] Extend the outbound-only acceptance profile with an external federation relay and
no publicly reachable Room member. Prove initial key distribution, join/leave/revoke
rotation, refusal of old-epoch ciphertext, bounded recovery, and that the relay audit
exposes metadata only and never plaintext or key material. The Phase 6B acceptance
now combines the exact in-process contract scenario with a separate multiprocess
deployment profile. Two successive non-member relay processes expose actual local
WebSocket endpoints behind host-owned TLS, while one-shot member processes connect
outbound. The profile proves initial distribution, join,
RoomMembershipOp::Leave, revoke, refusal after every stale membership epoch, revoked-session refusal, bounded O(n) fan-out, sealed-client reconnect from an authenticated checkpoint, metadata-only checkpoint restart, and failover to a strictly newerrelay/epochwithout cross-epoch replay merge. It emits a 21-check redacted report plus a separate relay audit capped at 64 transition records. Status, audit, and report must first match closed metadata-only field allowlists; recursive sensitive-key and private-marker scanning remains defense-in-depth evidence rather than the canonical redaction mechanism.
Open Questions¶
No unresolved question blocks Phase 6A or Phase 6B. MLS, QUIC, and direct-pair hole punching are explicit evidence-gated future profiles, not implementation choices inside the relocatable relay baseline.
Next Actions¶
- Accumulate evidence from non-loopback host-TLS deployments and compare repeated reconnect, cursor-expiry, revocation, and failover observations before changing any operational bound. The local POSIX deployment-evidence profile proves the mechanism; its timings are not a production SLO or a reason to tune defaults from one machine.
- Keep direct peer an optional latency adapter; do not move membership, grants, leases, or source cursors into either carrier.
- Collect non-member relay observations at realistic Room sizes before replacing the bounded O(n) sender-key distribution profile. MLS or another tree profile requires measured pressure plus a separate contract, implementation, and security review.