Skip to content
Registry StackDocsv0.25.0

Threat model

View as Markdown

This page is for a reviewer or auditor who already reasons in terms of assets, trust boundaries, and adversaries, but does not yet know where the Registry Stack’s boundaries actually sit. It answers one question: why does this design produce the security properties it claims: which threats does the architecture consider, what does it actually mitigate, and where do the residual risks lie? It is deliberately a map of boundaries and limits, not a runbook. Hardening procedures belong to the hardening checklist, and the invariant-by-invariant statement of what the Evidence Gateway runtime enforces belongs to the Evidence Gateway security model. Here the goal is a defensible threat model: the boundaries the design draws, the threats it places in and out of scope, and the residual risks.

The two-layer architecture and its primary trust boundary

Section titled “The two-layer architecture and its primary trust boundary”

The stack is a two-layer design, and the split between the layers is the first trust boundary the model relies on. A portable metadata layer only describes: it carries no production data, no authentication, and no secrets. A runtime services layer enforces: it is where authentication, authorization, and disclosure actually happen.

The consequence for a reviewer is that the metadata layer authorizes nothing, enforces nothing, and does not assert that any record exists or satisfies a question. Publishing a dataset, a policy, or an offering does not grant access and is not proof that a record exists. Read this strictly for policy documents: a published ODRL policy describes intent and nothing enforces it. Registry Manifest can carry an ODRL policy reference and an enforcement profile identifier, but no current runtime service reads them, so treat a published policy as a statement about a deployment rather than a control the stack applies.

This also closes one threat directly: because the metadata layer is meant to be distributed and inspected, secrets are never embedded in it. The operator injects them at deployment, which keeps secret material out of the portable artifact entirely.

Components and where they sit on the boundaries

Section titled “Components and where they sit on the boundaries”

Four formal components sit on or beside the trust boundaries, with one supporting service beside them. A separate adopter demo sits outside the production trust boundary:

  • Registry Manifest: the offline metadata producer. No production data, no auth, no secrets. It lives entirely on the describe side of the primary boundary.
  • Registry Relay: a contract-compiled read-only API over local read-only SQLite sources. It makes no outbound source call, holds no source credential, and signs nothing.
  • Evidence Gateway: a minimum-disclosure assertion service. It answers one bounded, predefined question about one subject with a signed assertion carrying the answer and not the record.
  • Registry Platform: the shared security primitives that the runtime services build on.
  • Registry Mint: a supporting service, not a pattern of its own. It issues the short-lived access tokens a resource server such as Evidence Gateway verifies, for a deployment with no identity provider. The dependency runs one way: Evidence Gateway does not depend on Registry Mint.
  • Solmara Lab: a separate adopter demo, running on synthetic data and demo configuration. Treat Solmara Lab as out of scope for production trust. Its demo and template integrations are integration examples, not a production freshness or replay-protection profile, and a team copying them into production must add request freshness, expiry, or nonce checks itself.

This model has no credential-issuance or peer-federation boundary, because no product here has one: Evidence Gateway issues no credential lifecycle and evaluates nothing on a peer’s behalf.

Security-critical primitives are concentrated in Registry Platform so that the behavior is identical across services and reviewable in one place. This is a deliberate attack-surface decision: it removes the risk of divergent, per-service security code that would each need auditing separately.

