Unreleased documentation. These pages follow the main branch and can change before the next release. For supported guidance, use v0.15.2.
Status: implemented Version 1 contracts, runtime, reference deployments, and reproducible Evidence Gateway-specific verification gates.
Evidence Gateway is a greenfield, sector-neutral minimum-disclosure assertion service. Given authenticated authority, an authorized purpose, a predefined requirement, and the configured selector data needed by an authoritative provider, it returns the smallest sufficient JSON assertion in an authorized response format.
The approved Version 1 product boundary is one registry-evidence crate, one
evidence binary, one serving process, and one operator-controlled trust domain.
The runtime depends on one portable library beside it,
registry-evidence-verifier, which owns the response formats, the Evidence Gateway
payload contract, and relying-party verification, so client tooling can verify a
signed response without the runtime. A process may host multiple evidence
definitions only when they share that trust domain. Governed configuration, Rhai
scripts, schemas, codelists, and fixtures are one trusted, immutable,
startup-only evidence bundle. A separate closed runtime file owns only
process-local listener, filesystem, audit-storage, secret-mount, signer
transport and pinned version, and TLS-trust bindings. It cannot override
governed semantics or the governed active public key.
The following contracts define and verify the implemented Version 1 boundary:
- Product concept: product boundary, data model, trust and privacy invariants, native API, and Version 1 acceptance set.
- Implementation schedule and Definition of Done: phases, exit gates, required tests, verification, and stop boundary.
- Source-testing contract: deterministic mock matrix, optional public-demo smoke tests, credential handling, and failure interpretation.
- Operator contract: supported deployment shape, requester authority and purpose duties, required configuration and secrets, readiness, audit, key, and verification obligations.
- SD-JWT VC demo: one deterministic local run that issues
the same assertion in both later-verifiable formats and re-verifies the
credential offline with
curland theevidencebinary. - Trusted request-adapter reference: complete Rhai API, configuration and fixture contracts, and deployable DHIS2 and OpenCRVS-shaped reference projects.
Any normative schemas, examples, and generated public artifacts live in their own tracked contract directories. Generated files must be reproduced by their documented generator and never edited by hand.
Version 1 boundary
Section titled “Version 1 boundary”Version 1 supports assertion evidence through one synchronous JSON operation with signed flattened JWS as the mandatory default format. Rust owns authentication, authorization, minimized preparation inputs, fixed source execution, response projection, bounded Rhai execution, output validation, evidence construction, response protection, and audit. Rhai owns reviewed request query/body rendering, source extraction, and requirement-specific derivation using only deterministic, bounded, domain-neutral primitives supplied by Rust.
Adult status, residence region, professional licence status, and legal-parent relationship are coequal full-path acceptance definitions. All four must pass the same offline and production path on one revision before Version 1 can be called implemented. None may become a Rust domain type, built-in operation, special route, or preferred implementation phase.
Version 1 serializes the same assertion as an SD-JWT VC when the bundle and the
matched grant both permit that response format, under the frozen profile in
contracts/sd-jwt-vc-profile.yaml. It does not include documents, credential
lifecycle, status lists, OID4VCI, presentation verification,
nonce or replay storage beyond stateless request-nonce echo and comparison,
server-issued challenges, OOTS execution, federation, delegated agents, MCP,
workflow, public or federated catalogs, runtime policy, runtime bundle mutation,
multi-source fulfillment, source planning, an application database, a message
broker, or workers.
DHIS2 and OpenCRVS are compatibility-shaped test profiles only. Their names and behavior may appear in tests, sanitized fixtures, test-only bundles, and local smoke documentation. Production Rust, Cargo metadata, public configuration, routes, CLI options, and generated public contracts must remain source-product neutral.
Adopter tooling
Section titled “Adopter tooling”evidencectl, built from the registry-evidencectl crate, is adopter tooling
beside the runtime, like registryctl for the rest of the stack. It sits
outside the frozen Version 1 runtime contract: it generates key material,
starts OpenAPI-assisted workspaces and runnable synthetic SQLite starters,
compiles a reviewed production candidate, and drives fixture runs for editable
or complete deployment projects. It
delegates runtime evaluation, signing, bundle validation, and fixture evaluation
to the evidence binary, and reuses registry-evidence-client and
registry-evidence-verifier for relying-party request preparation and offline
response verification. It adds no Evidence Gateway semantics of its own. Its source
remains covered by the same source-product and domain-neutrality checks as the
runtime.
evidencectl new <dir> --openapi <file-or-url> --profile local retains the
OpenAPI document exactly as source.openapi.yaml and creates empty
questions/, derivations/, and fixtures/ directories. It always creates
owner-only disposable local P-256 Evidence Gateway signing material plus distinct audit
and subject-binding masters. The command does not select an API operation,
invent a question, fixture, policy, production target, Mint configuration, or
deployable bundle. evidencectl dev additionally creates session-scoped P-256
Mint, caller, and holder keys so the local happy path needs no key ceremony.
evidencectl new <dir> --transport sqlite-extract --profile local needs no
OpenAPI document. It creates a source-neutral synthetic statement source,
question, derivation, schemas, and 13-case fixture. The first check is
evidencectl fixtures run --project <dir> --explain, which compiles a private
bundle and delegates bundle validation and fixture evaluation to evidence.
The starter creates no real extract, runtime, production target, or deployable
bundle.
evidencectl source suggest drafts one source from an OpenAPI description:
it derives a closed response schema, an extraction script, and the facts schema
from the chosen operation, the projection the operator selects, and an optional
sample response, leaving an explicit TODO wherever a bound cannot be derived
so evidence check rejects the draft until a human resolves it.
evidencectl source suggest --openapi ./api.yaml --project ./deployment-project--openapi also takes the URL a description is published at, which is read
under the same rule the runtime applies to the source URLs it will itself call:
https anywhere, plain http only to a numeric loopback host, and never a
credential in the URL. A description behind authentication is fetched with your
own client and passed as a file.
Production build and handoff
Section titled “Production build and handoff”An authored question may include optional governance metadata. Local dev
continues to work without it, using its explicit local defaults. When present,
dev uses the declared requirement semantics while retaining local assurance
and local authentication. A production build requires every question to carry
that metadata, stable concept identifiers, and one project-relative fixture.
It never invents requirement, framework, Evidence Type, concept, or
disclosure-family URIs.
Each explicit deployment-targets/<environment>/ target contains a complete
governance.yaml, runtime.yaml, and every governed public JWK referenced by
that governance document under public-keys/. Governance contributes the
bundle-owned service, authentication, audit, signing, rate-limit,
response-format, and authority values; it may not override compiler-owned
selectors, sources, or requirements. Runtime is copied unchanged and binds the
completed candidate to one target host. Targets are independent complete
inputs, not overlays. Secret values and absolute secret paths do not belong in
authored governance input.
evidencectl build --project <editable-project> --target <environment-target> --output <new-candidate-directory> is create-only. It reads regular files
without following symlinks, compiles one closed bundle, and delegates its
internal bundle-only check and every referenced fixture to the real evidence
binary. No temporary signing key or other validation secret is generated. The
candidate is published atomically only on success. The build makes no network
request, opens no listener, writes no production audit event, and never copies
local .evidence state, credentials, tokens, responses, or private keys into
the candidate.
The candidate contains runtime.yaml and bundle/; the bundle may contain
adapters, derivations, schemas, codelists, fixtures, and public keys where
referenced. The operator independently provisions the Transit key and
workload-local proxy, plus audit, subject-binding, and source secrets, then
runs one grouped handoff:
evidencectl doctor --project '<candidate>'evidencectl fixtures run --project '<candidate>'evidence --runtime '<candidate>/runtime.yaml' serveAn existing OIDC issuer and Registry Mint are equal issuer choices from
Evidence Gateway’s perspective. Mint remains separately authored and checked with
mint check. When selected, evidencectl doctor --mint-config <mint.yaml>
performs only a read-only mechanical comparison of issuer, JWKS URI, audience,
algorithm, token type, and configured claim names. It does not register a
client, decide authority, copy Mint files, or mint a token.
The initial deployment proof uses released bare binaries. Docker Compose is a documented adapter, not build output: it mounts the reviewed bundle unchanged, uses a separate container runtime file and secret mounts, preserves audit storage, and keeps Evidence Gateway private behind operator TLS. Container, Helm, Kubernetes, Terraform, cloud packaging, approval, promotion, and deployment commands remain outside this build command.
Relying-party client library
Section titled “Relying-party client library”registry-evidence-client is the Rust SDK for the application side of the
contract. It generates the request nonce and closes the verification policy
before any byte leaves the process, sends the request over the public HTTP
contract, and hands every judgement about the response to
registry-evidence-verifier, so it adds no Evidence Gateway semantics of its own.
registry-evidence-client-node binds that crate for Node.js callers through
napi-rs and registry-evidence-client-py binds it for Python callers through
PyO3; each is a thin surface plus a JSON conversion layer over the same Rust
decisions. Neither binding is published to a package registry: the npm package
is private and PyPI publishing is out of scope for the crate. Registry Stack
releases starting at v0.17.0 carry platform-specific Node tarballs and Python wheels as GitHub
Release assets for Linux amd64, Linux arm64, and macOS arm64. The release
candidate builds and runs an offline smoke against every package before the
packages enter the signed release checksum closure.
Install the Node package from the matching
evidence-client-node-<tag>-<platform>.tgz asset with npm install <path>.
The Node platform labels are linux-amd64-glibc, linux-arm64-glibc, and
macos-arm64; musl-based Linux distributions such as Alpine are not
supported by these packages.
Install the matching registry_evidence_client-<version>-<wheel-tag>.whl
asset with python -m pip install <path>. Linux wheels use the build runner’s
glibc baseline or newer. Registry Stack does not publish these packages to npm
or PyPI.
Each crate documents its own surface and test commands:
registry-evidence-client,
registry-evidence-client-node,
and
registry-evidence-client-py.
Installing the toolset
Section titled “Installing the toolset”Releases that include the Evidence Gateway toolset publish reproducible bare binaries
named <bin>-<tag>-<os>-<arch> (for example evidence-v1.2.0-linux-amd64)
plus a SHA256SUMS file that is cosign-signed at promotion. Older releases do
not carry these assets. To install the newest release that does:
curl -fsSL https://github.com/registrystack/registry-stack/releases/latest/download/evidencectl-install.sh | bashTo pin a release, run that release’s own installer asset:
tag=vX.Y.Zcurl -fsSL "https://github.com/registrystack/registry-stack/releases/download/${tag}/evidencectl-${tag}-install.sh" | bashEvery published installer carries the release it belongs to, so neither form
needs a tag in the environment and both refuse an EVIDENCECTL_VERSION naming
a different release. A copy taken from this repository carries none and
installs nothing until EVIDENCECTL_VERSION names one.
The installer installs the three-binary Evidence Gateway toolset, the evidence
runtime, evidencectl adopter tooling, and the mint token issuer, together
or not at all, verifying every asset against SHA256SUMS before anything
reaches the install directory. It supports Linux amd64, Linux arm64, and
macOS arm64. It checks integrity, not authenticity: for a higher-assurance
install, follow release/VERIFY.md for the pinned
tag, then rerun the installer with EVIDENCECTL_ASSET_DIR pointed at that
verified directory.
Three environment variables configure the installer: EVIDENCECTL_VERSION
names a vMAJOR.MINOR.PATCH release or workflow-produced development tag for
a copy that carries none,
EVIDENCECTL_INSTALL_DIR sets the install directory (default ~/.local/bin),
and EVIDENCECTL_ASSET_DIR installs from a locally verified asset directory
instead of downloading.
Manual development build
Section titled “Manual development build”To test the current protected main revision before the next release, run the
Registry Evidence Gateway Development Build workflow manually from the main
branch. It requires successful protected-main CI, builds the same three-binary
toolset for Linux amd64, Linux arm64, and macOS arm64, and creates a unique
prerelease named v<workspace-version>-dev.<run>.<attempt>.
The workflow summary and prerelease notes contain the exact install command:
curl -fsSL "https://github.com/registrystack/registry-stack/releases/download/<development-tag>/evidencectl-install.sh" | bashDevelopment prereleases use unique source-bound tags, and the workflow never
overwrites them. They are unsupported. Their installer checks each binary
against the included SHA256SUMS; those checksums are not signed, and the
prerelease is not a Registry Stack release. Use a normal released version for
production or release verification.
To build the toolset from source instead:
cargo build --release --locked -p registry-evidence -p registry-evidencectl -p registry-mintDiscovering available evidence
Section titled “Discovering available evidence”An authenticated caller lists the complete Evidence Gateway request shapes it can
currently invoke with GET /v1/evidence-definitions. The response is computed
from the immutable deployed bundle and the caller’s verified token. It contains
only combinations that match exactly one authority path, including requirement,
Evidence Type, purpose, output concepts, subject roles, selector profiles,
value origins, and safe selector field validation metadata. An unentitled
caller receives an empty list. An ambiguous authority shape is omitted because
the corresponding evidence request would be denied.
This is requester-scoped discovery, not a public or process-wide catalog. The
response excludes source URLs and identifiers, paths, projections, scripts,
adapter parameters, credentials, internal authority-profile names and tags,
selector values, codelist values, and definitions unavailable to that caller.
Discovery performs no provider request and creates no evidence-data audit
event. Metadata never grants authority; POST /v1/evidence authenticates and
authorizes the complete tuple again.
The generated OpenAPI defines both operations, and the running service publishes
that same document unauthenticated at GET /openapi.json. Operators still
publish static onboarding material through their API catalog, developer portal,
configuration repository, or bilateral process for token acquisition, human
descriptions, legal context, endpoint trust, and verifier policy. The public
JWKS at /.well-known/evidence/jwks.json supplies verification keys only. The
complete contract and change rules are in
the operator contract.
Requesting evidence
Section titled “Requesting evidence”POST /v1/evidence takes one complete request naming the requirement, purpose,
subjects, and a required requestNonce. The nonce is the canonical unpadded
base64url encoding of exactly 32 random bytes, so exactly 43 characters, and
must be freshly generated for every request. Evidence Gateway echoes it into the
Evidence Gateway payload under requestNonce and covers it by the signature, so a
caller that retained the value it sent can confirm the assertion answers that
request. Evidence Gateway never stores it, never rejects reuse, and never uses it for
authorization, rate limits, scripts, source requests, logs, metrics, traces, or
audit. Callers must not encode identifiers, selectors, secrets, or document
digests in it.
Signed flattened JWS is the default format. A missing Accept, */*, or the
exact application/jose+json all select it. The exact
application/vnd.registrystack.evidence-unsigned+json selects a visibly
unsigned envelope, and the exact application/dc+sd-jwt selects the same
assertion serialized as an SD-JWT VC. Every format other than the default is
released only when both the immutable bundle and the one complete matched grant
permit it; otherwise the request is refused with the ordinary not_authorized
problem (HTTP 403) before credentials or source access, without revealing which
layer refused. Every authorization refusal shares this one generic 403, so it is
never an oracle for which check failed. A duplicate, combined, parameterized,
weighted, or unknown Accept returns the response_format_not_acceptable
problem with HTTP 406 before source access. Unsigned output is
transport-authenticated convenience data for development and for consumers that
cannot process JWS. It is never later-verifiable evidence and never a fallback
when signing fails.
The SD-JWT VC format is a second encoding of the one stateless assertion the
signed default carries, under the frozen RFC 9901 and SD-JWT VC draft 18 profile in
the SD-JWT VC profile. It is not a
credential lifecycle: no issuance session, no holder binding ceremony, no
status list, no revocation, and no presentation or key-binding verification. The
shipped verifier checks an SD-JWT VC for exactly what it checks for a signed
JWS, namely issuer authenticity against a pinned key set and the output
contract, and it never falls back to the credential format when signing fails.
The SD-JWT VC demo issues one assertion in both formats and
re-verifies the credential offline with curl and the evidence binary.
Current verification
Section titled “Current verification”From the monorepo root, the Evidence Gateway-specific reproducible gate is:
cargo fmt --checkcargo check --locked \ -p registry-evidence -p registry-evidence-verifier \ -p registry-evidence-client -p registry-evidence-client-node \ -p registry-evidence-client-py -p registry-evidencectl \ --all-targetscargo test --locked \ -p registry-evidence -p registry-evidence-verifier \ -p registry-evidence-client -p registry-evidence-client-node \ -p registry-evidence-client-py -p registry-evidencectlcargo clippy --locked \ -p registry-evidence -p registry-evidence-verifier \ -p registry-evidence-client -p registry-evidence-client-node \ -p registry-evidence-client-py -p registry-evidencectl \ --all-targets -- -D warningsproducts/evidence/scripts/check-contracts.shproducts/evidence/scripts/check-source-neutrality.shproducts/evidence/scripts/check-verifier-portability.shThe two bindings also carry their own JavaScript and Python suites, which this
gate does not run; each binding’s README states the commands, and root CI runs
them in its client-bindings job. On macOS, cargo test for
registry-evidence-client-py needs the interpreter’s library directory on the
dynamic linker path, as
the Python binding README
describes.
Generated JSON Schema and OpenAPI artifacts are under generated/. The
contract gate recreates them from Rust in a temporary directory and requires an
exact diff. The complete workspace and dependency-policy gates remain those in
the implementation schedule.
Security
Section titled “Security”Changes to authentication, authorization, disclosure, audit, configuration
trust, signing, source credentials, or selector handling require explicit
security review notes naming the threat, Rust enforcement point, and negative
test. Report suspected vulnerabilities through the repository process in
SECURITY.md, not a public issue.