Skip to content
Registry StackDocsDocumentation preview

Verify a live registry-backed claim

View as Markdown

Continue from the protected spreadsheet API in my-first-api. You will add Registry Notary to the same project, evaluate claims through the live Relay-to-Notary path, prove that caller scope is enforced, and change one authored status policy.

Outcome
A live Registry Notary evaluation over the synthetic public-works workbook, with allowed, rejected, and absent-record results.
Time
About 15 minutes after completing the first API tutorial
Level
Local single-node
Prerequisites
The unmodified my-first-api project from the first tutorialRegistryctl 0.15.0 with its matching image lockLinux amd64Docker Engine with Compose v2curlPorts 4242 and 4255 available on loopback

The workbook, project identifiers, and caller credentials are synthetic and local. Do not substitute personal data or production credentials.

The first tutorial stops its containers but keeps my-first-api. Enter that project:

Terminal window
cd my-first-api

The command prints nothing. Run all remaining commands from this directory.

Add the maintained local Notary continuation:

Terminal window
registryctl add notary

Registryctl prints this structure. The project path is absolute on your computer:

Added local Notary add-on.
Project: <absolute-project-path>
Files:
registry-stack.yaml
environments/local.yaml
integrations/project-record-snapshot/integration.yaml
integrations/project-record-snapshot/fixtures/match.yaml
integrations/project-record-snapshot/fixtures/planned.yaml
integrations/project-record-snapshot/fixtures/no-match.yaml
Next:
registryctl test --environment local
registryctl restart
registryctl smoke

The command extends the same human-owned project. It adds a public-works-verification evidence service, a bounded project-record-snapshot consultation, three synthetic fixtures, and the local caller and deployment bindings. It does not ask you to edit generated Relay or Notary files.

Open registry-stack.yaml in your text editor. The added service includes:

public-works-verification:
kind: evidence
version: 1
subject_type: project
purpose: public-works-case-management
legal_basis: public-service-delivery
consent: not_required
access: { scopes: ["evidence:projects:read"] }
consultations:
project:
integration: project-record-snapshot
input: { project_id: request.target.identifiers.project_id }
claims:
project-record-exists: { cel: project.matched, disclosure: predicate }
project-status-accepted: { cel: 'project.matched && project.status == "active"', disclosure: predicate }

Relay performs an exact lookup against the same spreadsheet used in the first tutorial. It releases only the declared status output to Notary. project-status-accepted is this example project’s configurable policy. It does not establish programme eligibility or make a legal decision. Both tutorial claims return predicates. The caller does not receive the source status value.

Run the maintained active, planned, and absent-record cases:

Terminal window
registryctl test --project-dir .

Registryctl prints exactly:

PASS: 6/6 fixtures passed

The fixtures prove the authored lookup and claim meanings offline. The requests below separately prove the running Relay-to-Notary path.

Start the project so Registryctl compiles and starts the combined loopback topology:

Terminal window
registryctl start

Docker progress depends on the installed Compose version. After both products become ready, the final lines identify the local Relay and Notary endpoints:

PASS readiness: compiled workbook source is ready
PASS readiness: Registry Notary is ready
Relay API: http://127.0.0.1:4242
Relay 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

Relay remains the only product that reads the workbook. Notary reaches Relay through the generated private consultation binding and returns claim results.

Load the generated credentials into this shell without printing them:

Terminal window
set -a
. .registry-stack/runtime/local/secrets/local.env
set +a

These commands print nothing. Do not print, paste, or commit this environment file or either raw credential.

Prepare the active-project request:

Terminal window
active_request='{
"target": {
"type": "Project",
"identifiers": [
{ "scheme": "project_id", "value": "pw_001" }
]
},
"claims": ["project-status-accepted"],
"format": "application/vnd.registry-notary.claim-result+json",
"purpose": "public-works-case-management"
}'

The assignment prints nothing.

Send the request with the generated caller that lacks the service’s required evidence:projects:read scope:

Terminal window
curl -sS -o /dev/null -w 'HTTP %{http_code}\n' \
-X POST \
-H "x-api-key: $REGISTRYCTL_LOCAL_NOTARY_UNDER_SCOPED_TOKEN_RAW" \
-H "Data-Purpose: public-works-case-management" \
-H "Content-Type: application/json" \
-H "Accept: application/vnd.registry-notary.claim-result+json" \
--data "$active_request" \
http://127.0.0.1:4255/v1/evaluations

Notary prints exactly:

HTTP 403

The credential is valid, but its scope does not authorize this claim. Do not widen a caller’s scope merely to bypass this denial.

Repeat the request with the authorized tutorial caller:

Terminal window
curl -sS -w '\nHTTP %{http_code}\n' \
-X POST \
-H "x-api-key: $REGISTRYCTL_LOCAL_NOTARY_CALLER_TOKEN_RAW" \
-H "Data-Purpose: public-works-case-management" \
-H "Content-Type: application/json" \
-H "Accept: application/vnd.registry-notary.claim-result+json" \
--data "$active_request" \
http://127.0.0.1:4255/v1/evaluations

The response contains these stable fields and ends with HTTP 200. Dynamic evaluation IDs, timestamps, target handles, and provenance fields are omitted here. JSON whitespace and object-key order may differ:

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

The scoped caller is allowed to ask whether the selected project’s status meets this policy. Relay finds synthetic project pw_001 with status active, so Notary returns true.

Prepare the same claim request for synthetic project PW-002:

Terminal window
planned_request='{
"target": {
"type": "Project",
"identifiers": [
{ "scheme": "project_id", "value": "PW-002" }
]
},
"claims": ["project-status-accepted"],
"format": "application/vnd.registry-notary.claim-result+json",
"purpose": "public-works-case-management"
}'

