Skip to content
Registry StackDocsv0.34.0

Relayctl workflow reference

View as Markdown

relayctl is Relay adopter tooling. It initializes an authoring project, inspects SQLite structure, compiles and validates the project, generates reviewable artifacts, runs the project’s offline fixture cases, classifies changes between two project revisions, builds a sealed deployment package, and hosts the editor integration surfaces. It serves no request, opens no socket, and performs no deployment action.

Relayctl is pre-1.0 adopter tooling and sits outside any frozen runtime contract. Its command line can change before a compatibility promise covers it. The relay binary stays the authority for package verification, activation, and serving.

This page records current-source workflow boundaries from crates/registry-relayctl/src/lib.rs and the boundary tests in crates/registry-relayctl/tests/cli_contract.rs. Exact generated syntax is published only after its source catalog has completed review.

Eight top-level entries. Seven are flat; relayctl tooling groups two editor-integration subcommands.

CommandInputsResultDoes not do
relayctl init <PROJECT>One new or empty project directoryWrites a complete authoring project of visibly unreviewed starter documentsReuse a directory that already has entries, follow a symlinked project root, or read any database
relayctl inspect <DATABASE>One SQLite database and a source postureReports the schema fingerprint and the structure of each table, index, view, and trigger, and can write starter documentsRead row values, sample data, or run project queries
relayctl check <PROJECT>Authoring project directoryCompiles the project and reports diagnostics, the contract revision, and the accepted configuration key pathsWrite artifacts, seal a package, or approve a change
relayctl generate <PROJECT>Authoring project directoryWrites the deterministic artifact set and five authoring reports, each with its SHA-256 digestSeal a package or write into a destination that already has entries
relayctl test <PROJECT>Project fixture inputsRuns the project’s offline fixture cases through the shared kernelStart a listener, contact a network source, or start relay
relayctl diff <PREVIOUS> <CURRENT>Two project directoriesClassifies meaning, disclosure, and security changes between the reviewed project and the candidateAdd or remove change classes, or decide whether the change is acceptable
relayctl package <PROJECT> --output <DIRECTORY>Project that compiles under the production profileBuilds a sealed package directory carrying relay-package.jsonSign the package, deploy it, or write into a destination that already exists
relayctl tooling editor [PROJECT]Authoring project directory, defaulting to the current directoryWrites the project-local schema mappings VS Code and Zed read, and reports all six managed filesInstall an extension, change editor settings outside the project, or read any database
relayctl tooling language-serverNo arguments; speaks the Language Server Protocol over standard input and outputReports Relay V2 authoring diagnostics from the entry documents and exact governed closure held for the workspaceOpen a socket, observe SQLite, read source values, or adopt an unrelated file merely because a recursive watcher reports it
OptionEffect
--jsonThe seven shared workflow commands emit relayctl.report.v1 JSON in place of the readable rendering. tooling editor emits relayctl.editor.v1. tooling language-server continues to speak LSP and does not use this flag for report output. Accepted before or after the subcommand.
--versionPrints relayctl <version>, matching the release version of the asset.
--helpPrints usage on standard output and exits with status 0.

init takes one positional PROJECT path and no options. It writes exactly seven files:

  • registry.yaml
  • runtime.yaml
  • governance/identifier-lifecycle.yaml
  • governance/classification-review.yaml
  • governance/legal-basis.yaml
  • governance/processing.dpv.yaml
  • codelists/record-lifecycle.yaml

A directory that already contains entries is a domain refusal with the diagnostic code project.destination_not_empty. A symlinked project root is an unsafe path and is an operational failure. Generated governance judgments and classification defaults are marked suggested rather than reviewed, so relayctl check <PROJECT> --production refuses the project until an institution reviews them. runtime.yaml does not carry that review marker.

inspect takes one positional DATABASE path.