The Registry Stack trust boundaries in one map. One request path runs from Registry Mint,
which issues a short-lived access token, to the caller, to Evidence Gateway, and then to
Evidence Gateway's configured authoritative source, which it reaches over a fixed HTTP
request or a read-only SQLite extract. Registry Relay has a separate
protected-read path to its own sources. Dashed rules mark authenticated service edges.
Evidence Gateway
runs one OIDC bearer profile with one trusted issuer, one principal claim, and no trust
in proxy identity headers. Caller to Evidence Gateway carries a bearer token, a declared
purpose, a named requirement revision, and selector values; the answer, not the record,
comes back, only predefined revisions are evaluable, one authorization decision binds
the whole tuple, and possession is never authority. Evidence Gateway to its fixed sources
runs one fixed source request per acquisition stage, fixed by trusted configuration and
executed only by the core: an HTTP source fixes origin, method, path, headers,
authentication, TLS, projection, and redirect and proxy denial, and a statement source fixes
one reviewed SQL statement over a local read-only extract, with no origin, no credential,
and no network hop. Lookup collapses to match, no match, or ambiguous, and raw responses
are never logged. Evidence
Gateway and Registry Relay keep separate sources, credentials, authorization decisions, and
audit trails. Registry Mint to Evidence Gateway
runs one way: Evidence Gateway verifies the token as an ordinary resource server that knows
Registry Mint only as an issuer URL and a key set, and a deployment with an identity
provider does not run Registry Mint at all. Relay's separate path to its source is a local
read-only SQLite file opened in place, with no origin, credential, or outbound request of
any kind. A separate lane runs operator to key file to Evidence Gateway to public JWKS to
verifier, where private material reaches the process only by secret reference to an
owner-only file, signing is fail-closed, and the verifier pins the key set. Only
/health, /ready, /openapi.json, /.well-known/evidence/jwks.json, and
/.well-known/jwt-vc-issuer are unauthenticated at the Evidence Gateway edge.

The service edge (authentication). Authentication is the trust boundary at the service edge, and each service authenticates every record- or assertion-bearing route before responding. Registry Relay has exactly one authentication mode, OIDC bearer, and it is per-operation rather than per-route: an operation whose compiled access profile is Protected requires a verified token carrying the declared scope, and one whose profile is Public does not. A Relay contract with any protected operation refuses to start without a configured issuer, so a deployment cannot leave a protected operation unauthenticated by omission. There is no static-credential mode and no API-key mode; the runtime reads no credential from configuration.

Evidence Gateway runs one reviewed OIDC bearer profile with exactly one trusted issuer and exact audience, token type, and algorithm allowlists, and one configured principal claim with no client_id, azp, header, or request fallback: missing data denies rather than falling back to another claim (V1-I04). Evidence Gateway’s unauthenticated surfaces are operational and discovery only: /health, /ready, /openapi.json, /.well-known/evidence/jwks.json, and /.well-known/jwt-vc-issuer. None of them reveals which evidence definitions are enabled or which requesters may invoke them. Evidence Gateway never trusts a proxy-supplied identity header, whatever the gateway in front of it asserts.

Caller to Evidence Gateway. This is the boundary the whole product exists to draw. The caller crosses it with a bearer access token, a declared purpose, a named requirement revision, and selector values for the subject roles that requirement declares; what comes back is the answer, not the record. Four properties hold it:

  • Only predefined, versioned requirement revisions are evaluable, and the request schema is closed. A caller cannot supply thresholds, expressions, scripts, paths, headers, source fields, relationship types, adapter parameters, or response projections (V1-I01, V1-I02).
  • One authorization decision binds requester tags, optional actor, requirement revision, purpose, every role, profile, and origin tuple, authority, and audience together. Partial matches across separate grants cannot be unioned into access, and exactly one authority path must match: zero paths and two or more paths both deny (V1-I05).
  • Possession is not authority. A selector value, a demographic tuple, or a caller-supplied consent, approval, or grant reference is a lookup input, never proof that the caller may ask (V1-I06, V1-I07).
  • What is released is constructed by the core after full output validation, from declared typed concept values only. A script cannot inject envelope fields, subject identifiers, or unsupported claims (V1-I12, V1-I25).

Purpose is enforced rather than advisory: an unauthorized purpose is denied before credential acquisition and source contact, purpose is an input to every subject binding and audit pseudonym, and purpose sits inside the signed payload where a verifier can reject a mismatch. A reviewer should still read a declared purpose as the caller’s authorized selection from the granted set rather than an identity-provider attestation, unless the operator has issued a distinct requester tag per purpose.

