Skip to content
Registry StackDocsDevelopment (unreleased)

Control who can request Evidence Gateway

For the assertion provider

View as Markdown

Complete Return a governed value before starting this tutorial. You will give two applications different access to that Evidence Gateway service, add one while the service is running, and prove that a valid access token does not let its client ask every configured question.

Outcome
Two applications receive different policies, with live onboarding, refusal, and revocation.
Time
About 15 minutes
Level
Local development with synthetic data
Prerequisites
The completed governed-value tutorialIts adult-status project and registry.pyPython 3A shell with curl

This tutorial manages access for machine applications, not accounts for people.

Registry Mint authenticates each application from its own key and issues a short-lived access token. Evidence Gateway then checks whether the application’s access policy includes the configured question. These are separate decisions:

DecisionExampleOwner
Which application is calling?age-checkerRegistry Mint client registration
What may it ask?The age-checks policy includes adult-statusEvidence Gateway access policy
May Evidence Gateway read its source?GET /people/{person_id}Evidence Gateway source configuration
May a consumer trust the result?Expected issuer, audience, and signing keyConsumer verification policy

The first two rows are the access-management boundary you will configure. Source credentials and consumer trust remain independent.

Access policies are part of Evidence Gateway’s immutable governed configuration. Client membership is part of Registry Mint’s reloadable registry:

ChangeRuntime effect
Add or revoke a clientRegistry Mint receives a reload request; Evidence Gateway does not restart
Add a question or change an access policyStart a new Evidence Gateway generation

A client change affects tokens issued after Registry Mint reloads. A token issued before a client is revoked remains valid for up to 300 seconds.

In one terminal, return to the working directory from the first tutorial and start the synthetic registry:

Terminal window
python3 registry.py

Leave the registry running. In another terminal, enter the project containing both questions:

Terminal window
cd adult-status

The project has these two governed questions:

QuestionDeclared purposeAnswer
adult-statusage-checkBoolean adult status
age-bracketservice-path-selectionOne reviewed age bracket

An access policy names one or more questions. Each question already fixes its purpose, subject shape, source, and possible answers, so you do not repeat those fields in the policy.

Define a policy for each service task:

Terminal window
evidencectl access policy add age-checks --question adult-status
evidencectl access policy add service-routing --question age-bracket
Added access policy age-checks for adult-status.
Added access policy service-routing for age-bracket.

Review the governed policies before starting Evidence Gateway:

Terminal window
evidencectl access policy list
POLICY QUESTIONS
age-checks adult-status
service-routing age-bracket

The commands write the reviewable policy documents to access/policies/age-checks.yaml and access/policies/service-routing.yaml. Many clients can share one policy. A client can also belong to several policies when their question sets do not overlap. evidencectl rejects overlapping policy assignments because they would make the access decision ambiguous. Adding a question to either policy later changes the governed authorization boundary and requires a new Evidence Gateway generation.

Register age-checker under the age-checks policy:

Terminal window
evidencectl access client add age-checker \
--policy age-checks \
--generate-local-key
Added client age-checker with policy age-checks.

--generate-local-key keeps this tutorial self-contained. The private key represents the application’s identity and remains owner-only at .evidence/clients/age-checker/private.jwk. The reviewable registration at access/clients/age-checker.yaml contains the policy membership and public key, never the private key.

Compile the questions and both access policies, then start Evidence Gateway and Registry Mint:

Terminal window
evidencectl dev --detach
Evidence ready at http://127.0.0.1:8080
Mint ready at http://127.0.0.1:8081

Registry Mint recognizes age-checker. Evidence Gateway has both access policies available, even though no client belongs to service-routing yet.

Prepare an adult-status request as age-checker:

Terminal window
evidencectl request prepare adult-status \
--purpose age-check \
--subject person_id=person-123 \
--client age-checker \
--name age-checker-allowed

The Evidence client prepares the request and closes its pinned verification expectations locally. evidencectl separately signs a one-time client assertion with the local application key and exchanges it with Registry Mint for a short-lived access token. Preparation sends no HTTP request to Evidence Gateway and performs no source access.

Send the request across the Evidence Gateway HTTP boundary:

Terminal window
curl --silent --show-error --fail-with-body \
--config .evidence/requests/age-checker-allowed/authorization.curl \
--request POST \
--url http://127.0.0.1:8080/v1/evidence \
--header 'Content-Type: application/json' \
--header 'Accept: application/jose+json' \
--data-binary @.evidence/requests/age-checker-allowed/request.json \
--output age-checker-allowed.jws.json \
--write-out 'HTTP %{http_code}\n'
HTTP 200

Verify the response before reading it:

Terminal window
evidencectl verify age-checker-allowed.jws.json \
--context .evidence/requests/age-checker-allowed/verification.json \
--output age-checker-allowed.verified.json
VERIFIED

The application was authenticated, its age-checks policy matched the request, and Evidence Gateway called the registry only after both checks passed.

Keep Evidence Gateway and Registry Mint running. Register service-router under the existing service-routing policy:

Terminal window
evidencectl access client add service-router \
--policy service-routing \
--generate-local-key
Added client service-router with policy service-routing.
Registry Mint reload requested.

