Skip to content
Registry StackDocsDocumentation preview

Run your first protected registry API

View as Markdown

Registry Relay can publish a protected, read-only API over a spreadsheet without copying the workbook into a second database. In this tutorial, you will start the maintained synthetic sample and prove that an anonymous caller is denied while an authorized caller receives only its own projected record.

Outcome
A protected API over a synthetic workbook, available only on your computer, with denied and authorized requests you can inspect.
Time
About 15 minutes after Docker is installed
Level
Local single-node
Prerequisites
Linux amd64BashDocker Engine with Compose v2curlOutbound HTTPS access to GitHubPort 4242 available on loopback

The workbook and caller identifiers in this tutorial are synthetic. Do not put personal data or production credentials in this project.

This complete local runtime is supported on Linux amd64. Registryctl also has Linux arm64 and macOS arm64 command-line assets for authoring, validation, and build tasks, but the complete runtime is not supported on those hosts in this release.

Confirm that Docker Engine and Compose v2 are available:

Terminal window
docker info >/dev/null
docker compose version

The first command prints nothing when Docker is available. The second prints a Compose v2 version, for example:

Docker Compose version v2.x.x

This proves that the local container provider is reachable. Registryctl will perform its own stricter runtime and project checks before starting Relay.

Download and run the Registryctl 0.15.0 installer:

Terminal window
tag=v0.15.0
curl -fsSLO "https://github.com/registrystack/registry-stack/releases/download/${tag}/registryctl-${tag}-install.sh"
bash "./registryctl-${tag}-install.sh"
registryctl --version

The installer reports its chosen install directory. The last command prints exactly:

registryctl 0.15.0

The installer downloads release files over TLS and verifies their checksums. A checksum detects a damaged or mismatched download, but does not authenticate who published it. Follow the tag-frozen release verification guide when you need signature and provenance verification before installation.

If your shell cannot find registryctl, add the install directory reported by the installer to PATH.

Create the maintained spreadsheet starter in a new directory:

Terminal window
registryctl init --from spreadsheet --project-dir my-first-api
cd my-first-api

Registryctl prints:

Initialized Registry Stack project "fictional-public-works-registry".
Directory: my-first-api
Starter: spreadsheet (Registry Stack 0.15.0)
Starter content: matches bundled digest
Editor support: VS Code and Zed (my-first-api/.registry-stack-editor/manifest.json)
Next:
cd my-first-api
registryctl doctor --profile local
registryctl start

The starter digest proves that the authored sample files match the copy bundled with this Registryctl release. The cd command prints nothing.

You do not need to copy product configuration or edit generated Relay YAML. These are the files you will encounter:

PathWhat it controls
registry-stack.yamlThe API purpose, caller scopes, filters, and returned fields
entities/projects.yamlThe stable key and field types
environments/local.yamlThe workbook, worksheet, columns, and local test callers
data/public_works_projects.xlsxThe synthetic source data
.registry-stack/build/local/Generated product configuration. Review it, but do not edit it.
.registry-stack/runtime/local/Private, disposable local runtime files

The .registry-stack/ directory is ignored by version control. On Unix, Registryctl gives runtime secret directories mode 0700 and raw credential files mode 0600.

Check the project, release files, workbook, listener, and Docker runtime:

Terminal window
registryctl doctor --profile local

A successful check starts with:

Registry Stack doctor: ok
Project: <absolute-project-path>
Profile: local
registry-relay: ok (0 errors, 0 warnings)

<absolute-project-path> is the path to my-first-api on your computer. Additional provider and release-file lines identify the exact inputs Registryctl checked.

This proves that the authored project can be compiled, the workbook passes strict validation, the generated result matches the authored inputs, the listener is loopback-only, and the matching release runtime is available. If it fails, follow the reported correction before continuing.

Start the exact compiled project:

Terminal window
registryctl start

Container progress lines depend on your Docker version. After Relay becomes ready, Registryctl prints exactly:

PASS readiness: compiled workbook source is ready
Relay API: http://127.0.0.1:4242
API docs: http://127.0.0.1:4242/docs

This proves that Relay loaded the compiled workbook source and is ready on the loopback listener. The generated Compose document mounts both the compiled Relay configuration and workbook read-only.

Run the sample’s maintained checks:

Terminal window
registryctl smoke

Registryctl prints exactly:

PASS allowed public health check
PASS allowed match source is ready
PASS denied anonymous records request
PASS denied wrong local API key
PASS allowed matching principal returns one record
PASS wrong principal safely returns no match

Together, these checks prove that the public health route is available, the protected route rejects missing and invalid credentials, the matching caller receives one row, and a valid caller bound to an absent key receives no other row.

Registryctl saves a machine-readable report at .registry-stack/runtime/local/smoke-results.json. It contains check names, paths, status classes, and pass states. It does not contain raw keys or workbook rows.

Call the records route without a credential:

Terminal window
curl -sS -o /dev/null -w 'HTTP %{http_code}\n' \
-H "Data-Purpose: public-works-case-management" \
http://127.0.0.1:4242/v1/datasets/projects/entities/projects/records