Evidence Gateway to its fixed sources. Evidence Gateway’s outbound edge is deliberately not general-purpose. A source reaches its data over one of two coequal transports, a fixed HTTP JSON request or one reviewed SQL statement against a read-only SQLite extract file mounted beside the process, and the core owns both. Source calls are fixed by trusted bundle configuration and executed only by the core, which owns the closed single or search-then-fetch sequence, the limits, and the one-request-per-stage ceiling on either transport, and on the HTTP transport also the origin, method, fixed or tagged selector or prior-fact-bound path, fixed headers, authentication, TLS, projection, redirect denial, and proxy denial; this is what closes SSRF, credential forwarding, script-directed networking, and response-led routing (V1-I09, V1-I40). A preparation script renders only ordered query pairs and at most one JSON body on the HTTP transport, or a bounded map of scalar parameters on the statement transport, and Rust validates that output before a credential is resolved or a row is read. Every source declares a response schema that Rust validates the projected response against before any extraction script runs, so a response outside its reviewed shape is a source-protocol failure rather than an input to fact construction. Lookup collapses to match, no_match, or ambiguous: Evidence Gateway never fetches broad candidate sets, follows pages, scores candidates, or exposes counts (V1-I10). Raw source responses are never persisted or logged (V1-I14). Hostname and fixed-origin verification stay mandatory, there is no trust-all mode, and ambient HTTP_PROXY-family variables are ignored. The statement transport has none of those fields to fix: a statement source has no origin, scheme, host, port, method, path, request or response media type, redirect policy, or header, and Evidence Gateway holds no database credential for it. What trusted configuration fixes there is the one reviewed SQL statement with its declared result columns and parameter bindings, the read-only extract file the runtime binds by logical profile, and the row, cell, statement-step, timeout, response-byte, and concurrency limits.

Registry Relay and Evidence Gateway remain separate. A deployment may operate both products, and neither inherits the other’s decisions. Evidence Gateway reaches its own configured authoritative sources over either transport: a fixed HTTP JSON request, which does cross a network boundary, or one reviewed SQL statement against a read-only SQLite extract file mounted beside the process, which does not. Relay reaches no source over the network at all, and reads its own local SQLite file. The two local reads are still not the same thing: Relay compiles a whole registry contract into the fixed route set it serves over its source, while an Evidence Gateway statement source runs one reviewed statement and returns one assertion. Each product owns its own credentials, authorization decision, audit trail, and readiness checks. One composition is supported, and it runs one way only: a Relay-served API is an ordinary protected HTTP endpoint, so an Evidence bundle may name one as a fixed HTTP source. That is a deployment choice, not a feature either product declares. Relay does not know it is being read, and Evidence Gateway still makes its own authorization and disclosure decision. A reviewer should reject a deployment description that treats Relay authorization as Evidence Gateway authorization.

Registry Mint to Evidence Gateway. Registry Mint issues access tokens; Evidence Gateway verifies them as an ordinary resource server. The dependency runs one way, and Evidence Gateway’s only knowledge of Registry Mint is an issuer URL and a key set: it reads every claim by the name in its own authentication block, so any issuer writing those claims serves equally well. Registry Mint’s tests drive Evidence Gateway’s authenticator, and Evidence Gateway does not depend on Registry Mint. What a reviewer checks depends on which of the two shapes the deployment runs:

  • With Registry Mint. The client registry becomes part of the trust boundary. Registry Mint verifies the registered client’s selected authentication method, a signed assertion against only that client’s public keys by default, or an explicitly enabled secret against only that standard client’s fingerprints, then writes the authority from the registry and never from the request. Secret credentials must be Mint-generated, unique per managed installation, delivered outside configuration, and treated as lower assurance because a native client can expose them to its operator. The consequence is that whoever can write a registration file into clients.directory can mint a token carrying any requester tags, and the running process re-reads that directory on SIGHUP without a restart. Treat the registry directory as sensitive as the signing key. Registry Mint serves plain HTTP behind operator-terminated TLS, while Evidence Gateway requires an HTTPS issuer and key set with no loopback exception, so the TLS terminator is load-bearing.
  • Without Registry Mint. A deployment that already has an identity provider does not run Registry Mint at all; Evidence Gateway points at the provider’s issuer and JWKS directly. Tag issuance, client lifecycle, and revocation move entirely to that provider, and Evidence Gateway’s residual exposure is its trust in one issuer’s key set.