OptionDefaultPurpose
--profile <PROFILE>live-read-onlySource posture used while opening the database read-only. Accepts snapshot or live-read-only.
--starters <DIRECTORY>Not writtenWrites compiler-derived, visibly unreviewed starters to this directory, as schema-starter.yaml.
--statistical-view <VIEW>Not generatedGenerates a format-neutral statistical component starter for one view, as statistical-dataset-starter.yaml. Requires --starters, --time-column, and --measure-column.
--time-column <COLUMN>Not setExact source column for the required time-period dimension. Requires --statistical-view.
--measure-column <COLUMN>Not setExact source column for the required observation measure. Requires --statistical-view.
--attribute-column <COLUMN>Not setExact source column to treat as an observation attribute instead of a dimension. Repeatable. Requires --statistical-view.

A partial statistical selection is a usage error rather than a guess: naming the view without both required columns, or naming the columns without --starters, fails argument parsing.

The report carries the schema fingerprint and one entry per schema object, each with its kind (table, index, view, or trigger), its name, its table name, and its columns. Each column reports its name, its declared SQLite type, whether it is nullable, and whether it is part of the primary key. No row value, evaluated default, or query result can appear in the report.

check takes one positional PROJECT path.

OptionDefaultPurpose
--productionAuthoring profileCompiles under the production profile, which requires every generated suggestion to have been reviewed.

The report carries the contract revision, the profile that was applied, and the configuration key paths the compiler accepted for registry.yaml and runtime.yaml.

generate takes one positional PROJECT path.

OptionDefaultPurpose
--output <DIRECTORY><PROJECT>/generatedDestination for generated artifacts.

A destination that already contains entries is a domain refusal with the diagnostic code generation.destination_not_empty. A symlinked destination is an unsafe path.

Alongside the compiled artifact set, generate writes five authoring outputs:

IdentifierPath
identification-reportreports/identification-report.json
classification-inventoryreports/classification-inventory.json
access-profile-reportreports/access-profile-report.json
contextual-review-findingsreports/contextual-review-findings.json
classification-review-startergovernance/classification-review-starter.yaml

Every generated file is listed in the report with its identifier, its path relative to the destination, and its sha256: digest.

test takes one positional PROJECT path.

OptionDefaultPurpose
--fixture <IDENTIFIER>Every step, in orderRuns the one step whose identifier matches exactly.

Fixture evaluation first compiles the complete authoring project, then requires runtime.yaml, expected-http.yaml, and fixture.sql. A missing or unreadable fixture input is an operational failure with exit code 3. Invalid fixture YAML or SQL is a domain refusal with exit code 1 and a fixture.* diagnostic code.

--fixture selects one step and expands no prerequisite. The plan keeps only the step whose identifier matches, and the journey runs that step alone, so a step that reads another step’s observation cannot pass this way. Two expectations work that way: recordsEquivalentTo and etagSameAs each name an earlier step, and a missing observation is treated as a mismatch rather than as a skip. A step carrying either one selected on its own always refuses, with fixture.representation_mismatch or fixture.etag_mismatch. Prove those steps with a full journey run, and reserve --fixture for self-contained steps.

diff takes two positional paths: PREVIOUS, the previously reviewed project directory, and CURRENT, the candidate project directory. The report body is the compiler’s change-impact report. Relayctl neither adds nor removes change classes.

package takes one positional PROJECT path.

OptionDefaultPurpose
--output <DIRECTORY>RequiredNew sealed package directory.

The destination is mandatory and must not already exist. package always compiles under the production profile, so an unreviewed project cannot be sealed.

Every command that reaches the shared tooling facade prints one report. The report has three members:

  • status, either success or refused.
  • diagnostics, an array of objects with severity (error or warning), code, location, and message.
  • details, a tagged object whose kind is initialized, schema-inspection, check, generate, test, diff, or package.

