{:component/id :orbiplex/agora
 :component/title "Orbiplex Agora"
 :component/type :node-attached-module
 :component/status :done

 :summary
 "Node-attached topic-addressed record relay providing a shared signed
 substrate for records such as resource opinions, comments, whisper signals,
 and federated offer announcements. Runs as a supervised middleware service
 under the Node daemon and is discovered by clients through capability id
 `agora.relay`."

 :must-implement
 [{:cap/id :agora-record-sign
   :title "Agora Record Signing"
   :kind :signing
   :based-on ["doc/project/30-stories/story-008-cool-site-comment.md"
              "doc/project/40-proposals/035-agora-topic-addressed-record-relay.md"
              "doc/project/50-requirements/requirements-014-resource-opinions.md"]
   :schemas ["agora-record.v1"
             "resource-opinion.v1"]
   :responsibilities
   ["Canonicalize unsigned `agora-record.v1` payload for the `agora.record.v1` domain"
    "Sign through the daemon host signer using `KeyRef::PrimaryParticipant`"
    "Bind returned public key to the author's `participant/did:key` fingerprint"
    "Compute `record/id` as `sha256:<base32lower(canonical_signed_bytes)>`"]
   :status :done}

  {:cap/id :agora-record-ingest
   :title "Agora Record Ingest"
   :kind :protocol
   :based-on ["doc/project/30-stories/story-008-cool-site-comment.md"
              "doc/project/40-proposals/035-agora-topic-addressed-record-relay.md"
              "doc/project/50-requirements/requirements-014-resource-opinions.md"]
   :schemas ["agora-record.v1"
             "resource-opinion.v1"]
   :responsibilities
   ["Re-verify envelope structure and canonical `record/id` on ingest"
    "Verify `signature.value` against author public key in `agora.record.v1` domain"
    "Apply topic ACL gate before persistence"
    "Reject duplicates idempotently (same `record/id` → `200 OK`)"
    "Persist accepted records in the local relay backend"]
   :status :done}

  {:cap/id :agora-record-query
   :title "Agora Record Query"
   :kind :protocol
   :based-on ["doc/project/30-stories/story-008-cool-site-comment.md"
              "doc/project/40-proposals/026-resource-opinions-and-discussion-surfaces.md"
              "doc/project/40-proposals/035-agora-topic-addressed-record-relay.md"
              "doc/project/50-requirements/requirements-014-resource-opinions.md"]
   :schemas ["agora-record.v1"
             "resource-ref.v1"]
   :responsibilities
   ["Serve `record/id`-keyed exact lookup"
    "Serve bounded topic listings with cursor pagination"
    "Serve `record/about` subject-index listings keyed by `{resource/kind, resource/id}`"
    "Keep paging semantics stable across backends"]
   :status :done}

  {:cap/id :topic-subscribe-sse
   :title "Topic Subscribe Stream"
   :kind :protocol
   :based-on ["doc/project/40-proposals/035-agora-topic-addressed-record-relay.md"]
   :schemas ["agora-record.v1"]
   :responsibilities
   ["Hold SSE connection per subscriber"
    "Deliver newly accepted records on the subscribed `topic/key` in authored-at order"
    "Honor subscription start cursors (`from-now`, `from-record-id`)"
    "Enforce backpressure / lag bounds per subscription"]
   :status :done}

  {:cap/id :topic-acl-gate
   :title "Topic ACL Gate"
   :kind :policy-enforcement
   :based-on ["doc/project/40-proposals/035-agora-topic-addressed-record-relay.md"]
   :schemas []
   :responsibilities
   ["Enforce `(topic/key, record/kind, content/schema)` admission rules"
    "Decide whether the relay role (canonical/cache/origin) is authoritative for a topic"
    "Apply the same rules to local ingest and federated ingress"]
   :status :done}

  {:cap/id :org-authority-custody
   :title "Org Authority Custody"
   :kind :policy-enforcement
   :based-on ["doc/project/60-solutions/008-agora/008-agora.md"]
   :schemas ["org-custody-policy.v1"
             "org-custody-decision.v1"
             "key-delegation.v1"
             "agora-record.v1"]
   :responsibilities
   ["Resolve org authority roots through `custody_policy_ref` instead of raw publisher-key lists"
    "Validate custody policy and inline decision proofs at the Node schema gate"
    "Support `any-authorized` and real threshold/quorum custody modes"
    "Fail closed for unknown policy refs, unknown modes, missing decisions, duplicate-only quorum, signer mismatch, and target digest mismatch"]
   :status :done}

  {:cap/id :matrix-relay-transport
   :title "Matrix Relay Transport"
   :kind :transport
   :based-on ["doc/project/40-proposals/035-agora-topic-addressed-record-relay.md"]
   :schemas ["agora-record.v1"]
   :responsibilities
   ["Publish accepted records as Matrix events under topic-keyed rooms per relay role"
    "Ingest inbound Matrix events and re-validate as ordinary Agora records"
    "Treat Matrix as transport provenance only; trust the verified envelope and local admission policy, not the donor relay"
    "Expose canonical/cache/origin topic behaviors as deployment-configurable roles"]
   :status :done}

  {:cap/id :retention-policy-sweep
   :title "Retention Policy Sweep"
   :kind :policy-enforcement
   :based-on ["doc/project/40-proposals/035-agora-topic-addressed-record-relay.md"]
   :schemas []
   :responsibilities
   ["Run periodic sweep per configured interval"
    "Evict records older than `max_age_secs` per topic"
    "Trim topics exceeding `max_count` entries"
    "Preserve canonical-role durability contracts where applicable"]
   :status :done}

  {:cap/id :replay-fed-domain-projections
   :title "Replay-Fed Domain Projections"
   :kind :projection
   :based-on ["doc/project/40-proposals/026-resource-opinions-and-discussion-surfaces.md"
              "doc/project/40-proposals/035-agora-topic-addressed-record-relay.md"
              "doc/project/60-solutions/008-agora/008-agora-dir-simplify-impl.md"]
   :schemas ["agora-record.v1"
             "plain-comment.v1"
             "comment-thread-policy.v1"
             "resource-opinion.v1"
             "public-gossip.v1"
             "moderation-marker.v1"]
   :responsibilities
   ["Replay accepted public Agora records into local domain read models"
    "Keep comments, resource opinions, public gossip, and moderation markers in separate tables"
    "Share cursor, pruning, replay-diagnostic, and orphan mechanics without mixing domain semantics"
    "Expose projection status and query APIs under the Agora service surface"]
   :status :done}

  {:cap/id :operator-local-rejection-feed
   :title "Operator-Local Rejection Feed"
   :kind :inspection
   :based-on ["doc/project/40-proposals/035-agora-topic-addressed-record-relay.md"]
   :schemas []
   :responsibilities
   ["Persist local rejection diagnostics without publishing a public rejection oracle"
    "Store request digests and bounded metadata by default, not raw payloads or private proofs"
    "Expose the feed through `/v1/agora/status` and `GET /v1/agora/operator/rejections`"]
   :status :done}

  {:cap/id :agora-vault-encrypted-artifacts
   :title "Agora Vault Encrypted Artifacts"
   :kind :storage
   :based-on ["doc/project/40-proposals/035-agora-topic-addressed-record-relay.md"
              "doc/project/40-proposals/060-messaging-middleware.md"]
   :schemas ["agora-vault-entry.v1"
             "agora-vault-ref.v1"]
   :responsibilities
   ["Store generic encrypted-only artifacts keyed by opaque artifact/id"
    "Reject plaintext or missing cryptographic envelopes at ingress"
    "Keep public lookup limited to artifact/id, artifact/kind, vault-entry/id, encryption metadata, and ciphertext"
    "Provide scoped list/get/delete under opaque vault subjects"
    "Expose daemon host capability handlers agora.vault.put/list/get/delete"
    "Support recorded messaging as the first vault profile without turning message envelopes into ordinary agora-record.v1 records"]
   :status :done}]

 :may-implement
 [{:cap/id :story-005-public-signal-smoke
   :title "Story-005 Public Signal Smoke"
   :kind :e2e
   :based-on ["doc/project/30-stories/story-005-whisper-rumor-intake.md"
              "doc/project/40-proposals/013-whisper-social-signal-exchange.md"
              "doc/project/60-solutions/011-whisper/011-whisper.md"]
   :schemas ["agora-record.v1"
             "whisper-signal.v1"
             "whisper-interest.v1"
             "whisper-threshold-reached.v1"
             "association-room-proposal.v1"
             "public-gossip.v1"]
   :responsibilities
   ["Provide a three-node laptop smoke where node A and node B publish similar sanitized public/federated `whisper-signal.v1` records and node C runs the Agora relay/server"
    "Reject private-correlation whispers on the public relay path"
    "Replay accepted public Whisper records into a projection with diagnostics"
    "Create deterministic threshold state with the M4 rule: two distinct eligible nodes, same `topic/class`, same `signal/similarity-key`, bounded time window"
    "Emit deterministic `whisper-threshold-reached.v1` and `association-room-proposal.v1` records as projection-authority-signed, issuer-scoped derived claims"
    "Prevent derived-record loops with deterministic ids, source-kind exclusion, and derivation refs"
    "Create an association-room proposal without automatic human enrollment"
    "Keep Matrix-backed Agora sync parity as a separate M4 test from the three-node HTTP smoke"
    "Keep Story-009 Agora-primary smoke green while adding the Story-005 smoke"]
   :status :planned}

  {:cap/id :agora-relay-passport
   :title "Agora Relay Capability Passport"
   :kind :capability-passport
   :based-on ["doc/project/40-proposals/024-capability-passports-and-network-ledger-delegation.md"
              "doc/project/40-proposals/035-agora-topic-addressed-record-relay.md"]
   :schemas ["capability-passport.v1"]
   :responsibilities
   ["Advertise `agora.relay` as a capability for federation-scoped discoverability"
    "Expose relay endpoint, supported topics, and relay role through the passport payload"
    "Remain optional: a relay without a passport is still a valid local relay"]
   :status :optional}

  {:cap/id :proxy-key-delegated-signing
   :title "Proxy-Key Delegated Signing"
   :kind :signing
   :based-on ["doc/project/40-proposals/032-key-delegation-passports.md"]
   :schemas ["agora-record.v1"
             "key-delegation.v1"]
   :responsibilities
   ["Accept sign requests carrying a `DelegationProof` instead of direct participant signing"
    "Verify proof through a pluggable `DelegationProofVerifier` (fail-closed fallback by default)"
    "Bind proxy public key to the record and keep the principal's participant id as author"
    "Attach inline proof so verifiers can check delegation without remote lookups"]
   :status :done}

  {:cap/id :agora-action-trace
   :title "Correlated Agora Action Trace"
   :kind :inspection
   :based-on ["doc/project/30-stories/story-008-cool-site-comment.md"
              "doc/project/50-requirements/requirements-014-resource-opinions.md"]
   :schemas ["agora-trace.v1"]
   :responsibilities
   ["Emit non-secret trace events for `capability.lookup`, `record.sign`, `record.ingest`, and `record.readback`"
    "Carry `X-Orbiplex-Correlation-Id` from UI/CLI/API callers into daemon-owned trace storage"
    "Store the canonical local trace under daemon stream `trace/agora`"
    "Accept trace appends only through the module-authenticated host capability path"
    "Expose append failure/degraded status through `/v1/agora/status`"
    "Expose bounded operator readback via `GET /v1/traces/agora` without replaying the action"]
   :status :done}

  {:cap/id :aggregate-status-surface
   :title "Aggregate Status Surface"
   :kind :inspection
   :based-on ["doc/project/40-proposals/035-agora-topic-addressed-record-relay.md"]
   :schemas []
   :responsibilities
   ["Expose `/v1/agora/status` with relay role, configured topics, backend health"
    "Report last sweep timestamp and outbound Matrix transport state"
    "Remain cheap, cacheable, and suitable for operator inspection"]
   :status :optional}]

 :out-of-scope
 ["Public rejection feed and public rejection oracle semantics in M2/M2b"
  "Automatic policy decisions, moderation enforcement, or reputation weighting"
  "Private-disclosure mechanics for Whisper"
  "Payment / settlement of record ingest"
  "Long-running workflow execution driven by record content"
  "Replacement of seed directory or offer catalog as storage backends in v1"]

 :consumes
 ["agora-record.v1"
  "resource-ref.v1"
  "capability-passport.v1"]

 :produces
 ["agora-record.v1"
  "agora-vault-entry.v1"
  "agora-vault-ref.v1"]

 :notes
 ["Agora v1 runs as a supervised Rust binary `orbiplex-node-agora-service` under the Node daemon middleware supervisor."
  "The smallest useful Agora deployment is local-only: one node, one relay, no passport, one topic, one author."
  "Federated deployment layers on top without replacing the envelope, signer contract, or `record/id` formula."
  "Agora Vault is a sibling encrypted-artifact surface: local supervised routes are client-auth / daemon-dispatch gated, while remote provider deployments bind the same operations to `agora-vault@v1` passports."
  "Per-kind content semantics (e.g. `resource-opinion.v1`) are owned by their source proposals, not by Agora."
  "Future hot-path transport optimizations (batch ingest, in-process daemon library) are documented in `doc/project/60-solutions/008-agora/008-agora.md` §Future Hot-Path Optimizations; neither changes the envelope, signing domain, or `record/id` formula."]}
