Unreleased documentation. These pages follow the main branch and can change before the next release. For supported guidance, use v0.43.0.
You know which product calls your process makes and in what order, and you want Registry Coordinator to make them, with waits in between that survive restarts. Write the process as a project, check and test it with no service running, and package it for the operator who serves it. Endpoints, credentials, and caller policies never enter the package; Deploy Registry Coordinator adds them.
You need coordinatorctl on your PATH. Build it from a Registry Stack checkout of the release
you deploy:
cargo build --locked --release -p registry-coordinator --binsexport PATH="$PWD/target/release:$PATH"On macOS the binary loads the FIPS library its build produced, so run it from that checkout or follow the macOS install in the deployment runbook.
Start a project
Section titled “Start a project”coordinatorctl init --directory follow-upcreated: follow-up workflow.yaml functions.rhai runtime.yaml scenarios.yaml .gitignorenext: coordinatorctl test --project follow-upThe starter is a delayed follow-up: wait until a given time, read an application from a Base Registry Engine (BReg) registry, and send a notice through Registry Messaging only if the record still allows one. The project ID is the directory name. Run the test it suggests:
coordinatorctl test --project follow-upPASS notice-accepted-after-virtual-waitPASS permission-withdrawnPASS same-command-after-response-lossPASS read-only-reconciliation-after-response-lossPASS expired-receipt-does-not-prove-no-effectPASS refusal-after-unknown-preserves-unknown6 passed, 0 failedEach case in scenarios.yaml runs the workflow on a virtual clock against fake product replies:
the record allows a notice, the record no longer allows one, and four cases where a response is
lost or refused after the call may have taken effect. No case sends a request or reads a
credential.
Read the steps
Section titled “Read the steps”coordinatorctl explain --project follow-upSteps from start: due: wait until input /sendAfter, then read-application read-application: call applications applications-read-record (breg read, recovery read-again) with {collection: "applications", recordId: input /applicationId}, then permission permission: choose by notice-route(): send -> notify, skip -> skipped notify: call notices notices-submit-message (messaging mutation, recovery same-command-and-receipt) with notice-request(), then accepted skipped: finish no-current-permission accepted: finish message-accepted with {messageId: step notify /id}Each line is one step of workflow.yaml, in order from workflow.start. A step’s type is
wait-until, wait-for, call, choose, or finish. A call names an operation alias declared
under operations:
operations: applications-read-record: uses: breg.read-record/v1 connection: applications notices-submit-message: uses: messaging.submit-message/v1 connection: noticesuses picks one versioned host operation; the
configuration reference lists all
ten with the effect and recovery explain printed. connection is a logical name the operator
binds to an endpoint and credentials in runtime.yaml. There is no other way to reach a product:
no arbitrary HTTP method, SQL, or computed destination.
The call’s input is a mapping:
read-application: type: call operation: applications-read-record input: type: fields fields: collection: type: literal value: applications recordId: type: input path: /applicationId next: permissiontype: input reads the run’s input, type: step an earlier call’s output, and type: literal a
fixed value. A path is a JSON Pointer into it. type: fields builds an object of up to 64
members, and type: function computes the value with a Function, as permission and notify do.
Functions
Section titled “Functions”notice-route and notice-request are Rhai entrypoints in functions.rhai. workflow.yaml
declares each one’s inputs and output by schema, here the read operation’s output and the
Messaging operation’s input:
notice-request: runtime: rhai source: functions.rhai entrypoint: notice_request inputs: input: typeRef: workflow-input record: operationOutput: applications-read-record output: operationInput: notices-submit-messageA Function is pure: it has no clock, network, database, file, or credential. The host checks its
inputs and output against those schemas and stops it at the limits check --explain prints.
add_seconds and seconds_between compute over instants a Function is given.
Check a change
Section titled “Check a change”check fits every mapping to the schemas it reads and feeds. Misspell the input member the read
uses:
recordId: type: input path: /applicationId path: /applicationIDcoordinatorctl check --project follow-uperror[coordinator.mapping.path] <file> /workflow/steps/read-application/input/fields/recordId/path expected a path that the workflow input schema declares; it declares nothing at /applicationID next: Correct the path to a member the workflow input schema declares, or omit path to use the whole value.1 error, 0 warnings in 1 fileThe refusal names the file, line, and column, the step and field, and the repair. Some refusals hide the checks that depend on them, so check again after each repair. Put the path back:
recordId: type: input path: /applicationID path: /applicationIdCheck the project with the runtime file the starter carries, which binds applications and
notices to a BReg and a Messaging connection:
coordinatorctl check --project follow-up --runtime-config follow-up/runtime.yamlValid workflow follow-up version 1Definition: sha256:<digest>Valid runtime configurationThe Definition: digest is the workflow revision. It hashes what execution depends on: input,
steps, outcomes, and the operation and Function revisions used. Titles, descriptions, and
project.version do not change it. Every run records the revision it was admitted under.
Package the workflow
Section titled “Package the workflow”coordinatorctl package --project follow-up --output follow-up-packagecreated: follow-up-packagepackage digest: sha256:<digest>package runs scenarios.yaml first and writes nothing unless every case passes. It writes
definition.json and SHA256SUMS into a new directory and refuses one that exists. The package
digest is the SHA-256 of SHA256SUMS, so it covers the exact bytes; the operator pins it.
project.version is a label for people; the revision is what tells two definitions apart.
Export a typed caller
Section titled “Export a typed caller”A caller starts a run with the workflow name, the revision, and an input the workflow accepts. Generate those as code:
coordinatorctl types --project follow-up --language typescript --output follow-up.tsoutput: follow-up.tsstatus: exportedworkflow: follow-upworkflowRevision: sha256:<digest>follow-up.ts exports the workflow name and revision, an Input type, an Outcome type with one
member per outcome, and a client with start, get, and cancel. --language python writes the
same for Python. Regenerate after any change that moves the revision.
Hand over the package
Section titled “Hand over the package”Give the operator the package directory, its package digest, and a runtime.yaml template with
secret references and no secret values. For each connection, list the product, the access profile
or review policy it needs, and for Messaging the template and sender profile the Functions name.
The operator pins the package in deployment.packages by root and expectedDigest. One
deployment serves up to sixteen workflows; each is applied, upgraded, and rolled back on its own,
and a run keeps the definition it started with.
The
example projects
show the other step and operation shapes: a review wait, a governed BReg action, a typed decision,
an external read, and an appointment. Each runs under coordinatorctl test the same way.