Both shapes share one property worth stating plainly: Evidence Gateway has no token revocation. A minted or issued token is accepted until it expires, which is why token lifetime is bounded short. If the issuer’s key set becomes unreachable, an already-retrieved key set keeps being accepted for a bounded allowance and then every request is rejected with the same closed 401 a bad token receives, with the reason recorded only in the deployment’s own log.

Operator to signing key material, and consumer to the published key set. Private signing material is core-owned and stays out of bundle values, Rhai, logs, audit, and errors (V1-I23). Local development resolves a private JWK through the file secret provider, which accepts only a regular, owner-only, non-symlink, single-hard-link file of the expected mode and size. Production and evidence-grade deployments instead sign through a workload-local Vault/OpenBao Transit proxy over a Unix socket, so the process receives no provider token or exportable private key. The operator governs one active ES256/P-256 public JWK whose kid is its RFC 7638 thumbprint, and retains each previous public key in the published JWKS for at least the maximum assertion validity plus allowed clock skew. Signing is mandatory and fail-closed: a missing or failed signer returns a safe transient failure and never falls back to unsigned output (V1-I22).

On the consumer’s side, the published JWKS is discovery, not a trust anchor. A verifier obtains the provider identity and key-set location through governed configuration, pins that trust, allowlists the expected algorithm, and resolves kid only within the trusted set; it never follows a key URL supplied inside a message. Strict verification compares the response against independently retained expectations, the expected nonce, the expected unordered set of unique role-bound subject bindings, and the expected concept identifiers, forms, and cardinalities, and reports one generic policy mismatch so verification is not itself an oracle (V1-I31). A valid signature authenticates the provider and the payload; it does not assert legal-signature status or that the underlying source fact is true (V1-I24).

Relay to its registry source. Registry Relay owns its own source boundary, and in this version that boundary is a local file rather than a network edge. A Relay deployment binds one or more SQLite files by path in its runtime file, opens each with SQLITE_OPEN_READ_ONLY, observes only the SQL views in it, and executes only the statements the compiler produced from the reviewed contract. Four properties hold it:

  • The runtime rejects a symlinked database, a non-regular file, and a database carrying a -wal or -journal sidecar, so it never binds a database another process is mid-write on, and never follows a path an attacker redirected.
  • A snapshot source must be immutable to this process, either on a read-only filesystem or with no write bits, and is pinned by content digest plus device, inode, length, mode, and modification and change times. A live-read-only source is pinned by device and inode. A replaced or altered file is refused rather than read.
  • A SQLite authorizer callback gates every caller-supplied statement as defense in depth behind the compiler, and positional SQL parameters are refused outright: only the named parameters the contract declared are accepted, bound by pre-resolved ordinal.
  • Failures are categorical. The SQLite error type carries a kind and no value, so a source problem cannot leak a row, a path, or a statement into a response.

The assets the design sets out to protect are:

  • Private signing keys: The private signing key never leaves the service; only the public half is published through the JWKS route. A verifier needs no credential and no protected access to verify a stored response, which keeps verification off the protected side entirely.
  • Person-level source data: The source is read in place and is never copied out wholesale. What crosses is bounded differently per surface, and the difference matters: behind an Evidence Gateway assertion no source row crosses at all, only a computed, disclosure-shaped answer; behind a Relay response the properties a compiled disclosure profile declares do cross, to a caller the operation’s access profile authorized.
  • Audit integrity: Audit is treated as a security control rather than best-effort logging (see Audit as a control).
  • Secret material: Bearer tokens, raw credentials, and key material are kept out of both the portable layer and client-facing surfaces.

