Skip to content
Registry StackDocsv0.13.0

Evaluate a registry-backed claim locally

View as Markdown

This tutorial continues from the my-first-api project created in Run a protected registry API locally. Imagine that a benefits intake service has collected an applicant’s name and date of birth. It needs to know whether the registry accepts that person’s registration under the current intake policy, but it should not receive the registry row or a national identifier. The initial policy accepts active registrations and rejects pending ones. You will add Registry Notary, evaluate both cases, and then expand the policy to accept pending registrations too.

Later tutorials will show how to start from your own registry and author a custom integration. Here, reusing the first tutorial’s small workbook keeps the focus on making and changing a Notary claim.

Outcome
A live registry-backed boolean claim, matched without a national identifier, with a meaningful policy edit exercised end to end.
Time
About 10 minutes after completing the first tutorial
Level
Local single-node
Prerequisites
The my-first-api project from the first tutorialMatching Registry Stack v0.13.0 registryctl, release images, and image lockA Docker Compose providercurl

This tutorial uses synthetic data and local demo credentials. Do not use the generated keys or database settings in production.

From the my-first-api directory left by the first tutorial, add the local Notary journey:

Terminal window
registryctl add notary

The command reports the authored claim file, local Notary URL, and next command:

Added Registry Notary to "my-first-api".
Claim: notary/project/registry-stack.yaml
Notary API after start: http://127.0.0.1:4255
Next:
registryctl start

Add --format json when another program needs the versioned add-on report.

The add-on creates an editable Registry Stack project under notary/project/. It also adds a private consultation Relay that reads the same workbook. Relay still owns registry access. Notary receives only the minimized consultation result needed to evaluate the claim.

Read the evidence service before starting it:

Terminal window
sed -n '7,28p' notary/project/registry-stack.yaml

The important part is deliberately small:

registration-verification:
kind: evidence
version: 1
purpose: https://example.local/purpose/tutorial
legal_basis: public-service-delivery
consent: not_required
access: { scopes: ["benefits_casework:evidence"] }
consultations:
enrollment:
integration: person-demographics
input:
given_name: request.target.attributes.given_name
family_name: request.target.attributes.family_name
date_of_birth: request.target.attributes.date_of_birth
claims:
person-registration-accepted:
cel: enrollment.matched && enrollment.registration_status == "active"
disclosure: predicate

The caller supplies a name and date of birth already obtained through its intake process. Relay uses all three values for an exact lookup and returns registration_status only to Notary. The selector fields cannot also be projected as outputs, and the public result does not repeat the name, date of birth, or status. This keeps matching inputs separate from the registry fact being proved.

This is evidence that the registry contains exactly one matching record whose status satisfies the current acceptance policy. It is not proof that the caller is that person. Authentication, identity assurance, and authority to act for the person remain separate controls.

Names and dates of birth can collide or be recorded differently. Requiring exactly one match rejects ambiguity, but a production service still needs a jurisdiction-appropriate identity assurance and matching design.

Start the combined local project:

Terminal window
registryctl start

After the containers become ready, the command prints both APIs:

Relay API: http://127.0.0.1:4242
API docs: http://127.0.0.1:4242/docs
Notary API: http://127.0.0.1:4255
Notary docs: http://127.0.0.1:4255/docs

Load the generated local credentials into your current shell:

Terminal window
set -a
. ./secrets/local.env
set +a
test -n "$TUTORIAL_EVALUATOR_RAW"

The raw key stays in the ignored secrets/local.env file. Do not paste it into the request body or commit it.

Evaluate an active registration from the workbook:

Terminal window
curl -sS -X POST \
-H "x-api-key: $TUTORIAL_EVALUATOR_RAW" \
-H "Data-Purpose: https://example.local/purpose/tutorial" \
-H "Content-Type: application/json" \
-H "Accept: application/vnd.registry-notary.claim-result+json" \
--data '{
"target": {
"type": "person",
"attributes": {
"given_name": "Jo",
"family_name": "Elm",
"date_of_birth": "2019-02-03"
}
},
"claims": ["person-registration-accepted"],
"disclosure": "predicate",
"format": "application/vnd.registry-notary.claim-result+json",
"purpose": "https://example.local/purpose/tutorial"
}' \
http://127.0.0.1:4255/v1/evaluations

The response also includes dynamic evaluation, target-reference, timestamp, and provenance fields. The stable fields to look for are:

{
"results": [
{
"claim_id": "person-registration-accepted",
"value": true,
"satisfied": true,
"disclosure": "predicate",
"format": "application/vnd.registry-notary.claim-result+json"
}
]
}

true is the claim result. The response does not contain Jo, Elm, 2019-02-03, or the source value active.

Save a request for a person whose registry record is pending. You will repeat it after changing the policy:

