Documentation preview. These pages target Registryctl v0.15.0, which is not published yet. Public download commands will not work until that release exists. For runnable released instructions, use v0.13.0.
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.
The workbook, project identifiers, and caller credentials are synthetic and local. Do not substitute personal data or production credentials.
Enter the project
Section titled “Enter the project”The first tutorial stops its containers but keeps my-first-api. Enter that
project:
cd my-first-apiThe command prints nothing. Run all remaining commands from this directory.
Add Notary
Section titled “Add Notary”Add the maintained local Notary continuation:
registryctl add notaryRegistryctl 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 smokeThe 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.
Check the fixtures
Section titled “Check the fixtures”Run the maintained active, planned, and absent-record cases:
registryctl test --project-dir .Registryctl prints exactly:
PASS: 6/6 fixtures passedThe fixtures prove the authored lookup and claim meanings offline. The requests below separately prove the running Relay-to-Notary path.
Start Relay and Notary
Section titled “Start Relay and Notary”Start the project so Registryctl compiles and starts the combined loopback topology:
registryctl startDocker 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 readyPASS readiness: Registry Notary is readyRelay API: http://127.0.0.1:4242Relay docs: http://127.0.0.1:4242/docsNotary API: http://127.0.0.1:4255Notary docs: http://127.0.0.1:4255/docsRelay remains the only product that reads the workbook. Notary reaches Relay through the generated private consultation binding and returns claim results.
Load the local caller credentials
Section titled “Load the local caller credentials”Load the generated credentials into this shell without printing them:
set -a. .registry-stack/runtime/local/secrets/local.envset +aThese commands print nothing. Do not print, paste, or commit this environment file or either raw credential.
Prepare the active-project request:
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.
Prove scope is enforced
Section titled “Prove scope is enforced”Send the request with the generated caller that lacks the service’s required
evidence:projects:read scope:
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/evaluationsNotary prints exactly:
HTTP 403The credential is valid, but its scope does not authorize this claim. Do not widen a caller’s scope merely to bypass this denial.
Evaluate the active project
Section titled “Evaluate the active project”Repeat the request with the authorized tutorial caller:
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/evaluationsThe 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.
Evaluate the planned project
Section titled “Evaluate the planned project”Prepare the same claim request for synthetic project PW-002:
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:
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/evaluationsThe 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.
Check an absent record
Section titled “Check an absent record”Use the separate existence claim for a synthetic identifier absent from the workbook:
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/evaluationsThe 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.
Change the status policy
Section titled “Change the status policy”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: predicateThe 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: trueThe source interaction and outputs: { status: planned } remain unchanged.
You changed only the policy and its expected derived result.
Check the complete fixture set again:
registryctl test --project-dir .Registryctl prints exactly:
PASS: 6/6 fixtures passedRestart and see the changed result
Section titled “Restart and see the changed result”Restart from the authored files:
registryctl restartAfter 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:
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/evaluationsThe 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 local services
Section titled “Stop the local services”Stop the combined runtime:
registryctl stopDocker may print container shutdown progress. Registryctl prints no additional success line after Compose exits successfully.
Remove the generated raw credentials from this shell:
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_RAWThe 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.
What you built
Section titled “What you built”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.
- Connect your own data when you are ready to replace the synthetic workbook and review the resulting integration and policy.
- Understand generated files before handing compiled product inputs to an operator.
- Prepare the operator handoff before introducing real callers, identity providers, databases, or production networks.
Troubleshooting
Section titled “Troubleshooting”| Symptom | Cause | Resolution |
|---|---|---|
registryctl add notary reports an unsupported shape | The 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 edit | The authored rule and fixture expectation disagree. | Keep the planned source output unchanged and set its project-status-accepted expectation to true. |
Notary returns 401 | The 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 caller | The 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 fails | A file under .registry-stack/ was edited or replaced. | Rerun Registryctl from the human-owned project so it regenerates the disposable runtime. |