Shared Offer Catalog¶
Based on:
doc/project/40-proposals/021-service-offers-orders-and-procurement-bridge.mddoc/project/40-proposals/067-shared-offer-catalog-over-agora.md
Planned extension:
doc/project/40-proposals/090-inference-execution-provenance-and-non-local-disclosure.md
The Shared Offer Catalog is a Node-attached middleware solution component that
materializes a public/federated offer read model from Agora offer-snapshot
records.
It is deliberately not the provider authority. Dator owns local standing
offers and provider execution. Agora owns the durable publication log. The
Shared Offer Catalog owns the domain projection, admission diagnostics, and
query surface over that log.
Purpose¶
The component is responsible for:
- replaying configured Agora offer-snapshot topic pages,
- verifying outer Agora record admission through the daemon
agora.record.admithost capability, - verifying provider publication authority through the daemon
seed.directory.queryhost capability, - requiring an active
offer-snapshot-publishercapability for the provider node and provider participant, - verifying the inner provider-authored
service-offer.v1signature through the shared Pythonoffer_cataloglibrary, - storing the current offer projection in SQLite through the shared
lib/catalog.pySqliteCatalogsubstrate, - exposing active-offer query APIs with provenance and trust metadata,
- eventually indexing signed inference-posture characteristics without presenting them as realized execution facts,
- retaining rejected/skipped replay diagnostics.
Scope¶
The component does not own:
- local standing-offer publication,
- service-order dispatch or result delivery,
- provider execution,
- settlement or procurement authority,
- peer-message fetch/push listeners.
Those concerns remain in Dator, Arca, and daemon-owned transport layers.
Capability Names¶
local-offer-catalog— provider-side local catalog of one node's own offers, owned by Dator.offer-snapshot-publisher— provider authority to publish signed offer snapshots into shared catalog admission.shared-offer-catalog— public/federated projection and query role owned by this component.
Legacy peer-message names such as offer-catalog.fetch.request and
offer-catalog.push remain compatibility wire names only.
Implementation¶
Reference implementation:
node/middleware-modules/lib/offer_catalog.pynode/middleware-modules/offer-catalog/service.pynode/middleware-modules/offer-catalog/config/00-offer-catalog.jsonnode/middleware-modules/offer-catalog/config/profiles/public-shared-catalog.json
The same Python runtime is embedded by Arca as a buyer-local cache, so public catalog deployment and buyer-local cache deployment reuse one implementation instead of forking catalog mechanics.
Public Deployment Profile¶
The hard-MVP public deployment profile is data-only configuration:
deployment_shape = "public-shared",- Agora replay enabled with
source_mode = "agora-primary", - host
agora.record.admitrequired, - host
seed.directory.queryrequired foroffer-snapshot-publisher, shared-offer-catalogcapability passport publication enabled at startup.
The service keeps /healthz available for diagnosis, but /readyz stays
pending until the host issues and publishes the shared-offer-catalog
capability passport. The status surface exposes only passport identifiers and
pending reasons, not the full passport artifact.
The Node worktree carries a local public-profile smoke runner that imports the real middleware under that profile, mocks only host/Agora HTTP boundaries, verifies passport publication readiness, replays one authorized offer snapshot, rejects bad-signature and unknown-provider records, keeps withdrawn records hidden from active queries while visible for inspection, and checks the HTTP service-catalog result.
Passport Lifecycle¶
The shared-offer-catalog capability passport has a bounded validity requested
through passport_expires_in_sec. The hard-MVP public profile uses 24 hours
(86400 seconds), requests the passport at service start, publishes it once, and
keeps /readyz pending until publication succeeds.
Supervisor-driven restart is the default operational boundary for renewal. For the hard-MVP slice, operators should run the service under a supervisor that restarts it before passport expiry or restart it manually as part of the deployment runbook. Automatic in-process renewal remains optional future hardening, not the baseline contract.
Public deployments must use HTTPS/TLS for non-loopback agora.base_url values;
the Node middleware fails closed before replay when public/shared mode is configured
with non-loopback HTTP. Loopback HTTP remains acceptable for local smoke runs,
development, and reverse-proxy topologies where the public TLS boundary sits
outside the middleware process.
Query Surface¶
GET /v1/enact/service-catalogGET /v1/offer-catalog/replay/statusGET /v1/offer-catalog/replay/diagnostics?limit=NPOST /v1/offer-catalog/replay/resyncPOST /v1/offer-catalog/replay/reset-cursor
The service-catalog query supports service_type, provider participant,
provider node, active / active_only, and limit. Active-only is default.
Inference posture query (partial)¶
The additive Proposal 090 slice preserves the separate signed
inference-execution-posture.v1 value in the offer's
policy_annotations.inference/posture extension and exposes bounded filters for locality posture,
optional open provider refs, caller-owned provider allow/deny policy, and
withheld or unknown identity. The value binds assertion owner, exact offer
subject/generation/scope, and versioned processing-boundary ref. A filter
compares only the same boundary or a locally admitted explicit boundary
relation; unrelated boundaries are non-matching or unknown, never implicitly
local. The vocabulary remains extensible; the component does not define a
closed provider registry, deliberation-profile catalog, or evidence-policy
repertoire.
The optional inference_policy query reuses the host's pure policy evaluator,
filters before pagination and preserves independent assessments. Unavailable
evaluation and incomplete over-budget scans refuse instead of returning an
unfiltered result. Local and observed Arca catalog projections use the same
rules; policy-filtered observed reads retain ordinary expiry cleanup. P090-008b
separately binds selection/dispatch to the exact admitted signed revision,
so replacing this read model does not rewrite a purchase's declaration.
P090-008 completes the declaration/disclosure checkpoint with complete, partial,
withheld and unknown values preserved through signed admission and Rust/Python
projections. The receiving filter distinguishes deny/warn/allow for incomplete
provider identity and never softens a known provider prohibition. Valid resealed
posture substitution without the offer's signature is refused.
This query surface describes what an offer declares it may use. It cannot emit
or infer realized inference-execution-provenance.v1, which is produced only
after an invocation and validated by the service consumer. In particular,
corpus/model-class, a loopback endpoint, or a local adapter process is not
proof of local inference.
Withdrawal¶
Withdrawal is modeled as a higher-sequence/no service-offer.v1 snapshot with
offer/status = "withdrawn" and a contract-valid expires-at. The projection
retains the row for inspection but hides it from active queries.
Status¶
hard-MVP done: the reference Node implementation has the shared Python
runtime, standalone middleware service, Arca embedded-cache integration,
fail-closed host Agora admission hook, Seed Directory provider admission hook,
query surface, replay diagnostics, withdrawal active filtering, a public/shared
deployment profile, classified/redacted passport-publication readiness
semantics, classified/redacted Host Agora and Seed Directory admission
diagnostics, and a local public-profile smoke runner with positive and negative
admission coverage.
Remaining work is post-MVP hardening: richer production monitoring, broader
multi-catalog deployment matrices, and eventual retirement of legacy
offer-catalog.fetch / offer-catalog.push compatibility wire names after
Agora-backed deployments cover the needed federation paths. The Proposal 090
inference-posture query is implemented for the scoped inline catalog/selection
path; P090-008 also completes all four declaration/disclosure states. This additive
evidence does not change the completed hard-MVP claim.