The design treats these threat classes as security-relevant: authentication bypass, credential disclosure, audit redaction or integrity failure, signing-key handling bugs, source data leakage through a governed read, and privacy regressions that expose raw subject identifiers.

Threats the design considers and mitigates

Section titled “Threats the design considers and mitigates”
  • Secret material in deployment configuration: Relay holds no source credential, because its source is a local file. The only secrets it resolves are the audit chain integrity key and the optional cursor integrity key, and both are named indirectly as secret:env/<NAME> or secret:file/<name> rather than written inline. The file provider accepts only a single flat filename under the runtime root, refuses traversal and nesting, opens with O_NOFOLLOW, and requires a regular file owned by the effective user, with mode 0400 or 0600, and exactly one hard link. An Evidence Gateway statement source resolves no source credential either: that transport has no connection string, no secret reference, and no authentication kind, so it is credential-free by construction rather than by profile, and a credential that does not exist cannot leak, expire unnoticed, or be widened. An Evidence Gateway HTTP source is the case where a source credential does exist, and the bundle names it by logical secret reference rather than writing it inline. The one HTTP source that resolves none is the none authentication kind, and it is credential-free by allowance rather than by construction: only a local assurance bundle may declare it, only at a canonical numeric-loopback origin, and production and evidence-grade bundles reject that kind.
  • Token forgery or acceptance of untrusted tokens: A token is trusted only after signature verification against the configured issuer JWKS, plus issuer, audience, token type, and algorithm checks. The service still owns its route scopes and grants regardless of what the token asserts.
  • A sender-constrained token replayed as a bearer token: Evidence Gateway denies an access token carrying an RFC 7800 proof-of-possession confirmation claim rather than accepting it as an ordinary bearer, because it validates no sender proof and accepting one would silently discard the constraint the token was issued under (V1-I32).
  • Privilege escalation or over-reach: Authorization is deny-by-default. Relay refuses unauthorized callers before source work; Evidence Gateway requires exactly one complete entitlement match before audit, credential resolution, or source access. Reach is never widened at request time.
  • An existence or matching oracle: No-match, ambiguous, missing-fact, and inconsistent-derivation outcomes collapse by default to one public problem shape, with the granular category kept in audit without counts, so response shape, message, or timing cannot be used to probe registry membership (V1-I16). Every authorization refusal likewise collapses to one generic evidence.denied problem that does not reveal which check failed.
  • Cross-purpose or cross-relying-party tracking: Subject bindings are derived with a keyed, domain-separated function over audience, purpose, role, profile, key version, and the complete canonical selector bundle, so the same person is not linkable across purposes or relying parties (V1-I17).
  • Information leakage in error, log, and telemetry surfaces: problem+json bodies carry stable codes only. Selector, source, and disclosed values never appear in logs or native audit (V1-I15), and operational telemetry is off by default, served only on a separate operator-private listener, with every series label drawn from a closed set rather than from request content (V1-I33). Relay exposes no metrics endpoint at all, maps each request URI onto a closed set of literal route shapes before logging it so no identifier or query value can reach a log line, and accepts only six fixed values for RELAY_LOG, collapsing anything else to info so the variable cannot switch on a dependency’s own logging of URLs or headers.
  • Over-collection through the request: The caller names a requirement and supplies only the selector values that requirement’s named profiles admit. Undeclared concepts, extra fields, and values violating type, codelist, cardinality, precision, or size bounds are rejected before evidence construction (V1-I12).
  • Payload substitution and unsigned success masquerading as verified evidence: The signed flattened JWS is mandatory, available to every authorized grant issuing under the audience-scoped subject binding, and the default response. Unsigned output exists only through its own exact media type, and only when both the immutable bundle and the one matched grant permit it; requesting a format creates no permission of its own (V1-I21, V1-I28). The unsigned envelope is self-identifying and is rejected by the strict JWS verifier, so it cannot be stored and later mistaken for signed evidence.
  • A release with no accountability record: The final immutable response bytes exist before the disclosure-release audit is durably accepted, and those exact bytes are what is released afterward. Every authorized-material native audit event records the closed response-protection mode, with a signing key identity present exactly for a cryptographically protected release, while the minimal authorization-refusal event records neither field (V1-I29, V1-I30).
  • Unreviewed hot mutation of policy: Configuration is immutable for the life of the serving process. There is no runtime override, reload, merge, or fallback path, and the closed runtime file cannot reach any governed field (V1-I18).
  • Cross-tenant confusion: One process serves one operator-controlled trust domain, with one service trust domain, issuer governance boundary, bundle lifecycle, signer, and audit boundary (V1-I19).
  • SSRF and uncontrolled egress: Relay closes this by construction rather than by policy. It reaches no source over the network, so there is no source origin, no destination, and no request an attacker could steer. Its only outbound traffic is OIDC discovery and JWKS retrieval against the one configured issuer, over an https discovery URL with no userinfo, query, or fragment, which must be byte-identical to its canonical form and must end in /.well-known/openid-configuration. The health-check subcommand refuses a URL carrying userinfo, a password, a query, or a fragment, disables proxies, and follows no redirect. Evidence Gateway’s fixed executor closes the same class for its own HTTP sources: origin, method, path authority, headers, authentication, TLS, projection, redirect denial, proxy denial, and a one-request-per-stage ceiling, with scripts controlling request flow only inside that host-enforced authority. Evidence Gateway’s statement transport closes the class the way Relay does, by construction rather than by policy: that transport opens one local extract file and has no origin, no scheme, no host, no port, and no network path into a registry data tier, so there is nothing for a URL security rule or a redirect denial to govern and no destination an attacker could steer.
  • A hostile or defective reviewed script: Evidence Gateway’s script engine has no filesystem, environment, network, process, or module access, no ambient clock or randomness, and a normative operation ceiling that terminates a runaway invocation with a closed, value-free error. On the statement transport a script additionally never receives the statement text, a SQL fragment or constructed SQL, the extract path, the file handle, or the database connection, and Rust binds every value into the prepared statement by index rather than rendering it into statement text, so a statement’s shape is identical for every request it serves. Statement parameters are named only, and a positional parameter is refused.
  • A caller widening its own read (Relay side): a Relay caller supplies only the named parameters the compiled operation declares, and cannot add a filter, a projection, a source column, or a statement. Where an operation declares an authority row binding, every row it returns is pinned to a value taken from the caller’s verified token, either a named claim or the principal identifier, injected as a bound SQL parameter under an exact-equality COLLATE BINARY predicate rather than concatenated into a statement. A caller cannot substitute that value, because it is never read from the request.
  • Tampering with the governed contract on disk (Relay side): at startup Relay re-runs the compiler and the artifact generator over the sealed authored inputs and requires byte-for-byte equality with what the package claims, before it opens a source, an audit sink, or a listener. It also refuses a package or runtime file whose path contains a symlink or a component owned by a third party or writable by group or other. This detects a modified package; it does not authenticate one, because the package is not signed.