Terminal window
cat > notary/pending-registration-request.json <<'JSON'
{
"target": {
"type": "person",
"attributes": {
"given_name": "Nia",
"family_name": "Stone",
"date_of_birth": "1998-03-05"
}
},
"claims": ["person-registration-accepted"],
"disclosure": "predicate",
"format": "application/vnd.registry-notary.claim-result+json",
"purpose": "https://example.local/purpose/tutorial"
}
JSON
curl -sS -X POST \
-H "x-api-key: $TUTORIAL_EVALUATOR_RAW" \
-H "Data-Purpose: https://example.local/purpose/tutorial" \
-H "Content-Type: application/json" \
-H "Accept: application/vnd.registry-notary.claim-result+json" \
--data @notary/pending-registration-request.json \
http://127.0.0.1:4255/v1/evaluations

The person matches exactly, but pending does not satisfy the initial policy:

{
"results": [
{
"claim_id": "person-registration-accepted",
"value": false,
"satisfied": false,
"disclosure": "predicate"
}
]
}

This is a policy rejection, not a failed lookup. The public response still does not reveal the source status.

Change only the request date for this call:

Terminal window
curl -sS -X POST \
-H "x-api-key: $TUTORIAL_EVALUATOR_RAW" \
-H "Data-Purpose: https://example.local/purpose/tutorial" \
-H "Content-Type: application/json" \
-H "Accept: application/vnd.registry-notary.claim-result+json" \
--data '{
"target": {
"type": "person",
"attributes": {
"given_name": "Jo",
"family_name": "Elm",
"date_of_birth": "2019-02-04"
}
},
"claims": ["person-registration-accepted"],
"disclosure": "predicate",
"format": "application/vnd.registry-notary.claim-result+json",
"purpose": "https://example.local/purpose/tutorial"
}' \
http://127.0.0.1:4255/v1/evaluations

The lookup finds no exact record, so the predicate is false:

{
"results": [
{
"claim_id": "person-registration-accepted",
"value": false,
"satisfied": false,
"disclosure": "predicate"
}
]
}

No-match is a claim result, not a source error. Ambiguous matches are different: the private Relay rejects them rather than choosing a row.

Open notary/project/registry-stack.yaml in your editor. Find this line:

cel: enrollment.matched && enrollment.registration_status == "active"

Change the expression to accept either status and save the file:

cel: enrollment.matched && (enrollment.registration_status == "active" || enrollment.registration_status == "pending")

The pending fixture is an executable example of the original policy. Open notary/project/integrations/person-demographics/fixtures/pending.yaml and change its expected claim from:

claims: { person-registration-accepted: false }

to:

claims: { person-registration-accepted: true }

Now restart:

Terminal window
registryctl restart

registryctl restart validates and rebuilds the generated Relay and Notary inputs from the authored files before starting the services. The fixtures are executable examples of the claim, so restart refuses to deploy when an expected result no longer agrees with the edited rule. Edit the authored files under notary/project/, not generated files under .registry-stack/build/.

Repeat the saved request:

Terminal window
curl -sS -X POST \
-H "x-api-key: $TUTORIAL_EVALUATOR_RAW" \
-H "Data-Purpose: https://example.local/purpose/tutorial" \
-H "Content-Type: application/json" \
-H "Accept: application/vnd.registry-notary.claim-result+json" \
--data @notary/pending-registration-request.json \
http://127.0.0.1:4255/v1/evaluations

The same pending source record is now accepted by the expanded policy:

{
"results": [
{
"claim_id": "person-registration-accepted",
"value": true,
"satisfied": true,
"disclosure": "predicate"
}
]
}

You changed claim semantics in the authored project, rebuilt the governed contract, and observed the new result through the live API. You did not edit the workbook or expose its row.

When you are done:

Terminal window
registryctl stop

This keeps the workbook, authored Notary project, and local request file. To return to the original claim later, restore the equality expression and change the pending fixture expectation back to false before starting the project.

You extended the first tutorial’s protected registry with a live Notary evaluation path. An authorized caller supplied name and date of birth instead of a national identifier. Relay required one exact record and released only registration status to Notary. Notary returned one predicate, with no source fields in the public result. You saw a matched pending registration rejected by the initial policy, then expanded the policy and watched the same request produce a different governed answer.

SymptomCauseResolution
registryctl add notary is unknownThe CLI predates Registry Stack v0.11.0 or does not match the installed release image lock.Install registryctl, the immutable Relay and Notary images, and the image lock from the same Registry Stack v0.13.0 release. Do not substitute an ad hoc source build or a lock from another release.
registryctl add notary says the project already has a Notary add-onThe command has already completed for this project.Continue with registryctl start; do not run the add command twice.
Notary returns 401The evaluator key was not loaded or is from another generated project.Source secrets/local.env again in the current shell.
Notary returns 403The key lacks the evidence scope or the purpose does not match the authored service.Use TUTORIAL_EVALUATOR_RAW and the tutorial purpose shown above.
registryctl restart reports a claim mismatchThe edited rule no longer agrees with the fixture’s expected result.Update the expected claim in notary/project/integrations/person-demographics/fixtures/pending.yaml, then restart again.
The edited rule does not take effectA generated file was edited, or the running services were not rebuilt.Edit the authored claim and matching fixture under notary/project/, then run registryctl restart.