Without --json, the output is a readable rendering of that report: a first line stating the outcome, then the detail indented under it. The rendering summarizes, so it leaves out parts of the report a person reading a terminal does not need. check prints how many configuration key paths each document accepts where the report lists them, and generate prints each artifact’s identifier and path where the report also carries its digest. With --json, the report is the whole output. Both modes are deterministic and end with exactly one newline, so the same inputs produce the same bytes.

The JSON shape is best-effort for local automation. It is not a covered compatibility surface, and the compatibility promise does not cover adopter tooling.

CodeMeaning
0The command completed and the report status is success.
1Domain refusal. The command ran, and the report status is refused with at least one diagnostic.
2Usage error. The command line did not parse, and nothing reached the shared tooling facade.
3Operational failure. An input could not be read, an output could not be written, a path was unsafe, or inspection, generation, or packaging could not be completed.

For the seven shared workflow commands, an operational failure writes one line to standard error, relayctl: <message>, using one of six categorical messages:

  • the requested authoring input could not be read
  • the requested authoring output could not be written
  • the requested path is unsafe
  • the SQLite schema could not be inspected
  • the generated artifacts could not be constructed
  • the deployment package could not be constructed

None of them names a path, a SQL statement, a column value, or a row.

Four boundaries are asserted by crates/registry-relayctl/tests/cli_contract.rs and hold for every release:

  • Relayctl never starts relay and never links rusqlite. The production source contains registry_relay_v2::tooling and none of std::process::Command, Command::new, or rusqlite. SQLite access happens inside the shared library through the read-only boundary in crates/registry-platform-sqlite.
  • inspect offers no row or value sampling surface. Its help states that it inspects structure without reading row values, and the test refuses the options --sample, --rows, --values, and --limit.
  • A usage error discloses no project content. relayctl package <project> without --output exits 2 with empty standard output and an error that names --output and nothing about the project.
  • All seven flat workflow commands and both tooling subcommands print help successfully with an empty standard error, so the adopter workflow is exposed by one binary.

Relayctl defines no product-specific environment-variable configuration. Every governed input is a positional path, an explicit flag, a project file, the documented current-directory default for tooling editor, or a language-server document. The fixture runner derives its owner-only transient workspace from the explicit project path and ignores host temporary-directory configuration. Every secret reference stays inside the project’s runtime.yaml as a secret:env/<NAME> or secret:file/<name> reference that only relay resolves. See the environment variable reference for the variables the runtime may read.

On Linux amd64, the Relay installer downloads, verifies, and installs the matching relay and relayctl binaries together:

Terminal window
curl -fsSL https://github.com/registrystack/registry-stack/releases/latest/download/relay-install.sh | bash

The one-line form pipes a script into bash; replace | bash with | less to read it first. The installer checks both binaries against the release SHA256SUMS and stops on a mismatch, but it does not authenticate the release itself; OpenSSF and release trust records the signed checksum chain that does.

For Linux arm64, take the plain relayctl binary asset from the release. You can also use a plain asset on Linux amd64 or build from source.

Starting with v0.33.0, the macOS arm64 asset is a .tar.gz bundle. It keeps the executable beside the shared AWS-LC-FIPS libraries it loads and the applicable third-party notices. Extract the whole bundle into one private directory and keep those files together. Releases through v0.32.0 retain their plain macOS binary assets.

The release publishes three relayctl assets:

  • relayctl-<tag>-linux-amd64
  • relayctl-<tag>-linux-arm64
  • relayctl-<tag>-macos-arm64.tar.gz for v0.33.0 and later

Each asset is checked at build time to report relayctl <version> for the release version. Verify a downloaded asset against the release checksums and signatures documented in release/VERIFY.md before running it.

To build from source at a pinned tag:

Terminal window
cargo build --release --locked -p registry-relayctl

The binary lands at target/release/relayctl. A macOS source build also requires CMake and Go for the always-on AWS-LC-FIPS backend and retains a build-tree shared-library closure. It is not the published bundle, so do not copy that executable away from its built libraries by itself.