Audit as a control. Evidence Gateway writes one mandatory access-attempt record before every actual acquisition stage, plus one disclosure-release record per successful response. Each access-attempt event must be durably accepted before its source read, and the disclosure-release event must be durably accepted after signing and before the response is released. Either failure blocks the corresponding action. The chain is keyed and hash-linked, has no severity levels and no way to turn records off, and carries reviewed identifiers and decision categories only: never raw selector values, per-field selector hashes, source values, credentials, tokens, or raw subject identifiers. Where correlation is required, one keyed, domain-separated, versioned pseudonym covers the complete canonical selector bundle and is deliberately not globally stable across purposes or audiences.

Relay audits on the same principle under its own schema, registry.relay.audit/v2alpha1. It records an attempt phase, a refusal phase, and a terminal phase, carrying resource, operation, contract revision, access and disclosure profile names, transform identifiers, and the principal kind, and never a row value or a selector value. Failure to durably accept a record refuses the request with a 503 audit.unavailable problem, and this is not configurable: there is no best-effort mode to fall back to. Readiness re-verifies the sink, so replacing the audit file under a running process takes the deployment out of /ready.

Three caveats for the auditor. First, exactly one Evidence Gateway process may write a given audit path: the sink takes an exclusive advisory lock, because the chain head lives in process memory and two writers would interleave records and destroy tamper evidence. That makes the supported shape one replica per audit path, active/passive rather than active/active. Second, startup recovers the chain head from the newest sealed segment and fully verifies only the active segment, so corruption inside an already sealed segment is not detected at startup; only an out-of-band evidence verify-audit pass over the whole directory detects it. Third, audit records hashed principals and correlation identifiers, so operator-side investigation depends on retaining the hashing secret and request context.

