Versioned archive. You are viewing v0.38.0. For the latest released guidance, use Latest release. Report archive issues on GitHub.
Build and deploy an Evidence Gateway project
For the assertion provider and operator
Complete Prove an Evidence Gateway project and its review before starting
this tutorial. You will turn that editable project into one reviewed production candidate, then
bind its secrets and runtime paths on the target host. This tutorial uses released evidence and
evidencectl binaries. It does not copy .evidence/dev, generate a key, register a caller, or
deploy a service for you.
Before you start
Section titled “Before you start”You need a released Evidence Gateway toolset on PATH, an editable project that runs locally, and an
operator who can provision owner-only secrets and a private listener on the target host.
Configure a compatible OpenID Connect (OIDC) issuer for Evidence Gateway access tokens.
A production or evidence-grade target also needs a Vault or OpenBao Transit key before you build,
not after. The target’s governance.yaml names one governed public JWK, and for these profiles
that JWK is the public half of a non-exportable Transit key, so the key has to exist before the
target is complete. Provision it with
Configure Transit signing for Evidence Gateway.
The build itself never contacts Transit; the workload-local proxy is what the target-host ceremony
and the running service reach.
Confirm the released binaries before changing the project. Replace <released-tag> with the tag of
the latest release unless you
target an earlier one:
curl -fsSL "https://github.com/registrystack/registry-stack/releases/download/<released-tag>/evidencectl-<released-tag>-install.sh" | bashevidence --versionevidencectl --versionThese binaries produce the candidate you hand to an operator, so verify the downloaded assets against the
signed SHA256SUMS for that tag before you install them, following
OpenSSF and release trust. To read the installer before it runs,
replace | bash with | less.
Keep all source responses, credentials, tokens, and local .evidence/ state outside the candidate.
Add production metadata and fixtures
Section titled “Add production metadata and fixtures”Create a synthetic fixture for every question under fixtures/, then add one governance block and
stable concept identifiers to each question. The metadata binds the reviewed requirement, frameworks,
Evidence Type, validity, observation timezone, fixture, and disclosure families.
answers: - concept: is_adult id: urn:example:concepts:is-adult type: boolean
governance: requirement: urn:example:requirements:adult-status:v1 kind: criterion referenceFrameworks: - urn:example:frameworks:adult-eligibility:v1 evidenceType: urn:example:evidence-types:adult-status:v1 validitySeconds: 300 observationTimezone: UTC fixtures: fixtures/adult-status.yaml disclosureFamilies: - urn:example:disclosure-families:adult-statusDo not invent these URIs during the build. Review them with the institution that owns the requirement and disclosure decision.
Create complete environment targets
Section titled “Create complete environment targets”Keep reviewed Evidence semantics and complete environment bindings in one protected branch of the deployment repository:
shared/ evidence-project/environments/ local/ evidence/{governance.yaml,runtime.yaml,public-keys/} staging/ evidence/{governance.yaml,runtime.yaml,public-keys/} transit/{proxy-configs/,policies/} production/ evidence/{governance.yaml,runtime.yaml,public-keys/} transit/{proxy-configs/,policies/}Every environment target is complete. Do not use overlays, environment branches, symlinks, or runtime substitutions. Git contains public keys and nonsecret provider configuration, but never private JWKs, HMAC keys, provider tokens, auto-auth credentials, access tokens, live responses, or real identifiers.
The unit that moves from staging to production is not a built package. It is a reviewed commit of
shared/evidence-project plus each environment’s complete target. Build a separate package for
every environment from that same commit with evidencectl package, and let each build run the same
fixtures. The target’s governance and public keys are compiled into the package, so environments
whose governance.yaml or public-keys/ differ get different package digests; runtime.yaml stays
outside the package, so a runtime-only difference leaves the digest unchanged. Most of that
governance, including service identity, issuer, authentication, audit, rate limits, and authority
profiles, is also in each requirement’s configurationRevision. When two environments differ there, as separate environments normally do in
service identity and issuer, the same question carries a different revision in each, and relying
parties pin per environment: a revision pinned against staging does not verify a production
assertion. Runtime bindings and the signing public keys are in no revision, so environments that
differ only in those share a revision. Record the reviewed commit beside each environment’s package
digest, so the approval shows that every package came from the same reviewed semantics.
governance.yaml provides bundle-owned production values. It contains version 1,
assuranceProfile: production, service and issuer, authentication, audit, subject binding, rate
limits, signing, optional response formats, and authority profiles. Secret references use
secret:file/<name> only. Do not place secret values or absolute secret paths in this file.
runtime.yaml is the ordinary Evidence Gateway runtime document. It binds the stable absolute
installed package path, private listener, secret root, audit path, and optional private certificate
authority files. It stays in the deployment target and is never copied into the package, so the
target host remains the authority for path, ownership, permission, secret, and trust validation.
Set package.root to the stable installation path where this and future approved packages will be
placed, and keep the listener on a numeric
loopback or private address. listener.networkExposure declares which addresses that means. It
defaults to private-address, which accepts loopback, RFC 1918 private IPv4, or RFC 4193
unique-local IPv6. A container listener that must bind the wildcard 0.0.0.0 inside an isolated
network sets networkExposure: container-private instead, as
docker/compose/runtime.docker.yaml does in the Registry Stack repository at the release tag you
install; that value permits the wildcard bind for an operator-confined container network and
authorizes no public or direct-TLS serving. The runtime cannot override the governed service,
authentication, authority, source, disclosure, or signing fields.
public-keys/ contains the exact active and published service JWKs named by governance.yaml.
Production and evidence-grade targets bind the matching non-exportable provider key through the
Transit signer in runtime.yaml. Use
Configure Transit signing for Evidence Gateway
before building the first strict candidate.
Build the candidate
Section titled “Build the candidate”Choose a new output path. The command refuses an existing path and does not modify the editable project:
evidencectl package "<deployment-repository>/shared/evidence-project" \ --target "<deployment-repository>/environments/production/evidence" \ --output "<new-candidate-directory>"The output is one closed package. Environment-specific runtime configuration stays in the deployment target:
<new-candidate-directory>/ SHA256SUMS evidence.yaml adapters/ derivations/ schemas/ fixtures/ public-keys/The package command validates the generated package through the real evidence binary and every referenced
fixture before it publishes the candidate. It asks that binary for its version first and refuses one
that is not this evidencectl’s, so the candidate is always the one the matching runtime shaped:
evidencectl package takes the binary from EVIDENCE_BIN or the first evidence on PATH, and
evidencectl test accepts --evidence-bin for the same handshake. A bundle that declares a
publication also has to render the description that advertises it, and a package whose description
comes back empty is refused rather than written, because the candidate would carry no catalog.jsonld. The package does not contain runtime.yaml or secret material. Packaging does not contact an identity provider or a source endpoint. It
validates governed public-key semantics without contacting Transit or
generating an unrelated signing key. The target-host check performs the provider self-test. Record
the printed package digest with the approved package path. Pass --revision <review-reference>
when you also want one printable source or review reference recorded in REVISION.
Provision the target host
Section titled “Provision the target host”Transfer the exact package to the stable absolute path named by the deployment target’s
runtime.yaml at package.root. Replacing the package does not require editing that runtime path.
The operator provisions the audit HMAC key, subject-binding HMAC key,
and source credentials beneath the runtime secret root. The workload-local Transit proxy holds the
provider token and auto-auth state; Evidence Gateway receives only access to the configured Unix
socket. Its non-exportable signing key remains in Transit.
Make the installed package and target runtime non-writable to the Evidence Gateway service identity,
for example chmod -R a-w "<package.root>" && chmod 444 "<deployment-target>/runtime.yaml". evidencectl test below runs
evidence check first, which refuses a writable runtime file, bundle artifact, or CA bundle file,
a secret root reachable by group or other, or a writable extract, before it evaluates any fixture.
Start the workload-local Transit proxy. Run the grouped offline ceremony once after the candidate, runtime bindings, trust files, secrets, and proxy socket are in place:
evidencectl doctor --runtime-config "<deployment-target>/runtime.yaml"evidencectl test "<editable-project>" --target "<deployment-target>"Start Evidence Gateway only after both commands pass:
evidence serve --runtime-config "<deployment-target>/runtime.yaml"Route traffic through operator-controlled TLS only after GET /ready succeeds. The listener stays
private. Each requirement’s own configuration revision remains the deployed assertion
configurationRevision.
Exercise and verify the HTTP boundary
Section titled “Exercise and verify the HTTP boundary”Create an owner-only Curl configuration. Put its Authorization: Bearer header in the file through
your approved secret-management path, not on a command line or in shell history. Do not commit it.
umask 077install -m 600 /dev/null "<owner-only-curl-config>"The file contains this Curl configuration directive, with the actual token supplied outside the command line:
header = "Authorization: Bearer <access-token>"Send one request for a synthetic deployment test record:
curl --fail --silent --show-error \ --config "<owner-only-curl-config>" \ --header 'Content-Type: application/json' \ --data-binary "@<synthetic-request.json>" \ "https://<evidence-host>/v1/evidence" \ --output "<assertion.jws.json>"Verify the retained response with an independently prepared production verification policy and trusted public keys:
evidence verify \ --jws "<assertion.jws.json>" \ --jwks "<trusted-evidence-jwks.json>" \ --policy "<production-verification-policy.json>"Then confirm that the request’s audit entries reached the deployment’s append-only audit store,
not only the local audit file: an access-attempt entry and a disclosure-release entry that
share one correlation. For example, against a shipped copy of the audit file:
jq -c 'select(.record.phase == "disclosure-release") | {correlation, time, evidenceId: .record.evidenceId}' \ "<shipped-audit-file.jsonl>"The signing policy and secret references are governed bundle content. Rotate a key by publishing a
new public JWK whose kid is its RFC 7638 thumbprint, not by replacing a key under an unchanged
identifier.
Expected result
Section titled “Expected result”You have a retained signed response that verifies under the independent production policy, audit entries for the synthetic request in append-only storage, and a recorded package digest for the exact package serving the synthetic request.
Clean up the request credential
Section titled “Clean up the request credential”Remove the temporary Curl configuration when the deployment check is complete. Retain the signed response and the audit confirmation according to the operator’s evidence-retention procedure.
rm -f "<owner-only-curl-config>"