The command writes the public registration to access/clients/service-router.yaml, keeps the private key under .evidence/clients/, and asks Registry Mint to reload its complete client registry. The message confirms that the reload was requested, not that Registry Mint accepted the new registry. The next token exchange provides that functional proof. Evidence Gateway does not restart because both access policies were already part of its governed configuration.

Review the active client assignments:

Terminal window
evidencectl access client list
CLIENT STATUS POLICIES
age-checker active age-checks
service-router active service-routing

Prepare an age-bracket request as service-router:

Terminal window
evidencectl request prepare age-bracket \
--purpose service-path-selection \
--subject person_id=person-456 \
--client service-router \
--name service-router-allowed

Send and verify the response:

Terminal window
curl --silent --show-error --fail-with-body \
--config .evidence/requests/service-router-allowed/authorization.curl \
--request POST \
--url http://127.0.0.1:8080/v1/evidence \
--header 'Content-Type: application/json' \
--header 'Accept: application/jose+json' \
--data-binary @.evidence/requests/service-router-allowed/request.json \
--output service-router-allowed.jws.json \
--write-out 'HTTP %{http_code}\n'
evidencectl verify service-router-allowed.jws.json \
--context .evidence/requests/service-router-allowed/verification.json \
--output service-router-allowed.verified.json
HTTP 200
VERIFIED

The successful token exchange proves that Registry Mint accepted the live client-registry reload. service-router belongs to the policy required for this question. Preparation made zero Evidence Gateway HTTP requests, curl made the single POST /v1/evidence assertion request, and evidencectl verify checked the response offline.

Try a question the application was not granted

Section titled “Try a question the application was not granted”

Prepare the same age-bracket request using the age-checker identity:

Terminal window
evidencectl request prepare age-bracket \
--purpose service-path-selection \
--subject person_id=person-456 \
--client age-checker \
--name age-checker-refused

The command can prepare the request because age-bracket is a valid project question and age-checker is a registered client. The client preparation path closes the request and its verification expectations without calling Evidence Gateway or deciding whether this application may ask the question. Registry Mint only issues the token. Evidence Gateway makes the authorization decision when you send the request.

Send the request:

Terminal window
curl --silent --show-error \
--config .evidence/requests/age-checker-refused/authorization.curl \
--request POST \
--url http://127.0.0.1:8080/v1/evidence \
--header 'Content-Type: application/json' \
--header 'Accept: application/jose+json' \
--data-binary @.evidence/requests/age-checker-refused/request.json \
--output age-checker-refused.json \
--write-out 'HTTP %{http_code}\n'
HTTP 403

Inspect the safe problem response:

Terminal window
python3 -m json.tool age-checker-refused.json
{
"type": "https://registrystack.org/problems/evidence/not_authorized",
"title": "Request is not authorized",
"status": 403,
"code": "not_authorized",
"operation": "<opaque-operation-id>"
}

The access token is valid, but the client’s age-checks policy does not authorize the age-bracket question. Changing only the authenticated application changes the access decision. The registry terminal shows no new GET /people/person-456, because Evidence Gateway refuses the request before source access. The response does not reveal which application, question, or policy caused the refusal.

Stop issuing new access tokens to age-checker:

Terminal window
evidencectl access client revoke age-checker
Revoked client age-checker.
Registry Mint reload requested.

The command updates access/clients/age-checker.yaml and requests another Registry Mint reload without restarting Evidence Gateway. Revocation cannot erase a stateless access token already issued to the application. That token remains valid for up to 300 seconds.

Try to prepare a fresh request as the revoked client:

Terminal window
if evidencectl request prepare adult-status \
--purpose age-check \
--subject person_id=person-123 \
--client age-checker \
--name age-checker-revoked
then
echo 'unexpected request preparation success' >&2
exit 1
fi
evidencectl: unknown or revoked active client age-checker

evidencectl rejects an unknown or revoked named client locally, before contacting Registry Mint or publishing request artifacts. service-router remains registered and keeps its service-routing membership.

Stop the local services before reading their completed audit chain:

Terminal window
evidencectl dev stop
evidencectl audit show --last-operation
Local Evidence stopped
ACCESS REFUSED requester=<pseudonym> reason=not_authorized

The privacy-safe refusal event contains a requester pseudonym and the closed not_authorized reason. The event does not contain the rejected requirement, purpose, selector, authority, or response format. It also does not contain a client identifier or access token. The revoked-client preparation remains local and creates no Evidence Gateway audit event because no HTTP request was sent.

Remove the stopped generated runtime while preserving the editable questions, access policy, and request artifacts:

Terminal window
evidencectl dev clean

Return to the registry terminal and press Ctrl+C.

  • Authentication identifies the calling application but grants no question by itself.
  • Many applications can share a governed access policy without client-specific Evidence Gateway config.
  • A client added while the services run can obtain a token without restarting Evidence Gateway or Registry Mint.
  • Each application receives only the questions included in its assigned policies.
  • Evidence Gateway refuses unauthorized questions before reading the source.
  • Evidence Gateway retains a privacy-safe audit event for an authenticated authorization refusal.
  • Revoking a local client requests a Registry Mint reload and prevents new local request preparation.
  • Client membership changes affect new tokens while existing tokens remain valid for up to 300 seconds.
  • Adding a question or changing an access policy requires a new Evidence Gateway generation.
  • Source authentication and consumer trust remain separate from caller access.