Released docs. You are viewing the documentation published with v0.46.0. Development docs are available at Latest.
Run one Registry Coordinator workflow, reviewed-notice, from an application
in Base Registry Engine (BReg), through your decision as reviewer in Registry Casework, to an
approval email from Registry Messaging. Coordinator does the rest, even across a restart.
Prepare verified candidate or release assets
Section titled “Prepare verified candidate or release assets”The published v0.43.0 release does not include Coordinator. The verified local
evaluation uses v0.44.0 candidate native tools and the candidate unified wheel
with published Messaging v0.43.0 at
sha256:695b6a6dc2f72ab2f2341510f171d683cc87a19c7d37c253b545198557bd8a2c.
It ran on macOS arm64 with the Linux amd64 image under OrbStack. An actual
Linux amd64 host run, the macOS binary platform release work, and a fully
published Coordinator distribution are still pending. Use this path only
with a release or a reviewed candidate that supplies the Coordinator pair and
unified client. A candidate rehearsal is not proof that those assets have been
published. The deployment runbook
retains the contributor build procedure.
Verify the selected release’s manifest and checksums using the
release operations procedure.
For a local candidate, use the maintainer’s reviewed source and checksum-bound
artifact handoff. The asset directory must contain its verified SHA256SUMS,
the host’s unified wheel, and all six native assets named below. Do not use
v0.43.0 downloads as substitutes for missing Coordinator assets.
Get GitHub’s source-code archive for that verified source tag or commit, or the
maintainer’s checksum-bound git archive export of the same reviewed commit.
These are ordinary Git source archives, not a separate published support asset.
Extract it and run from its root. Rust is not required. Use a fresh working
directory; the maintained macOS extractor refuses an existing destination.
Set REGISTRY_STACK_VERSION to the candidate or release version and
REGISTRY_STACK_ASSETS to the absolute directory containing its verified local
artifacts. The preparation checks the selected file checksums, renames raw
Linux assets to executable tool names, and uses the existing strict macOS
archive extractor while retaining its adjacent FIPS libraries.
: "${REGISTRY_STACK_VERSION:?set the verified candidate or release version}": "${REGISTRY_STACK_ASSETS:?set the absolute verified artifact directory}"mkdir -p tutorial-workpython3 - "$REGISTRY_STACK_VERSION" "$REGISTRY_STACK_ASSETS" >tutorial-work/prepared-tools.json <<'PY'import hashlib, json, platform, shutil, sysfrom pathlib import Pathsys.path.insert(0, "release/scripts")from macos_fips_packaging import extract_macos_binary_archive
version, assets = sys.argv[1], Path(sys.argv[2])if not assets.is_absolute(): raise SystemExit("Use an absolute artifact directory.")host = (platform.system(), platform.machine())if host == ("Darwin", "arm64"): target, suffix = "macos-arm64", "_arm64.whl"elif host in (("Linux", "x86_64"), ("Linux", "amd64")): target, suffix = "linux-amd64", "_x86_64.manylinux2014_x86_64.whl"else: raise SystemExit("This tutorial supports macOS arm64 and Linux amd64.")tools = ("breg", "bregctl", "casework", "caseworkctl", "coordinator", "coordinatorctl")checksums = {}for line in (assets / "SHA256SUMS").read_text().splitlines(): if not line.strip(): continue digest, name = line.split(maxsplit=1) checksums[name.removeprefix("*")] = digestwheels = [path for path in assets.glob(f"registry_stack_client-{version}-*.whl") if path.name.endswith(suffix)]if len(wheels) != 1: raise SystemExit("Supply exactly one verified host wheel.")files = [assets / (f"{tool}-v{version}-{target}" + (".tar.gz" if target == "macos-arm64" else "")) for tool in tools]for path in [*files, wheels[0]]: if checksums.get(path.name) != hashlib.sha256(path.read_bytes()).hexdigest(): raise SystemExit(f"Checksum mismatch: {path.name}")output = Path("tutorial-work/native-tools").resolve()output.mkdir(mode=0o700)for tool, path in zip(tools, files): stem = f"{tool}-v{version}-{target}" if target == "macos-arm64": extracted = Path("tutorial-work/unpacked") / tool extract_macos_binary_archive(path, extracted, stem) source = extracted / stem for library in extracted.glob("libaws_lc_fips_*.dylib"): destination = output / library.name if destination.exists() and destination.read_bytes() != library.read_bytes(): raise SystemExit("Conflicting FIPS library bytes; stop and inspect the candidate.") if not destination.exists(): shutil.copy2(library, destination) else: source = path shutil.copy2(source, output / tool) (output / tool).chmod(0o755)print(json.dumps({"tools": str(output), "clientWheel": str(wheels[0])}))PYFor the tested candidate evaluation, set MESSAGING_IMAGE to the exact public
Messaging v0.43.0 digest above, prefixed by
ghcr.io/registrystack/messaging@. Future published journeys must use the
combination recorded for their release, including the supported binary
platform roster. The helper accepts the public Messaging repository only; it
does not accept a private candidate-image reference.
: "${REGISTRY_STACK_VERSION:?set the verified release or candidate version}": "${COORDINATOR_TUTORIAL_BIN_DIR:=$(jq -r .tools tutorial-work/prepared-tools.json)}": "${REGISTRY_STACK_CLIENT:=$(jq -r .clientWheel tutorial-work/prepared-tools.json)}": "${MESSAGING_IMAGE:?set the published Messaging image digest}"export COORDINATOR_TUTORIAL_BIN_DIR REGISTRY_STACK_CLIENTfor tool in breg bregctl casework caseworkctl coordinator coordinatorctl; do test -x "$COORDINATOR_TUTORIAL_BIN_DIR/$tool"doneexport PATH="$COORDINATOR_TUTORIAL_BIN_DIR:$PATH"docker pull --platform linux/amd64 "$MESSAGING_IMAGE"The session’s --released-clients option reads BReg and admits the run through
the installed unified Python client. It uses the active workflow revision and
repeats one identical start key to confirm the same run identity. No Rust input
helper or Rust toolchain is needed. The preparation selects the exact local
wheel through REGISTRY_STACK_CLIENT. When that variable is unset, the start
command instead selects the named published version. Keep this terminal open.
Read the workflow
Section titled “Read the workflow”A workflow is a finite list of named steps. Ask coordinatorctl to explain this one:
coordinatorctl explain --project products/coordinator/examples/reviewed-noticeThe output ends with the steps from start, among them:
verify-review-subject: choose by review-subject-route(): changed -> changed-before-review, current -> submit-review changed-before-review: finish application-changed submit-review: call review review-submit-review-request (casework mutation, recovery same-command) with review-request(), then await-review await-review: wait for the result of submit-review, at most 1209600 seconds, then decide; on timeout withdraw-timeout; on cancel withdraw-operatorEach line is one step. A call reaches a product through a named connection, here review for
Casework; a choose picks the next step; a finish ends with an outcome.
await-review is a wait: it holds up to fourteen days for the review result. Later steps read
the application again before calling Messaging.
Start the products and the run
Section titled “Start the products and the run”The session script starts BReg, Casework, and Coordinator from the selected native directory,
with Messaging, databases, and the Mailpit mail sink in Docker. It seeds one application for Amina Example and starts one
run of the workflow for it with POST /v1/runs: one admitted
execution that Coordinator keeps in its database. The registry project, review policy, and
notice template it uses are in products/coordinator/scripts/real_service_fixtures.py. Docker host networking lets Messaging reach the owned loopback services. This is
local evaluation configuration. Every service listens on loopback HTTP
(tlsTermination: development-loopback), and Coordinator applies
its schema as the database superuser and serves as a separate runtime role: a local setup only.
mkdir -p tutorial-workuv run --quiet --project products/coordinator/examples/reviewed-notice --locked \ --with "${REGISTRY_STACK_CLIENT:-registry-stack-client==$REGISTRY_STACK_VERSION}" \ products/coordinator/examples/reviewed-notice/session.py start \ --released-clients --messaging-image "$MESSAGING_IMAGE" \ --bin-dir "$COORDINATOR_TUTORIAL_BIN_DIR" \ --session-dir tutorial-work/reviewed-notice > tutorial-work/session.jsonjq '{status, workflow}' tutorial-work/session.json{ "status": "ready", "workflow": "reviewed-notice"}session.json holds service addresses and owner-only token file paths. Keep them in variables,
and copy the Coordinator token out of its header file for coordinatorctl:
session=tutorial-work/session.jsonrun_id="$(jq -r .runId "$session")"coordinator_url="$(jq -r .coordinatorApi "$session")"casework_url="$(jq -r .caseworkApi "$session")"mailpit_url="$(jq -r .mailpit "$session")"staff_headers="$(jq -r .staffHeaderFile "$session")"(umask 077 && sed -n 's/^Authorization: Bearer //p' \ "$(jq -r .coordinatorHeaderFile "$session")" > tutorial-work/coordinator.token)Inspect the waiting run
Section titled “Inspect the waiting run”coordinatorctl inspect --run "$run_id" \ --url "$coordinator_url" --token-file tutorial-work/coordinator.tokenThe run is at await-review, after Casework accepted the review request:
state: running step: await-reviewwaits:- acceptedStep: submit-review completion: resultNothing has gone to Messaging. Inspection never shows the application data or the commands sent.
Claim and approve the review task
Section titled “Claim and approve the review task”As staff, find the open task in the applications queue and read the facts you decide on. The
applicant’s email address stays in BReg.
caseworkctl api '/v1/review-tasks?queue=applications' \ --url "$casework_url" \ --header @"$staff_headers" \ --header 'Registry-Casework-Profile: staff' \ --output tutorial-work/tasks.jsontask_id="$(jq -r '.items[] | select(.state == "open") | .taskId' tutorial-work/tasks.json)"caseworkctl api "/v1/review-tasks/$task_id/context" \ --url "$casework_url" \ --header @"$staff_headers" \ --header 'Registry-Casework-Profile: staff' | jq .context.snapshot{ "applicantName": "Amina Example", "benefitProgram": "housing-support", "requestedAmount": 1200}Claim the task at the revision you read:
revision="$(jq -r '.items[] | select(.state == "open") | .revision' tutorial-work/tasks.json)"caseworkctl api "/v1/review-tasks/$task_id/claim" \ --url "$casework_url" \ --header @"$staff_headers" \ --header 'Registry-Casework-Profile: staff' \ --method POST \ --if-match "\"$revision\"" \ --idempotency-key "$(openssl rand -hex 16)" \ --output /dev/nullThe command exits successfully when Casework returns the claimed task.
Before you decide, restart Coordinator. The run is durable state, not a running process:
python3 products/coordinator/examples/reviewed-notice/session.py restart-coordinator \ --session-dir tutorial-work/reviewed-notice > "$session"coordinator_url="$(jq -r .coordinatorApi "$session")"jq .coordinatorRestarts "$session"1Now approve at the task’s current revision, with the policy’s approved outcome:
caseworkctl api "/v1/review-tasks/$task_id" \ --url "$casework_url" \ --header @"$staff_headers" \ --header 'Registry-Casework-Profile: staff' \ --output tutorial-work/task.jsonrevision="$(jq -r .revision tutorial-work/task.json)"cat > tutorial-work/decision.json <<'JSON'{"decision":{"type":"approve","outcome":"approved"}}JSONcaseworkctl api "/v1/review-tasks/$task_id/decisions" \ --url "$casework_url" \ --header @"$staff_headers" \ --header 'Registry-Casework-Profile: staff' \ --if-match "\"$revision\"" \ --idempotency-key "$(openssl rand -hex 16)" \ --input tutorial-work/decision.json \ --output /dev/nullThe command exits successfully when Casework records the decision and answers 204.
If a request answers 401, the local token expired. Refresh the staff token, and the Coordinator
token for coordinatorctl, then repeat:
owner="$(jq -r .ownerProject "$session")"bregctl dev token staff "$owner"bregctl dev token coordinator-producer "$owner"(umask 077 && sed -n 's/^Authorization: Bearer //p' \ "$(jq -r .coordinatorHeaderFile "$session")" > tutorial-work/coordinator.token)See the run finish
Section titled “See the run finish”Casework tells the restarted Coordinator a result is ready. The workflow reads the application again, checks it is unchanged and still allows a notice, and asks Messaging to send to the address on that record. Check the run:
coordinatorctl status --run "$run_id" \ --url "$coordinator_url" --token-file tutorial-work/coordinator.tokenoutcome: notice-acceptedoutput: messageId: <message-id> resultId: <result-id>state: finishedIf it is still running, repeat it after a few seconds. Then look at the local mail server:
curl -fsS "$mailpit_url/api/v1/messages" \ | jq '{count: (.messages | length), to: .messages[0].To[0].Address, subject: .messages[0].Subject}'{ "count": 1, "to": "amina@example.invalid", "subject": "Application approved"}One email reached Amina’s address on record. A changed application, a withdrawn noticeAllowed,
or a rejection would each have finished without calling Messaging.
Stop the session
Section titled “Stop the session”The script removes only the containers and sessions it created:
python3 products/coordinator/examples/reviewed-notice/session.py stop \ --session-dir tutorial-work/reviewed-noticeHow Registry Coordinator works explains the run, its wait, who owned each effect, and how an operator recovers a run.
Cookies on Registry Stack
We use optional cookies to understand how you use the main Registry Stack website and this documentation. If you contact us, we may link this activity to your enquiry. You can change your choice at any time. Read the privacy policy.
Your browser asks sites not to track you, so optional tracking stays off.
We could not use or save that choice. Optional tracking remains off.