Residual risks and what is left to the operator

Section titled “Residual risks and what is left to the operator”

The canonical inventory of every current limit is Known limitations and non-guarantees; the residual risks in this section are the subset a threat model must weigh.

These are the risks the design does not close:

  • Key custody is not certified by health checks: Readiness, liveness, and offline validation confirm that governed key material and the selected signer agree. They do not independently certify the Transit service, workload-local proxy, or operator policy. A local-assurance deployment using demo-generated software keys can be reachable and internally consistent yet not production-secure. Custody, rotation, and provider approval remain operator responsibilities.
  • The combined disclosure surface is a human review: The runtime validates one bundle at startup and refuses two simultaneously enabled requirements that declare the same disclosure family, but a declared family is a trusted operator attestation, not a semantic classifier. Threshold ladders, overlapping categories, increasingly precise regions, and coexisting revisions that together reconstruct a protected value are caught by the operator’s review of the whole enabled bundle or not at all. Rate controls do not make an unsafe bundle safe (V1-I03, V1-I20).
  • Overlapping authority paths are not detected at startup: Startup validation does not detect two authority paths covering the same requirement, purpose, and subject tuple. At request time that ambiguity denies, which fails safe but presents as an unexplained refusal; the operator owns the review that prevents it.
  • Rate limits are per process and in memory: The configured request, burst, and failed-selector budgets are tracked in one process’s memory, never shared across replicas, so running N instances behind a load balancer multiplies every limit by N and a restart resets each budget to full. This matters most for the failed-selector budget, because that budget, not throughput, is the selector-enumeration defense.
  • A released assertion cannot be recalled: Evidence Gateway has no revocation, status list, or presentation-time check. The only levers over a released assertion are its declared validity window and signing-key rotation, and rotation does not invalidate assertions a retained public key still verifies.
  • The request nonce is not replay protection: It is uninterpreted correlation data echoed into the payload. Reuse is not rejected, and no uniqueness check exists.
  • The operator boundary is where coverage ends: Secret and key provisioning, key custody and rotation, audit retention, backup, restore and access control, tenant isolation, TLS termination and certificates, edge rate limiting, per-client quotas, deployment configuration, and incident response are operator responsibilities, not behavior the model defines.
  • Aggregate data is not a privacy budget: Relay’s SDMX aggregate-data routes serve compiled statistical datasets and track no cumulative disclosure across repeated or overlapping queries. Per-operation quotas bound request rate, not information released. Do not describe a Relay aggregate as privacy-budgeted unless a separate deployed control provides it. This is a residual disclosure risk left to the operator.
  • Relay quotas are per process and in memory: the configured requests-per-minute and burst budgets are a token bucket per compiled operation held in one process’s memory, never shared across replicas, so running N instances multiplies every budget by N and a restart resets each bucket. Treat them as an availability control, not an enumeration defense.
  • A Relay package is not authenticated: packageRevision is a SHA-256 integrity digest that any holder of the package can recompute. Startup detects drift and tampering by re-deriving every artifact, but it cannot tell a legitimate package from a well-formed forgery. Package provenance is an operator duty: the delivery path, the filesystem ownership checks, and the release verification procedure carry it, not the package format.
  • Project approval remains a semantic control: Schemas and validators reject malformed configuration, duplicate identifiers, and unsupported modes. They cannot decide whether an institution selected the correct source fields, lawful purpose, derivation rule, or disclosure default. Those decisions require project review and fixture evidence.
  • Admin reload is not a capability: neither product has an admin listener, a reload route, a posture endpoint, or a configuration apply path. Relay serves a fixed route set with no administrative surface at all, and Evidence Gateway has none either. A configuration change is a new reviewed revision and a restart, in both cases.

