Released docs. You are viewing the documentation published with v0.46.0. Development docs are available at Latest.
Registry Evidence is built so that a caller cannot turn it into a fact-query engine, splice permissions across grants, redirect source access, learn from an absence, or receive an unauthenticated response, and so that its configuration cannot change underneath a request. The operator keeps the duties no service-side control can hold.
What the runtime guarantees
Section titled “What the runtime guarantees”A caller cannot turn the service into a fact-query engine. It evaluates only requirements, each pinned by a requirement revision, and the request schema is closed: no thresholds, expressions, scripts, paths, headers, source fields, or response projections. The complete enabled package is reviewed as one disclosure surface, because several safe definitions together can reconstruct a protected value.
A caller cannot splice permissions. Principals and attributes come only from
configured, validated authentication sources, and missing data denies rather
than falling back to another claim. One authorization decision binds the
requester, client, requirement revision, purpose, every subject role and
selector profile, authority, and audience, so partial matches across grants
never add up to access. Selector values are lookup inputs, never proof of
authority. Every authorization refusal is durably audited before the generic
403; if that audit fails, the caller receives the generic 503. Refusals
decided before authorization, for a failed authentication, a malformed
request, an invalid selector, or a rate limit, write no native audit entry, so
watch for them in operational logs and metrics instead.
Source access cannot be redirected. Source calls are fixed by trusted configuration and executed by the core alone, which closes off SSRF, credential forwarding, and script-directed networking. Rhai returns one closed lookup result and declared typed values; the core validates them and assembles the public evidence itself.
Absence does not leak. Missing facts, script failures, audit failures, and evaluation failures fail closed. Raw source responses are never persisted or logged, and selector, source, and disclosed values never reach logs or audit. No-match, ambiguous, and missing-fact outcomes collapse to one public problem status and body, and the runtime removes the timing differences it can avoid. It does not pad responses to a constant time, so a source that answers a no-match faster than a match can still leak membership through latency; measure that at your source before you expose a definition to callers who should not learn it. Audience-scoped subject bindings derive from the audience and the purpose, so one subject is not linkable across relying parties. A holder-bound requirement binds to the holder key’s thumbprint and the purpose instead: a holder who presents the same key to several verifiers is linkable across them for that purpose, and the wallet, not Registry Evidence, decides whether to use a fresh key.
The response is authenticated. A signed flattened JWS is the default, unsigned output exists only where the package and the matched grant both permit it, and failed signing never falls back to unsigned evidence. Private signing material stays with the core, out of package values, Rhai, logs, audit, and errors. The exact response bytes exist before the disclosure-release audit is durably accepted and are the bytes released. A signature authenticates the provider and the payload; it does not assert legal-signature status or source truth.
Configuration cannot change underneath a request. The package, runtime file,
and active target authority are immutable for the life of the process, and one
process serves one operator-controlled trust domain with one signer and one
audit boundary. Operational telemetry is off by default and served only on a
separate private listener with closed labels. Public responses carry a W3C
trace identifier but never the internal audit operation identifier, and
tracestate is not echoed.
A caller cannot mistake a presentation for a source read. Every assertion carries a signed
channel, and nothing a source script emits can set it. A holder-presented assertion exists only
when Evidence itself verified the presented credential against the pinned issuer allow-list, never
against a key the presentation supplies, and holder binding is always required. A relying party’s
channel policy decides whether to accept one; the default accepts source only. Base Registry Engine (BReg)
refuses a correctly signed holder-presented assertion for a governed action with action.evidence_failed,
and no configuration key changes that.
The public certificate check
Section titled “The public certificate check”The experimental POST /v1/public-check route is unauthenticated and reads registry data. It exists only when the package declares publicCheck and the target authority
activates it. It answers one configured requirement with a status, the printed fields, and the
evaluation time, and nothing else.
- Enumeration. The check code is 120 random bits stored on the record at issuance, not derived from the number or from any key, so guessing is not feasible. Evidence has no rate limiter on this route and never reads a client address. The operator’s proxy limits each client and the whole path.
- Identical miss. A wrong number, a wrong code, an ambiguous match, a record with no usable
stored code, and the other unresolved cases return the same
404, with the same headers and body apart from the fresh trace identifier. - Code confinement. The code is compared in constant time and removed from the facts before any Function runs, on every route that reads that source. It is never a source input, and it appears in no log, audit record, or answer.
- Timing. Evidence adds no delay, so a response can take longer when the number exists than when it does not. That can reveal whether a number exists. It reveals nothing about the code. The certificate number is printed on the paper and is not the secret, so this residual is accepted. If the number alone would reveal sensitive membership in your deployment, do not activate the route.
- Before the comparison. Evidence compares the check code only after the Source returns a
record, so a Source failure before that answers
503 source.unavailable, audited as a dependency failure. A number with no record cannot fail that way. A pack that refuses one record’s content before the comparison (a facts schema that constrains values beyond their type, or an extract script that throws on one record’s data) lets that503reveal that a record exists for the number, though neither its status nor its fields. A fault that hits every request answers503for every number and reveals nothing. Keep the facts schema to types, treat a returned record whose number does not exactly match as no-match, and check record content in the derivation, which runs after the comparison, so only a caller holding the right code sees its failures. - Input errors read as outages. A number that passes the selector profile but that the Source
cannot carry in a request path segment answers
503 source.unavailable, so a caller can make an input error look like a registry outage. The audit tells them apart: that refusal is a dependency failure recorded after the access attempt, while a number the selector profile or the Source inputs schema refuses writes nothing. - Checker privacy. The number and code travel in a POST body, never in a path or query. Evidence
mints its own trace identifier on this route, ignores inbound trace context, and reads no request
header except
Content-Type. The audit record holds a keyed pseudonym of the number, not the number. The operator’s proxy keeps no client addresses for this location. - Unsigned answer. The answer carries no signature, subject identifier, or nonce. The checker trusts the page origin and its TLS, so print that origin as text beside the QR code. A party that needs a provable answer uses the signed relying-service path.
- Blast radius. Run the check on its own Evidence instance with its own signing, audit, and subject-binding keys, so a flood or a compromise on the public path does not reach institutional keys or starve relying services.
How the guarantees are held
Section titled “How the guarantees are held”The invariant matrix holds the complete, normative text: numbered invariants and named cross-cutting controls, each with its rule, threat, enforcement, and negative test, including those for holder-bound responses, request batches, and the OID4VCI front end. An invariant the matrix adds is binding from the change that adds it. Every invariant is held by a named test; the traceability contract links them, and the build fails when an invariant drops its test.
What the operator must uphold
Section titled “What the operator must uphold”The matrix describes what the evidence binary enforces.
The operator contract
holds the duties no service-side control can hold for you:
- Keep every secret file below
secretProviders.file.roota regular, non-symlink file owned by the service identity with mode0400or0600; the file provider refuses anything else. - Generate the audit and subject-binding masters independently, at least 32 bytes each, under distinct references. The runtime refuses equal references or equal bytes.
- Mount the package and
runtime.yamlread-only and unwritable by the service process. There is no upload, reload, or override: a change is a new candidate. - Pass source credentials and signing material only through secret references, never in YAML values, Rhai, command arguments, logs, or snapshots.
- Sign production and evidence-grade deployments through a workload-local Transit key that stays non-exportable, and retire signing keys as Rotate Registry Evidence signing keys shows.
- Ship the audit log to append-only storage and rotate its master as Operate the Registry Evidence audit log shows.
Report a vulnerability
Section titled “Report a vulnerability”Suspected Registry Evidence vulnerabilities, including credential disclosure, authentication bypass, audit redaction failure, source connector data leakage, and signing-key handling bugs, go through the private disclosure process in SECURITY.md, never a public issue or pull request. See Report a vulnerability for the complete in-scope list and reporting steps.
Cookies on Registry Stack
We use optional cookies to understand how you use the main Registry Stack website and this documentation. If you contact us, we may link this activity to your enquiry. You can change your choice at any time. Read the privacy policy.
Your browser asks sites not to track you, so optional tracking stays off.
We could not use or save that choice. Optional tracking remains off.