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.
Use this tutorial after running the sample registry API. You will derive every source and access value from a workbook you control, validate the project, and prove denied and allowed behavior on the resulting route.
Use a synthetic, public, or otherwise approved non-personal workbook for this local evaluation. Do not use live country data, personal data, production credentials, or an unreviewed export.
Before you start
Section titled “Before you start”Make a reviewed derivative that contains:
- one selected worksheet with one header row;
- exactly two columns for this first adaptation;
- one stable, non-empty, unique text key;
- one text field approved for disclosure;
- no formulas in the selected worksheet; and
- at least one approved row whose key you can use as a local principal.
Registry Stack does not evaluate formulas, follow external workbook links, or infer relationships between sheets. Review the XLSX readiness contract before adapting a larger or less regular workbook.
Write down the decisions you will substitute for the angle-bracket placeholders in this tutorial:
| Placeholder | Derive it from |
|---|---|
<entity-id> and <key-field> | Stable lower-snake-case names for the selected worksheet and its unique key |
<released-field> | A lower-snake-case name for the second, approved column |
<key-header> and <released-header> | The two exact workbook header cells, including spaces and capitalization |
<worksheet-name> | The exact worksheet tab name |
<matching-key-value> | One approved, non-personal key cell that exists in the worksheet |
<purpose> | A reviewed identifier for why callers may use this API |
<registry-id> and <service-id> | Stable identifiers for the authored registry and its records policy |
The examples below quote exact workbook header cells. Keep the angle brackets only while reading. Replace every placeholder in the authored files before running validation.
Set the two values reused by shell commands in this tutorial. Replace the quoted placeholders with your actual identifiers:
entity_id='<entity-id>'request_purpose='<purpose>'These assignments print nothing. Keep using the same shell for the remaining commands.
Create the project
Section titled “Create the project”Create a new project from the maintained spreadsheet starter:
registryctl init --from spreadsheet --project-dir reviewed-registrycd reviewed-registryRegistryctl prints:
Initialized Registry Stack project "fictional-public-works-registry". Directory: reviewed-registry Starter: spreadsheet (Registry Stack 0.15.0) Starter content: matches bundled digest Editor support: VS Code and Zed (reviewed-registry/.registry-stack-editor/manifest.json)Registryctl follows those lines with a Next section that tells you to enter
the directory, check the local project, and start Relay. The starter is the
reviewed baseline you will adapt. The cd command prints nothing.
Copy your workbook into the project and give the entity file its authored name:
cp /path/to/your-reviewed-workbook.xlsx data/reviewed-source.xlsxmv entities/projects.yaml "entities/${entity_id}.yaml"These commands print nothing when they succeed. Registryctl reads the copied workbook but never writes its cells.
You will edit only these human-owned inputs:
| File | Decision it owns |
|---|---|
data/reviewed-source.xlsx | The exact approved source bytes |
entities/<entity-id>.yaml | Logical field names, stable key, strict types, and row limits |
environments/local.yaml | Worksheet headers, workbook path, and local caller principals |
registry-stack.yaml | Registry identity, service, purpose, scopes, filters, and returned fields |
Do not edit .registry-stack/. Registryctl generates that directory and may
replace it.
Describe the two workbook fields
Section titled “Describe the two workbook fields”Replace entities/<entity-id>.yaml with this strict two-field schema after
substituting your three logical names:
version: 1id: <entity-id>revision: 1primary_key: <key-field>schema: type: object additionalProperties: false required: [<key-field>, <released-field>] properties: <key-field>: { type: string, minLength: 1, maxLength: 128 } <released-field>: { type: string, minLength: 1, maxLength: 256 }materialization: max_records: 10000 max_bytes: 16MiB refresh: manual retain_generations: 2primary_key names the stable key. Every selected column is required and
explicitly typed. additionalProperties: false makes a new source column a
review event instead of silently adding it to the entity.
Do not use a row number or mutable display name as the key. If either column may legitimately be empty or needs a different type or length, make that decision explicitly using the entity definition reference before continuing.
Bind exact workbook headers
Section titled “Bind exact workbook headers”Replace environments/local.yaml with the following content after substituting
the entity, worksheet, header, and matching-key values:
version: 1entities: <entity-id>: provider: type: xlsx project_file: data/reviewed-source.xlsx path: /var/lib/registry/reviewed-source.xlsx sheet: "<worksheet-name>" header_row: 1 columns: <key-field>: "<key-header>" <released-field>: "<released-header>" source_revision: reviewed-source-v1 generation: "1"relay: origin: https://records-relay.internal.invalid issuer: https://workload-issuer.internal.invalid jwks_url: https://workload-issuer.internal.invalid/.well-known/jwks.json audience: registry-relay allowed_clients: [local-reviewed-client] local_api_keys: match_principal: "<matching-key-value>" no_match_principal: registryctl_local_no_match scopes: [<entity-id>:metadata, <entity-id>:rows]deployment: profile: local relay: { service: records-relay }The keys on the left side of columns are the logical field names from the
entity schema. The quoted values on the right are exact header cells in the
workbook.
Set match_principal to an approved key cell that exists in the workbook.
Keep registryctl_local_no_match absent from the key column. These two
principals let you prove one-row and no-row behavior without creating
production identities.
Local API keys exist only for this local profile. Registryctl writes their raw values to a private runtime directory. A deployed environment must use its reviewed identity provider and secret management.
Author the records policy
Section titled “Author the records policy”Keep the version and starter block that Registryctl created at the top of
registry-stack.yaml. Replace the registry, entities, and services
sections beneath it with:
registry: id: <registry-id>
entities: <entity-id>: file: entities/<entity-id>.yaml
services: <service-id>: kind: records_api entity: <entity-id> title: Reviewed local records description: Protected view of the approved workbook derivative. owner: Local evaluation team sensitivity: confidential access_rights: restricted update_frequency: as_needed api: scopes: metadata: <entity-id>:metadata rows: <entity-id>:rows aggregate: <entity-id>:aggregate evidence_verification: <entity-id>:evidence_verification purposes: [<purpose>] projection: [<key-field>, <released-field>] pagination: { default_limit: 50, max_limit: 100 } filters: { <key-field>: [eq] } required_principal_filters: [<key-field>] standards: { ogc_features: false, sp_dci: false }This policy derives the API contract:
- the request purpose header is
Data-Purpose: <purpose>; - the returned object contains only
<key-field>and<released-field>; - the allowed filter is equality on
<key-field>; and - the required key filter is bound to trusted caller identity.
The service identifier owns the policy but does not appear in the URL. Registryctl derives the records URL from the entity identifier:
http://127.0.0.1:4242/v1/datasets/<entity-id>/entities/<entity-id>/recordsA caller-supplied query value cannot satisfy or replace the required principal filter. Relay obtains that value from authenticated gateway identity. A field is not readable merely because it exists in the workbook or entity schema. It must also appear in the reviewed projection.
Validate and start the protected API
Section titled “Validate and start the protected API”Ask Registryctl to validate the workbook, authored policy, generated configuration, release files, listener, and Docker runtime:
registryctl doctor --profile localA successful result starts with:
Registry Stack doctor: ok
Project: <absolute-project-path>Profile: localregistry-relay: ok (0 errors, 0 warnings)<absolute-project-path> is the path to reviewed-registry on your computer.
If the check fails, correct the workbook or one of the three authored YAML
files named in the diagnostic. Do not edit .registry-stack/.
A successful check proves that:
- the selected worksheet and headers exist;
- every selected row matches the strict field schema;
- required keys are present and unique;
- formulas, unexpected columns, unsafe paths, and invalid values were rejected;
- the route, scopes, purpose, filters, and projection form a valid Relay policy; and
- the matching release runtime can start on the loopback listener.
Start Relay:
registryctl startAfter Docker progress, Registryctl prints exactly:
PASS readiness: compiled workbook source is readyRelay API: http://127.0.0.1:4242API docs: http://127.0.0.1:4242/docsRelay parses the workbook again during its initial load. The listener is
limited to 127.0.0.1:4242. The generated configuration and workbook are
mounted read-only.
Do not run registryctl smoke for this adapted project. In v0.15, that command
checks the unmodified starter’s projects route and sample purpose. Use the
derived requests below to check your authored route and purpose.
Prove the anonymous request is denied
Section titled “Prove the anonymous request is denied”Substitute your entity and purpose in this request:
curl -sS -o /dev/null -w 'HTTP %{http_code}\n' \ -H "Data-Purpose: ${request_purpose}" \ "http://127.0.0.1:4242/v1/datasets/${entity_id}/entities/${entity_id}/records"Relay prints exactly:
HTTP 401This proves that the protected route rejects a missing credential. A valid purpose header does not substitute for authenticated caller identity.
Prove the matching caller is allowed
Section titled “Prove the matching caller is allowed”Load the private local keys without printing them:
set -a. .registry-stack/runtime/local/secrets/local.envset +aThese commands print nothing. Do not print, paste, commit, or retain the raw keys outside this disposable local runtime.
Substitute the same entity and purpose:
curl -sS -w '\nHTTP %{http_code}\n' \ -H "Authorization: Bearer $REGISTRYCTL_LOCAL_RELAY_MATCH_KEY_RAW" \ -H "Data-Purpose: ${request_purpose}" \ "http://127.0.0.1:4242/v1/datasets/${entity_id}/entities/${entity_id}/records"Relay returns this structure using your actual field names and row values. JSON whitespace and object-key order may differ:
{ "data": [ { "<key-field>": "<matching-key-value>", "<released-field>": "<value-from-that-row>" } ], "pagination": { "has_more": false }}HTTP 200This proves that the credential authenticates the local caller, the purpose and scopes authorize the operation, the principal-bound key selects exactly the matching row, and the projection limits the disclosed fields. The request URL, purpose, key, and response fields all come from the authored values you derived from the workbook and access decision.
Stop and clean up
Section titled “Stop and clean up”Stop the local runtime:
registryctl stopDocker may print container shutdown progress. Registryctl prints no additional success line after Compose exits successfully.
The authored project and workbook remain. Generated build and runtime directories are disposable and can be regenerated.
What you built
Section titled “What you built”You built a loopback-only protected API over one reviewed XLSX worksheet. Its entity and request route come from your logical entity name. Its source mapping comes from exact worksheet headers. Its authorized row comes from the stable key used as local principal identity. Its purpose and returned fields come from explicit service policy.
You validated the workbook and policy, then confirmed the running API’s denied and allowed behavior.
- Prepare the operator handoff without copying workbook rows, raw keys, or private runtime details into public evidence.
- Review the security guidance before connecting a real identity provider or widening access.
- Look up project configuration when you need more columns, types, fixtures, or environment-specific policy.
Common source problems
Section titled “Common source problems”| Problem | What to change |
|---|---|
| Wrong worksheet or header | Correct sheet, header_row, or the exact quoted header mapping. |
| Missing or unexpected column | Align the reviewed workbook derivative, entity schema, and column mapping. |
| Missing or duplicate key | Resolve the source identity problem. Registry Stack will not choose one duplicate. |
| Invalid type or null required value | Correct the workbook derivative or deliberately change the schema. |
| Formula or external reference | Replace it with an approved static value in the derivative. |
| Path traversal or workbook symlink | Copy the reviewed file under data/ and use that regular file. |
An unintended field appears in projection | Remove it before building, or obtain the required review. |