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
Extended by:
doc/project/40-proposals/090-inference-execution-provenance-and-non-local-disclosure.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. Room is topic-agnostic: technical diagnosis,
scientific inquiry, social or mutual-aid coordination, and creative collaboration are
illustrative, non-exhaustive uses, not a closed Room domain enumeration; the owning
consumer supplies their grammar and completion rules. 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.v1; Phase 8 addsroom-topic.v1androom-summary.v1. All are Agorarecord/kindvalues in the same Room topic. - 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.moderatealso gates changes to the Room's descriptivetopicandsummary; this is an additional effect of the existing grant, not a new grant value. - 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. - Acknowledgement identity: the WSS send acknowledgement MUST echo the full
admitted
room-live-messagevalue, including optional fields and extensions, without adding, removing, or normalizing values. JSON object key order is not significant. In particular, peers MUST NOT normalizesent-ator fill an absentcontent/digestin the ACK. Admission metadata belongs outside the echoed message. A changed message is a protocol refusal, not send success. - 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. The wiremembership/epochis derived from the monotonic Room membership high-water: it is theseq/noof the latest admitted change to the decrypting recipient set, not a separately mutable Room counter. Join, leave/remove, ban, or another grant change that actually changes that recipient set advances this crypto epoch, fences prior ciphertext, and rotates active sender-key material with bounded O(n) fan-out per active sender. Aspeakgrant change and an ephemeral floor change do not alter the decrypting audience and therefore do not rotate sender keys. Join rotates rather than reusing the prior key, so a newly admitted recipient does not receive key material capable of opening pre-join ciphertext. These rules apply only tosealed-sender-key-v1;member-visible-tls-v1has no group sender key to rotate. 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 relevant recipient-set 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 implemented Phase 7 classifier derives a separaterecipient-set/high-water-seq-noand the sealed runtime uses it as the crypto epoch. Golden and carrier tests prove that mute and floor changes preserve this value while join and decrypting-recipient removal advance it. 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.
4A. Root Authority and Scoped Moderation¶
Room moderation is expressed through existing Room authority and grants, not through
IRC-compatible wire flags. A UI MAY render the opening authority/subject and a
properly delegated moderator as op, and a member holding speak as voice, but
those labels are read-model projections only. They do not create authority and MUST
NOT be accepted as input at an admission boundary. Corpus Chair is governed by the
same rule and becomes Room authority only after canonical scoped Room admission.
The opening authority is the Room's non-removable root while the Room remains active. It may close the Room, change policy within the policy contract, and issue or revoke bounded moderator delegations. A moderator is a delegate, not a second owner. The delegation MUST carry closed scopes, expiry, issuer, sequence, and revocation lineage; it is non-transitive unless a separate scope explicitly permits delegation. The baseline moderation scopes distinguish:
| Scope | Permitted effect | Baseline issuer |
|---|---|---|
membership/invite |
issue a Room-bound invitation | root or explicitly scoped moderator |
membership/remove |
remove a non-root member and drop live sessions | root or explicitly scoped moderator |
membership/deny |
ban or reinstate a non-root subject | root or explicitly scoped moderator |
grant/speak |
grant or revoke speak for a non-root member |
root or explicitly scoped moderator |
metadata/update |
append or clear Room topic and summary; never change name |
root or explicitly scoped moderator |
moderation/delegate |
issue or revoke moderator scopes | root only by default |
These are subscopes that narrow the existing closed moderate and delegate grants;
they are not additional top-level Room grants. Their validator reuses the same closed
supported-value gate as the existing Room grant vocabulary and refuses every unknown
scope. A moderator is therefore a holder of moderate constrained by explicit scopes,
not a competing authority class.
Neither moderate nor any scoped delegation permits transfer of root authority,
removal or banning of the root, lifecycle mutation outside the delegated scope, or
policy mutation by implication. A consumer role is equally non-authoritative: Corpus
may map its current Chair to a scoped Room delegation, but Room never infers Chair
status from a Corpus message, Agent outcome, or model output.
After schema, Room-id, and canonical subject validation, the first authority check for every moderation target is whether it is the opening/root authority. A matching target fails closed before delegation lookup, policy evaluation, mutation, or transport work. This ordering is an invariant of the pure projection path, not a UI safeguard.
4B. Speak Grants and Ephemeral Floor Control¶
speak is the durable right to submit live Room messages. Revoking it is a membership
projection change and MUST affect existing sessions without relying on reconnect or
presence. Every send is admitted against the current projection; a bearer minted while
speak was present cannot preserve that right after revocation.
Mute and unmute do not introduce a new durable fact kind. Under the current
room-membership.v1 snapshot semantics, mute is RoomMembershipOp::Grant carrying the
complete current grant set with speak omitted, and unmute is Grant carrying the
complete admitted set with speak restored. RoomMembershipOp::Revoke and Leave
remove the member; they MUST NOT be overloaded to mean one-grant removal. Kick reuses
that existing member-revocation path and RoomEventKind::MemberRevoked vocabulary.
The host derives each replacement snapshot from the current projection and changes
only speak; a caller cannot submit the remaining grant set or widen another grant as
part of a voice intent.
Orderly turn taking is a separate, ephemeral policy layer. Repeatedly rewriting
durable speak grants for each utterance would turn conversational scheduling into
authority-history churn, so controlled rooms use bounded floor leases. Room policy
defines one of three modes:
| Mode | Send condition after membership and denial checks |
|---|---|
open |
current speak grant |
moderated |
current speak grant and a current floor lease |
round-robin |
current speak grant and the floor lease issued by the deterministic queue projection |
A floor lease binds the Room, subject, policy generation, issuance sequence, and a short expiry. It grants no membership, moderation, observation, voting, actuation, or durable authority. Queue size, lease TTL, renewal count, and outstanding leases are bounded. On restart, relay failover, policy-generation change, timeout, or ambiguous lease ownership, the floor fails closed and must be reissued. Moderator override is an explicit control intent and remains constrained by the moderator's current scope. Floor modes are a closed enum and unknown values fail at schema and typed DTO boundaries; extension maps cannot smuggle another execution mode.
Expired leases are pruned at every lease admission, assignment, release, live-send, and
operator-status boundary. Membership projection refresh also removes lease and queue
entries whose subject no longer has current speak. Consequently an idle expired lease
cannot retain an outstanding-lease slot or remain visible as the current floor owner;
the queue remains a separately bounded scheduling projection rather than an expiry cache.
The effective live-message decision is therefore one composed admission result:
member
AND room is active
AND no effective access denial
AND current speak grant
AND (floor mode is open OR current floor lease matches)
The transport consumes this decision but does not derive it. Room Core/Room Service
compose membership, denial, speak, and floor into the projection above the transport
trait, then reuse the existing RoomLiveTransport::refresh_projection operation to
reconcile active sessions. Implementations MUST NOT add carrier-specific mute, floor,
or denial methods. This keeps WSS, Matrix, and future carriers behaviorally equivalent.
4C. Kick, Ban, Reinstatement, and Fencing¶
Kick and ban are deliberately different operations:
| Operation | Durable consequence | Live consequence |
|---|---|---|
| kick/remove | membership is removed; no standing denial is created | current sessions are dropped |
| ban/deny | an access-denial fact overrides positive admission evidence | sessions are dropped and rejoin is refused |
| unban/reinstate | the denial is superseded | no membership or grant is silently restored |
| mute | speak is revoked while membership may remain |
later sends are refused; observation may continue |
| unmute | speak is granted under current authority |
later sends may proceed subject to floor policy |
The access-denial projection is evaluated before invitations, passports, membership attestations, and positive grants. Its record binds Room, subject, issuer authority, reason code or private reason reference, sequence, optional expiry, and supersession. The root authority cannot be denied by this mechanism. Expired or reinstated denials remain auditable facts but no longer affect admission.
Although room-access-denial.v1 is a separate record shape, it is folded in the same
ordered Room projection pass and shares the Room seq/no authority order. It MUST NOT
create a parallel denial database, independently advancing high-water, or a second
authorization read-model that can disagree with membership.
Control follows facts-before-effects: validate authority, commit the durable fact, refresh the deterministic projection, and only then narrow or remove live sessions. Carrier cleanup failure is degraded state, never rollback of the committed denial or grant decision. Member-visible TLS needs projection refresh and affected-session drop, but no crypto rotation. In the sealed relay profile, membership removal or ban advances the recipient-set-derived membership epoch described in Section 3B and rotates active sender keys before further protected content is emitted. Mute, unmute, and floor changes do not rotate. Stale attestations, bearers, cursors, frames, and prior-epoch ciphertext cannot restore access.
Every typed moderation intent emits a bounded metadata-only audit fact for admitted, refused, replayed, expired, and cleanup-degraded decisions. It records refs, actor, scope, target, policy generation, decision/reason code, Room sequence, and timestamps, never private reason text, bearer material, keys, or live content. Intent replay uses the host's existing actor-bound idempotency discipline and registry; Room MUST NOT add a private idempotency store with different conflict semantics.
4D. Message Identity and Reply Linkage¶
The next live-message contract revision adds a stable, opaque, Room-scoped
message/ref. The sender supplies it before first admission and reuses it for retries;
the live runtime binds it to the exact (room/id, from, seq/no) tuple. Reusing one ref
for a different tuple or payload is a conflict and fails closed. Carriers may add their
own delivery identifiers, but they do not replace the Room message reference.
In room-live-message.v2, message/ref replaces the v1 nonce as the sender-supplied
stable idempotency identity; v2 MUST NOT carry both fields. The exact replay comparison
uses (room/id, from/subject, seq/no, message/ref, content/digest). A v1 compatibility
adapter may project its validated nonce into a message ref, but the canonical v2 value
has one source and a conflict never selects one identity over the other.
A message MAY contain a unique bounded list of reply refs, with eight as the baseline
maximum. The refs express conversational linkage to one or more earlier messages. They
do not prove delivery or observation, authorize content retrieval, require the target
to remain in a replay buffer, or promote either message to a durable fact. A missing
target is therefore valid on an ephemeral plane and is rendered as unavailable context,
not treated as an integrity failure. A participant that needs durable evidence must
explicitly capture it under a separate classification and retention contract.
Reply refs are conversational hints and remain separate from P081
causal-context.v1 causation/refs, which express execution lineage. A reply MUST NOT
be promoted into a receipt, trace edge, causal proof, or effect dependency merely
because the referenced message is locally available.
4E. Descriptive Name, Topic, and Summary¶
Room description follows the mutability of the represented value rather than placing three editable fields in one row:
nameis an optional opener-set field ofroom.v1. Its absence projects as the empty string. It is immutable because the signed opening fact is immutable; Room has no name-change fact or update operation.topicis the value of the latest acceptedroom-topic.v1fact in the Room's monotonicseq/nostream. The empty string explicitly clears it.summaryis the value of the latest acceptedroom-summary.v1fact in the same stream. The empty string explicitly clears it.
The current root authority satisfies the metadata moderation check directly. Every
non-root actor changing topic or summary MUST hold current active moderate
authority narrowed by the closed metadata/update scope, including expiry,
revocation, and Room-lifecycle checks. metadata/update is not a new top-level grant
and does not imply any membership, floor, relay, or delegation authority. Admission
validates and authorizes before appending the fact or changing the projection.
Metadata changes do not target another subject, so the non-removable-root target rule
is irrelevant to this operation and MUST NOT be repurposed as a denial.
room-topic.v1 and room-summary.v1 carry room/id, the value, set-by/subject,
seq/no, and created-at. They are Room payloads signed by their enclosing
agora-record.v1; they do not add an inner signature. set-by/subject MUST match the
authenticated actor and the enclosing author evidence accepted at the Agora/Room
boundary. Duplicate facts retain ordinary digest-idempotency semantics. A different
fact at the same seq/no is a projection conflict, not a latest-wins tie-break.
The authoritative semantic limits are measured by Unicode scalar values in the pure
Room core: name <= 32, topic <= 64, and summary <= 3000, using the equivalent of
Rust .chars().count(). Every value MUST also reject a Unicode control character
unless that character is whitespace; this rejects NUL and other non-whitespace control
characters while allowing intentional whitespace. JSON parsing supplies the UTF-8/
Unicode validity boundary. The wire schemas use coarse maxLength ceilings of 128,
256, and 12000 respectively, but those ceilings are not byte limits and do not replace
the semantic core checks. Admission additionally enforces UTF-8 byte ceilings of 128,
256, and 12000 before a value can consume projection or audit resources.
The opening name is bound to the exact accepted opening payload. Replay of the same
opening digest is idempotent; another room.v1 for the same Room that differs in
name or any other opening identity field is an opening conflict and fails closed.
No later fact may reinterpret an absent or empty opening name as mutable state.
All three values inherit the Room policy's current exposure. If the host or a
consumer binds Room content to an explicit classification/declassification contract,
the descriptive values inherit that same contract; Room does not infer a
classification from exposure, create an ambient default, or authorize a downgrade.
They are content-like descriptive data, not routing metadata, authority, membership,
or discovery evidence. Membership attestations do not include them. A query,
projection, notification, relay diagnostic, or non-member surface MUST NOT disclose
them beyond the audience authorized by the current Room policy and any explicit
content-classification contract merely because name looks harmless.
set-by/subject, source record ref, and accepted sequence provide the bounded audit
lineage for the effective topic and summary without copying their text into
metadata-only audit records.
4F. Scoped Inference Posture and Per-Contribution Provenance¶
Room identity and authority do not say where a participant's inference runs. A participant may use different admitted runtimes across tasks or turns, while the speaking subject, membership, grants, floor state, and delegation remain unchanged.
The Proposal 090 extension therefore keeps two values separate:
- a signed, Room- and time-scoped
inference-execution-posture.v1value bound to assertion owner, participant/runtime-binding subject, policy generation, validity, and exact processing-boundary ref, used for admission, consent, policy, and presentation; and - realized
inference-execution-provenance.v1carried by or immutably referenced from each inference-derived live contribution, captured product, or durable consumer-owned outcome.
The Room read model may derive bounded presentation metadata such as "non-local inference observed" and a disclosed provider-ref set. This projection is not a permanent participant attribute, identity claim, capability, grant, membership fact, floor decision, or evidence that every contribution used the same path. Provider runtime sessions, accounts, endpoints, request ids, and credentials never enter Room membership or message identity.
Room preserves the descriptor or uses the canonical conservative join when it derives
an aggregate. Known non-local or mixed execution cannot become local; missing
legacy metadata becomes unknown; redacting an exact provider does not erase the
non-local characteristic. A receiving node preserves the sender-relative boundary and
records the disclosed claim as peer-attested rather than upgrading it to a local host
observation. Local Room policy may refuse, warn, allow, or filter contributions, but
the decision does not rewrite their provenance.
Posture/locality comparisons require the same processing-boundary ref or an
explicit, versioned boundary relation admitted by the receiving node. An
unrelated sender boundary remains non-matching or unknown; it is not treated as
the receiver's local-only posture merely because the vocabulary matches.
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 |
implemented; Phase 8 extension planned | Subject-addressed room identity, immutable optional opener-set name, opener, policy ref, and lifecycle status. Signed by the enclosing Agora record. |
room-membership.v1 |
new | Signed join/leave/full-grant-snapshot/revoke with rights. Durable; Phase 7 mute/unmute reuses grant snapshots rather than adding a fact kind. |
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; Phase 7 adds moderation subscopes of moderate/delegate. Durable. |
room-topic.v1 |
planned Phase 8 | Durable append-only, moderate-gated topic fact. The highest accepted global Room seq/no wins; an empty value clears the projection. Signed by the enclosing Agora record. |
room-summary.v1 |
planned Phase 8 | Durable append-only, moderate-gated summary fact with the same ordering, clearing, exposure, and signing rules as the topic fact. |
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, sender-supplied nonce, 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. |
room-access-denial.v1 |
implemented Phase 7 | Durable authority-signed ban/reinstatement record whose effective denial precedes positive Room admission evidence. |
room-floor-policy.v1 |
implemented Phase 7 | Bounded open, moderated, or round-robin policy, generation, queue limits, and lease bounds. |
room-floor-lease.v1 |
implemented Phase 7 (ephemeral) | Short-lived subject- and generation-bound permission to use an existing speak grant in a controlled-floor mode. It grants no membership. |
room-moderation-audit.v1 |
implemented Phase 7 | Bounded metadata-only audit fact for admitted, refused, replayed, expired, and cleanup-degraded moderation intents. |
room-live-message.v2 |
implemented Phase 7 (ephemeral wire) | Replaces v1 nonce with stable Room-scoped message/ref and adds bounded reply linkage without adding shared retention. |
inference-execution-posture.v1 admission/projection |
planned Phase 9, schema owned by P090 | Signed Room- and time-scoped participant/runtime-binding declaration with exact assertion owner, generation/validity, and processing-boundary ref. It is policy input and presentation metadata, not identity, membership, grant, or realized evidence. |
inference-execution-provenance.v1 carriage/projection |
planned Phase 9, schema owned by P090 | Conditionally required value or immutable ref for inference-derived contributions, absent for non-inference contributions, plus derived Room read-model metadata. It does not change Room identity, membership, authority, ordering, or retention. |
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 decrypting-recipient-set changes. |
| 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. |
UI op or consumer Chair is accepted as authority |
Consumer labels bypass Room admission | Treat labels as read-model aliases only; require a current scoped Room delegation. |
Revoked speak survives in an old session |
Muted participant can continue sending | Re-evaluate the current projection for every send and fail closed on stale authority. |
| Floor scheduling is represented by durable grant churn | Agora history grows with conversational timing | Keep speak durable and represent turn allocation as bounded ephemeral floor leases. |
| A positive passport bypasses a ban | Denied subject rejoins through stale evidence | Evaluate current access denial before every positive invitation, passport, attestation, or grant. |
| Ban commits but carrier cleanup fails | Runtime appears to restore authority | Keep the denial authoritative and report bounded cleanup as degraded state. |
| Reply refs imply hidden transcript retention | Ephemeral conversation becomes durable by accident | Permit unresolved refs and require explicit classified capture for durable evidence. |
| Moderation state is implemented beside the Room projection | Membership and denial authorities diverge | Fold membership, scoped delegation, denial, and policy in one ordered Room projection and expose one effective decision. |
| A carrier grows its own mute or floor API | Transport behavior diverges from Room authority | Compose the effective projection above the trait and reuse refresh_projection for every carrier. |
| Mute or floor rotates sealed sender keys | Conversational scheduling causes avoidable O(n) crypto churn | Keep the derived sealed recipient-set epoch unchanged; rotate only for actual decrypting-recipient changes after the Phase 7 classifier lands. |
nonce and message/ref coexist in v2 |
Two conflicting message identities create replay ambiguity | Replace nonce in v2 and allow only a validating v1 compatibility projection. |
| Reply linkage is treated as causal evidence | Conversation hints become false receipts or execution lineage | Keep reply refs outside P081 causal context, traces, receipts, and effect dependencies. |
| Descriptive metadata is treated as routing or authority | A human-readable label widens discovery, membership, or effects | Keep name, topic, and summary content-like and exposure-governed; they grant no authority and never replace Room ids, policy refs, or attestations. |
| A private Room summary leaks through an attestation, notification, or diagnostic | Up to 3000 scalar values of problem context escape the authorized audience | Exclude descriptive text from membership attestations and metadata-only audit; disclose it only through a Room projection authorized by current exposure and any explicit content-classification contract. |
A later opening or in-place update changes name |
Replay produces different Room identity from the same durable history | Bind name to the sole accepted opening payload; exact replay is idempotent and any differing opening is a typed conflict. Define no name-update operation. |
| Character and byte limits are conflated | Multibyte text is refused inconsistently or oversized input consumes resources | Enforce scalar-value limits in room-core, independent UTF-8 byte ceilings at admission, and coarse schema ceilings; prove both with multibyte fixtures. |
| Participant posture or a provider badge is treated as proof for every turn | A declaration becomes a false realized-execution claim and may silently authorize disclosure | Scope posture to policy/time, require per-contribution realized provenance, and keep the participant aggregate explicitly derived. |
| Provider identity is redacted by dropping all provenance | Consumers lose the material non-local fact and may present a remote result as local | Preserve locality, egress, disclosure completeness, and peer-evidence limits even when exact provider refs are withheld. |
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. - Moderation authority.
opis a possible UI alias, not a Room grant or wire role, and is never admission input. Root authority remains non-removable while the Room is active; after canonical target validation, the projection rejects a root target before evaluating delegation or policy. Moderators act only through closed, expiring, revocable, non-transitive subscopes ofmoderateordelegate. Consumer roles such as CorpusChairdo not become Room authority without canonical admission. - Voice and floor separation. The durable
speakgrant answers whether a member may speak at all. Temporary floor leases answer whether that member may speak now under a moderated or round-robin policy. Mute/unmute reuse full grant snapshots inroom-membership.v1; kick reuses member revocation. Neither mechanism substitutes for the other or adds a transport operation. - Access-denial precedence. An effective Room ban is a durable deny overlay that precedes positive invitations, passports, attestations, membership, and grants. Reinstatement removes the denial but does not recreate prior positive authority.
- Reply semantics. Stable message and reply references belong to the ephemeral
Room contract. They express linkage without requiring history, dereferenceability,
shared capture, durable fact status, or P081 causal lineage. In v2,
message/refreplaces v1nonce; they are never parallel identity sources. - Projection-owned moderation. Membership, scoped moderation, access denial,
speak, and floor policy compose into one effective Room projection. ExistingRoomLiveTransport::refresh_projectionreconciles carriers; Room adds no carrier-specific moderation methods. - Sealed rotation scope.
membership/epochis derived from the latest recipient-set-changing membership high-water. The target Phase 7 rule rotates on join and decrypting-recipient removal/ban, not onspeakor floor changes, and applies only tosealed-sender-key-v1. The implemented Phase 7 runtime and golden vectors enforce that classifier. - Moderation audit and replay. Typed controls produce metadata-only audit facts and reuse host actor-bound idempotency semantics. Room does not create a private idempotency database or persist live content in the audit path.
- Descriptive metadata mutability.
namebelongs only to the immutableroom.v1opening payload. Mutabletopicandsummaryare separate append-only Room facts folded latest-wins by the shared globalseq/no; clearing is represented by an accepted empty value, never by deletion. Current root authority or currentmoderateauthority narrowed bymetadata/updatemay set them. All three values remain content-like, exposure-governed, non-authoritative data and are excluded from membership attestations and metadata-only diagnostics. - Inference execution metadata. A scoped
inference-execution-posture.v1value binds its assertion owner, participant/runtime-binding subject, policy generation, validity, and processing-boundary ref and remains an admission and presentation input, not realized evidence. Inference-derived contributions carry or reference per-result P090 provenance, while participant/provider badges are derived read-model metadata only. Neither value changes identity, membership, grants, floor control, moderation, ordering, or effect authority.
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.
The descriptive metadata extension adds these named invariants:
inv-room-name-immutable:nameis fixed by the one accepted Room opening; exact replay is idempotent and no later record may change it;inv-room-metadata-moderate-gated:topicandsummaryare appended only after current root ormoderateauthority scoped tometadata/updatesucceeds, before any projection effect;inv-room-metadata-char-bounded: scalar-value limits and non-whitespace-control refusal are enforced in the pure core, independently of UTF-8 byte ceilings;inv-room-metadata-exposure-bounded: descriptive values inherit Room exposure and any explicit content-classification contract, and never become routing, membership, or attestation metadata.
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,room-topic.v1,room-summary.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.
Before Phase 8 runtime work, extend that gate with closed schemas and positive examples
for the optional room.v1.name, room-topic.v1, and room-summary.v1; schema-gate
import/export registration; core/admission vectors for exact scalar and byte bounds;
moderation refusal; opening immutability conflict; exposure redaction; and a deterministic
metadata replay vector. A semantic over-limit value that remains below a coarse schema
ceiling MUST reach and fail the Room core test, rather than being mislabeled as a JSON
Schema refusal.
Durable Store and Projection Contract¶
The durable room plane is append-only. Implementations should separate:
- Fact store: accepted
room.v1,room-membership.v1,room-event.v1,room-topic.v1, androom-summary.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, immutable opening
name, effective latesttopicandsummary, their bounded setter/source lineage, and high-waterseq/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 and stop the in-memory fold; recovery constructs a fresh projection from a trusted fact prefix after an operator/policy path chooses quarantine, rollback, or authoritative replay. The live fold never clears a sequence conflict in place; - 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;
- the first accepted opening fixes
name; any differing opening for the same Room is a conflict, while exact replay is idempotent; - accepted topic/summary facts replace only their corresponding effective projection
value and lineage when their global Room
seq/noadvances; they do not create a second metadata high-water; - 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; - operator projection: status exposes the typed projection status and bounded technical conflict reason, without payload or private moderation text, so terminal conflicts are distinguishable from recoverable sequence gaps;
- 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 | Replaces the subject's complete admitted grant snapshot. |
opened or delegated |
member-revoked |
unchanged | Removes membership and all grants for one subject, then 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>",
"name": "Mail server diagnosis",
"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-topic.v1 and room-summary.v1 (durable Room payloads; signed by their
enclosing Agora records):
{
"schema/v": 1,
"room/id": "room:<authority>:<id>",
"topic": "Diagnose rejected outbound mail",
"set-by/subject": { "kind": "nym", "id": "nym:did:key:moderator" },
"seq/no": 5,
"created-at": "2026-06-23T10:02:00Z"
}
{
"schema/v": 1,
"room/id": "room:<authority>:<id>",
"summary": "Inspect DNS, queue state, TLS policy, and the latest delivery logs.",
"set-by/subject": { "kind": "nym", "id": "nym:did:key:moderator" },
"seq/no": 6,
"created-at": "2026-06-23T10:02:30Z"
}
The setter is not self-asserted authority. The admission boundary binds it to the
authenticated actor and enclosing Agora author evidence, checks current root or
moderate authority scoped to metadata/update, validates content, then appends the
fact. Reversing that order would permit an unauthorized write even if the projection
later hid it.
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, room-topic.v1, room-summary.v1, and
room-relay-endpoint.v1; consumers fold them into one membership/lifecycle,
descriptive-metadata, and carrier-endpoint projection keyed by room/id with one
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.
Phase 7 Implementation Shape¶
Phase 7 extends the existing strata rather than adding a moderation service beside Room:
- extend the pure
RoomProjectionfold with scoped moderation and access-denial input; after schema and canonical identity validation, reject a root target before every other authority decision; - reuse
RoomMembershipOp,RoomEventKind, the closedROOM_GRANTSvalidator, and scoped authority-delegation machinery. Mute/unmute are full grant-set snapshots, member removal is the existing revoke path, and moderation scopes narrowmoderate/delegaterather than extending the top-level grant vocabulary; - compose floor policy and leases above
RoomLiveTransport, then reconcile all carriers through the existingrefresh_projectionmethod. Do not add mute, floor, kick, ban, or denial methods to the transport trait; - define floor modes as a closed enum with
deny_unknown_fields, and define one sharedROOM_REPLY_REFS_MAXconstant with baseline value8; schema and runtime validators import the same bound instead of repeating a literal; - fold
room-access-denial.v1into the same ordered Roomseq/nostream and projection; do not create a second high-water or authority store; - reuse the host's actor-bound idempotency registry and conflict semantics for typed
controls. Emit
room-moderation-audit.v1only after bounded metadata validation and never include live payload, private reason text, bearer, or crypto material; - land member-visible moderation first because it needs projection refresh and session drop only. Add recipient-set classification and selective sealed rotation as a separately tested profile-gated slice; keep conservative Phase 6B rotation until it passes the sealed-relay golden and adversarial vectors;
- register each frozen schema in schema-gate import/export mapping and the schema sync checker, with positive and negative fixtures for unknown fields, unsupported enum values, root targeting, ref bounds, identity conflict, and projection-order drift.
Phase 8 Implementation Shape¶
Phase 8 adds descriptive values without creating a mutable Room row or a second moderation service:
- extend the existing
room.v1DTO with optionalname, projecting absence as""; - add
room-topic.v1androom-summary.v1DTOs as ordinary durable Room facts in the shared globalseq/nostream and Agora topic; - centralize scalar count, UTF-8 byte count, and control-character checks in pure
room-corehelpers used by opening, topic, summary, replay, and service admission; - extend the closed moderation-scope vocabulary with
metadata/update, then reuse the current root/scopedmoderateauthority evaluator and host actor-bound idempotency semantics; authorize before fact commit or projection mutation; - fold accepted metadata facts into the existing Room projection and source lineage; add no metadata table, independent high-water, or in-place update path;
- expose descriptive fields only through exposure-authorized Room read models and, where present, the same explicit content-classification contract as Room content. Keep them out of membership attestations, carrier diagnostics, notification pings, and metadata-only audit bodies;
- register schemas and examples through schema-gate and Node schema sync, then prove deterministic Agora replay and restart reconstruction before adding UI editing controls.
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. - [ ] Requalify the Phase 6A POSIX deployment profile against the current P082/P083
host-projection authority boundary. A 2026-08-27 GNU/Linux run advances past TLS
join after the Room-WSS first-frame timeout fix, then correctly refuses the runner's
default
mixed-sensoriumpublication withhost-projection-required. Restore the claimed Sensorium latest-state/invoke evidence through the admitted host projection adapter; do not bypass that authority check or silently weaken the profile to generic relay payload injection. This does not block P074-009's explicitly Room-onlyroom-live-message.v2transport conformance profile.
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.
Phase 7 — Generic moderation, floor control, and message linkage¶
This is consumer-neutral Room work. Terms such as op and voice MAY be used as UI
aliases, but they are not wire-level roles: Room owns scoped moderation authority,
the existing speak grant, temporary floor allocation, and access denial. A Corpus
Chair or another consumer-specific role may request or map to these primitives, but
Room MUST NOT infer that domain role or transfer its authority automatically.
P069 Phase 8A is the first implemented consumer of this generic surface and maps its
domain vocabulary through canonical Room admission rather than extending Room Core.
- [x] Freeze the moderation vocabulary and authority matrix before extending runtime
behavior. The opening
authority/subjectremains the non-removable root authority while the Room is active. Define which actions require root authority, scopedmoderateauthority, ordinary membership, or currentspeakplus floor authority; explicitly forbid a moderator from transferring root authority, delegating another moderator unless separately authorized, changing lifecycle/policy outside its scope, or removing, muting, banning, or demoting the root authority. After schema and canonical target validation, make root targeting the first fail-closed projection authority check. - [x] Extend durable authority delegation with closed moderation scopes rather than an
ambient
opbit. At minimum, distinguish participant invitation, participant removal, access denial/reinstatement, andspeakgrant/revocation. Each delegation binds the Room, subject, issuer, scope, expiry, sequence, and revocation reference; it is non-transitive by default, monotonically narrowable, and ineffective after expiry, revocation, Room closure, or an authority projection conflict. Model every scope as a closed narrowing of existingmoderateordelegate, not as a new top-level grant. - [x] Make
speakchanges effective on active sessions without treating connection presence as authority. Sending MUST re-check the current Room projection, and stale bearer sessions MUST fail closed after grant replacement. ReuseRoomMembershipOp::Grantwith a complete grant snapshot for mute/unmute, the existing revoke path for member removal, andRoomLiveTransport::refresh_projectionfor carrier convergence; add no transport-specific operation. Derive the snapshot from the current projection and prove every non-speakgrant remains byte-equivalent. - [x] Define a bounded, ephemeral floor-control contract for orderly turn taking instead
of rewriting durable membership facts for every utterance. Add explicit
open,moderated, andround-robinmodes plus short-lived floor leases bound to Room, subject, policy generation, issuance sequence, and expiry. Effective send authority in controlled modes iscurrent speak grant AND current floor lease; restart, failover, stale generation, timeout, and ambiguous ownership all revoke the floor fail closed. Prune expired leases before lease mutation, live send, and operator-status projection, and reconcile lease/queue membership on every durable projection refresh. Operator/moderator override and queue bounds must be explicit. Use a closed enum plusdeny_unknown_fields; unknown modes fail at both schema and DTO boundaries. - [x] Define a durable Room access-denial record and projection with precedence over invitations, passports, membership attestations, and positive grants. Ban records bind Room, subject, issuer authority, reason code/ref, sequence, optional expiry, and supersession/reinstatement evidence. Reinstatement removes only the denial and MUST NOT silently restore former membership or grants. Kick remains a distinct immediate session-and-membership action without an implicit durable ban. Fold denial into the same ordered projection and Room high-water; no parallel authority store is allowed.
- [x] Implement kick, ban, unban, mute, unmute, invite, and remove as typed,
idempotent control intents admitted through current Room authority rather than
caller-supplied role labels. Admission and durable fact commit precede transport
effects; projection refresh then drops or narrows affected live sessions. Partial
carrier cleanup is reported as degraded state and cannot roll back the authoritative
denial or grant decision. Reuse host actor-bound idempotency and emit bounded
room-moderation-audit.v1decisions; do not add a Room-local replay store. - [x] Land member-visible moderation fencing first. Ban and membership removal refresh
the projection and drop affected sessions; mute/unmute and floor changes refresh
effective send authority.
member-visible-tls-v1has no sender key and performs no crypto rotation. - [x] Add the separately profile-gated sealed moderation slice. Derive
membership/epochfrom the latest decrypting-recipient-set change, rotate on join and recipient removal/ban, and do not rotate forspeakor floor changes. Replace the current conservative all-membership-change trigger only after golden and adversarial vectors prove old-epoch refusal, pre-join isolation, bounded redistribution, and restart/failover recovery. - [x] Add stable, carrier-neutral message references and bounded reply linkage to the
next live-message contract revision. Replace v1
noncewith one sender-supplied Room-scoped immutablemessage/ref; v2 cannot carry both. Bind exact replay to sender, sequence, ref, and payload digest. Add a unique reply-ref set bounded by the sharedROOM_REPLY_REFS_MAX = 8constant. Reply refs remain non-durable conversation hints, not P081 causal refs, receipts, trace edges, or retrieval authority. - [x] Provide metadata-only moderation diagnostics and operator affordances showing
effective authority,
speakstate, floor owner/queue occupancy, active denials, policy generation, membership high-water, sealed recipient-set epoch where relevant, and degraded cleanup reason. Diagnostics must not expose bearer tokens, sender keys, sealed payloads, live message content, or private reason text, and available actions must be derived from current authority rather than rendered from static UI assumptions. - [x] Add schema fixtures, pure projection golden vectors, adversarial tests, and a multiprocess federated acceptance profile. Cover unauthorized delegation, moderator re-delegation, root-authority removal, grant races, duplicate and expired floor leases, queue overflow, kick versus ban, unban without regrant, stale attestation and bearer replay, member-visible no-rotation, selective sealed-key rotation, nonce/ref conflict, reply-ref bounds, unknown floor modes, missing referenced messages, relay failover, idempotent retries, audit redaction, and deterministic replay on independent nodes. Wire all frozen schemas into schema-gate mappings and positive/negative fixture checks.
- [x] Document consumer mappings without coupling Room to them. Corpus may map its
current
Chairto a bounded moderator delegation and use floor control for carousel deliberation; other components may choose different role names and policies. No consumer role, model output, or Agent decision becomes Room authority until the canonical Room admission path commits the corresponding scoped fact.
Implementation evidence lives in room-core, room-service, room-wss, the Agora
Room projection, Schema Gate fixtures, and the Story 011 process acceptance profile.
The tests cover projection replay, root and scope refusal, restart-invalidated floor
leases, member-visible convergence without sender-key churn, sealed recipient-set
epochs, and the Corpus consumer bridge without moving Corpus roles into Room Core.
Phase 8 — Exposure-governed descriptive metadata¶
This phase is post-MVP descriptive state. It does not reopen the completed Room authority, membership, relay, or moderation baselines.
- [ ] Freeze and register the contracts: add optional immutable
nametoroom.v1; add closedroom-topic.v1androom-summary.v1schemas, positive/empty examples, Agora record-kind mappings, schema-gate ingress/export mappings, Node schema sync, and generated docs. Keep signatures inagora-record.v1, not inside Room payloads. - [ ] Add shared
room-corevalidation with authoritative Unicode scalar limits32/64/3000, UTF-8 byte ceilings128/256/12000, and refusal of every non-whitespace control character. Prove empty values, exactly-at-limit multibyte values, and refusal at 33/65/3001 scalars with emoji fixtures; separately prove the byte ceilings and coarse schema ceilings so each failing layer is named honestly. - [ ] Extend the pure Room DTO and projection fold with immutable opening
nameplus latest-winstopicandsummarylineage. Exact opening replay is idempotent; a differing opening is a typed conflict. Metadata facts use the existing global Room high-water and same-sequence digest conflict semantics, never an independent metadata sequence or mutable row. - [ ] Add typed topic/summary control intents to the existing Room moderation/service
path. Bind
set-by/subjectto the authenticated actor and Agora author evidence, extend the closed scope vocabulary and typed evaluator withmetadata/update, require current root or activemoderateauthority carrying that scope, validate and authorize before append, and reuse host actor-bound idempotency. Missing/revoked/expired authority and closed/expired Room state fail before storage or projection effects. - [ ] Persist and replay topic/summary facts through the existing Agora Room projector, rebuild all three values after restart, and expose them only through Room read models authorized by current exposure and, where present, the same explicit content-classification contract as Room content. Membership attestations, notification pings, carrier diagnostics, and metadata-only audit must omit the text. Non-member projections default to omission and may include it only when the current exposure and any explicit classification contract authorize that audience. Operator evidence may expose bounded setter/source refs and sequences without copying private content.
- [ ] Add schema, core, service, replay, exposure, and multiprocess acceptance tests. Cover unknown fields, malformed subjects, NUL/controls, scalar and byte bounds, empty clearing, unauthorized moderation, immutable-name conflict, duplicate replay, same-sequence digest conflict, deterministic latest-wins projection on two nodes, restart reconstruction, and absence of private descriptive text from attestations, diagnostics, notifications, and redacted audit.
Phase 9 — Inference posture, contribution provenance, and projection¶
This additive phase depends on the accepted P090 schema and canonical join. It does not reopen the completed Room identity, membership, authority, relay, moderation, or message-ordering contracts.
P090-010a checkpoint (2026-09-06): explicit room-live-message.v3 carries the
content-bound room-contribution-inference.v1 with minimal disclosure. Local WSS
delivery/replay, durable evidence-aware sequence recovery, Agent-owned source
attachment, receiving policy and bounded sender-relative UI badges have executable
coverage. The open rows below are broader: captured-product adapters, scoped
participant declarations and general federated projections are not closed by that
inline contribution checkpoint. No new physical federation acceptance is claimed.
-
[x] P090-010a inline live contribution carriage, durable replay, independent receiver policy and bounded participant badges; control frames are excluded from inference counts and missing ancestry stays unknown.
-
[ ]
room-inference-posture-characteristic: define and admit a signed, Room- and time-scopedinference-execution-posture.v1value; bind its assertion owner, participant/runtime-binding subject, policy generation, validity, and exact processing-boundary ref without making it a membership grant or a permanent participant property. Sender boundaries are compared only through explicit locally admitted relations; unrelated boundaries remain non-matching orunknown. - [ ]
room-contribution-inference-provenance: carry the bounded descriptor or immutable ref on inference-derived live contributions and captured products; preserve it across carrier adapters, replay/capture boundaries, and federated projections without exposing provider-native sessions or credentials. - [ ]
room-inference-provenance-read-model: derive per-contribution and bounded participant/Room aggregates with the canonical monotonic join, explicit unknown and provider-withheld states, sender-relative boundary refs, and peer-attested evidence. - [ ] Add policy and UI projection tests for deny, warn, allow, and filter behavior; local plus non-local, non-local plus unknown, provider redaction, legacy absence, participant runtime changes, and a receiver that must not upgrade peer evidence.
Open Questions¶
No unresolved question blocks Phase 6A, Phase 6B, or Phase 8. 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.
- Implement Phase 8 contract and pure-projection tests before exposing editable metadata controls in daemon or Node UI surfaces.
- Accept the P090 schema and canonical join before adding Room posture, contribution, badge, or UI fields independently; then implement Phase 9 producer-neutral carriage before consumer presentation.
- 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.