These are non-goals for this version. None of them should be read into a Registry Stack conformance claim:

  • A credential lifecycle: Evidence Gateway’s SD-JWT VC output is a second serialization of the same stateless assertion. There is no issuance session, credential offer, status list, revocation, reissuance, persisted credential state, or OID4VCI endpoint of any kind, in either subject binding mode. Under the default audience-scoped mode there is no holder binding ceremony either, and an optional caller-supplied holder key is echoed unverified into a confirmation claim rather than proved. A requirement that declares the holder-bound mode adds a proof of possession the relying party checks at presentation, and nothing else: the service appends no key-binding JWT, issues no challenge, and retains no presentation or replay state.
  • Delegated or federated evaluation between peers: no service in this model evaluates a question on another deployment’s behalf, and there is no trust-chain discovery, peer registry, or cross-institution evaluation route.
  • Identity resolution: lookup is match, no-match, or ambiguous. Evidence Gateway is not an identity-resolution or record-linkage engine and returns no candidate material.
  • Cross-verifier credential use under the default binding mode: an audience-scoped subject binding makes an assertion meaningful to one relying party and correlatable across none. A requirement that declares the holder-bound mode opts out of this non-goal deliberately, and accepts the correlation that follows: holder key reuse links a holder across every verifier that sees it, and the service can neither prevent nor detect that. Batch issuance under distinct keys reduces the deterministic key-based link; it does not make credentials unlinkable, because members still share an issuance timestamp, purpose, requirement, Evidence Type, configuration revision, and disclosed values.
  • Replay prevention at presentation: a relying party verifying a key-binding JWT compares the nonce from the challenge it issued, and comparing a nonce is not consuming it. The same presentation bytes verify again under the same stateless policy. RFC 9901 section 7.3 places the challenge lifecycle in the surrounding protocol, so issuing a nonce, retaining it, and retiring it are the relying party’s own duties.
  • External policy enforcement of any kind: no runtime service in this stack evaluates an external policy. There is no policy decision point, no ODRL term enforcement, and no policy discovery. Registry Manifest can publish a policy reference, and that reference describes intent rather than naming a control the stack applies.
  • Certified standards compliance: Aligning with a standard is not conforming to it. Speaking the shape of OIDC, OAuth 2.0, SD-JWT VC, CCCEV, or the rest does not certify conformance to any of them, and CCCEV-shaped output is not conformant to CCCEV 2.00. Relay’s aggregate-data routes speak a narrow aligned subset of SDMX REST 2.2.2 and its geospatial responses use CRS84 points; it is explicitly not conformant to OGC API Features, CQL2, EDR, or tiles.
  • Adapters, connectors, and feature-gated protocol surfaces: Relay has none. Its route set is fixed by the compiled contract and does not vary by build feature, so there is no OGC API Features, Records, or EDR adapter and no SP-DCI synchronization surface to reason about.
  • Non-Unix targets: both runtimes are Unix-only, and both fail closed rather than degrading. Evidence Gateway’s secret and audit invariants depend on owner, mode, no-follow, link-count, and open-file identity checks. Relay refuses to start on a non-Unix target outright, because its package and runtime trust checks have no equivalent there, and its published binary is built for Linux amd64 only.