Proposal 035: Agora — Topic-Addressed Record Relay and Shared Record Substrate¶
Based on:
doc/project/40-proposals/013-whisper-social-signal-exchange.mddoc/project/40-proposals/021-service-offers-orders-and-procurement-bridge.mddoc/project/40-proposals/023-federated-offer-distribution-and-catalog-listener.mddoc/project/40-proposals/024-capability-passports-and-network-ledger-delegation.mddoc/project/40-proposals/025-seed-directory-as-capability-catalog.mddoc/project/40-proposals/026-resource-opinions-and-discussion-surfaces.mddoc/project/40-proposals/032-key-delegation-passports.mddoc/normative/20-vision/VISION.md(Memarium, Swarm)doc/project/60-solutions/SOLUTIONS.mdorbidocs/AGENTS.md(Node-attached roles)
Related configuration work (proposed):
Proposal 091: File-backed Configuration and Explainable Composition. P091 inventories Agora's standalone configuration reader and its legacy file-selection/error behavior, then plans shared Rust consumption and explicit migration. This affects operator configuration, not Agora record identity, publication authority or relay semantics. See P091-001a/010a/011/015.
Status¶
Accepted
Date¶
2026-04-11
Executive Summary¶
Orbiplex today has several subsystems that each maintain their own append-only, signed, topic-shaped state:
- the seed directory replicates capability passports and service offers,
- the offer catalog holds federated offer listings,
resource-opinion.v1from proposal 026 authors opinions about external resources with no shared substrate to store or retrieve them,- and
whisper-signal.v1from proposal 013 dispatches privacy-bounded social rumors without any durable query surface.
Every one of these subsystems invents its own topic, its own relay, its own replication rhythm, and its own query API. The consequence is that adding one more kind of shared record — a comment under a URL, a note under a product, a public log entry under a workflow run — means designing one more bespoke store.
This proposal introduces one neutral substrate:
- Agora — a topic-addressed, append-only, signed record relay, operated by umbrella operators and consumable by any Node.
And one neutral envelope:
agora-record.v1
The key decisions are:
- a record is a signed, timestamped, content-addressed artifact authored by a participant and placed under one topic,
- a topic is addressed by an opaque
topic/keystring; the substrate does not interpret, type, or split it, and no single use case owns the shape of topic keys, - the record envelope is extensible by
record/kindandcontent/schema, so that opinions, comments, public logs, offer snapshots, or swarm gossip can all live side by side under one transport contract, - when a record is meaningfully about an external subject (a URL, a
product, a node, an org), it MAY carry an optional
record/aboutlist that references the resource identity model from proposal 026; this relation is orthogonal to topic placement and is not used as the primary key, - Agora relays are umbrella-operated and federated, following the same operational pattern as the seed directory (proposal 025) and the offer distribution layer (proposal 023),
- Matrix (via a dedicated homeserver deployment) is the reference backend
for the first implementation, but the
agora-record.v1envelope is backend-neutral and a later native transport may replace or complement it.
Agora is orthogonal to Memarium. Memarium, as described in the vision document, is the curated memory-and-knowledge layer that decides what must not disappear. Agora is a lower primitive: a durable, citeable log of public records indexed by topic. A community-scale Memarium space may use Agora as one of its underlying substrates, but it is not the only one and Agora does not depend on Memarium being present.
Context and Problem Statement¶
Several existing proposals implicitly demand a public, topic-addressed, append-only substrate:
- Proposal 013 (Whisper) defines a privacy-bounded rumor flow. Rumors are ephemeral by design and therefore do not need Agora, but the interest semantics and the threshold-reached events can cleanly degrade into durable records once a threshold is crossed.
- Proposal 023 (federated offer distribution) is a domain-specific append-only replication layer. It works, but it reinvents parts of what Agora would provide once, centrally.
- Proposal 025 (seed directory as capability catalog) is a similar replication layer with a different payload shape and a different query model.
- Proposal 026 (resource opinions) defines the opinion artifact and the generic resource identity model, but explicitly leaves the storage and discussion surface as a later attached layer.
Three separate systems, three separate relay mechanisms, three separate caches, three separate federation rhythms, three separate retention rules. The cost of adding the fourth one — comments on proposal pages, public notes under workflow runs, shared annotations under a code artifact — is disproportionately high.
The missing contract is not a schema. The missing contract is one neutral substrate that any future topic-shaped record can sit on without rewriting the transport.
What is absent today¶
| Need | Current state |
|---|---|
| One portable record envelope usable for many record kinds | Not present |
| A generic, backend-neutral topic addressing model shared across subsystems | Not present; each subsystem invents its own (proposal 026's resource identity addresses subjects, not topics, and is orthogonal to this question) |
| One umbrella-operated relay role for topic replication | Per-subsystem ad hoc |
| Subscription and replay under one topic | Per-subsystem ad hoc |
| A stable place for comments, public annotations, long-lived notes | Missing |
| A substrate for the community layer of Memarium | Missing |
Goals¶
- Define one neutral, signed, content-addressed record envelope
(
agora-record.v1) suitable for many record kinds. - Address topics by an opaque
topic/keystring, so that the substrate never has to interpret, type, parse, or split topic identifiers, and no single use case dictates their shape. - Keep resource identity (proposal 026) as an orthogonal axis, reachable
through an optional
record/aboutfield, so that the two questions where is this record? and what is this record about? remain cleanly separated. - Define the role of an Agora topic relay and its federation responsibilities.
- Define one minimal query and subscription API that any Node can call.
- Pick a reference backend (Matrix homeserver) but keep the envelope backend- neutral.
- Leave room for migrating existing subsystems (offer catalog, opinion storage) onto Agora without breaking them.
Non-Goals¶
- This proposal does not define how Memarium curates, indexes, or retrieves records semantically. Memarium remains a separate primitive.
- This proposal does not define E2E-encrypted private topics. Agora records are public-by-default at the relay level; private channels are a later proposal.
- This proposal does not define moderation, sanctions, or content policy. Those attach to record kinds, not to the substrate.
- This proposal does not mandate Matrix. Matrix is picked as the reference backend to avoid inventing a new transport before the envelope is proven.
- This proposal does not migrate existing offer catalog or seed directory storage onto Agora. That migration is a later decision, discussed under Consequences.
Decision¶
1. Record Identity and Addressing¶
Every Agora record MUST be addressable by the pair:
topic/key— an opaque string identifying the topic under which the record lives,record/id— a content-addressed identifier of the record itself.
1.1. Topic Key¶
topic/key is an opaque UTF-8 string. The substrate MUST NOT:
- parse
topic/keyfor internal structure, - split it on any delimiter,
- attach type semantics to any prefix or suffix,
- assume URI, URN, hierarchical, or resource-identity semantics.
The substrate MUST enforce only the following canonicalization rules:
- Unicode NFC normalization,
- printable characters only; control characters (C0, C1, DEL) are rejected,
- leading and trailing whitespace is rejected (not trimmed),
- maximum length of 512 bytes after UTF-8 encoding,
- empty
topic/keyis rejected.
Applications choose their own topic-key conventions. Common patterns include:
- namespace-prefixed path-like strings, for example
ai.orbiplex.proposals/035,mycompany/build-log, - random or content-derived identifiers, for example
01JRCY0Y7T4Y9JQK8K7R6K4M3Morsha256:4b7c..., - human-readable channel names scoped by operator, for example
example-coop/announcements.
Applications that want topics derived from resource identity (proposal 026)
MAY construct topic keys that embed the resource identity as a substring
(for example ai.orbiplex.opinions/url/sha256:4b7c...), but this is a convention of
the record kind contract, not a rule of the substrate.
Two applications that pick the same topic/key will share a topic. The
substrate does not enforce global namespacing; applications SHOULD use
operator- or project-scoped prefixes to avoid collisions. Kind contracts
MAY require stricter prefixes.
The recommended namespace scheme — six named prefix classes
(ai.orbiplex.<subsystem>/<name>, participant:<pid>/…,
node:<node-id>/…, federation:<fid>/…, local/…, private/…)
plus the rules that turn those prefixes into cryptographic ownership
at the relay ingress — is defined in proposal 046 (Agora Topic-Key
Namespace Conventions). That proposal is non-normative for the
substrate: topic/key remains opaque here; all enforcement lives in
relay policy and author-side hygiene. Deployments that adopt 046
run the one-compare core discriminator (starts_with("ai.orbiplex."))
and the five-check ingest policy before any 041 attestation tiering.
1.2. Record Identifier¶
record/id is a content-addressed identifier computed as:
record/id = "sha256:" + base64url-no-pad(sha256(canonical-json(record-without-id-signature-and-relay-fields)))
The relay fields excluded from this payload are relay/received-at,
relay/id, and relay/hops.
This matches the Orbiplex canonical hash convention already used by
node/capability/src/lib.rs::sha256_base64url for signed artifacts and
passport hashes, so that a single content-address prefix covers both the
capability layer and the record layer.
Content addressing is necessary so that citations across relays, mirrors, and cached views reliably resolve to the same record regardless of which relay served it.
1.3. Optional Subject Reference¶
A record MAY carry an optional record/about field that links the record
to one or more external subjects using the resource identity model from
proposal 026:
"record/about": [
{ "resource/kind": "url", "resource/id": "https://example.org/article" },
{ "resource/kind": "ean", "resource/id": "5901234123457" }
]
Rules:
record/aboutis OPTIONAL and MAY be empty or absent.- When present, each entry MUST follow the resource identity rules from proposal 026.
record/aboutis not a topic key and MUST NOT be used as the primary index by the substrate. It is a secondary, optional index that enables cross-topic queries of the form show me every record about this subject, regardless of where it was posted.- A kind contract MAY require
record/aboutto be present for records of a givenrecord/kind(for exampleopinionrecords MAY be required to carry exactly onerecord/aboutentry). The substrate itself does not enforce such rules.
2. agora-record.v1 Envelope¶
The MVP envelope is:
schema = "agora-record.v1"
Required fields:
schemarecord/idrecord/kindtopic/keyauthor/participant-idauthored/atcontent/schemacontentsignature
Optional fields:
record/about— list of{ resource/kind, resource/id }entries naming external subjects this record refers to (see section 1.3)record/parent— one parentrecord/idwhen the record is a reply, annotation, or successorrecord/supersedes— one priorrecord/idreplaced by this revisionrecord/policy— one policy record reference. For comment threads this points to athread-policyrecord whosecontent/schemaiscomment-thread-policy.v1.record/tags— short free-form tags (application use)record/lang— BCP 47 language tagauthor/nym-proof— whenauthor/participant-idcarries anym:did:key:...pseudonymous identity (see invariant 9 below), this optional field carries anym-authorship-proof.v1; in the M4 baseline the proof inlines enoughnym-certificate.v1material to verify the current nym without remote lookup, while optional refs may point to longer lineage, audit, or reputation history. The proof is part of the signed canonical bytes so the binding cannot be stripped in transit.relay/received-at— stamped by the relay on ingest; never part of the signed payloadrelay/id— identifier of the relay that first ingested the recordrelay/hops— relay-local hop count stamped by relay implementations; never part of the signed payload
Ingest invariants:
topic/keyMUST satisfy the canonicalization rules from section 1.1.record/idMUST equal the content hash of the canonical payload withrecord/id,signature,relay/received-at,relay/id, andrelay/hopsomitted.signatureMUST be a valid Ed25519 signature by the envelope author subject key named byauthor/participant-id. For ordinary records this is the participant key and is verifiable against the participant's current capability passport chain (proposals 024 and 032). For records whose kind permits pseudonymous authorship, invariant 10 defines the nym-key path.content/schemaMUST be present and MUST name a known content schema identifier; relays MUST accept unknown schemas but MAY mark them as non-indexable until a schema contract is registered.authored/atMUST be within the relay's configured clock skew window (default: ±10 minutes) relative to relay wall clock, or the record is flaggedclock_skewand still stored but excluded from live query results until the window passes.record/parentandrecord/supersedes, when present, MUST resolve to known records under the sametopic/keyor be flaggeddanglinguntil the parent appears.author/participant-idMUST carry a currently valid author subject: normallyparticipant:did:key:..., ornym:did:key:...only when the record kind explicitly permits pseudonymous authorship. Sanctioned or revoked author subjects are still stored but excluded from default query output.record/about, when present, MUST follow the resource identity rules of proposal 026. The substrate MUST NOT derivetopic/keyfromrecord/aboutand MUST NOT requirerecord/aboutto match the topic.record/policy, when present, MUST resolve to a known policy record under the sametopic/keyor be flaggeddanglinguntil the policy appears. The substrate validates only the reference shape. Kind-specific policy evaluators decide whether a missing or stale policy blocks publication.-
Pseudonymous authorship.
author/participant-idMAY carry anym:did:key:...identity in addition to aparticipant:did:key:...identity, when the record kind permits pseudonymous authorship (currently:whisper, see §3). In that case: -
the envelope signature MUST be Ed25519 by the private key of the nym, not of any underlying participant;
- the envelope MUST carry
author/nym-proofcontaining a currently validnym-authorship-proof.v1; in the M4 baseline this proof inlines anym-certificate.v1(proposal 015) that binds the nym to its issuing council's authorization scope and can be verified without a network lookup; - the ingest-attestation gate (proposal 041) MUST accept the nym certificate in place of a participant capability passport for ingest admission, under the consumer-local ingest mode configured for the topic;
- subject sanctions and revocations attach to the nym identity for the nym's bounded validity window; no attempt is made to retroactively correlate nym records with the underlying participant on the substrate layer.
3. Record Kinds and Content Schema Extension¶
record/kind names the what of the record. content/schema names the
shape of the payload inside content.
The envelope is generic: it does not prescribe the list of kinds, only the registration rule. Each record kind is defined by a separate, small schema contract that lives next to the subsystem that owns it. Examples for MVP:
record/kind |
content/schema |
Owner |
|---|---|---|
opinion |
resource-opinion.v1 |
proposal 026 |
comment |
plain-comment.v1 |
Agora base kind |
thread-policy |
comment-thread-policy.v1 |
Agora base kind |
gossip |
public-gossip.v1 |
Agora base kind |
annotation |
plain-annotation.v1 |
Agora base kind |
whisper |
whisper-signal.v1 |
proposal 013 |
whisper-durable |
whisper-threshold-record.v1 |
proposal 013 follow-up |
public-log |
public-log-entry.v1 |
application use |
Rules:
- An unknown
record/kindMUST be stored and served, but MUST be treated as opaque by the relay query layer until a schema contract is registered. - A schema contract MUST be a separate proposal or schema file; it MUST NOT be merged into this proposal.
- Applications MUST NOT assume that all relays understand all kinds.
4. Topic Relay Role¶
An Agora topic relay is an umbrella-operated Node-attached service whose responsibilities are:
- Ingest — accept signed
agora-record.v1records from authorized participants, verify them, content-address them, and write them to durable storage under theirtopic/key. - Federate — replicate records across peer relays according to a configured federation policy; relays do not have to replicate every topic, only the topics they carry.
- Serve — expose query and subscription APIs for Nodes.
- Retain — enforce per-topic retention rules (time, count, size).
- Exclude — honor participant sanctions and record-level flags by excluding records from default query output without deleting them.
Three operational modes are defined:
- Canonical relay — operated by an umbrella operator, publishes a stable base URL, holds the durable source of truth for a set of topics.
- Cache relay — operated by a Node, mirrors a subset of topics for local availability and offline operation. A cache relay does not have to be reachable by other relays.
- Origin relay — operated by the author's Node, used to sign and first- ingest the record before it reaches a canonical relay. An origin relay is ephemeral and MAY be the same process as a cache relay.
The deployment pattern is analogous to the seed directory (proposal 025) and the federated offer catalog (proposal 023): each umbrella operator runs one or more canonical relays, Nodes run cache relays, and federation happens between canonicals.
5. Query and Subscription API¶
Minimum Node-visible API contract. Every path parameter that carries a
topic/key MUST be percent-encoded by the caller; the substrate MUST NOT
interpret the decoded value beyond the canonicalization rules from
section 1.1.
5.1. List records under a topic¶
GET /v1/agora/topics/{topic-key}/records
?since={cursor}
&limit={n}
&kind={record-kind}
&include_flagged={bool}
Response:
{
"records": [ { ...agora-record.v1... }, ... ],
"next_cursor": "opaque-next-cursor",
"cursor_pruned": {
"requested_from": 5,
"first_available": 10
},
"query_attestation": { "...agora-query-attestation.v1...": "..." }
}
Cursors MUST be opaque. Relays MAY reorder within a given time slice but MUST be stable for a given cursor.
5.1a. Query attestations¶
Historical query responses SHOULD include an agora-query-attestation.v1
object under query_attestation.
The attestation binds:
- query mode (
topic-recordsorsubject-records), - topic key or subject resource,
- normalized filter after URL decoding and limit defaults,
- returned
record/idvalues in page order, next_cursor,- cursor-pruning notice, if any,
- deterministic response digest.
The digest is sha256: over canonical JSON using the Agora NFC profile
(jcs-nfc-sha256-base64url). It covers the query scope, normalized filter,
returned record ids, and pagination/pruning metadata. It intentionally does
not cover local HTTP headers or transport details.
An attestation MAY be unsigned in early local relays. In that case it is still
useful as a deterministic page proof for local audit and replay comparison,
but it does not prove relay accountability. Signed deployments SHOULD sign the
canonical JSON of agora-query-attestation.v1 with signature omitted.
Subject-index queries MUST attach the attestation after subscribe-policy filtering. Otherwise the proof would describe the unfiltered subject index instead of the response actually returned to the caller.
5.2. Fetch one record by content id¶
GET /v1/agora/records/{record-id}
Returns the stored record or 404. This endpoint MUST return the same
record regardless of which relay serves it, for a given record/id.
5.3. Subscribe to live updates¶
POST /v1/agora/topics/{topic-key}/subscribe
Opens a streaming connection (long-poll or server-sent events in MVP; WebSocket post-MVP) that delivers new records under the topic. The subscription is advisory and MAY be dropped by the relay under load.
5.4. Submit a record¶
POST /v1/agora/topics/{topic-key}/records
Body: agora-record.v1 signed by the author. Relay verifies invariants and
returns the canonical stored record (with relay/received-at, relay/id,
and relay/hops) or an error.
5.5. Cross-topic query by subject¶
GET /v1/agora/about/{resource-kind}/{resource-id}/records
?since={cursor}
&limit={n}
&kind={record-kind}
&include_flagged={bool}
Returns records whose record/about list contains an entry matching the
given resource identity, regardless of topic/key. This endpoint answers
questions like what records across all topics refer to this URL / this
product / this node?. It is a secondary index and MAY be served more
slowly than topic-keyed queries; relays MAY limit its result window.
This endpoint MUST NOT be used as a substitute for topic-keyed queries.
Authors who want their records to be findable under a subject SHOULD
populate record/about explicitly; the substrate does not synthesize
record/about from topic keys.
5.6. Local deployment channels and authentication¶
When Agora is attached to a Node as a supervised local service, it SHOULD expose the Agora HTTP API on its own port rather than through the daemon's request-response middleware proxy. SSE subscriptions are long-lived streams, and a dedicated port lets the operator decide whether the relay API remains on loopback or is exposed on a public interface.
The Node-attached deployment has three distinct communication relationships:
| Channel | Direction | Purpose | Token boundary |
|---|---|---|---|
| Lifecycle | daemon -> Agora | readiness, initialization, shutdown | daemon-generated middleware authtok, validated by Agora |
| Host capability API | Agora -> daemon | node identity, capability passport lookup/publication, future policy checks | daemon-generated host-capability authtok, validated by daemon |
| Agora API | UI/CLI/client -> Agora | ingest, query, fetch, subscribe | open in MVP; a separate client authtok MAY be enabled post-MVP |
The Agora middleware SHOULD declare its own operator-facing UI surface through
the middleware module report. The host UI may mount that surface under its local
middleware namespace, but the Agora component owns the Agora-specific views and
operator vocabulary. A Node-attached Agora service SHOULD expose a local
operator_visibility status read model covering relay endpoint, namespace
policy, topic ACL, authority-root summary, strict gates, Matrix posture,
HTTP/SSE limits, rate-limit persistence, retention summary, passport state, and
client-auth posture. Full authority-root lists should be shown only on a
client-authenticated local operator surface; unauthenticated status responses
should expose counts and redaction markers instead. This read model is
diagnostic only; it is not a federation protocol and does not authorize publish
or subscribe operations.
The host capability API is a local, authenticated, module-initiated channel. Agora MUST NOT implement node identity, Seed Directory publication, or capability-passport issuance itself. It asks the daemon for those host-owned capabilities, and the daemon remains the broker for identity and passport policy. In concrete Node deployments this is represented by environment variables equivalent to:
ORBIPLEX_HOST_CAPABILITY_BASE_URL
ORBIPLEX_HOST_CAPABILITY_AUTH_HEADER
ORBIPLEX_HOST_CAPABILITY_AUTHTOK_FILE
If client authentication is enabled for the Agora API, it MUST use a token separate from both lifecycle and host-capability tokens. Absence of such a token means open access for development and local-only MVP operation.
5.6.1. Agora CLI TLS trust configuration¶
Local CLI clients MAY connect to Agora over either http:// or https://
base URLs. The CLI TLS trust contract is deliberately small:
- the default HTTPS mode uses the built-in WebPKI root set and rejects self-signed or otherwise untrusted certificates;
--tls-ca-file PATHadds one or more PEM-encoded CA certificates to the trust store;ORBIPLEX_AGORA_TLS_CA_FILEprovides the same value from the environment, and the explicit CLI flag takes precedence;--tls-insecure-skip-verifyis a diagnostic-only bypass for local debugging. It MUST print an operator-visible warning and MUST NOT be used in production.
The custom CA mode is for local labs, private CA deployments, and small operator-controlled federations. It is not a substitute for explicit relay identity policy. Certificate or SPKI pinning remains a future extension for public relay deployments that need tighter endpoint continuity than CA trust alone.
5.7. Capability passport semantics for agora.relay¶
The middleware capability report and the capability passport are separate facts:
- the module report declares capability: "this supervised process can serve
agora.relay"; - the capability passport declares authorization: "the operator officially
offers
agora.relayfrom this node under this issuer, scope, and metadata".
Passport issuance MUST NOT be automatic merely because Agora reports
agora.relay. The operator issues the passport through the same host-owned
identity and capability flow used by other passport-backed capabilities. This
allows the passport to be signed by a high-assurance participant or delegated
operator key rather than by an ephemeral module identity.
Operationally:
- Agora without an
agora.relaypassport remains a valid local relay: UI, CLI, and local Python clients may still ingest and query records. - Agora with an
agora.relaypassport becomes discoverable through the Seed Directory and eligible for federation according to local policy. - At startup, a Node-attached Agora service SHOULD query the daemon for its
current
agora.relaypassport through the host capability API and expose the result in its status surface.
The agora.relay passport metadata schema is defined in
doc/project/60-solutions/008-agora/agora-relay-capability.v1.md. It includes at least the relay API
endpoint, relay role, relay domain, supported transport set, API version, and
advertised canonical topic set.
6. Reference Backend: Matrix¶
The first reference implementation of the topic relay role MUST use a Matrix homeserver (Dendrite or Conduit are preferred for footprint reasons) as the underlying durable substrate. Rationale:
- Matrix rooms map cleanly to Agora topics (one room per
topic/key). - Matrix events are already signed, content-addressed, and ordered in a DAG,
which matches the
agora-record.v1invariants. - Matrix federation provides cross-relay replication without Orbiplex having to invent transport.
- Matrix is already cited as prior inspiration for Orbiplex's messaging strata.
Mapping rules:
- A topic MUST be represented as a Matrix room. The room alias MUST be
derived deterministically from the opaque
topic/keyusing a fixed transformation, so that the same topic resolves to joinable rooms on every relay that carries it. The MVP derivation is:
alias = "#agora." + lowercase-hex(sha256(topic/key)) + ":" + relay-domain
The hash is computed over the UTF-8 bytes of the already-canonicalized
topic/key. Lowercase hex is used here — rather than the canonical
Orbiplex sha256_base64url convention used for record/id — because
the Matrix room-alias localpart is case-insensitive and restricted to
[a-z0-9._=/+-]; base64url would introduce uppercase letters that
Matrix would silently fold, breaking deterministic resolution. The
choice is a Matrix-compatibility concession, not a second content
address: the authoritative record identifier remains record/id.
Relays MUST NOT derive the alias from parsed or typed parts of the
topic key; the key is treated as an opaque byte string by the
derivation function.
2. The relay MUST maintain a separate mapping from topic/key to
human-readable Matrix room names for operator tooling. This mapping is
advisory; it MUST NOT be used as the canonical identifier of the topic.
3. The default writer MUST carry an agora-record.v1 in a Matrix event of type
org.orbiplex.agora.record.v2, using the opaque carrier defined below. Its
base64url author bytes retain the signed envelope, including topic/key and,
when present, record/about, without local relay stamps. The initial default
publication profile uses room v10. Explicit legacy configuration MAY instead
write org.orbiplex.agora.record.v1 with the envelope as a JSON object in
content, under its integer-only number restrictions. Readers support both.
4. record/id is computed from the Agora canonical payload, not from the
Matrix event_id. Consumers MUST index by record/id for citation.
5. The Matrix homeserver's own event signature provides relay-level
authority; the Orbiplex signature field provides author-level
authority. Both MUST be verified.
6. Matrix E2E encryption MUST be disabled on Agora rooms in MVP; Agora
records are public by default.
7. The cross-topic record/about index from section 5.5 is maintained by
the relay outside the Matrix room model, as a secondary index over
ingested events. Matrix itself is not asked to answer subject-based
queries.
Matrix carrier compatibility correction (2026-10-12)¶
The legacy v1 object mapping in rule 3 is narrower than Agora's payload contract.
Matrix canonical JSON
allows only integer JSON numbers in [-(2^53)+1, (2^53)-1], without decimal or
exponent syntax. Modern room versions enforce this restriction. A conforming
homeserver cannot carry the ordinary public-gossip.v1 confidence value 0.5
in the v1 event object. The observed Conduit IntConvert / HTTP 500 is
retained evidence of this mismatch, not a Conduit-specific protocol restriction.
The same boundary affects future accepted payloads with fractional numbers,
including applicable P096 sample content. P026-003's Matrix evidence is scoped
to integer JSON numbers (confidence=1).
The replacement direction is an explicitly versioned opaque Agora carrier: Matrix stores a string containing JSON or base64url of the exact serialized Agora envelope bytes. Decode once under a byte bound, preserve the envelope, then apply the existing Agora signature, schema, topic and admission gates. A Matrix room/event signature remains transport provenance, not Agora authority. P035-004 below freezes the event type, discriminator, base64url encoding and legacy-object read policy, and qualifies v2 with fractional confidence. The write-default decision below adopts v2 as the normal writer and v1 as an explicit integer-only legacy mode. JSON text remains a future encoding option.
The authoring budget must include carrier expansion and the rest of the PDU. Base64url of 48 KiB occupies 64 KiB before wrapper or PDU metadata; escaped JSON strings have content-dependent expansion. The old 48 KiB raw-envelope budget therefore cannot be transferred unchanged. Derive bounded encoding and metadata headroom and verify actual events against the 65,536-byte full-event limit, including homeserver signatures. Refuse before signing/publication, without truncation, when the selected backend budget cannot be met.
P035-004 opaque carrier decision (2026-10-12)¶
Status: accepted design; implementation and runtime qualification are separate gates.
The transport preserves Agora author authority and auditability without imposing
Matrix's integer-only JSON model on Agora content. agora-matrix owns encoding;
Whisper is a consumer. The gossip and signature contracts remain unchanged.
The v2 event type is org.orbiplex.agora.record.v2. Its content is:
{"schema":"agora-matrix-carrier.v1","envelope/encoding":"base64url","envelope/bytes":"<unpadded canonical base64url>","relay/id":"<forwarding relay>","relay/hops":1}
The immutable buffer contains the signed author envelope without top-level
relay/received-at, relay/id or relay/hops. Hop provenance is unsigned
carrier context; each relay records its own receipt time. Forwarding and replay
reuse this buffer. Typed adapters serialize once and cannot recover discarded
JSON spelling. Decode rejects padding, invalid alphabet/noncanonical trailing
bits, BOM, invalid UTF-8, trailing non-whitespace data, duplicate decoded keys
at every object level and nesting beyond 128. Verification, content schema,
topic, delegated/nym proof, admission and Whisper disclosure gates still apply.
Malformed v2 never falls back to v1. Read v1 as legacy object content without an
original-byte claim. json-text remains a considered future encoding only.
The original pre-release rollout decision defaulted writers to v1 and required
explicit matrix_carrier_write_version=v2. The write-default decision below
supersedes that policy: v2 is the adopted default and v1 is explicit legacy.
All participating readers must support v2. There is no room-state negotiation
or automatic downgrade. V1 refuses unsupported numbers before signing.
V2 transaction IDs include both version and
room: agora.v2.<record-digest>.<room-digest>. Device/endpoint transaction
idempotency and domain record/id idempotency are distinct. Publishing v2 after
v1 must create a new event; retries reuse the prepared carrier bytes.
The first profile is room v10 with the explicitly patched pinned Conduit fixture: 36 KiB author bytes, 48 KiB base64url, 4 KiB carrier metadata and 12 KiB remaining PDU metadata, within 64 KiB for the complete Matrix canonical JSON including signatures. The analytical profile assumes at most 10 auth references, 20 predecessor references, 44-byte event references, 255-byte Matrix identifiers, one origin server, two Ed25519 signatures, 128-byte signing-key identifiers and 1 KiB of other metadata. Count JSON escaping. These are profile assumptions, not universal homeserver bounds. Keep the historical ingress bound of 48 KiB separate from creation's 36 KiB; SQLite creation retains its 48 KiB limit. Pre-sign estimates and final-byte checks precede local publication admission. Actual full PDU exports must independently confirm the profile.
Fixture correction (2026-10-12): the replacement profile is
conduit-v0.10.15-full-pdu-1, upstream
db3776328839604e2a1b0b0bf6cd0e8e98cf5cc5 plus the disclosed
node:tools/matrix-fixture/opaque/full-pdu.patch. The initial inspection found
Ruma's content-hash check, which excludes hashes, signatures and unsigned fields.
Runtime boundary testing corrected that incomplete inference: the pinned Ruma
also checks the full signed PDU, with a conservative 65,535-byte maximum;
Conduit maps this refusal to HTTP 400 M_INVALID_PARAM. Preserve that response
and the pinned format-check source alongside actual HTTP 413 M_TOO_LARGE.
The patch independently checks complete Matrix
canonical JSON after signing and unsigned assembly before persistence and returns
M_TOO_LARGE. Pin the source archive, builder and patch digests; retain them with
new reports. Qualification applies to this corrected fixture profile and does
not assert conformance of unmodified upstream Conduit. The historical failed
run remains evidence. See node:tools/matrix-fixture/opaque/README.md.
Sources are additive facts keyed by (room,event_id), with author bytes,
record ID, transport context and local admission sequence. Alternative valid
bytes for one ID (including 0.50/0.5) record alternate-envelope-bytes and
retain the first admitted source. Legacy provenance never fabricates original
bytes. Replay chooses that first retained source deterministically and reports
missing/pruned sources as incomplete. Sources and relay retention are coordinated.
One receiver owns an account checkpoint. Network, decoding and verification
happen outside the write transaction. Sources, admission decisions, diagnostics
and next_batch commit together under S028; index notifications follow commit.
Limited timelines trigger /messages backfill to the retained continuity
anchor: 64 events per page, at most eight pages per room per cycle, including
the initial current-end page. Each page commits admitted source facts, rejected
decisions and its pagination token together; verification remains outside the
transaction. Recovery retains constant-size progress, not accumulated events.
The existing 8 MiB bound limits each HTTP response, not total history. Never advance beyond an
unresolved gap; unavailable history records matrix-history-gap and incomplete
status. Operator replay supports source=matrix-carriers without forwarding.
Local HTTP 201 proves local persistence. The publication read model separately
reports not-requested, pending, blocked, forwarded or rejected-permanent through
record HATEOAS and /v1/agora/records/{id}/publication, under record-read ACLs.
Prepared publications and attempt outcomes are durable S028 facts. Size refusals
and M_TOO_LARGE stop retry; transient failures retain bounded retry and bytes.
M_FORBIDDEN is transient: three attempts use durable exponential backoff and
respect retry_after_ms; exhaustion becomes blocked. A HATEOAS POST retry
requires client authentication and topic write access and reuses the same prepared
bytes and transaction. Permanent content/size refusals cannot be requeued.
New room registration preserves the account checkpoint and triggers no-anchor
history backfill, including a non-limited timeline or absence from incremental
sync. Admission uses the same room snapshot as recovery, so registration racing
network I/O cannot establish an unrecovered anchor. Completed bootstrap stores
its start token even for an empty room; first-page facts and completion are atomic.
Preserve Matrix sender independently of self-declared relay identity. Optional
sender expectations produce diagnostics rather than author admission authority;
untrusted hops saturate and remain loop hints. Typed v1/v2 bridge reasons and
strict-parser reasons must not depend on diagnostic message wording.
Acceptance extends Story-008's existing report with matrix-opaque-envelope:
gossip confidence 0.5, proof-bearing permitted Whisper signals, trace and signed
threshold regression, exact byte variants, two clients, new-client history,
restart, crash boundaries, limited sync/backfill, carrier replay, mixed v1/v2,
malformed/proof/disclosure refusals and creation/full-PDU size boundaries.
Runtime evidence owns process observations and actual requests; conformance and
controlled M_TOO_LARGE fault injection are distinct classes. A consistent
fixture SQLite export supplies full PDU hashes/signatures/auth/prev references.
The export retains Conduit's original stored row separately. For room v10 the
protocol PDU omits only its local event_id lookup field, since the identifier
is derived from the reference hash; every remaining field is counted.
Matrix canonicalization does not apply Agora NFC. Missing Node PDU evidence,
unresolved gaps, or a Node-publication PDU above the protocol bound prevent
carrier qualification. Cross-check every Node PDU against observed /send
acknowledgements. Direct Client-Server boundary sends separately determine
fixture/full-pdu-enforced; an accepted oversized direct event is an environment
interoperability risk, not a Node publication failure or success. Preserve this
result independently and never substitute it for the Node gate. The disclosed
corrected fixture supplies a separate positive environment measurement. One
homeserver proves distribution only; it does not qualify federation, production
readiness, all Whisper surfaces or broaden historical P026 confidence=1 evidence.
Initial measured result (2026-10-12): refused, retained unchanged. The
implementation is present with
default-v1/optional-v2 writes. Frozen Node 31daa07c and Orbidocs 5624bcb
runtime evidence passes four SQLite cases and 17 of 18 Matrix cases, including
confidence 0.5, permitted Whisper signal/trace/threshold paths, exact byte variants,
restart/crash recovery, limited backfill and deterministic carrier replay.
The original combined full-PDU gate failed: pinned Conduit v0.7.0 admitted a
65,798-byte
complete canonical PDU above 65,536. Node's 36-KiB author boundary publications
measured 50,042/50,043 bytes; the oversized event used the separate direct
Client-Server protocol path. The qualifier refused closure, including negative
controls against falsified success markers and corrupted artifacts. Retained
reports, full PDU exports and the refusal are at
node:docs/evidence/matrix-opaque-envelope/README.md.
The reviewed decision separates Node publication proof from homeserver enforcement;
this initial refused run is never relabelled as qualified.
Reviewed measured result (2026-10-12): qualified. Frozen clean Node
20c4c16c52021582f5536b34b9c04c514f43b323 and Orbidocs
862586f9547d438b6b76a759505b112e214e755b reports pass all four SQLite and
21 Matrix cases. The qualifier returns exit 0 at
node:docs/evidence/matrix-opaque-envelope/2026-10-12-review/qualification.json.
Host-signed/UI gossip confidence 0.5, permitted inline-proof Whisper signals
from two independent nodes, trace and host-signed threshold, exact author bytes,
restart/crash recovery, limited backfill and deterministic source replay qualify.
Review cases additionally prove durable transient backoff, blocked membership
refusal with explicit retry, late room binding recovery, and authenticated Matrix
sender provenance independent of relay declarations. Record-bound nonce receipts
already share bounded parent retention; expiry/reopen conformance preserves the
same-fact retry rule and rejects reuse for another record.
All 41 Node-created PDUs are measured completely and matched against observed
/send acknowledgements; maximum 50,043 bytes, within 65,536. Six separate
direct Client-Server boundary events reach 65,534 bytes.
fixture/full-pdu-enforced=true is an independent environment finding for the
pinned, disclosed conduit-v0.10.15-full-pdu-1 profile. Actual M_TOO_LARGE and
Ruma's conservative 65,535-byte M_INVALID_PARAM refusals retain their codes and
source provenance. Seven qualifier controls prevent replacing old failures with
success markers, missing Node PDU proof, artifact tampering and path escape.
Both backends retain 59 separate conformance tests without invented HTTP results.
This closes the carrier slice, not unmodified homeserver conformance, all Whisper,
nym issuance, Anon, federation or production readiness. The frozen run used
default-v1/explicit-v2 configuration; it does not prove the subsequently adopted
v2 default. Historical P026 confidence=1 evidence and the global Agora partial
assessment remain unchanged. P035-005 separately
owns positive interoperability qualification on an unmodified reference server.
Matrix write-default decision (2026-10-12)¶
Status: accepted; implemented and qualified separately by P035-006 on 2026-10-14. This supersedes the pre-release default-v1/optional-v2 rollout policy, without changing P035-004's qualified transport contract or its frozen reports.
Context: no release or deployed infrastructure exists. Maintaining a default object carrier that cannot carry ordinary fractional Agora content has no deployed compatibility benefit. The opaque carrier already qualifies author-byte identity, fractional gossip and the named Whisper families.
Decision: omitted matrix_carrier_write_version and explicit v2 select v2.
Explicit matrix_carrier_write_version=v1 selects legacy object writing, retaining
its pre-sign number refusals and lack of an original-byte guarantee. Readers keep
v1/v2 support for mixed histories, fixtures and retained evidence. No room-state
negotiation, silent downgrade or history rewrite is introduced. Existing prepared
publications retain their recorded carrier version, bytes and transaction ID.
Consequences: the initial default publication profile is still room v10 with 36 KiB author creation, 48 KiB historical Matrix ingress and the complete-PDU budget; SQLite creation remains 48 KiB. Require v2-capable readers for new rooms. Unsupported room profiles use the typed blocked-publication path. Other room versions need their own profile; changing the default does not qualify them, unmodified reference homeservers, federation or production readiness.
Implementation gate: satisfied by P035-006 on 2026-10-14. Node selects v2 in
configuration when absent; CarrierVersion has no domain-wide default. Clean
Node 42fde666 / Orbidocs 4ecec76 pass five SQLite and 27 Matrix cases on the
unmodified Synapse room-v10 profile. A/B omit the config key; protected confidence-0.5
host/UI signing, forwarding, B sync/read, exact bytes, restart and carrier replay
pass. Explicit-v1 controls preserve frozen intents and typed fractional refusal.
Existing Story-008 report conventions and bounded archive integrity/equivalence
are retained. Old reports keep their actual default-v1/explicit-v2 selection.
Evidence: node:docs/evidence/matrix-opaque-envelope/2026-10-14-default-v2/qualification.json.
Unmodified reference homeserver qualification (P035-005)¶
Status: qualified reference profile; independent Node and homeserver gates passed. Date: 2026-10-13.
The reference profile selects official, unmodified Synapse v1.162.0 at upstream
63dc0cbfe514f60743212566374ffeb5e9e295c2, pinned image digest, PostgreSQL 16.13
and room v10. That frozen Node run explicitly selects v2; P035-006 separately
qualifies the omitted-config path. No homeserver patch, custom validation module or silent downgrade is allowed.
The existing 36 KiB creation and 48 KiB historical ingress profiles remain scoped.
The Story-008 matrix-opaque-envelope report retains two independent gates:
every acknowledged Node publication must have a complete canonical PDU within
65,536 bytes; the reference homeserver must separately refuse oversized direct
Client-Server sends while retaining accepted signed PDUs near the boundary.
Direct protocol tests do not qualify Node authoring. Actual request/response and
process observations, the pinned upstream size-check source and complete PDU
exports establish the environment result, rather than proxy or body-size limits.
A consistent read-only PostgreSQL snapshot preserves selected public storage rows; the same stock Synapse version's room-v10 serializer exports protocol PDUs with hashes, server signatures, auth/prev references and all protocol fields. Matrix canonical JSON sizing does not normalize strings through Agora's NFC profile. The runtime matrix repeats protected host signing of gossip confidence 0.5, permitted Whisper signal/trace/threshold carriage, exact author bytes and byte variants, A/B and homeserver/database restart, limited-sync backfill, source-based replay without forwarding, compatibility and typed refusal/recovery controls.
P035-005 qualified at clean Node dcc8133c and Orbidocs c84df99: four SQLite
and 22 Matrix cases. Every one of the 42 acknowledged Node PDUs fits 65,536 bytes
(maximum 49,927). Seven separately attributed direct Client-Server PDUs reach
65,535; actual HTTP 413 M_TOO_LARGE refusals include 64,901-byte requests, proving
the gate is not merely a request-body ceiling. Content hashes, server signatures
and event IDs are verified through the stock serializer and public server keys.
Retained qualification: node:docs/evidence/matrix-opaque-envelope/2026-10-13-synapse/qualification.json.
The initial stock-Synapse attempt failed honestly: resetting the account cursor on
room registration could return a cached initial sync older than the durable anchor.
Node now preserves the checkpoint and bootstraps a room without an anchor through
bounded /messages, including rooms absent from incremental sync. Restart/offline
limited recovery and late binding both pass in the fresh qualified run. The failed
report and qualifier refusal remain retained; no Synapse validation code was changed.
P035-005 requires both gates and the runtime cases at clean frozen sibling revisions. A failed environment gate does not erase a passed
Node budget gate. Preserve original Conduit findings and patched-fixture evidence;
one unmodified server does not qualify cross-homeserver federation, all Whisper
or production readiness. Runnable fixture and exporter ownership stays in Node:
node:tools/matrix-fixture/synapse/README.md.
History recovery and evidence retention decision (2026-10-13)¶
Status: accepted; Node implementation and focused conformance completed after P035-005 qualification. This is not a new homeserver runtime qualification.
Context and options: collecting all recovered events imposed a deterministic
8 MiB bootstrap ceiling. An explicit operator limit would preserve that failure
for long-lived rooms. Choose incremental page admission instead: persist each
page's sources, admission decisions and progress in one S028 transaction. The
account cursor and continuity anchor advance only when its pending batch finishes.
Old buffered progress is admitted once; where it omitted the reached-anchor fact,
continuity is rechecked from the retained batch boundary. Empty completion retains
the /messages start token without fabricating an event ID.
Consequences: total history is no longer bounded by the HTTP response cap. Crash recovery repeats at most an uncommitted page. Pages arrive backwards, with events within a page admitted oldest first; retained source sequence expresses local admission order, not global room chronology. First-source selection and replay remain deterministic. Exhausting one room's page budget does not skip the others' recovery work in that round, but one unfinished room still holds the account checkpoint, current timeline delivery and subsequent sync batches. Eight pages per room per round plus the five-second retry delay and request/verification time bound each round's work; unavailable continuity has no finite account-latency bound. A failed current-end bootstrap request can still stop batch preparation. Per-room independent sync is a separate architectural change, not implied by this correction.
The direct regression is: skip events before a room binding, bind during prepared I/O, receive a new event in a non-limited timeline, then backfill the old events before establishing its anchor. Conformance also covers more than 8 MiB, resume, empty rooms, atomic rollback, common admission and legacy progress. P035-006 separately qualifies long-history, empty-bootstrap and late-binding runtime below; P035-005's original source revisions and results remain frozen.
New public evidence larger than 256 KiB uses reviewable reports, qualification and
a complete member/hash index alongside a deterministic archive. Actual bytes stay
retained; hashes alone cannot substitute for runtime observations or PDUs. Initial
retention is in the repository, with no unowned external blob dependency. Bounded
extraction, member integrity and loose/archive qualifier equivalence are integrated
and qualified by P035-006 before archived output is adopted. The existing report
format remains unchanged. Node owns the operational convention:
node:docs/evidence/RETENTION.md. Do not rewrite dated loose evidence.
Config policy, immutable publication intent and retention (2026-10-13)¶
Status: accepted; implemented and qualified by P035-006 on 2026-10-14.
The carrier enum denotes v1/v2 values and has no Default. The deployment config
owns default_matrix_write_version: absence selects v2, explicit v1 selects legacy,
and null/unknown versions/wrong types are errors. Relay constructors require an
explicit account and writer. The first intent per (account, topic_key, record_id)
freezes its version. Re-ingest under another config cannot create another version;
operator retry resumes that intent. Legacy multi-version requests are preserved,
with the earliest request selected for scheduling/status. Their prepared frames,
transaction IDs and attempt evidence are not rewritten. The former Node v1-to-v2
republishing case stays frozen historical evidence; new multi-carrier checks use
the direct transport boundary instead. A last claimed attempt without its result
becomes blocked with publication-outcome-unknown at worker startup, requiring
operator retry; this must not become an invisible, exhausted pending request.
History /messages filters v1/v2 Agora types. An empty chunk with end does not
prove exhaustion: pagination continues to the anchor or absent end; token cycles
are diagnosed. A legacy non-Agora anchor is reached once without the filter before
transactional retirement. Empty bootstrap retains a start token without a fabricated
event ID. P035-007 owns future isolation of room recovery from account delivery.
Archive content identity is canonical PAX tar bytes and member hashes; gzip hash identifies the stored representation, with compression level and zlib versions. Packing sets fixed tar metadata and a filename-free zero-time gzip header. Bounded extraction counts actual compressed/decompressed bytes, metadata, files and ratio, independently of the index. New runs qualify loose files, pack/verify/extract, qualify again, compare results and only then remove loose payload copies. Retain both qualification outcomes, including refusal. Historical evidence is unchanged.
Default-path qualification (P035-006, 2026-10-14)¶
Clean Node 42fde666799951e00b88c28072ddb59736b32920 and Orbidocs
4ecec760749730afaffb8e7948ef30c235ca0d40 qualify five SQLite and 27 Matrix
cases. Both independent gates pass: every one of 692 Node-created complete PDUs
fits 65,536 bytes (maximum 49,927); direct Client-Server events reach 65,535 and
unmodified Synapse returns actual M_TOO_LARGE. Stock serialization checks event
IDs, content hashes and server signatures. Protected gossip confidence 0.5 and
permitted Whisper signal/trace/threshold families retain exact bytes through
restart, homeserver/database recovery and deterministic source replay.
A/B omit the writer setting; explicit v1 remains legacy. The first publication intent preserves version/bytes/transaction across configuration changes, including larger legacy envelopes. Unknown third outcomes restart as blocked for operator retry. Long history includes 640 fixture-signed records and three separate UI/host samples, 11,008,420 author bytes, 12 filtered pages and 512 durable records before crash; all 643 resume, restore in the projection and receive representative pair/signature checks after replay. Empty bootstrap and non-limited late binding pass. Refused history holds the observed gap token; a complete prefix may commit before that gap. One recovering room still delays account delivery (10.32 seconds measured here, no bound); P035-007 owns isolation.
The existing reports and 1516 payload members have bounded PAX/gzip retention,
with identical loose/extracted qualifications and retained initial refusals.
Evidence: node:docs/evidence/matrix-opaque-envelope/2026-10-14-default-v2/README.md.
Global Agora remains partial. This does not qualify federation, all Whisper, nym
issuance, other room versions or production readiness; P026/P035-004/005 dated
reports remain unchanged.
Evidence storage review and recovery baseline (2026-10-14)¶
Status: accepted repository cost policy; budget guards implemented in Node. The proposed reconstruction recipes below are superseded by the measured xz decision; P035-008a owns compression and P035-008b owns cause capsules.
The P035-006 positive gzip is 38,121,914 bytes and its incomplete-attempt archives add 62,261,130 bytes across eleven archives. Archiving reduced file count, but repeated full carriers in page observations and PDU/source dumps still dominate bytes. Existing evidence is frozen, with explicit historical storage exceptions; do not rewrite Git history. Before another long-history evidence commit, deduplicate exact author buffers by content hash and retain event/source references with lossless reconstruction. Preserve distinct alternate bytes and complete PDU metadata, including hashes and server signatures. Require artifact integrity and equal normal qualification before/after reconstruction; digest-only evidence is insufficient.
New Git retention is limited to 16 MiB per qualified run, 1 MiB per refused
attempt/probe and 4 MiB for all refusals in one profile campaign, including controls
and review files. These are repository cost limits, independent of protocol and
decoder limits. Failed attempts may retain reports, the actual original refusal
and cause-related artifact dependencies instead of unrelated full passing-case
payloads. Declare omissions; a cause capsule is incomplete evidence, not a full
qualification archive. Oversize output stays private until reduced or retained
under an accountable immutable external-storage contract; no automatic budget
increase. Node owns exact checks and procedures in node:docs/evidence/RETENTION.md.
P035-007 compares against 64 events per filtered page, eight pages per room per
round (at most 512 events, including current-end bootstrap), five seconds between
unfinished rounds, /sync timeline limit three and an 8 MiB per-response HTTP cap.
The frozen run restored 643 records over twelve pages; 640 fixture-signed buffers
totaled 11,008,420 author bytes, plus three UI/host-sign samples. Its 10.32 seconds
starts at B startup and includes the deliberate crash/restart after 512 durable
records, polling and source checks. It does not measure isolated steady-state
throughput. Retain scale, actual pages/events per round, restart overhead and
other-room delivery timing for comparable available/unavailable-history workloads.
The unfiltered non-Agora-anchor branch preserves only earlier development state; it is not required by the v1/v2 wire-reader contract. There is no released/deployed infrastructure to migrate. P035-007 should retire it during the recovery redesign, specify developer-state reset/refusal and keep the direct non-limited late-binding regression. P097 provides a pre-release precedent in Messaging, not a blanket norm for transport migrations. This review does not change qualified runtime code.
Measured xz retention and separate cause capsules (2026-10-14)¶
Status: accepted; 008a qualified on historical data; 008b qualified on new runtime. The earlier recipe/pool decision is superseded, with the historical evidence frozen.
A private conversion of the 115,261,440-byte P035-006 tar using actual liblzma 5.8.3, LZMA2 preset 6, 128 MiB dictionary and CRC64 produces 12,212,980 bytes. Existing controls bring the total to 13,011,948 bytes, within 16 MiB. The original tar hash, 1516 members, two reports and ordinary qualification are identical. This measures storage on historical data, not new Matrix runtime behavior.
P035-008a uses a v2 tar+xz wrapper with actual compressor parameters/version, a 256 MiB decoder memory limit independent of metadata, all existing streaming ceilings, one stream and no trailing bytes. v1 remains readable. No recipes, JSON rewriting or harness changes are required. Qualify the committed tool on private historical copies; retain a small reproducible proof and source refs, not another 12 MB copy in Git. Tar/member hashes identify the preserved content.
P035-008b is independent: per-case witness indices bound by original report hashes, dependency closure, explicit cause selection and cause-only capsule verification and actual runtime/qualification/retention refusals within 1 MiB per capsule and 4 MiB per campaign. Success at one stage must not be relabelled when a later stage refuses. It requires a new full Synapse/SQLite run because the harness changes, plus boundary-PDU and long-history refusal tests. Oversize necessary material remains private; no automatic trimming or budget increase. Parent 008 closes only when both children close; 008b does not block 008a or the next qualified positive evidence after 008a. Global Agora remains partial.
P035-008b is qualified on 2026-10-14 at clean Node bc83d310 / Orbidocs
a515a5d: 27 Matrix and five SQLite cases on stock Synapse 1.162.0 room v10,
with independent Node/homeserver gates, confidence 0.5, permitted Whisper
families, immutable bytes, restart/replay, empty bootstrap and 643-record
long-history recovery. Ordinary qualification is identical before/after bounded
extraction. The full retained set is 13,245,341 bytes. Three cause-only capsules
retain actual history HTTP refusal, two changed near-boundary PDU integrity
refusals and a separate retention refusal after qualifier success; each fits
1 MiB, and their collection is 1,508,971 bytes within 4 MiB. Necessary missing
dependencies and substituted witnesses refuse. Evidence:
node:docs/evidence/matrix-opaque-envelope/2026-10-14-capsules/README.md.
Both children and parent 008 are done. P035-007 remains open; this does not
qualify federation, all Whisper or production readiness.
Matrix Carrier Tracker¶
Post-qualification review (2026-10-14): the profile owns typed cause predicates,
dependency declarations and witness selection. Shared storage tools execute only
closed data checks. New cause manifests declare observed, fault-injected or
synthetic-input; integrity qualification binds the exact paths from a typed
ordinary-qualifier refusal. Retention proves an attested measurement receipt,
not reconstruction of omitted private input. Frozen 008b v1 capsules preserve
their original limits and require their pinned verifier; corrective subsets are
storage/diagnostic conformance, not new runtime. The main-tree gate formerly
blocked by membership edits now passes.
Review conformance passed at clean Node 8d46afc0 / Orbidocs 0b758ee:
all 32 witness indices are byte-identical and ordinary qualification is unchanged.
Corrected v2 cause subsets occupy 1,516,502 bytes, with exact typed refusal-path
binding and actual diagnostic qualifier source. Original plus corrected refusal
collections total 3,025,473 bytes within 4 MiB per campaign/profile. Ten completed
sets are frozen. Evidence:
node:docs/evidence/matrix-opaque-envelope/2026-10-14-capsule-review/README.md.
This review qualifies storage and diagnostics; the original runtime pins remain.
Accepted cost boundary: 136 MiB aggregate Matrix-area bytes, separately for retained/staged files and distinct historical/current Git blobs. Registered tracker/campaign IDs bind one profile; refusals also share 4 MiB per profile. The three completed 008a/008b evidence sets are frozen. P035-009 must decide accountable external retention before another full runtime campaign and no later than 2026-11-14; pending it, no new full qualified set above 256 KiB may enter Git. The existing 16/1/4 MiB local limits remain unchanged.
This transport continuation is independent of AD publisher migration and keeps its status here; S008 Agora P18 links to this row.
Independent recovery implementation contract (2026-10-16)¶
Status on 2026-10-16: implemented with focused runtime qualification; then P035-007 partial. The full 2026-10-19 closure is recorded below. P035-007 introduces accepted / rejected / deferred admission, local attempt-time claim evaluation and persisted admission evidence for replay without current-time admission or nonce reservation. First carrier and topic sequence denote local finalization order, not a common room chronology. Public UI labels records recovered later.
One account cursor records durable reception, including deferred inbox material;
room checkpoints and unresolved history intervals are independent. Two workers,
one request per room and fair one-page scheduling use a 35-second history timeout
and no built-in HTTP retry. History 429 imposes an account-history cooldown only.
Binding revisions are checked at commit. Local operator-only P072 capabilities
provide CAS-bound inspect/retry/accept-gap; acknowledged gaps stay incomplete.
Old nonempty recovery state requires explicit control reset, preserving sources.
The Node-owned exact contract is node:agora-relay-matrix/docs/ROOM-RECOVERY.md.
The per-room-recovery-v1 qualifier rejects old account-blocking expectations in long-history, sync-gap, sync-gap-refusal and late-room-binding. A shared content pool supplies deterministic fixture payloads; topic-bound author envelopes and signatures remain distinct. A synthetic-input xz sizing forecast measured 14,278,751 bytes; it is not an upper bound or runtime qualification.
Four focused cases qualify on unmodified pinned Synapse v1.162.0 at clean Node
5d5392b2 / Orbidocs b3813122: blocked history with continuing confidence-0.5
host-signed delivery, operator CAS/acknowledgement/restart/retry and source replay,
two hung workers with continuing sync/publication, and observed 429 backoff.
The strict retry observation is 5.863827 seconds after a 5000-ms response; the
third room starts at 35.004141 seconds after two held requests. These are single
observations, not latency bounds. Durable whole-second deadlines add conservative
rounding; queue delay remains explicit. The 181979-byte focused archive preserves
identical loose/extracted qualification; three observed setup refusals and the
earlier strict timing refusal occupy a separate 335456-byte collection in the
same campaign. Original results remain unchanged.
Evidence: node:docs/evidence/matrix-opaque-envelope/2026-10-16-room-recovery-focused/README.md.
This qualifies focused room isolation only. Full PDU, Whisper, SQLite, the complete admission matrix and large fairness workloads were not rerun. P035-009 still blocks the full 31-Matrix/five-SQLite campaign, so P035-007 remains partial. The retention decision remains due 2026-11-14; global Agora stays partial and federation/production readiness is not raised.
Restart review (2026-10-16) found that publication-bound topics outside
canonical_topics were absent from memory after restart: sync could advance while
silently skipping their events. The fix restores every active stored binding
before first sync and removes the receiver's second topic registry. Three new
conformance regressions pass (344 selected Agora tests). The older 5d5392b2
focused proof remains frozen and does not qualify this changed receiver. Fresh
focused-v2 qualification passes five cases at clean Node b0a44ade / Orbidocs
aee179e, including that exact publication/restart path: non-limited sync, unchanged
binding, B API/index, signature and source replay. The complete retained set is
192528 bytes with identical loose/extracted qualification. The new cadence repeats
hung-worker and 429 isolation; observed retry is 6.042543 seconds after 5000 ms.
Evidence: node:docs/evidence/matrix-opaque-envelope/2026-10-16-room-recovery-restart/README.md.
The future full campaign has 31 Matrix/five SQLite cases and waits for P035-009;
P035-007 stays partial.
Idle workers poll once per second, with up to one second of discovery delay and
no sleep under the relay lock. A binding revision race still retries the account
transaction without advancing its checkpoint. P035-010 owns excessive Retry-After
policy; this fix does not silently shorten the homeserver's lower bound.
Retention and excessive-delay closure contract (2026-10-19)¶
The operator selected a dedicated retained-evidence subtree of the DEV acceptance
volume as the first external-to-Git filesystem store. Owner: Node public-evidence
retention maintainer. No automatic deletion; keep bundles until explicit reviewed
retirement or verified migration. Store immutable inventory-addressed complete
sets, reject replacement, and prove fresh empty-directory retrieval plus ordinary
qualification. Committed metadata uses a store id/digest rather than private
paths. This local profile does not claim an independent backup or public hosting.
P035-009 is complete for this local filesystem profile: committed tool revisions
published the unchanged historical focused proof under its inventory identity,
freshly retrieved it, checked all bytes and re-ran ordinary qualification with
an identical result. Unavailable/corrupt-store controls refuse. The small receipt
is node:docs/evidence/matrix-opaque-envelope/2026-10-19-external-store/README.md.
This is retention conformance, not new runtime qualification. Original
Git/retained-set budgets and frozen history remain unchanged.
The first full 34-Matrix/five-SQLite recovery campaign now qualifies runtime at
Node 1bc523a6 / Orbidocs 5993f7d0, but its original 128 MiB dictionary archive
refuses retention at 19,058,130 bytes. The successful runtime outcome stays
unchanged. A measured explicit 192 MiB writer profile compresses the same
343,388,160-byte deterministic tar to 14,233,148 bytes and preserves its exact
hash under the existing 256 MiB decoder bound. Node may expose this as an explicit
bounded writer choice; default 128 MiB, format v2, tar/member identity and all
retention/decoder budgets stay unchanged. Full closure still requires the actual
complete-set measurement and fresh retrieval/ordinary qualification.
P035-010 uses deployment matrix_history_max_retry_after_ms (default300000,
range1000..3600000). Larger server delays create an explicit durable history
block with original delay/not-before metadata. Sync/publication and deferred
admission continue. No new account history request starts before the server
lower bound. Existing local-operator retry requires task CAS and refuses before
not-before; later retry is audited. Gap acknowledgement never clears the bound.
The exact Node contracts are node:docs/evidence/EXTERNAL-RETENTION.md and
node:agora-relay-matrix/docs/ROOM-RECOVERY.md. The full closure result below discharges that runtime gate; design decisions alone
do not qualify a campaign.
Full independent room recovery qualification (2026-10-19)¶
P035-007 and P035-010 are done for per-room-recovery-v2. Clean Node
1bc523a63130d8b4707c84d90b0278cad63f843d / Orbidocs
5993f7d0644e65d1d29921b9a56182023cb58e2d qualify 34 Matrix and five SQLite
cases. Both independent gates pass on unmodified Synapse 1.162.0/PostgreSQL 16.13,
room v10: all 2041 Node-created complete PDUs fit 65536 bytes (maximum 49927),
with direct protocol boundary/refusal measured separately. Confidence-0.5 gossip
and permitted Whisper signal/trace/threshold families preserve exact author bytes
through restart and replay. The complete final snapshot retains 2048 full PDUs
and distinct homeserver source rows with verified hashes and signatures.
Long history restores 643 records (640 fixture-signed and three UI/host signed) over 12 pages after a crash at 512 durable records. Other-room delivery precedes completion (6.215358 s observed). Two 640-record held-worker histories preserve sync/publication; the third history starts after 35.004563 s. Ordinary 429 retry is 6.025411 s after its response. These observations are not latency bounds. The deferred real scoped-claim suite preserves its durable inbox, later accepts exactly once and replays retained admission without current verifier availability. Late history evaluates the local clock and preserves its expiry-specific rejection. A 15000-ms server Retry-After against a 5000-ms fixture policy persists an explicit block across restart, refuses early CAS retry with HTTP 409 and later recovers.
All 4315 logical members, two reports and ordinary qualifications match after
extraction. Explicit 192 MiB XZ compression retains the identical 343388160-byte
tar in a complete 16511847-byte set; publication including external controls is
16515590 bytes, within the unchanged 16 MiB cap. Default writer remains 128 MiB;
reader ceiling remains 256 MiB. The earlier 19058130-byte retention refusal stays
preserved as an attested measurement receipt with its successful runtime result.
Fresh retrieval from the adopted local store requalifies identically. Git controls
occupy 2282442 bytes and contain no duplicate payload. Retention tooling was tested
separately at Node 384c7324 / Orbidocs 0a0fffa9; it does not change runtime.
Evidence: node:docs/evidence/matrix-opaque-envelope/2026-10-19-room-recovery-full/README.md.
Earlier incomplete campaigns/development probes remain private with original
outcomes. The debug fixture pins a 60000-ms attachment budget for over 2000 records;
production defaults and the 35-second history timeout are unchanged. UI samples
unlock before preview; P095 inline-unlock acknowledgement remains separate.
The retained store is one local volume with reviewed retirement, not independent
backup/public hosting. Agora stays partial; no federation, all-Whisper/issuance,
production or automatic release-readiness claim follows.
Review correction: credible delays and retained refusals (2026-10-19)¶
History Retry-After has a fixed local credibility ceiling of 86400000 ms (24 h),
separate from the deployment automatic-wait threshold. A value at or below that
ceiling keeps its conservative checked not-before deadline. A larger value or an
unrepresentable deadline is matrix-history-retry-after-implausible, with no
trusted not-before. Preserve the exact original delay as a decimal string.
The account's history remains explicitly blocked until operator action; live
sync, publication and local deferred admission remain independent.
agora.matrix.recovery.ignore-implausible-retry-after is a new host-local,
LocalControlOnly P072 operation. It requires active local operator authority,
task id, inspected revision and a nonempty bounded reason. In one transaction it
revalidates the active binding and implausible state, records actor/reason/original
delay, clears only that task's limit and rearms it. It cannot clear a credible
server floor or any other task's block. Ordinary retry cannot bypass implausible
state. Previously retained oversized development limits must expose the same
escape without editing carrier/source bytes. A detached room cannot clear a limit;
a parked task exposes the escape after an active rebind and fresh inspected CAS.
Public views hide room ids/tokens.
Observed incomplete attempts must be retained as cause-only capsules in the adopted append-only store, with small Git references and fresh cause verification. The generic store supports full qualifications and cause-only capsules without Story-008 predicates. Preserve original reports, actual qualifier refusal and minimal bound observations. Predicates describe what the original evidence proves (authoring HTTP 423, admission nonconvergence, receiver exit during restart), not an unretained root-cause analysis. Origin and evidence strength remain explicit; cause verification never means full qualification. Complete capsule bytes, including external controls, count toward 1 MiB/capsule and 4 MiB/campaign/profile. Historical successful/failing evidence is immutable.
Next large or expanded campaign is gated by P035-011, not a larger dictionary or budget. P035-012 owns an independently qualified second copy; until then one-volume scope stays explicit. Fresh verification of every release-relevant bundle is a mandatory manual pre-release step under S020; missing/corrupt material blocks that step. P035-013 owns proactive S039 notification and bounded batch CAS, using the existing local operator boundary rather than granting authority through a button.
Fairness comparison baseline (single run, not an SLO):
| Parameter / observation | Qualified 2026-10-19 value |
|---|---|
| History workers per account | 2 |
| Records in each of two large held-worker rooms | 640 fixture-signed |
| Separate long-history room | 640 fixture-signed + 3 UI/host signed |
| Filtered page / room round | 64 events / 8 pages, then 5 s room pause |
| History HTTP timeout / internal retry | 35 s / 0 |
| Sync timeout / timeline limit | 1000 ms / 3 |
| Debug fixture attachment budget | 60000 ms |
| Other-room delivery before 643-record recovery completed | 6.215358 s |
| Third history start after both workers were held | 35.004563 s |
The five-second room pause is a retry lower bound, conservatively rounded to a whole-second deadline; queueing and polling may add delay. It is not an exact five-second scheduling period.
Baseline source: clean Node 1bc523a6 / Orbidocs 5993f7d0, unmodified Synapse
1.162.0/PostgreSQL 16.13, room v10, retained full recovery report. Later scheduler
changes must record the same scale parameters and distinguish startup/polling from
request latency; these observations do not establish a bound.
Review closure: the separate history-delay-credibility-focused-v1 proof at
clean Node ac386090 / Orbidocs fbba8e73 qualifies four cases on unmodified
Synapse 1.162.0 / PostgreSQL 16.13. Both u64 maximum and ten-year delays persist
as implausible across restart, refuse ordinary/public/stale/unreasoned control,
and resume history plus exact-source replay only after the audited operator CAS.
Exactly 24 h stays credible and cannot be overridden. Live host-signed confidence
0.5 gossip and UI continue. Reviewed qualifier 121c19fb checks actual restart,
inspect, audit, replay and source-row observations; it is later than runtime and
does not alter its source snapshots. Complete retained proof with external
controls is 142758 bytes, default 128 MiB XZ, within the 256 KiB focused boundary.
Evidence: node:docs/evidence/matrix-opaque-envelope/2026-10-19-delay-credibility-focused/README.md.
Three original incomplete attempts now have complete externally retrieved
cause-only capsules (437246 bytes including their references/receipts, plus the
collection README), with exact reports inside the compressed logical archive.
The first new probe's incorrect public-body status expectation also has its
original refusal capsule, rather than a renamed success. Complete profile refusals
remain below 4 MiB and every capsule below 1 MiB. Readable references:
node:docs/evidence/matrix-opaque-envelope/2026-10-19-review-refusals/README.md and
node:docs/evidence/matrix-opaque-envelope/2026-10-19-delay-probe-refusals/README.md.
Two fresh reads verify each original cause; current-store availability must still
be freshly verified before release. No frozen history, full-PDU/runtime scope,
retention budget, dictionary default or global Agora assessment changes.
P035-011 source-observation decision (2026-10-19)¶
Accepted design, implemented and qualified on 2026-10-20 for the source-evidence
profile below. This closes P035-011 without changing global Agora partial status.
The Node-local contract is node:docs/evidence/SOURCE-OBSERVATIONS.md. It preserves
traceability and evidence boundaries while reducing repeated capture buffers,
without changing signed bytes, transport or recovery scheduling. Reports select
public-evidence-observations.v2 only through evidence_observation_format;
unknown or mismatched versions refuse without shape inference or fallback.
Each run owns self-contained SHA-256 raw-object packs up to 32 MiB and an index bounded to 8 MiB. Each observation retains its ordinal, process/time, case, source and fault-injection context. A/B/source/replay first observe their own bytes. Full PDUs and homeserver source-row artifacts stay indivisible with distinct provenance. Only a structurally valid v2 carrier with strict relay-free author JSON, whose decoded bytes re-encode to the exact observed canonical base64url string, may use that transform; all other representations remain literal. Storage eligibility does not establish signature, proof or admission validity. No recipes, hex or JSON rewrite is introduced. Readers are lazy and bounded; cross-run lookup and incomplete capsule dependencies refuse. Archive safety limits and all retention budgets stay unchanged.
Before runtime harness edits, convert a private copy of the frozen 343388160-byte, 4315-member full proof and require identical retained values, author/PDU bytes and ordinary qualification. Measure XZ categories, whole and leave-one-out variants (non-additive), member counts, memory, 16 MiB full-set fit at 128 MiB dictionary and new Git evidence controls <=256 KiB. Compact external references pin manifest, member-index and object-index digests; fresh retrieval validates actual material. A failed historical gate leaves partial status without a larger dictionary, budget, campaign alias or automatic PDU sampling.
Only then integrate source capture and qualify fresh 34 Matrix/five SQLite cases on pinned unmodified Synapse, including full PDU, confidence 0.5, existing Whisper families, history, restart and replay. Dual inline/reference views prove coding and reference equivalence, not independent capture; timings disclose dual-write I/O. Closure requires identical loose/extracted/fresh-retrieved qualification and all budgets. Agora remains partial; P035-012 and P035-013 remain todo.
Historical conformance passed at clean Node 3b06e1b4 / Orbidocs 51de82e9:
the existing 34-Matrix/five-SQLite ordinary qualification is identical for
original/source views, after extraction and fresh retrieval. The private converted
candidate measured 15201407 complete bytes at the default 128 MiB dictionary and
5878 bytes of prospective compact controls. A later supplemental private candidate
at tools Node ee64042f / Orbidocs a2addea1 measured 15209692 complete bytes and
6126 bytes of prospective controls. It distinguishes standalone category needs
from zero marginal cost when PDU/stored-row bytes coincide; category sizes are
not additive. Git keeps a small historical measurement report pointing to the
original frozen input, not another converted full archive or an adopted runtime
bundle. The supplemental measurement does not replace the first result or renew
runtime qualification.
Fresh runtime qualification on 2026-10-20 at clean Node ee64042f / Orbidocs
a2addea1 passes all 34 Matrix and five SQLite cases on unmodified Synapse
1.162.0, room v10. The same-capture legacy/source values, non-representation facts,
exact author bytes (including distinct 0.50/0.5 encodings) and ordinary results
match, also after archive extraction and fresh external retrieval. Full evidence
retains 2048 indivisible PDUs and 2048 independently observed stored-row artifacts;
all 2041 Node PDUs fit 65536 bytes, maximum 49927. Node and homeserver gates remain
separate. Qualified Whisper scope is signal, trace and threshold only.
Complete retained evidence is 15220597 bytes within 16777216, with a 15161024-byte
XZ payload, 164802560-byte tar and 289 members. Writer dictionary is the default
128 MiB; the reader remains bounded to 256 MiB. Compact Git controls are 5878 bytes,
compared with 2282442 bytes for the earlier full recovery proof. All new public
proof metadata totals 49484 bytes in Git, including those runtime compact controls;
this aggregate is distinct from the per-reference cost. Conservatively charging
all new collection metadata, including the small historical reports and README,
counts the 5878 runtime controls once and totals 15264203 bytes within 16 MiB,
leaving 1513013 bytes. Member/object indexes remain in the complete store with
explicit Git digest pins. No budget,
PDU coverage or historical archive was weakened. Same-capture dual-write timings
(7.211888 s other-room delivery; 35.0048 s third-history start) include extra I/O
and do not replace the 6.2 s / 35 s baseline or establish bounds. Evidence:
node:docs/evidence/matrix-opaque-envelope/2026-10-20-source-observations/README.md.
At that P035-011 checkpoint, P035-012/013 remained todo and Agora partial. This one
homeserver/local-volume profile does not qualify federation, independent backup,
nym issuance, all Whisper or release/production readiness.
P035-012: adopted mirrored evidence protection (2026-10-20)¶
The operator selected a secondary mirror whose first physical member overlaps
the primary store and whose second online member is a separate device. This
explicitly adopts primary-media-loss-with-overlapping-mirror protection instead
of disjoint media. Both owner/retirement profiles remain unchanged, and original
per-location independent_backup: false facts are preserved. Host, power,
administration, site, the shared member and mirror-management risks remain.
Clean Node dc996b24 / Orbidocs 83c77609 qualifies actual secondary-only
recovery of eight exact bundle identities and 32517795 original file bytes per
logical store. Four full/focused sets and four cause capsules return their exact
original qualification or cause-verification results. A real OS sandbox denies
the primary and previous capture/temporary roots before and after fresh bounded
extraction. Full-PDU and author-byte predicates remain those of the original
profiles; no new Matrix runtime is claimed. Mounted mirror reads and online
member observations do not prove an actual detached-member recovery exercise.
A separate clean Node 70d507cb / Orbidocs 83c77609 release rehearsal performs
sixteen fresh retrievals, covering both locations and every selected bundle.
The v2 release attestation binds the attempt, candidate, ledger/profile hashes,
trusted tools, policy and evidence inventory. The actual outer gate accepts this
retention check and rejects another attempt id; other release checks are
explicitly unqualified. Historical receipts cannot authorize a new candidate.
Small custody/procedure archives qualify identically before and after extraction.
Additional original results from temporary locations are deduplicated and held
without changing their old outcomes or deleting sources. A private oversized
retention-refused original is not admitted as a public qualified bundle.
Evidence: node:docs/evidence/matrix-opaque-envelope/2026-10-20-replica-recovery/README.md.
This closes P035-012 for the operator-adopted protection level and manual
procedure. Automatic maintenance, independent-host recovery, federation, all
Whisper and release/production readiness remain outside this result. P035-013
stays todo and global Agora stays partial. Existing retention budgets remain.
P035-012 review boundaries and P035-014 follow-up (2026-10-20)¶
The adopted mirror protection is a point-in-time observation. There is no
installed periodic health, sample-verification or notification job, so P035-012
does not establish continuing health between manual checks. OS Online status
is not a sector scrub or an independently exercised read from each mirror member.
Host and volume pseudonyms use the OS platform UUID and filesystem volume UUID,
not hostnames or mount labels; lower-entropy physical attachment/model/size hashes
are identity bindings without a general anonymization claim.
Auxiliary originals left in temporary storage do not count as durable replicas. The three temporary cause capsules and focused temporary run preserved during P035-012 have their durable retained copy at the secondary, separately from the eight adopted release bundles held at both stores. A 358762740-byte private original includes loose inputs; its actual retained-set refusal receipt measures 19058130 bytes against 16777216. Cause-only retention verification attests that measurement, not reconstruction of omitted bytes or a new runtime qualification.
P035-014 owns continuing supervision, through the existing S020 execution and S039 notification owners rather than a new storage-owned scheduler:
- Check current adopted topology at a proposed 15-minute cadence, including every
member's presence, online/rebuilding state and protection relation. Unavailable,
unsupported or stale observations mean
unknownordegraded, not healthy. - Rotate a bounded daily sample of two bundle identities through both locations, covering the selected inventory at least every seven days. Run existing fresh ordinary or cause-only verification; a cache cannot substitute. Final limits and timeouts belong to the reviewed job policy, with explicit catch-up on wake.
- Publish idempotent operator lifecycle notifications for degraded, unavailable, overdue and recovered states. Delivery failure remains visible; logging a line is not notification acceptance. Preserve private locators and keep notifications informational rather than authority to mutate evidence.
- Record observation age and per-store sample progress. Stopping/removing the job makes supervision stale. Fault-inject member loss, rebuild, stale job, corrupt sampled bytes, unavailable location and notification failure; retain real process and operator-notification observations before marking done.
- Keep full manual candidate-bound pre-release verification mandatory. Do not retire evidence or repair storage automatically, and do not block emergency retrieval of intact bytes on healthy-topology checks.
The operator-authorized local integration prerequisite for P035-013 is validated
on 2026-10-20. The review reconciles ledger/MVP, all frozen evidence and policy
budgets, P095 and S039 against the actual primary baselines. The full workspace,
affected fixture follow-up, documentation gates and fresh pre-closure reads pass;
the exact candidates and original baseline failures are recorded in
node:docs/audits/WHISPER-INTEGRATION-2026-10-20.md. Retained worktrees support
future isolated changes under node:docs/development/WHISPER-BRANCH-INTEGRATION.md.
P035-013/014 stay todo, Agora stays partial, and no historical Matrix/runtime or
release qualification is renewed by this integration. Git push is a separate action.
The review now retains a fresh-checkout manual rehearsal at clean Node 16f20d0e
/ Orbidocs c02653b2: sixteen new per-location retrievals, actual outer retention
acceptance for the exact attempt, and validation refusal for another. It is a
same-agent clean-checkout exercise, not independent-person review; the other three
checks remain unqualified. Clean 8a27e826 / 8c4514de separately produces the
336181-byte observed retention capsule, freshly verified at both stores. Initial
capsule publication refused the 4 MiB profile cap and remains recorded; no budget
or required dependency was relaxed. Evidence:
node:docs/evidence/matrix-opaque-envelope/2026-10-20-replica-review/README.md.
| ID | Task | Status | Depends on | Completion gate |
|---|---|---|---|---|
| P035-004 | Opaque Agora envelope carrier over Matrix | done | Existing Matrix mapping and Agora verification; S028 | Qualified 2026-10-12 at clean Node 20c4c16c and Orbidocs 862586f: 4 SQLite and 21 Matrix cases, confidence 0.5, permitted Whisper families, exact bytes, B /sync index, crash/restart/replay and review corrections. All 41 Node PDUs fit 65,536 (maximum 50,043); direct-protocol maximum 65,534 and fixture enforcement are separate observations. Retained qualification: node:docs/evidence/matrix-opaque-envelope/2026-10-12-review/qualification.json. That frozen run selected v2 explicitly; the adopted default-v2 policy is implemented/qualified separately in P035-006. One disclosed patched fixture does not qualify federation or production readiness. |
| P035-005 | Unmodified reference homeserver PDU interoperability | done | P035-004; room-version budget profile | Qualified 2026-10-13 at clean Node dcc8133c / Orbidocs c84df99 on official unmodified Synapse v1.162.0, PostgreSQL 16.13 and room v10: 4 SQLite and 22 Matrix cases. Separate gates pass: all 42 Node complete PDUs fit 65,536 (maximum 49,927); direct protocol maximum 65,535 and actual HTTP 413 M_TOO_LARGE, including requests below the PDU ceiling. Confidence 0.5, permitted Whisper families, exact bytes, A/B and server/database restart, limited recovery, late binding and source replay pass. Stock serializer verifies hashes, signatures and event IDs. Evidence: node:docs/evidence/matrix-opaque-envelope/2026-10-13-synapse/qualification.json. Initial Synapse failure, v0.7.0 finding and patched proof stay frozen; explicit v2 only, no federation or production claim. |
| P035-006 | Default v2 writer and explicit legacy v1 | done | P035-004; accepted write-default and history-recovery decisions | Qualified 2026-10-14 at clean Node 42fde666 / Orbidocs 4ecec76: five SQLite and 27 Matrix cases with omitted A/B config, confidence 0.5, immutable bytes, legacy intent controls, third-claim crash/retry, restart and source replay. All 692 Node PDUs fit 65536 (maximum 49927); independent unmodified Synapse gate and direct 65535 boundary pass. Filtered history resumes 643 records over 12 pages after 512 durable before crash; empty bootstrap and non-limited late binding pass. Bounded archives qualify identically before/after extraction. Evidence: node:docs/evidence/matrix-opaque-envelope/2026-10-14-default-v2/qualification.json. P035-007, global Agora partial and federation/production exclusions remain open. |
| P035-007 | Isolate room history recovery from account delivery | done | P035-006; S028; P035-008a; P035-009 | Qualified full per-room-recovery-v2 on 2026-10-19 at clean Node 1bc523a6 / Orbidocs 5993f7d0: 34 Matrix/five SQLite, two-by-640 fairness, continuing live delivery, reversed gap/late-binding cases, durable deferred and local-expiry admission, bound-room restart, operator CAS, exact bytes and replay. Independent Node/stock Synapse gates: 2041 Node full PDUs, maximum 49927 within 65536. Complete 16511847-byte retained set, identical tar/member and loose/extracted/fresh-retrieved qualifications; payload external, controls 2282442 bytes in Git. Evidence: node:docs/evidence/matrix-opaque-envelope/2026-10-19-room-recovery-full/README.md. Historical refusals/scope, global Agora partial and broader exclusions remain. |
| P035-008 | Public evidence retention within byte budgets | done | P035-006; measured xz decision | Both 008a and 008b qualified: bounded xz with historical-byte equivalence, new full runtime within 16 MiB, and independent verified cause capsules within 1/4 MiB. No recipe language. Frozen evidence and global partial scope remain unchanged. |
| P035-008a | Bounded xz archive and historical-data qualification | done | P035-006; accepted measured compression profile | Qualified at clean Node 7df7949a: identical historical tar, 1516 members, 2 reports and ordinary qualifier; temporary full set 13006802 bytes within 16 MiB. Twenty-four retention controls pass, including memory/stream faults. Small proof: node:docs/evidence/matrix-opaque-envelope/2026-10-14-xz-conformance/proof.json; no duplicate payload or new runtime claim. |
| P035-008b | Self-contained cause capsules | done | P035-008a; S028 | Qualified clean Node bc83d310 / Orbidocs a515a5d: all 27 Matrix and 5 SQLite cases; independent stock Synapse and Node gates; full 13245341-byte set and identical loose/extracted results. Runtime history HTTP 403 after 512 durable of 640 fixture plus 3 UI records, changed 65535/65534-byte PDU integrity refusal, and actual 31062930-byte storage refusal retain original results. Verified cause-only capsules total 1508971 bytes including collection index; each fits 1 MiB. Missing dependencies/substituted witnesses refuse. node:docs/evidence/matrix-opaque-envelope/2026-10-14-capsules/retention-result.json. |
| P035-009 | Decide accountable external evidence retention | done | P035-008; repository retention owner | Adopted local profile orbiplex-dev-whisper-v1, owner Node public-evidence retention maintainer, no automatic deletion until explicit reviewed retirement or verified migration. Inventory-addressed append-only sets, bounded fresh copy/extraction and identical ordinary qualification pass at node:docs/evidence/matrix-opaque-envelope/2026-10-19-external-store/README.md; unavailable/corrupt stores refuse. Policy binds the proof and profile; original budgets/history remain. Local single-volume scope is external to Git, without independent-backup or public-hosting claims. |
| P035-010 | Bound excessive history Retry-After through explicit operator policy | done | P035-007; Matrix rate-limit semantics; P072 local operator authority | Historical 15000-ms out-of-policy floor remains qualified in the frozen full profile. Reviewed focused proof at clean Node ac386090 / Orbidocs fbba8e73 qualifies four cases on stock Synapse: checked 24 h credibility, exact u64/ten-year delay, durable implausible account block and reasoned per-task CAS escape, with unchanged credible floor, continuing live confidence-0.5 publication, restart and source replay. Active binding/current revision required, including after rebind; other tasks remain held. Qualified/retrieved 142758-byte proof at node:docs/evidence/matrix-opaque-envelope/2026-10-19-delay-credibility-focused/README.md; full PDU gates not rerun. |
| P035-011 | Deduplicate observations at the evidence source before larger campaigns | done | P035-008; P035-009 | Qualified 2026-10-20 at clean Node ee64042f / Orbidocs a2addea1: 34 Matrix/five SQLite, same-capture legacy/source facts and exact author/PDU bytes, identical ordinary loose/extracted/fresh-retrieved results. Retains all 2048 PDUs plus 2048 stored-row artifacts; 2041 Node PDUs, maximum 49927 within 65536. Runtime tool total 15220597 bytes at the default 128 MiB dictionary; conservative whole collection 15264203 within 16 MiB. Tar 164802560 bytes/289 members, compact Git controls 5878 bytes versus prior 2282442. Historical gate and supplemental category facts remain separate. Evidence: node:docs/evidence/matrix-opaque-envelope/2026-10-20-source-observations/README.md. Existing budgets, one-homeserver/volume scope, Agora partial and P035-012/013 remain unchanged. |
| P035-012 | Qualify a retained-evidence replica and verification procedure | done | P035-009; P035-011; operator-adopted protection profile | Qualified 2026-10-20: clean Node dc996b24 / Orbidocs 83c77609, eight exact bundles and 32517795 original file bytes per logical store, OS-enforced primary/previous-capture denial and identical fresh secondary qualifications. Operator explicitly adopted an overlapping mirror with a separate second member; no disjoint-media/host claim. Clean 70d507cb release rehearsal freshly verifies both locations, binds the exact candidate/attempt/policy/inventory and is accepted by the v2 outer gate; another attempt refuses and other release checks stay unqualified. Small custody/procedure proofs qualify equally after extraction. Auxiliary originals deduplicate without deletion or outcome changes; oversized refused input stays private. Evidence: node:docs/evidence/matrix-opaque-envelope/2026-10-20-replica-recovery/README.md. Global partial scope, shared risks and existing budgets remain. |
| P035-013 | Notify the operator of durable history blocks and support bounded batch CAS | todo | P035-010; S039; S020; P072 | Host-owned source adapter emits idempotent lifecycle updates for blocked/awaiting-operator state after commit, without tokens. Notification action resolves fresh authority/task CAS and is enabled only at the credible not-before; implausible override remains a distinct reasoned action. Batches are bounded, retain a CAS per item and report partial results without erasing other limits; notification delivery failure cannot change recovery state. |
| P035-014 | Supervise adopted evidence-replica health and bounded fresh samples | todo | P035-012; S020; S039; reviewed operator job policy | Periodic topology/member and stale-job checks plus rotating fresh verification at both stores deliver real idempotent degradation/recovery notifications. Qualify loss/rebuild, corruption, unavailability, missed checks and notification failure. Preserve full manual pre-release verification, private locators, emergency recovery and explicit retirement; a past receipt cannot establish continuous protection. |
The envelope is backend-neutral. A later native Orbiplex transport may replace Matrix for topics that must not leave the Orbiplex federation, but such a transport is out of scope for this proposal.
6.1. Federation trust boundary¶
Matrix federation is a transport and replay channel, not an admission oracle.
A receiving Agora relay MUST NOT trust a donor relay merely because the record
arrived through Matrix or because the Matrix event itself is valid. The receiving
relay MUST treat the Matrix event as a carrier for an agora-record.v1 envelope
and run the same local admission pipeline it would run for any other inbound
record.
The receiver therefore verifies, at minimum:
- the
agora-record.v1envelope shape and canonicalrecord/id, - the envelope signature, author key, nym certificate, or delegated/proxy proof, as applicable,
- content-schema conformance for
content/schema, - topic ACL and relay-role policy,
- authority, capability, revocation, and custody policy required by the topic or record kind,
- idempotent deduplication by
record/id.
The Matrix homeserver signature and Matrix event id are transport provenance and diagnostics. They may support relay health, abuse analysis, or federation trust scoring, but they are not sufficient proof that the embedded Agora record is valid or locally admissible.
Operationally:
relay is transport
envelope is evidence
local policy is admission
This allows two Agora servers to synchronize accepted records without requiring full mutual trust between relay operators. A malicious or buggy donor may deliver bytes, but the receiver admits only records whose envelope and local policy pass.
7. Retention¶
Retention is configured per topic and per record kind, with relay-level defaults. Minimum retention policy dimensions:
max_age— oldest allowed record age,max_count— maximum number of records kept per topic,max_bytes— maximum total size per topic,pin_by_kind— kinds that MUST NOT be evicted regardless of age or count.
The relay MUST expose the active retention policy for every topic via
GET /v1/agora/topics/{topic-key}/retention. Authors MAY submit records
that exceed policy; the relay MAY reject, truncate, or accept-and-evict
depending on configuration, but MUST return a clear error in the reject
case.
Retention does not imply deletion from origin relays. Content-addressed records may persist in cache relays or in participant-local archives even after a canonical relay evicts them.
8. Sanctions and Exclusion¶
Agora is a substrate, not a moderation engine. It honors two exclusion signals without defining policy:
- Participant revocation — records authored by a participant whose
current capability passport chain is revoked MUST be excluded from
default query output. The records are retained so that audit queries
(
include_flagged=true) can still reach them. - Record-level flags — a separate artifact (out of scope for this
proposal) may flag a record as
hidden,under-review, orwithdrawn. Relays MUST honor these flags in default query output. Flag artifacts MUST be content-addressed and verifiable themselves.
No substrate-level moderation is defined. Kind-specific moderation rules belong in the schema contract for that kind.
Record Envelope Examples¶
An opinion about a URL. The topic is a named channel dedicated to URL
opinions; the subject relation is carried in record/about:
{
"schema": "agora-record.v1",
"record/id": "sha256:4Q7x3kCDfm2yVwrbn8Hj5tlsOe9zApiU6Gq3wXYAbCd",
"record/kind": "opinion",
"topic/key": "ai.orbiplex.opinions/url",
"record/about": [
{ "resource/kind": "url", "resource/id": "https://example.org/article" }
],
"author/participant-id": "participant:did:key:z6MkExample",
"authored/at": "2026-04-11T08:15:00Z",
"content/schema": "resource-opinion.v1",
"content": {
"schema": "resource-opinion.v1",
"opinion/text": "Useful overview, but the sourcing is thin in the final section.",
"opinion/lang": "en",
"opinion/rating": 1
},
"signature": { "alg": "ed25519", "value": "BASE64URL..." }
}
The same opinion, placed instead under a per-URL topic (an alternative
convention some applications may prefer). The substrate accepts both
shapes; which one to use is a decision of the kind contract for
record/kind = "opinion":
{
"schema": "agora-record.v1",
"record/id": "sha256:4Q7x3kCDfm2yVwrbn8Hj5tlsOe9zApiU6Gq3wXYAbCd",
"record/kind": "opinion",
"topic/key": "ai.orbiplex.opinions/url/sha256:4b7c9fzkMyAL8GBfQExamplePerUrlTopic01",
"record/about": [
{ "resource/kind": "url", "resource/id": "https://example.org/article" }
],
"author/participant-id": "participant:did:key:z6MkExample",
"authored/at": "2026-04-11T08:15:00Z",
"content/schema": "resource-opinion.v1",
"content": {
"schema": "resource-opinion.v1",
"opinion/text": "Useful overview, but the sourcing is thin in the final section.",
"opinion/lang": "en"
},
"signature": { "alg": "ed25519", "value": "BASE64URL..." }
}
A plain comment in a general channel. No external subject, no
record/about:
{
"schema": "agora-record.v1",
"record/id": "sha256:Zp2m9ThKqgX4r7v8C5n1Yb6jD3WlFsTaephQkU2mov8",
"record/kind": "comment",
"topic/key": "ai.orbiplex.announcements/default",
"author/participant-id": "participant:did:key:z6MkExample",
"authored/at": "2026-04-11T09:00:00Z",
"content/schema": "plain-comment.v1",
"content": {
"body": "Agora MVP landed on canary relay.",
"lang": "en"
},
"signature": { "alg": "ed25519", "value": "BASE64URL..." }
}
A comment thread under a proposal. The topic is freely named by the publishing application; the parent link points to the first record in the same topic:
{
"schema": "agora-record.v1",
"record/id": "sha256:TH7q1WknMyAL8GBfQC5XrExampleThreadStart01",
"record/kind": "comment",
"topic/key": "ai.orbiplex.proposals/035/discussion",
"author/participant-id": "participant:did:key:z6MkExample",
"authored/at": "2026-04-11T10:30:00Z",
"content/schema": "plain-comment.v1",
"content": {
"body": "Retention rules should be configurable per record kind, not only per topic.",
"lang": "en"
},
"record/parent": "sha256:PR0p05kFtQYABcD1eFgH2iJKlMnOpExampleParent9",
"signature": { "alg": "ed25519", "value": "BASE64URL..." }
}
A thread policy record can be published in the same topic before the root
comment. The root comment then references it through record/policy. The
policy is not embedded in plain-comment.v1; comments carry speech, while a
thread-policy record carries the access rule for joining the thread.
{
"schema": "agora-record.v1",
"record/id": "sha256:PolicyPhoneConfirmedExample001",
"record/kind": "thread-policy",
"topic/key": "ai.orbiplex.proposals/035/discussion",
"author/participant-id": "participant:did:key:z6MkExample",
"authored/at": "2026-04-11T10:29:00Z",
"content/schema": "comment-thread-policy.v1",
"content": {
"schema": "comment-thread-policy.v1",
"policy/thread-topic-key": "ai.orbiplex.proposals/035/discussion",
"policy/min-attestation": "phone-confirmed",
"policy/inheritance": "descendants",
"policy/may-tighten": true,
"policy/may-loosen": false
},
"signature": { "alg": "ed25519", "value": "BASE64URL..." }
}
The effective policy for a comment is inherited from the nearest applicable ancestor policy:
root or ancestor policy
-> child comment
-> child may attach stricter policy for its own subtree
-> child MUST NOT loosen inherited policy
Domain policy, not the Agora substrate, defines the ordering among attestation
levels such as phone-confirmed, national-id, or community-trusted.
A public log entry produced by a workflow run. The topic is derived from the run identifier, and there is no external subject to reference:
{
"schema": "agora-record.v1",
"record/id": "sha256:WF001rk5d8m2hxQP4N7bT6vLYcFj3gsOwueazR9ikmt",
"record/kind": "public-log",
"topic/key": "ai.orbiplex.workflow-runs/01JRCY0Y7T4Y9JQK8K7R6K4M3M",
"author/participant-id": "participant:did:key:z6MkWorkflow",
"authored/at": "2026-04-11T11:00:00Z",
"content/schema": "public-log-entry.v1",
"content": {
"step": "fan-out",
"outcome": "any_one_satisfied",
"dispatch_count": 3
},
"signature": { "alg": "ed25519", "value": "BASE64URL..." }
}
Hard MVP Scope¶
| Feature | MVP |
|---|---|
agora-record.v1 envelope |
Yes |
Content-addressed record/id |
Yes |
Opaque topic/key with canonicalization rules |
Yes |
Optional record/about linking to proposal 026 resource identity |
Yes |
Cross-topic query by record/about (secondary index) |
Yes |
| Matrix backend (Dendrite or Conduit) | Yes |
| Three relay roles (canonical, cache, origin) | Yes |
| Query by topic with cursor | Yes |
Fetch by record/id |
Yes |
| Subscribe via long-poll or SSE | Yes |
| Retention: age + count | Yes |
| Retention: size + pin-by-kind | Deferred |
| Record-level flags and hidden/withdrawn handling | Deferred |
agora-vault-entry.v1 encrypted opaque artifact vault |
Yes |
| E2E-encrypted private topics | Deferred |
| Kind-specific schema registry | Deferred |
| Native Orbiplex transport (non-Matrix) | Deferred |
| WebSocket subscription | Deferred |
Deferred items are post-MVP and MUST NOT be implemented before the MVP scope is clean and tested.
Hard-MVP closure note: the implemented Node runtime covers every Yes row in
the table above for the reference deployment, including local signing/ingest,
query, SSE subscription, Matrix-backed federation, retention, subject indexing,
and Agora Vault. The later "deferred or limited" ingest invariants remain
explicit post-MVP hardening or deployment-policy choices; they MUST NOT keep the
P035 hard-MVP tracker below completion.
Agora Vault¶
Agora Vault is a separate encrypted-artifact surface, not a topic record
profile. It exists for cases where the system needs durable,
admission-controlled opaque storage but must not expose the author,
participant, nym, topic, or domain metadata that ordinary agora-record.v1
would reveal.
The canonical entry schema is agora-vault-entry.v1. Its public shape exposes
only:
vault-entry/id;- opaque
artifact/id; artifact/kind;- the cryptographic envelope;
- ciphertext.
The artifact payload and all domain metadata live inside ciphertext. A vault
entry that contains plaintext payload fields, lacks ciphertext, or lacks at
least one key envelope is invalid. Public lookup is GET
/v1/agora/vault-artifacts/{artifact_id} and returns ciphertext-bearing entries
only. Scoped list/get/delete use GET|DELETE
/v1/agora/vaults/{vault_subject}/artifacts.... In the supervised local Node
runtime those routes are gated by the Agora service client token and daemon host
capability dispatch. Federated or remote-provider deployments bind the same
operations to the frozen agora-vault@v1 capability/passport profile scoped to
the opaque vault_subject.
The MVP writer used by recorded messaging may emit key-wrap =
"dev-key-commitment". This is a development custody commitment to the
discarded symmetric key; it proves that ciphertext was produced but does not
make the entry decryptable by itself. Production recovery uses recipient key
wrapping under x25519-hkdf-sha256+xchacha20-poly1305.
The implemented MVP runtime exposes these concrete operations:
| Operation | Runtime path | Authorization boundary | Notes |
|---|---|---|---|
| Put encrypted artifact | POST /v1/agora/vaults/{vault_subject}/artifacts |
Agora client token locally; agora-vault@v1 remotely |
Request body is an agora-vault-entry.v1; invalid plaintext or missing key envelopes are refused. |
| List scoped artifacts | GET /v1/agora/vaults/{vault_subject}/artifacts |
Same scoped vault authority | Response includes list items with artifact/id, artifact/kind, vault-entry/id, stored/at, and optional ciphertext digest. |
| Get scoped artifact | GET /v1/agora/vaults/{vault_subject}/artifacts/{artifact_id} |
Same scoped vault authority | Returns the full encrypted entry after export schema validation. |
| Delete scoped artifact | DELETE /v1/agora/vaults/{vault_subject}/artifacts/{artifact_id} |
Same scoped vault authority | Performs a soft delete; scoped and public reads ignore deleted entries. |
| Public encrypted lookup | GET /v1/agora/vault-artifacts/{artifact_id} |
Public lookup by opaque artifact id | Returns filtered ciphertext-bearing entries only and does not reveal vault_subject, participant, nym, author, topic, or plaintext domain metadata. |
The supervised agora-service stores MVP vault entries in
agora-vault.v1.sqlite. The scoped uniqueness boundary is
(vault_subject, artifact_id), so the same opaque artifact/id may exist in
multiple vault subjects. Public lookup by artifact id may return more than one
ciphertext entry, but each entry remains subject-opaque.
The supervised agora-service also registers daemon host capability handlers:
agora.vault.put;agora.vault.list;agora.vault.get;agora.vault.delete.
Recovery is deliberately separated from the public Agora record surface.
agora-vault-ref.v1 is a plaintext record shape intended to be sealed inside
pseudonym-vault.v1; it maps recovered participant or nym material to the
provider, opaque vault/subject, capability references, and allowed artifact
kinds. A bare public participant id is not enough to list, get, or delete vault
contents.
Recorded messaging is the first profile that uses Agora Vault: it stores a
signed message-envelope.v1 as a generic encrypted artifact rather than as an
Agora topic record. Vault writes are best-effort: temporary failure does not
roll back message delivery, and the messaging outbox worker retries due
failed-retryable vault jobs.
Interaction With Existing Proposals¶
| Proposal | Relation |
|---|---|
| 013 — Whisper social signal exchange | whisper-signal.v1 is a first-class Agora content/schema under record/kind = "whisper", using pseudonymous nym:did:key:... authorship (§2 invariant 9) anchored by author/nym-proof with inline-first nym-certificate.v1 material. Bounded provenance statements use the separate record/kind = "whisper-trace" with content/schema = "whisper-trace.v1", so they cannot enter social-signal projections accidentally. Agora is one of two distribution mechanisms (the other is direct node-to-node exchange, see proposal 013 §Distribution), and Memarium is the local storage surface for both. Threshold-reached events remain a separate whisper-durable kind for when a whisper crosses bootstrap thresholds and needs a different durability posture. |
| 023 — federated offer distribution | The offer catalog today maintains its own storage; a future migration may express offer snapshots as Agora records under an operator-chosen topic key (for example orbiplex/offer-catalog) with record/kind = "offer-snapshot", retiring the bespoke replication layer |
| 024 — capability passports | Author signatures on Agora records are verified via the same passport chain |
| 025 — seed directory as capability catalog | Seed directory remains a separate primitive for capability discovery; it MAY later publish its listings as records on an Agora topic for uniform federation |
| 026 — resource opinions and discussion surfaces | Proposal 026 remains the single source of truth for resource identity (resource/kind + resource/id). Agora does not embed resource identity in its primary key; it reaches proposal 026 only through the optional record/about field. resource-opinion.v1 becomes the first content/schema on Agora, with record/kind = "opinion", and opinion records carry the referenced resource in record/about |
| 032 — key delegation passports | Delegated signing keys used for authoring Agora records follow the passport verification rules |
| 034 — node operator binding | Orthogonal. Agora does not participate in operator assurance gating |
Relationship to Memarium¶
Memarium, as defined in the vision document, is the memory-and-knowledge layer: curated archives, community resources, cultural artifacts, with federation, replication, versioning, durability rules, and semantic retrieval. Memarium answers the question what must not disappear.
Agora is a narrower primitive. It answers the question where do topic- addressed public records live and how are they federated. It is a substrate, not a curator, and it makes no decisions about what is worth preserving.
The relationship:
- A community-scale Memarium space MAY use Agora as one of its underlying substrates for the ingestion of public records into the community layer.
- Memarium's private and personal spaces are out of scope for Agora. Personal notes, idiolectal archives, and private working spaces belong in a Node-local store.
- Memarium curation (indexing, semantic search, summarization) happens on top of Agora records or Node-local stores, not inside the substrate.
- A Node may run a cache relay for Agora without running any Memarium component, and vice versa.
When a separate Memarium proposal lands, it will describe how curation attaches to Agora records and Node-local stores, and will not redefine Agora.
Implementation Notes¶
The suggested first implementation path is:
- Relay shell — a Rust service that wraps a Dendrite or Conduit homeserver, exposes the Agora HTTP API, and translates calls into Matrix room operations.
- Record verification library — a standalone Rust crate that encodes
the canonical JSON rules, computes
record/id, verifies signatures, and enforces ingest invariants. Reusable across relay, Node, and any future native transport. - Node client — a thin client in
node/agora-clientthat lets the daemon submit, query, subscribe, and cache. - Middleware seam — Agora relay may run as a middleware-attached service on an umbrella operator's Node, following the same supervision pattern as proposal 019 (supervised local HTTP-JSON middleware).
The first concrete migration target is proposal 026: serve resource
opinions through Agora as record/kind = "opinion" with
content/schema = "resource-opinion.v1".
Implemented crate architecture¶
The implementation follows a stratified layering, where each crate has a single responsibility and communicates through thin trait or data contracts:
L4.b agora-http REST/SSE surface (framework-neutral)
L4.a agora-relay-matrix composition: SQLite store + Matrix transport
L3 agora-matrix-client MatrixRelayTransport<S: MatrixEventSink>
agora-matrix pure data bridge (alias, content translation)
L2 HttpMatrixEventSink thin Matrix CS API sink (reqwest + ruma types)
L1 MatrixCsClient typed join/send/sync over Matrix CS API
L0 AuthenticatedHttpClient bearer-auth HTTP + rate-limit retry
agora-relay-sqlite persistent local relay (SQLite WAL)
agora-relay-mem in-memory reference relay
agora-relay-trait AgoraRelayTransport / AgoraRelay / SequencedRecord
agora-core envelope verification, canonical JSON, signatures
Matrix transport: thin HTTP sink¶
The Matrix transport uses reqwest::blocking and ruma 0.14.1 (types
and endpoint definitions only, feature client-api-c) plus a small
hand-written Client-Server HTTP wrapper. This keeps the transport
auditable, avoids a full client state machine, and keeps Agora relay
semantics in the relay crates rather than in a Matrix runtime. The
MatrixEventSink trait decouples MatrixRelayTransport from the
concrete HTTP implementation while preserving a single production path
for the MVP.
Three Matrix CS API endpoints are used:
POST /_matrix/client/v3/join/{roomIdOrAlias}— idempotent room joinPUT /_matrix/client/v3/rooms/{roomId}/send/{eventType}/{txnId}— send event with Agorarecord/idas deterministictxnIdfor server-side idempotencyGET /_matrix/client/v3/sync— one global long-poll loop per sink, events demultiplexed to per-(room, event_type) subscribers
Federated relay composition (L4.a)¶
MatrixBackedRelay<S: MatrixEventSink> composes SqliteRelay (local
persistent store) with MatrixRelayTransport<S> (federation channel)
and implements the full AgoraRelay trait (ingest + subscribe + query +
fetch). Key properties:
- Local-first ingest: records are persisted in SQLite before being forwarded to Matrix. Matrix failure does not block local persistence.
- Forward-only-fresh: duplicate records (by
record/id) are not forwarded to Matrix, preventing redundant federation traffic. - Relay metadata stamping:
relay/received-at(RFC 3339 UTC),relay/id, andrelay/hopsare stamped by the relay on every ingest, as specified in section 2. These fields are excluded from content address and signature (viaVerifiedRecordrelay metadata helpers), so stamping does not break the envelope invariant. - Inbound bridge: a background thread per topic subscribes to the Matrix transport, verifies inbound events, and ingests them into the local store. The bridge starts lazily (Cache mode) or eagerly (Canonical mode) and includes a cooldown to prevent tight respawn loops on transport failure.
- Retention sweep:
sweep_retention()enforcesmax_ageandmax_countper topic, then rebuilds the subject index to prune evicted entries. - Three relay roles are configuration, not separate implementations:
Canonical(eager inbound bridges for configured topics),Cache(lazy bridges on first subscribe/query),Origin(outbound only).
Subject index (L4.a internal)¶
The cross-topic record/about index from section 5.5 is implemented as
a backend-neutral SubjectIndex trait in agora-relay-trait. It is populated
as a side-effect of ingest and queried by the HTTP API for subject-based
lookups. The MVP implementation is in-memory (InMemorySubjectIndex)
and rebuildable from the local SQLite store via
rebuild_subject_index(). The index is idempotent: re-indexing the same
record is a no-op. The trait supports clear() for rebuild cycles
triggered by retention sweeps or process restarts.
HTTP API surface (L4.b)¶
agora-http is a framework-neutral adapter that maps proposal 035 §5
endpoints to AgoraRelay + SubjectIndex calls. It depends on no HTTP
framework (axum, hyper, actix-web); a daemon or middleware layer
wraps it in actual HTTP routing. Key design choices:
- Opaque cursors: pagination uses base64url-encoded sequence numbers.
Clients must treat cursors as opaque strings. The
Cursortype encodes/decodes on the HTTP boundary; relay internals use bareu64sequences viaSequencedRecord. - SSE for subscriptions:
SseRecordStreamformats verified records as standard Server-Sent Events (event: agora.record,id: {record/id},data: {json}). - Topic mismatch validation:
POST /topics/{topic-key}/recordsrejects records whosetopic/keydoes not match the URL path. - Partial about-filter rejection: when exactly one of
about_kindorabout_idis present, the API returns an explicit error instead of silently ignoring the filter. include_flaggedwire compatibility: the parameter is accepted but has no effect in MVP; flag semantics are deferred per section 8.- Query attestations: historical query pages include
agora-query-attestation.v1. The reference adapter emits unsigned attestations that bind query scope, normalized filter, record ids, cursor metadata, and a deterministic digest. Relay signatures over attestations are a later signer/authority integration, not anagora-coreconcern.
Local runtime data-dir ownership¶
An Agora service instance has exclusive ownership of its local runtime data directory. This is the Agora middleware data directory, not the Node profile root. Under daemon supervision the default path is:
<node-data-dir>/middleware/agora-service/data
The Agora middleware data directory is a single-writer local state root, not a multi-process database contract. At most one live Agora service instance MUST use a given Agora middleware data directory at a time.
This ownership invariant covers the SQLite relay store, the subject index rebuild cycle, retention sweeps, rate-limiter snapshots, and any other process-local caches or timers derived from the same root. Adding file locks around individual snapshot reads and writes would protect only those I/O operations; it would not make two Agora runtimes share one coherent timeline, one cache state, or one retention policy execution.
Implementations SHOULD enforce the invariant with an exclusive startup-time lock file inside the Agora middleware data directory and fail fast with an operator-readable error when the lock is already held. Snapshot writes SHOULD remain crash-safe through the usual temp-file, fsync, and atomic rename discipline.
Per-file shared/exclusive locks MAY be added later as a compatibility mechanism for external diagnostic, backup, or migration tools that need to read snapshot files while Agora is running. Such locks MUST NOT be interpreted as permission to run multiple Agora service instances against the same Agora middleware data directory.
Explicit local service backend (2026-10-05)¶
The operator accepted an explicit local-sqlite backend for agora-service
during P094 qualification. This uses the existing SQLite store and shared
relay contracts, not a fake Matrix sink. Signature/delegation verification,
namespace and topic policy, duplicate admission, queries, subscriptions,
subject indexing and retention remain unchanged. The receipt proves durable
local admission; remote discovery and order/result delivery require their own
network evidence. The status surface must name the selected backend and must
not claim Matrix forwarding in this mode. The default remains Matrix, and
missing Matrix credentials must not cause an implicit local fallback.
| ID | Task | Status | Closure gate |
|---|---|---|---|
P035-local-sqlite-service |
Expose an explicit local SQLite service backend | done |
2026-10-05: backend-neutral subject index/retention/stamping reuse the existing core/relay/store contracts; signed local ingest/query/subscription and restart tests pass, and ambiguous Matrix/local configuration refuses. The service reports local durable admission, never Matrix distribution. Deployed Dator ordinary publication/replay/withdrawal passes in P094's clean-source local MLX/vfkit run s13-1791156863-88b339. P094-016 independently qualifies network discovery/order/AD-result; this item does not claim it. |
MVP implementation status vs this proposal¶
Implemented¶
- Envelope verification (
agora-core): content address, Ed25519 signature, schema version, topic key syntax (empty, length, whitespace, control chars). Cross-language fidelity verified against a Python verifier (agora-verifiermiddleware module). - Local relays (
agora-relay-mem,agora-relay-sqlite): ingest with idempotent duplicate detection, per-topic append log, monotonic sequence assignment,QueryFilter-based query withSequencedRecordresults, replay-capable subscriptions, fetch byrecord/id. - Relay metadata stamping:
relay/received-at,relay/id, andrelay/hopsstamped by the federated relay (agora-relay-matrix) on ingest, viaVerifiedRecordrelay metadata helpers which preserve the envelope invariant (relay fields are excluded from content address and signature). - Matrix transport (
agora-matrix-client): thin HTTP sink usingreqwest::blocking+ruma 0.14.1types. Three CS API endpoints (join, send with deterministictxnId, global sync with demux). - Matrix data bridge (
agora-matrix): deterministic room alias derivation (#agora.<hex-sha256-nfc>:<domain>), event typeorg.orbiplex.agora.record.v1, bidirectional record ↔ event content translation with full envelope verification on inbound. - Federated relay (
agora-relay-matrix):MatrixBackedRelay<S>composing SQLite + Matrix transport. Local-first ingest, forward-only-fresh, lazy/eager inbound bridges with cooldown, three relay roles (Canonical/Cache/Origin), retention sweep (max_age,max_count). - Subject index (
agora-relay-trait): in-memorySubjectIndexfor cross-topicrecord/aboutqueries, idempotent, rebuildable from local store. - HTTP API surface (
agora-http): framework-neutral adapter for proposal 035 §5 endpoints. Opaque cursors, SSE subscriptions, topic mismatch validation, partial about-filter rejection. - Service and daemon integration (
agora-service+ supervised middleware): the relay runs as a daemon-managed local HTTP middleware service. The daemon supervises the process, provisions module/client auth tokens, and exposes capability-provider discovery while the service owns its REST/SSE surface. - Retention (age + count + scheduling):
sweep_retention()with automatic subject index rebuild after purge, driven by theagora-serviceretention sweep thread when topic policies are configured.
Ingest invariants still deferred or limited¶
The following invariants from section 2 use "flag, not reject" semantics in this proposal and are not blocking for the first deployment:
- Invariant 5 (
authored/atclock skew window) — not checked; the field is validated as non-empty only. - Invariant 6 (
record/parent/record/supersedesdangling detection) — implemented at the publisher edge inagora-service. The default is warn-and-accept to preserve eventual consistency;strict_parent_refs = trueturns unresolved references into HTTP 422. Federation ingress remains warn-only. - Invariant 7 (passport chain verification via proposals 024 and
032) — the Rust
AgoraSignaturestruct now carries a stratified shape withkey/public(the actual signing key bytes) and an optionalkey/delegationblock (delegation proof chain per proposal 032). Direct-key signatures are fully verified end-to-end (key match against the author's participant id is a hard block viaSignAdapterError::PublicKeyMismatch). Delegated signatures are accepted structurally and verified through theCapabilityDelegationVerifiertrait wired in L4.b of the 035-impl doc; the default deployment installs a permissive verifier, and hardened deployments plug in theagora-capability-bridgeproof checker. The legacysignature.key/refshape is retired and no longer appears in the schema or the code. - Invariant 8 (
record/aboutresource identity structural validation per proposal 026) — the array is accepted structurally; individual entries are not validated againstresource-ref.v1. - Schema-level patterns (
record/kind,content/schema,record/idlength bounds) — not enforced by the verification library; the library checks non-empty semantics only. Schema-level validation is a relay-side concern. - Topic key NFC normalization — the verification library checks syntax only (empty, length, whitespace, control chars). NFC normalization is applied by the canonical JSON layer before hashing and signing, so content addresses are stable regardless of input NFC form, but the relay does not currently reject or normalize non-NFC topic keys at the ingest boundary.
Still deferred / intentionally limited¶
- Subject index persistence: for MVP, the
record/aboutSubjectIndexis intentionally a derived, rebuildable view over the local relay store. It is not a source of truth and does not need its own persisted projector to satisfy the v1 contract.rebuild_subject_index()reconstructs it from the local SQLite store on startup; a SQLite-backed subject index should be added later only when listing volume, restart latency, or retention churn makes rebuild cost material. - Room-to-topic name mapping (section 6, rule 2): the relay does not yet maintain a human-readable room-name mapping for operator tooling.
Post-MVP hardening queue¶
The hard-MVP relay is complete without these items, but production hardening SHOULD track them explicitly rather than rediscovering them from prose:
authored/atskew policy: decide the local/federated skew budget and reject records outside it at the relay boundary.- Delegated-signature proof enforcement: replace the permissive default
CapabilityDelegationVerifierwith theagora-capability-bridgechecker in hardened deployments. record/aboutstructural validation: validate each resource reference againstresource-ref.v1before subject-index admission.- Schema-pattern parity: ensure relay-side schema-gate rejects malformed
record/kind,content/schema, andrecord/idvalues before verification library calls. - Topic-key NFC admission: decide whether the relay rejects non-NFC topic keys or normalizes them before ingest diagnostics.
- Federation ingress parent/supersedes policy: decide whether Matrix/federated ingress stays warn-only or gains a stricter reference-resolution mode.
- Operator status product: expose federation lane diagnostics, ingest rejection counters, retention sweeps, and relay role health in the Node UI.
Consequences¶
Positive:
- one portable record envelope replaces several bespoke storage contracts across future subsystems,
- proposal 026 gains a concrete storage and federation substrate without having to invent one,
- the offer catalog and seed directory gain a future migration target if umbrella operators want to consolidate replication on one substrate,
- Matrix reuse avoids inventing a federation transport,
- topic addressing is unified with resource addressing across Orbiplex.
Tradeoffs:
- introducing Matrix as a backend adds an operational dependency for umbrella operators running canonical relays; Nodes running cache relays can avoid it via HTTP-only cache mode,
- Agora records are public by default in MVP, which forces applications that need private channels to wait for a later proposal,
- content addressing forces strict canonical-JSON discipline on authors; any post-signature mutation invalidates the record,
- the offer catalog currently maintains its own storage; migration is a future decision, not a hard requirement of this proposal.
Alternatives Considered¶
- NATS JetStream as backend. Rejected because NATS is optimized for low-latency messaging, not citeable archive state, and because its cluster model assumes trusted operators rather than cross-umbrella federation. A later proposal may add JetStream as an optional backend for latency-sensitive record kinds that do not require long-term archival.
- Invent a native transport from the start. Rejected because the envelope value is independent of the transport, and shipping the envelope on a proven transport first lets the schema mature before the transport is locked in.
- Extend proposal 025 (seed directory) into a generic substrate. Rejected because the seed directory has a specific payload contract (capability passports and offers) that would blur if generalized.
- Extend proposal 023 (offer distribution) into a generic substrate. Rejected for the same reason.
Known Limitations¶
- MVP has no private topics. Any record submitted to Agora is considered public and visible to every participant that can reach the relay.
- MVP has no substrate-level moderation. Record-kind contracts must carry their own moderation rules.
- MVP has no formal schema registry for
record/kind; registration happens through proposals and schema files. - Matrix homeservers have their own operational footprint; small Nodes running cache relays only SHOULD NOT be required to run a full Matrix homeserver.
P081 Scoped-Claim Ingest Path¶
Agora now has an optional P081 path for nym-authored records. A
nym-authorship-proof.v1 may carry both scoped-claim/request and
scoped-claim/presentation; either both are present or neither is. The service first
verifies the complete signed Agora envelope, then binds the scoped request to a
canonical digest of the exact ingest candidate with the nested scoped values removed
to avoid a self-referential digest. Only after that check may the durable replay cache
consume the nonce.
The shared verifier returns evidence only. Agora still applies topic ACL, namespace, content-schema, disclosure, certificate-scope, and publish authority policy through its existing gates. Valid scoped evidence can therefore be denied, and a malformed or badly signed outer envelope cannot consume a valid presentation nonce. The path is disabled by default and fails closed unless trusted council issuers and fresh local revocation snapshot metadata are configured.
Open Questions¶
- Should Agora topics have a lightweight per-topic policy document (for
example
agora-topic-policy.v1) that constrains authorized record kinds, retention, and writer set, or should those constraints remain relay-wide? - Should
record/supersedesbe enforced as one-author-one-current-record per topic for specific kinds (for exampleopinion), or is that a kind-specific rule? Proposed: kind-specific, not substrate-level. - Should the offer catalog and seed directory migrate onto Agora? Proposed: not in this proposal. Evaluate after the MVP is in production for at least one umbrella operator.
- Should Agora records be signable by delegated keys issued under a capability passport, or only by the participant root key? Proposed: delegated keys are allowed via proposal 032 passport chains.
- Should the substrate offer any supported topic-key conventions (for example a recommended hierarchy grammar) even though it does not parse them, or should convention be left entirely to kind contracts? Proposed: publish a non-normative conventions note alongside the schema file, not a substrate rule.
- Should the
record/aboutcross-topic index be rate-limited or quota- bounded by relays to prevent abuse of subject-driven fan-out? Proposed: relays MAY impose result windows and rate limits, with defaults chosen during MVP deployment. - Should the relay expose a canonical federation tick rate and a federation lag metric, and if so, where should it be surfaced? Proposed: reuse the observability pattern from proposal 023.
Full closure qualification profile (2026-10-19)¶
per-room-recovery-v2 keeps the 31 Matrix/five SQLite cases of the previous
full profile and adds three observed process paths: excessive history Retry-After,
deferred scoped admission across restart, and a claim published while valid but
expired at local historical admission. The first two use labelled fault injection;
the third uses a fixture-signed claim and the actual receiver clock. The full
profile therefore requires 34 Matrix and five SQLite cases. Existing reports keep
their original variant and meaning.
Scoped-claim context binding uses JCS over the author candidate, excluding
record/id, signature, all three unsigned relay fields, and the two circular
scoped-claim request/presentation fields inside the nym proof. Nested author
content remains bound. A transport's local stamping cannot change claim context
or grant admission. Node's shared Agora core derivation is used by the API,
Matrix receiver and fixture; actual admission still uses each receiver's local
time and retained decisions. The development deferred-admission probe exposed
and corrected the earlier stamp-dependent context.
Full canonical PDU export occurs after all publication/recovery probes, so its Node gate covers every acknowledged Node-created event in the campaign. The homeserver gate remains separate. Qualification rejects early retry, a final source created while admission is deferred, current-time re-admission during replay, and historical expiry hidden under a generic rejection. Fixture signing and host/UI authoring remain separately labelled. This accepted test contract alone does not mark P035-007 or P035-010 done.
Producer Delivery Through Artifact Delivery¶
Planned continuation (2026-10-11), not a reopening of the completed relay MVP.
Node-owned producers should submit already signed agora-record.v1 artifacts
through S023 Artifact Delivery
and its existing agora-default adapter. This includes P026 authoring, P067
offer publication and accepted Seed Directory facts. It does not replace the
Agora ingest API, historical queries, SSE or Matrix relay replication; those
remain independently usable protocol surfaces. Public publication does not
require INAC, and private artifacts must never acquire a public fallback.
Implementation Recommendations¶
- Keep
authorize -> sign -> AD submission -> Agora admissionexplicit. AD outbound permission does not substitute for Agora topic, author, capability, attestation or fresh revocation checks, including on delayed attempts. Preserve the original caller and publication authority across the host bridge; the adapter must not become an ambient privileged publisher. - Reuse
ad-host, the AD delivery ledger, object retention and deferred-operation status. Producers retain domain intent and exact signed bytes before submission; restart retries the same artifact and destination, not a newly signed record. Journal the intent-to-delivery-id association so a crash between domain commit and AD submission is reconcilable without a second transport retry queue. - Project
record/id, target relay, AD delivery id and domain publication outcome into the existing UI/status surfaces. Queued, relay-accepted and failed are different observations; relay acceptance is not proof of all-peer replication or permanent retention. A timeout after remote acceptance remains uncertain until idempotent retry/readback reconciles it. Do not report it as unpublished. - Feed bounded publication outcome facts into Memarium through existing observation ports and S028 temporal storage conventions. Retaining a local artifact is not permission to republish after expiry, withdrawal or deletion. Inventory existing publisher outboxes first; preserve domain jobs but give AD sole ownership of transport retries after successful submission.
- Keep adapters thin under
node:DEV-GUIDELINES.md: pure mapping below host I/O, schema-gated boundaries, bounded retention and redacted diagnostics. Migrate producers incrementally with one selected path per publication, never dual-send.
Delivery Integration Tracker¶
All rows are post-MVP todo; done requires retained Node runtime evidence and
reconciliation of its implementation ledger and any affected MVP scope. S008
links here rather than maintaining duplicate task status.
| ID | Task | Status | Depends on | Completion gate |
|---|---|---|---|---|
| P035-001 | Freeze producer-to-AD publication binding | todo | Existing S023 agora-default |
Inventory UI, Dator, Seed Directory and other direct publishers; classify local-only/test clients separately; pin caller/authority propagation, exact signed bytes, intent/delivery correlation, timeout semantics and ownership of every existing outbox. Contract fixtures cover policy refusal before effects and recovery after submission ambiguity. |
| P035-002 | Migrate Node-owned publishers | todo | P035-001 | P026 compose/opinion/comment authoring, P067 Dator offer snapshots and accepted Seed facts use the same host delivery seam with explicit route configuration. UI reports pending versus confirmed publication. No duplicate sends or component-private transport retries; raw/unaccepted Seed requests are never published. Domain state survives delivery failure. |
| P035-003 | Retain local and remote relay acceptance | todo | P035-002 | Exercise actual producer -> signer -> AD -> local/remote Agora -> query with unchanged record id/signature/about fields; outage, remote-accepted/lost-response, restart, duplicate retry, expired/revoked authority and denied topic fixtures pass. Private fallback is refused; Matrix federation and direct relay API compatibility remain intact. |
Follow-Up¶
If adopted, the next artifacts should be:
- one schema file for
agora-record.v1, - one schema file for
plain-comment.v1as the first general-purpose base kind, - one schema file registering
resource-opinion.v1as an Agorarecord/kind = "opinion", - one implementation note describing the Matrix mapping and the Dendrite or Conduit deployment footprint,
- one separate proposal for private (encrypted) Agora topics,
- one separate proposal for Memarium, describing how curation attaches to Agora records and Node-local stores,
- proposal 040 (custodial redelivery and tombstones) defines the readback
tombstone vocabulary (
410 Gonewithretention_expired,removed_by_policy,storage_lost,superseded), operator sovereignty over re-acceptance, custodian negotiation viacapability-passport.v1, and the author-scoped sondage endpoints (count, listing, digest) gated byagora-author-proof.v1. Agora implementations SHOULD implement the tombstone surface as soon as the relay serves more than one node; pure local-only MVP relays MAY keep404as the sole absence signal, - proposal 041 (Agora ingest attestation and tiered access) defines the
author/attestation_refoptional signed envelope field, theagora-attestation-proof.v1short-lived proof token, the ingest mode matrix (open,allowlist,passport,passport_scoped,layered), a predicate grammar for tiered gates, and a stable refusal reason-code vocabulary symmetric with proposal 040's tombstone matrix. The verification primitive is placed as a reusableattestation-gatealongside signer and sealer in the daemon's trust-boundary zone and is consumable by every component that needs to ask "is this author attested under a passport I currently accept", not only Agora.