The assignment prints nothing. Evaluate it:

Terminal window
curl -sS -w '\nHTTP %{http_code}\n' \
-X POST \
-H "x-api-key: $REGISTRYCTL_LOCAL_NOTARY_CALLER_TOKEN_RAW" \
-H "Data-Purpose: public-works-case-management" \
-H "Content-Type: application/json" \
-H "Accept: application/vnd.registry-notary.claim-result+json" \
--data "$planned_request" \
http://127.0.0.1:4255/v1/evaluations

The stable claim fields are:

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

The response ends with HTTP 200. Relay found PW-002, but its planned status does not meet the active-only policy. This is a policy non-match, not an absent registry record.

Use the separate existence claim for a synthetic identifier absent from the workbook:

Terminal window
absent_request='{
"target": {
"type": "Project",
"identifiers": [
{ "scheme": "project_id", "value": "pw_999" }
]
},
"claims": ["project-record-exists"],
"format": "application/vnd.registry-notary.claim-result+json",
"purpose": "public-works-case-management"
}'
curl -sS -w '\nHTTP %{http_code}\n' \
-X POST \
-H "x-api-key: $REGISTRYCTL_LOCAL_NOTARY_CALLER_TOKEN_RAW" \
-H "Data-Purpose: public-works-case-management" \
-H "Content-Type: application/json" \
-H "Accept: application/vnd.registry-notary.claim-result+json" \
--data "$absent_request" \
http://127.0.0.1:4255/v1/evaluations

The assignment prints nothing. The stable claim fields are:

{
"results": [
{
"claim_id": "project-record-exists",
"value": false,
"satisfied": false,
"disclosure": "predicate",
"format": "application/vnd.registry-notary.claim-result+json"
}
]
}

The response ends with HTTP 200. false means this bounded lookup produced no match in the selected workbook. It does not prove global nonexistence, fraud, ineligibility, or another legal negative.

In registry-stack.yaml, replace the active-only claim:

project-status-accepted: { cel: 'project.matched && project.status == "active"', disclosure: predicate }

with a rule that also accepts planned:

project-status-accepted:
cel: 'project.matched && (project.status == "active" || project.status == "planned")'
disclosure: predicate

The claim name remains accurate because it describes this example project’s authored acceptance policy, not an intrinsic meaning of either status.

Open integrations/project-record-snapshot/fixtures/planned.yaml and change its maintained claim expectation:

project-status-accepted: true

The source interaction and outputs: { status: planned } remain unchanged. You changed only the policy and its expected derived result.

Check the complete fixture set again:

Terminal window
registryctl test --project-dir .

Registryctl prints exactly:

PASS: 6/6 fixtures passed

Restart from the authored files:

Terminal window
registryctl restart

After Docker progress, Registryctl prints the same readiness and endpoint lines shown earlier. Registryctl regenerates the Relay and Notary inputs. You do not edit .registry-stack/.

Repeat the unchanged planned-project request:

Terminal window
curl -sS -w '\nHTTP %{http_code}\n' \
-X POST \
-H "x-api-key: $REGISTRYCTL_LOCAL_NOTARY_CALLER_TOKEN_RAW" \
-H "Data-Purpose: public-works-case-management" \
-H "Content-Type: application/json" \
-H "Accept: application/vnd.registry-notary.claim-result+json" \
--data "$planned_request" \
http://127.0.0.1:4255/v1/evaluations

The stable fields now show the changed policy result:

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

The response ends with HTTP 200. The source record is still planned; the authored acceptance policy now includes that status.

Stop the combined runtime:

Terminal window
registryctl stop

Docker may print container shutdown progress. Registryctl prints no additional success line after Compose exits successfully.

Remove the generated raw credentials from this shell:

Terminal window
unset REGISTRYCTL_LOCAL_NOTARY_CALLER_TOKEN_RAW \
REGISTRYCTL_LOCAL_NOTARY_UNDER_SCOPED_TOKEN_RAW \
REGISTRYCTL_LOCAL_RELAY_MATCH_KEY_RAW \
REGISTRYCTL_LOCAL_RELAY_NO_MATCH_KEY_RAW

The command prints nothing. The authored workbook, service, integration, fixtures, and policy edit remain in my-first-api. Generated runtime files and local credentials remain disposable.

You extended the first tutorial’s protected spreadsheet project with a live Registry Notary path. You proved that:

  • A valid but under-scoped caller is denied.
  • The scoped tutorial caller receives predicate results without receiving a workbook row.
  • A policy non-match and an absent source record have distinct claim meanings.
  • An authored policy and its maintained expectation change the live result after registryctl restart.

Relay owns the exact workbook lookup. Notary owns claim evaluation and disclosure. Registryctl compiles and runs both from the same human-owned project.

SymptomCauseResolution
registryctl add notary reports an unsupported shapeThe project differs from the maintained spreadsheet starter or already has a conflicting Notary integration.Start from the unmodified my-first-api project created by Registryctl 0.15.0.
registryctl add notary prints Verified local Notary add-on.The exact maintained add-on is already present.Continue with the fixture check. The command made no further changes.
registryctl test fails after the policy editThe authored rule and fixture expectation disagree.Keep the planned source output unchanged and set its project-status-accepted expectation to true.
Notary returns 401The caller credential is missing or belongs to another project.Source this project’s private local.env again without printing it.
Notary returns 403 for the authorized callerThe caller lacks evidence:projects:read, or the purpose does not match the service.Use the generated tutorial caller and public-works-case-management. Do not widen the scope merely to bypass the denial.
A generated artifact integrity check failsA file under .registry-stack/ was edited or replaced.Rerun Registryctl from the human-owned project so it regenerates the disposable runtime.