Relay prints exactly:

HTTP 401

The API is ready, but this protected route does not accept an anonymous caller. Supplying a purpose does not substitute for caller identity.

Load the private local caller keys into this shell:

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

These commands print nothing. Do not print, paste, or commit the environment file.

Use the matching synthetic caller to make the same request:

Terminal window
curl -sS -w '\nHTTP %{http_code}\n' \
-H "Authorization: Bearer $REGISTRYCTL_LOCAL_RELAY_MATCH_KEY_RAW" \
-H "Data-Purpose: public-works-case-management" \
http://127.0.0.1:4242/v1/datasets/projects/entities/projects/records

Relay returns this structure, followed by the exact status line shown. JSON whitespace and object-key order may differ:

{
"data": [
{
"project_id": "pw_001",
"district_code": "north-01",
"sector": "water",
"status": "active"
}
],
"pagination": {
"has_more": false
}
}
HTTP 200

The credential authenticates the local caller. The requested purpose and scopes authorize the records operation. Relay then derives the required project_id filter from that caller’s principal and returns only the four fields in the reviewed projection.

A caller cannot satisfy this required filter by adding another project_id to the URL. The principal-bound filter comes from trusted gateway identity, not from caller-controlled query parameters. The smoke check’s no-match result proves the safe alternative: a valid caller whose principal does not match a workbook key receives an empty collection.

Open the following files in your text editor:

Authored inputDecision to inspect
registry-stack.yamlPurpose, operation scopes, projection, allowed filters, and principal-bound filters
entities/projects.yamlStable key, strict record schema, and materialization limits
environments/local.yamlWorkbook binding, exact worksheet headers, and synthetic local principals

Then ask Registryctl to explain the effective plan:

Terminal window
registryctl check --project-dir . --environment local --explain

The output begins:

Registry Stack project: fictional-public-works-registry (valid)
Environment: local
Baseline: initial_without_baseline
Semantic changes: claim, integration, service_policy, operator_security, disclosure

Later sections report one records API service, the effective scopes and disclosure plan, and redaction counts. This proves that Registryctl validated the human-authored project and derived a reviewable effective plan.

Project fields compile as sensitive regardless of whether their physical provider is XLSX, CSV, Parquet, or PostgreSQL. A field is not externally readable merely because it exists in the source or entity schema. It must also appear in the reviewed service projection.

Stop the local runtime:

Terminal window
registryctl stop
cd ..

Docker may print container shutdown progress. Registryctl prints no additional success line after Compose exits successfully. cd .. returns you to the directory that contains my-first-api, ready for the next tutorial.

This stops and removes the local containers and network. It keeps the authored project and workbook. The generated build and runtime directories are disposable and can be regenerated from the authored project.

You ran a loopback-only Relay API over a synthetic XLSX workbook. You proved that:

  • the anonymous and invalid-key paths are denied;
  • an authorized principal receives only its own matching record;
  • a valid non-matching principal cannot see another record; and
  • the API returns only fields selected by authored disclosure policy.

The workbook, entity schema, environment binding, and service policy remain human-owned. Registryctl generates and validates the product configuration that Relay consumes.

Verify a live registry-backed claim to add Registry Notary to this my-first-api project and evaluate a bounded claim through the running Relay path.

When you are ready to replace the sample, use your own spreadsheet to derive the entity, workbook mapping, disclosure policy, local principal, purpose header, and request URL from a reviewed workbook.

Before a broader adoption, decide when Registry Stack fits and review the supported product and trust boundaries.

SymptomCauseResolution
registryctl is not foundThe install directory is not on PATH.Add the directory printed by the installer, usually ~/.local/bin, to PATH.
The installer reports an unsupported platformNo Registryctl asset is published for that OS and CPU.Use a published CLI platform for authoring, or Linux amd64 for this complete local runtime.
doctor cannot find Docker or Compose v2Docker Engine is absent or stopped.Start Docker and confirm docker info and docker compose version, then retry.
doctor rejects the image lockRegistryctl and the image lock are absent, changed, or from different releases.Rerun the same versioned installer. Do not substitute a mutable image tag or another release’s lock.
doctor reports a workbook failureThe declared file, sheet, header, strict columns, types, formulas, or primary key failed validation.Correct the human-owned workbook, entity, or environment file. Do not edit generated Relay input.
A generated artifact integrity check failsA file under .registry-stack/build/ or .registry-stack/runtime/ was edited or replaced.Remove the disposable generated closure and rerun registryctl doctor. Reapply intended changes only in human-owned files.
A request returns 401The credential is missing or invalid.Load the generated local environment without printing it and use the matching local key.
A request returns 403The caller lacks the reviewed scope or purpose.Review the service and environment bindings. Do not widen scopes merely to bypass the denial.
The documented recovery does not solve the problemThe local tutorial does not cover every host or workbook condition.Open a public Registry Stack issue with the command, stable code, operating system, and Registryctl version. Do not include keys, workbook rows, or private paths.