Released docs. You are viewing the documentation published with v0.25.0. Development docs are available at Latest.
Use this page to assess the security posture of Registry Stack before you deploy it or integrate with it. It summarizes the model the software enforces, names the responsibilities that stay with the operator, and links the public evidence a reviewer can verify. The normative source for everything on this page is the security model specification RS-SEC-G; this page is its narrative summary.
For a focused review, continue with the threat model, known limitations, security self-assessment, and release trust evidence.
At a glance
Section titled “At a glance”| What | Where it stands |
|---|---|
| Authentication on every route that touches a record or a claim | Built in |
| One inbound authentication mode, and it is OIDC | Built in; there is no API-key or static-credential mode in either maintained runtime |
| Deny-by-default authorization, checked before any data is read | Built in |
| Only the public half of a signing key is ever published | Built in |
| Person-level access is written to an audit log | Built in, and fail-closed with no switch to turn it off |
Three disclosure modes, with redacted revealing neither value nor answer | Built in (Evidence Gateway) |
| Governed contract verified before the process serves anything | Built in (Relay re-derives its package and compares bytes at startup) |
| A signature over the governed Relay package | Not provided; packageRevision is an integrity digest, not an authenticity proof |
| Selective-disclosure SD-JWT VC serialization of an assertion | Built in (a second encoding, not a credential lifecycle) |
| Holder-bound subject binding, proven at presentation by a key-binding JWT | Declared per requirement, verified by the relying party (frozen profile) |
| Replay prevention for a presented credential | Not provided anywhere; the challenge lifecycle is the relying party’s |
| Token issuers authenticate clients by one registered method, asymmetric by default | Built in; client-secret compatibility is explicit and limited to standard authority |
| Hardened HTTP headers and outbound-traffic limits | Recommended, confirm your build applies them |
| Key custody, retention, isolation, TLS, rate limiting | Your deployment’s job |
| Compliance with any external standard it cites | Not claimed |
The model in one pass
Section titled “The model in one pass”Every request that returns person-level records or claim results moves through the same pipeline: authenticate, authorize, serve, audit.
- Authenticate: a maintained runtime service runs exactly one inbound authentication mode, and it
is OIDC (REQ-SEC-G-002). Each service trusts exactly one issuer, and tokens are accepted only
after signature, issuer, audience, token-type, and algorithm checks (REQ-SEC-G-004). Registry
Relay narrows this further: the issuer’s discovery URL must be an
httpsURL byte-identical to its canonical/.well-known/openid-configurationform, the accepted token type is exactlyat+jwt, and exactly one of EdDSA, ES256, or RS256 is permitted per deployment. - Authorize: authorization is scope-based and deny-by-default. The required scope is enforced before any source is read, and a caller’s reach is never widened at request time (REQ-SEC-G-005, REQ-SEC-G-006). Where a Relay operation declares a purpose constraint or an authority row binding, both are read from the verified token, never from a request field.
- Serve: the service returns the configured surface: a governed read shaped by a compiled
disclosure profile, a claim result, a selected value, a signed assertion, or a denial. Relay
denials come from a closed set of 26 problem codes. Every Evidence Gateway claim is answered in
one of three disclosure modes:
valuehands back the computed value,predicatereturns only a yes/no, andredactedreturns neither the value nor the yes/no; the claim’s policy fixes which modes are allowed. Disclosure modes and computed answers covers this in depth. - Audit: the request is recorded in a hash-chained audit envelope capturing the caller
principal kind, the authority exercised, a request identifier, and the governed revision the
answer was produced under (REQ-SEC-G-008). Audit is fail-closed and there is no configuration to
make it otherwise: a Relay request whose audit record cannot be written returns
503 audit.unavailableinstead of an answer, and Relay re-checks its audit sink for readiness rather than trusting the check it did at startup (REQ-SEC-G-009).
Every route that returns person-level records or an evidence assertion requires authentication.
Operational probes, public verification keys, issuer metadata, and docs or OpenAPI when configured
may be public because they carry no person-level data.
Evidence Gateway exempts only its probes, its OpenAPI document, its issuer JWKS, and its issuer metadata
document; both its assertion route and its evidence-definition discovery route require a verified
access token (REQ-SEC-G-006).
Registry Relay exempts GET /health, GET /ready, and GET /openapi.json, and beyond those it is
the contract, not the route, that decides: each compiled operation carries an access profile that is
either public or protected, so a Relay deployment can legitimately publish an open operation and
must therefore be reviewed at the contract, not at the route table. Metadata is separately gated by
a declared visibility of public, operation-bound, or operator-only, and an operator-only artifact
answers 404 resource.not_found rather than confirming it exists.
Issuers sign with asymmetric keys and publish only the public half, so any verifier can check a
signed artifact without holding a credential of its own (REQ-SEC-G-007).
Evidence Gateway can serialize the same signed assertion as an SD-JWT VC, where the signed body carries hashes of each disclosable field rather than the values. That is a second encoding of one response, not a credential lifecycle: there is no issuance session, credential offer, status list, revocation, reissuance, or persisted credential state, and no part of OID4VCI. A requirement in the immutable bundle declares which of two subject binding modes it issues under. The vocabulary is closed at two values and has no default, and no caller input selects a mode.
By default the binding is audience-scoped: the subject identifier is a pseudonym derived over the
audience, so the same person requested for a different audience yields a different identifier. A
request may carry an optional holderKey, echoed into the cnf claim; Evidence Gateway binds
nothing to it and verifies nothing, so cnf is an unverified caller-supplied convenience for
whatever presentation layer the operator runs elsewhere.
A requirement may instead declare the holder-bound mode, where the binding derives from the RFC
7638 thumbprint of the holder key rather than from the audience, cnf is required, and the
audience check moves from issuance to presentation. Possession is then proven by a key-binding JWT
under RFC 9901 section 4.3, which the relying party verifies. The service still issues no
key-binding JWT, holds no challenge or presentation state, and runs no presentation endpoint;
verification is a relying-party act, offered as the portable verifier library and the
evidence verify-presentation operator command. Reviewers should read three consequences
together: the mode accepts cross-verifier correlation through holder key reuse, which the service
can neither prevent nor detect; comparing a challenge nonce is not consuming it, so nothing here
is replay prevention; and the mode’s profile is frozen, a status it carries only because every
negative test it names resolves to an executable test.
Known limitations states both modes side by side.
Where a deployment has no identity provider, Registry Mint issues the access tokens Evidence Gateway or Registry Relay verifies. Mint authenticates each registered client by the one method its registration selects. Asymmetric client assertions remain the default and the only method for Evidence or delegated authority; a standard authorization client may explicitly use a Mint-generated secret represented in the registry only by its fingerprint. Mint reads token authority from its own server-side client registry rather than from the request. Its token endpoint does not distinguish an unknown client, an authentication-method mismatch, a bad signature or secret, a replayed assertion identifier, and an expired assertion, so it cannot probe the client registry (REQ-SEC-G-016).
Delegated evaluation, where one service asks a trusted peer to evaluate a claim, does not exist in this stack: no service holds such a surface, and REQ-SEC-G-010 is retired.
What’s recommended, not enforced
Section titled “What’s recommended, not enforced”Two behaviors are recommended rather than required, so confirm them rather than assume them.
- Registry Platform sourcing: security-critical primitives (authentication, OIDC verification, audit envelopes, HTTP security, outbound HTTP policy, cryptography, and SD-JWT VC helpers) are meant to come from Registry Platform rather than be reimplemented per service, so they behave the same everywhere and can be reviewed in one place. RS-SEC-G recommends this rather than forcing it (REQ-SEC-G-001), so confirm the service you deploy sources them from Registry Platform rather than reimplementing its own.
- HTTP hardening: security response headers and limits on outbound source-fetch traffic are recommended posture (REQ-SEC-G-012). Check that your build applies them rather than assuming it. The outbound half applies to Evidence Gateway, which fetches from configured authoritative sources. Registry Relay makes no outbound source request at all: its only outbound traffic is OIDC discovery and JWKS retrieval, so for Relay this reduces to response headers and the request and response limits its runtime file sets.
What Registry Relay checks before it serves
Section titled “What Registry Relay checks before it serves”Relay has no declared assurance level, no posture endpoint, and no findings-and-waivers catalog. A deployment does not state the assurance it claims; it either satisfies the startup checks or the process does not serve. Those checks are the security surface worth reviewing:
- The package is verified first. Before Relay opens a source, a sink, or a socket, it re-runs the compiler and the artifact generator over the sealed inputs and compares the results byte for byte against what the package contains. A package that does not re-derive does not start.
- The runtime file cannot widen the package.
runtime.yaml(kind: RelayRuntime) is a closed schema that rejects unknown fields, and it binds only local concerns: listen address, package path, source paths, the audit sink, an optional OIDC issuer, cursor settings, limits, quotas, and shutdown. Nothing in it can add an operation, a readable column, or a disclosure. - Contract and runtime must agree. A contract with any protected access requires a configured issuer, and a contract that declares any lookup requires quotas. Neither is a warning.
- Secrets are referenced, never inlined. The runtime file accepts exactly two reference grammars,
secret:env/<NAME>andsecret:file/<name>, and nothing else. A file secret must be a single flat filename, owned by the running user, with mode exactly0600and a link count of one, and no secret may exceed 64 KiB. - The source is opened read-only and pinned. Relay opens SQLite with
SQLITE_OPEN_READ_ONLY | SQLITE_OPEN_URI | SQLITE_OPEN_NO_MUTEX, refuses a symlink, refuses a-walor-journalsidecar beside a snapshot, and requires a snapshot to sit on a read-only filesystem or to be non-writable by its own mode bits. - Operational logging is a closed enum.
RELAY_LOGacceptsoff,error,warn,info,debug, ortrace; any other value falls back to the crate’s owninfofilter rather than enabling a dependency’s logging. - Unix only, and it fails closed off it. On a non-Unix target Relay’s permission check returns false unconditionally and runtime-path validation returns an error, so the process refuses to start rather than running without the ownership and mode checks it relies on.
Two of these are worth stating as limits rather than as controls. The package check proves the package is internally consistent and unmodified, not that a trusted party produced it: there is no signature over a Relay package. And a snapshot’s immutability past startup is an operator duty, because a process comparing two hashes cannot exclude a privileged writer that changes and restores the bytes between them.
The runtime keys are documented in the Relay configuration reference and the day-two procedures in operate Relay. Evidence Gateway states its own operator obligations in its operator contract rather than through any posture block.
What stays with the operator
Section titled “What stays with the operator”The security model ends where the deployment begins. Secret and key provisioning, key custody and rotation schedules, audit retention and storage, tenant isolation, transport termination and certificate management, edge rate limiting, deployment configuration, and incident response are operator responsibilities (RS-SEC-G Section 9). The software keeps secrets out of distributable artifacts (REQ-SEC-G-013) and provides the primitives; the operator provisions, configures, and operates them. Record those responsibilities in the operator handoff checklist before activation.
Evidence Gateway for reviewers
Section titled “Evidence Gateway for reviewers”Public, checkable evidence, from most to least formal:
- RS-SEC-G is the normative security model; RS-PR-RELAY and RS-PR-EVIDENCE carry its wire-level form per service.
- OpenSSF and release trust records which release-integrity checks are verifiable today (signatures, provenance, Scorecard) and which are still incomplete.
- The security support window states which release lines receive fixes.
- The standards register records the adoption mode and evidence for each cited standard.
- DPI safeguards alignment maps the stack to safeguards language for review programs.
These pages are review material, not a certification. The hosted lab is a synthetic-data demo, not a production assurance claim.
Report a vulnerability
Section titled “Report a vulnerability”Suspected vulnerabilities go through the private disclosure process, never public issues or pull requests. See Report a vulnerability.