Skip to content
Registry StackDocsDevelopment (unreleased)

Review Base Registry Engine changes in Casework

For the operator

View as Markdown

If you are evaluating Registry Casework as the inbox for a team that reviews changes to a registry the Base Registry Engine (BReg) holds, start with one change request and one review. You will generate a registry project and a Casework project that fit each other, connect them, run both on your machine, submit a change request to the registry, approve and apply it from the Casework inbox as Staff, and read the applied change back from the registry. Everything you keep goes into one directory, tutorial-work.

Outcome
One registry change request reviewed and applied from a Casework inbox, with the registry's own history showing the resulting revision.
Time
About 30 minutes, plus the image download
Level
Local evaluation only
Prerequisites
Linux amd64 or arm64, or macOS on Apple SiliconA Bash or zsh shellRunning Dockercurl 7.76 or laterPython 3, to read JSON responsesAn editor

Install breg, bregctl, and mint, then casework and caseworkctl. Registry Mint issues the local access tokens this tutorial uses, standing in for the identity provider a deployment would have; both installers ship it, and either copy serves:

Terminal window
curl -fsSL https://github.com/registrystack/registry-stack/releases/latest/download/breg-install.sh | bash
bregctl --version
mint --version
Terminal window
curl -fsSL https://github.com/registrystack/registry-stack/releases/latest/download/casework-install.sh | bash
caseworkctl --version

Each installer checks its binaries against the release SHA256SUMS before anything reaches ~/.local/bin, and installs them together or not at all. Keep that directory on your PATH. The two commands must print the same version, because caseworkctl dev drives bregctl and refuses one from another release.

Open a terminal in a directory of your choice and generate both projects. Keep this terminal in the same directory for the rest of the tutorial; every path starts with tutorial-work/.

Terminal window
mkdir -p tutorial-work
bregctl init tutorial-work/registry --template professional-licences
caseworkctl init tutorial-work/casework --template professional-review

bregctl init reports 12 artifacts written, then 0 errors, 10 findings and three next steps. The findings name profiles that may list every row; they are the starter’s declared authority, reviewed in Create and query your first registry, and nothing in this tutorial depends on them. caseworkctl init reports init succeeded., lists the files it created, and names caseworkctl source add as what to run next.

The professional-licences starter is a registry of professional licences, with a professional-license entity and a scope-correction change request that proposes a correction to one licence and applies it only after a review stage named review. Its dev-clients.yaml declares the reader, editor, and reviewer clients the local session registers with Mint.

The professional-review template is a Casework deployment over that registry: one source, professional-register, using the breg adapter; one request kind, the scope-correction entity, routed to the corrections queue; and the staff, supervisor, and administrator profiles. Its dev-clients.yaml declares one client per profile and a corrections-team serving the queue.

The two fit because the Casework template names the entity the registry starter declares. A registry that declared no change request would give Casework nothing to present.

Casework needs a description of the registry it presents, and the registry needs an event destination and a reader profile for Casework. caseworkctl source add derives both from the registry project, and previews before it writes:

Terminal window
caseworkctl source add tutorial-work/registry --project tutorial-work/casework --source-id professional-register

The report begins source add succeeded. with status: preview at its end, and its bregAuthoringChanges line lists the six places it will change in the registry project:

bregAuthoringChanges: [{"file":"registry.yaml","operation":"ensure_exact","path":"/entities/scope-correction/events/casework-lifecycle-v1"},{"file":"registry.yaml","operation":"ensure_exact","path":"/accessProfiles/casework-reader"},{"file":"dev-clients.yaml","operation":"ensure_exact","path":"/clients/casework-reader"},{"file":"dev-clients.yaml","operation":"ensure_exact","path":"/clients/administrator"},{"file":"dev-clients.yaml","operation":"ensure_exact","path":"/clients/supervisor"},{"file":"dev-clients.yaml","operation":"ensure_exact","path":"/clients/staff"}]

The rest of the report is omitted here: bregAuthoringPatch carries the exact declarations those six paths receive, and candidateRuntimeBinding the event destination a deployment would configure. In registry.yaml, a lifecycle event on scope-correction tells Casework when a request changes, and a casework-reader access profile lets Casework read requests and nothing else. In dev-clients.yaml, a casework-reader client is the credential the local Casework session reads the registry with, and administrator, supervisor, and staff are the Casework project’s own people, registered with the registry’s Mint so one token serves both products. Staff and Supervisor are explicitly admitted to BReg because they review and apply its requests; Administrator remains a Casework-only caller.

Read the preview, then write it:

Terminal window
caseworkctl source add tutorial-work/registry --project tutorial-work/casework --source-id professional-register --apply

The report now ends status: applied, and next names the two steps a deployment takes from here: provisioning the webhook secret the runtime binding references, and running caseworkctl doctor. The local sessions do both for you. The command wrote tutorial-work/casework/sources/professional-register.json, the source description Casework serves from, and professional-register.breg-runtime.yaml beside it, the runtime binding a deployment would hand to the registry.

Start the registry:

Terminal window
bregctl dev tutorial-work/registry

The first start downloads the pinned PostgreSQL image, so it takes longer than the later ones. The command returns once the registry answers, with status ready in its report. dev started PostgreSQL in a container, started Mint, registered the seven clients from dev-clients.yaml under a fresh key each, built the registry package, and started breg.

The report names the registry address, the token endpoint, the audience, and one credential directory per client, as absolute paths under your directory:

bregctl dev succeeded.
status ready
project <your-directory>/tutorial-work/registry
breg url http://127.0.0.1:8090
token endpoint http://127.0.0.1:8091/token
audience urn:breg:dev:<identifier>

The remaining lines, the package revision, the state and runtime files, the webhook receiver, and the seven client blocks, are omitted here. The four clients source add wrote are among the seven: casework-reader, administrator, supervisor, and staff. The services keep running after the command returns, so this one terminal is enough.

Start the Casework project beside the running registry:

Terminal window
caseworkctl dev tutorial-work/casework --source-project tutorial-work/registry

--source-project names the registry project whose local session stands in for the deployed registry. The command returns once Casework answers, with status: ready in its report. dev started a second PostgreSQL container, read the registry session’s state, borrowed its Mint as the token issuer, exported the casework-reader credential and the three human clients from the registry session, wrote an operator file binding the source to the registry’s address, migrated the database, started casework, and seeded the directory from dev-clients.yaml.

The report names the Casework address, the token endpoint it shares with the registry, the audience, and the credential paths, as absolute paths under your directory:

dev succeeded.
audience: urn:breg:dev:<identifier>
caseworkUrl: http://127.0.0.1:8092
sources: {"professional-register":{"bregUrl":"http://127.0.0.1:8090","project":"<your-directory>/tutorial-work/registry"}}
stateFile: <your-directory>/tutorial-work/casework/.casework/dev/state.json
status: ready
tokenEndpoint: http://127.0.0.1:8091/token

The clients, directory, journal, operatorConfig, and project lines between them are omitted here. The audience and the token endpoint are the registry session’s: a Staff token minted there is valid at both products, which is what lets Casework carry your authority to the registry.

Use the two addresses from the reports. These assignments use the default ports or the CASEWORKCTL_DEV_CASEWORK_PORT override; if you started the registry with --mint-port, replace the token endpoint:

Terminal window
casework_url="http://127.0.0.1:${CASEWORKCTL_DEV_CASEWORK_PORT:-8092}"
token_endpoint="http://127.0.0.1:8091/token"
credentials=tutorial-work/casework/.casework/dev/credentials

Keep this terminal open so the values remain available to the later commands.

The registry starter ships two examples: first-record creates a licence, and reviewed-change drafts and submits a scope-correction against it. Run the first, as the registry’s own editor client:

Terminal window
bregctl examples run first-record tutorial-work/registry

The report begins bregctl examples run succeeded. and names the attempt and the record it created, then shows the create and get results as JSON. The licence MY-FIRST-001 holds one licensed activity, example-assessment, and the condition Supervized community practice. The identifiers vary per run; the rest of the report is omitted here.

Submit the correction:

Terminal window
bregctl examples run reviewed-change tutorial-work/registry --step submit

The report names a second attempt and, under Example progress retained, the request it created. Its draft result shows the proposed values, two licensed activities and an empty condition, and its submit result reads "operation": "submit_request". The request now waits in the registry’s review stage. The registry is what holds the request; Casework learns of it through the lifecycle event source add declared and through reconciliation, which the local session runs every five seconds.

Ask Mint for a token as the staff client and write it into a file curl can send as an authorization header:

Terminal window
umask 077
mint token --url "$token_endpoint" \
--client-id "$(cat "$credentials/staff/client-id")" \
--key "$credentials/staff/assertion-key.jwk" \
| sed 's/^/Authorization: Bearer /' > tutorial-work/staff.header

Success prints nothing. mint token signs a request with the client’s private key and posts it to the token endpoint, the way an application would; the token carries the casework:staff scope Casework’s staff profile requires and the starter:reviewer scope the registry’s reviewer profile requires, and lasts five minutes.

List the work items the Staff client’s team serves, polling until the request arrives, because Casework learns of it through the registry’s event and its own reconciliation rather than at the moment you submitted:

Terminal window
for _ in $(seq 1 30); do
curl --silent --show-error \
--header @tutorial-work/staff.header \
--header 'Registry-Casework-Profile: staff' \
--header 'Registry-Source-Profile: reviewer' \
--output tutorial-work/inbox.json --write-out 'HTTP %{http_code}\n' \
"$casework_url/v1/work-items?view=my_teams&queue=corrections&limit=25"
python3 -c 'import json, sys; items = json.load(open("tutorial-work/inbox.json")).get("items", []); sys.exit(0 if any(item["occurrenceKind"] == "review" for item in items) else 1)' && break
sleep 2
done
HTTP 200

Each poll prints one status line; the first poll is enough when the event has already arrived. If the loop runs for its full minute, the item never came: see Troubleshooting.

Every Casework request carries Registry-Casework-Profile, the profile the caller acts under. A request touching a registry source also carries Registry-Source-Profile, the source profile the caller acts under at the registry: Casework reads the registry as you, under that profile, so what you see is what the registry lets a reviewer see.

Read the inbox:

Terminal window
python3 -m json.tool tutorial-work/inbox.json
{
"items": [
{
"itemId": "5a7c9171-4eee-4fdc-ae0c-6c99b88ef496",
"subject": {
"sourceId": "professional-register",
"kind": "scope-correction",
"id": "3610928e-39c5-4085-94fd-a34109011922"
},
"occurrenceKind": "review",
"stage": "review",
"binding": {
"sourceRevision": "2",
"version": "1",
"integrity": "sha256:3d36e5d90957f52d675844dd16bfd2f0d41156df43a233ba5fe11ee2d9068b6f",
"generation": "sha256:1bd6b87fce7b9f214402b9297862ac581765734df7d53f4b19e7b39a007adc50"
},
"bindingReference": "sha256:183223f7c0702240c70b00b7b951d188e8c05685fb4de07857a2706bb18ef8f2",
"state": "open",
"queueId": "corrections",
"revision": 1,
"firstObservedAt": "2026-09-12T12:42:14.890712Z",
"passiveDueAt": "2026-09-14T12:42:14.890712Z",
"updatedAt": "2026-09-12T12:42:14.890712Z",
"routing": {
"policyDigest": "sha256:d44d1f8398aaceab1cbbaee94e78130403f7cd7e634227dae8efe36c12f2b67c"
},
"actions": [
{
"operation": "claim",
"href": "/v1/work-items/5a7c9171-4eee-4fdc-ae0c-6c99b88ef496/claim",
"ifMatch": "\"1\""
}
]
}
],
"status": "complete",
"servedQueues": [
"corrections"
]
}

Identifiers, digests, and timestamps vary per run. subject is the registry’s request: its id is the request identifier the submit step reported. occurrenceKind is review and stage is review, because the registry’s scope-correction declares one review stage of that name and the request is waiting in it. binding pins the registry revision and the request’s effect digest this item was observed at, so a decision you make names exactly what you looked at. passiveDueAt is the first-review-response target the Casework template declares, 48 hours after the item was observed. actions offers claim with the ifMatch value that version of the item answers to.

A claim makes you the holder, so two people do not review the same request twice. Renew the Staff token before you act, so time spent reading the inbox cannot turn this request into an authentication refusal:

Terminal window
mint token --url "$token_endpoint" \
--client-id "$(cat "$credentials/staff/client-id")" \
--key "$credentials/staff/assertion-key.jwk" \
| sed 's/^/Authorization: Bearer /' > tutorial-work/staff.header

Take the identifier and the offered ifMatch value of the review item from the inbox rather than typing them:

Terminal window
item_id=$(python3 -c 'import json; items = json.load(open("tutorial-work/inbox.json"))["items"]; print(next(item["itemId"] for item in items if item["occurrenceKind"] == "review"))')
claim_match=$(python3 -c 'import json; items = json.load(open("tutorial-work/inbox.json"))["items"]; actions = next(item["actions"] for item in items if item["occurrenceKind"] == "review"); print(next(action["ifMatch"] for action in actions if action["operation"] == "claim"))')

Claim it:

Terminal window
curl --silent --show-error \
--header @tutorial-work/staff.header \
--header 'Registry-Casework-Profile: staff' \
--header 'Registry-Source-Profile: reviewer' \
--header "If-Match: $claim_match" \
--header 'Idempotency-Key: tutorial-claim-review' \
--header 'Content-Type: application/json' \
--data '{}' \
--output tutorial-work/claimed.json --write-out 'HTTP %{http_code}\n' \
"$casework_url/v1/work-items/$item_id/claim"
HTTP 200

A write carries Idempotency-Key so a retry after a lost response returns the first result instead of acting twice. Read the part of the response that changed:

Terminal window
python3 -c 'import json; item = json.load(open("tutorial-work/claimed.json"))["item"]; print(json.dumps({key: item[key] for key in ("state", "revision", "holder", "actions")}, indent=2))'
{
"state": "claimed",
"revision": 2,
"holder": {
"issuer": "http://127.0.0.1:8091",
"subject": "professional-review-staff"
},
"actions": [
{
"operation": "release",
"href": "/v1/work-items/5a7c9171-4eee-4fdc-ae0c-6c99b88ef496/release",
"ifMatch": "\"2\""
},
{
"operation": "delegate",
"href": "/v1/work-items/5a7c9171-4eee-4fdc-ae0c-6c99b88ef496/delegate",
"ifMatch": "\"2\""
},
{
"operation": "approve",
"href": "/v1/work-items/5a7c9171-4eee-4fdc-ae0c-6c99b88ef496/decisions",
"ifMatch": "\"2\""
},
{
"operation": "reject",
"href": "/v1/work-items/5a7c9171-4eee-4fdc-ae0c-6c99b88ef496/decisions",
"ifMatch": "\"2\""
},
{
"operation": "request_correction",
"href": "/v1/work-items/5a7c9171-4eee-4fdc-ae0c-6c99b88ef496/decisions",
"ifMatch": "\"2\""
}
]
}

The holder is the issuer and subject Mint put in the token, the registry’s Mint and the registry_principal claim the staff client declares, and the revision moved to 2. actions now offers the three decisions the registry’s review stage admits, beside release and delegate, each carrying the new ifMatch.

Build the approval from the claim response, because a decision names the binding you had in front of you and the registry profile you decide under:

Terminal window
python3 -c 'import json; item = json.load(open("tutorial-work/claimed.json"))["item"]; json.dump({"displayedBinding": item["binding"], "sourceProfileId": "reviewer", "operation": "approve"}, open("tutorial-work/approve.json", "w"), indent=2)'
approve_match=$(python3 -c 'import json; actions = json.load(open("tutorial-work/claimed.json"))["item"]["actions"]; print(next(action["ifMatch"] for action in actions if action["operation"] == "approve"))')

Approve:

Terminal window
curl --silent --show-error \
--header @tutorial-work/staff.header \
--header 'Registry-Casework-Profile: staff' \
--header 'Registry-Source-Profile: reviewer' \
--header "If-Match: $approve_match" \
--header 'Idempotency-Key: tutorial-approve' \
--header 'Content-Type: application/json' \
--data @tutorial-work/approve.json \
--output tutorial-work/approved.json --write-out 'HTTP %{http_code}\n' \
"$casework_url/v1/work-items/$item_id/decisions"
HTTP 200

A decision without If-Match is refused with 428 and code precondition.required, because Casework will not forward a decision made on an unknown version of the item. Read the attempt the decision created:

Terminal window
python3 -c 'import json; attempt = json.load(open("tutorial-work/approved.json"))["attempt"]; print(json.dumps({key: attempt[key] for key in ("state", "operation", "receipt")}, indent=2))'
{
"state": "completed",
"operation": "approve",
"receipt": {
"sourceRevision": "3",
"resultingState": "approved",
"binding": {
"sourceRevision": "3",
"version": "1",
"integrity": "sha256:3d36e5d90957f52d675844dd16bfd2f0d41156df43a233ba5fe11ee2d9068b6f",
"generation": "sha256:1bd6b87fce7b9f214402b9297862ac581765734df7d53f4b19e7b39a007adc50"
},
"actorReference": "hmac-sha256:016f12c016e49957aa570b4b6de8ca157b189803a9328fad15301397cd0d82f6",
"metadata": {
"nativeReceipt": "{\"actorReference\":\"hmac-sha256:016f12c016e49957aa570b4b6de8ca157b189803a9328fad15301397cd0d82f6\",\"id\":\"3610928e-39c5-4085-94fd-a34109011922\",\"request\":{\"application\":null,\"bregState\":\"approved\",\"effectDigest\":\"sha256:3d36e5d90957f52d675844dd16bfd2f0d41156df43a233ba5fe11ee2d9068b6f\",\"proposal\":{\"applicationDisposition\":\"queue\",\"reviewMode\":\"staged\"},\"proposalVersion\":1},\"revision\":3,\"snapshot\":\"breg1_945a9384-6249-42d8-92ae-5017b55c0d63\"}",
"traceId": "01a095a46ed9087e47b3b8c15dd23dee"
}
}
}

Casework did not approve anything itself: the attempt carried your decision to the registry under the reviewer profile, and the receipt is the registry’s answer. "resultingState": "approved" and "bregState":"approved" inside nativeReceipt are the registry saying the request passed its review stage at its revision 3. The item in the same response reads "state": "synchronizing" with no actions, because the review occurrence is finished and Casework is catching up with what the registry did next. "applicationDisposition":"queue" in the receipt is why there is a next step: this change request declares that an approved change is applied by a second explicit decision, not on approval.

The registry queued the approved change for application, and Casework presents that as a second work item on the same request. Renew the token, then poll the inbox for an item whose occurrenceKind is application:

Terminal window
mint token --url "$token_endpoint" \
--client-id "$(cat "$credentials/staff/client-id")" \
--key "$credentials/staff/assertion-key.jwk" \
| sed 's/^/Authorization: Bearer /' > tutorial-work/staff.header
for _ in $(seq 1 30); do
curl --silent --show-error \
--header @tutorial-work/staff.header \
--header 'Registry-Casework-Profile: staff' \
--header 'Registry-Source-Profile: reviewer' \
--output tutorial-work/inbox.json --write-out 'HTTP %{http_code}\n' \
"$casework_url/v1/work-items?view=my_teams&queue=corrections&limit=25"
python3 -c 'import json, sys; items = json.load(open("tutorial-work/inbox.json")).get("items", []); sys.exit(0 if any(item["occurrenceKind"] == "application" for item in items) else 1)' && break
sleep 2
done
HTTP 200

A poll in the seconds after the approval may print HTTP 409, because the review item is still synchronizing; the loop reads again. Read the new item’s identity:

Terminal window
python3 -c 'import json; items = json.load(open("tutorial-work/inbox.json"))["items"]; item = next(item for item in items if item["occurrenceKind"] == "application"); print(json.dumps({key: item[key] for key in ("occurrenceKind", "binding", "state", "revision", "actions")}, indent=2))'
{
"occurrenceKind": "application",
"binding": {
"sourceRevision": "3",
"version": "1",
"integrity": "sha256:3d36e5d90957f52d675844dd16bfd2f0d41156df43a233ba5fe11ee2d9068b6f",
"generation": "sha256:1bd6b87fce7b9f214402b9297862ac581765734df7d53f4b19e7b39a007adc50"
},
"state": "open",
"revision": 1,
"actions": [
{
"operation": "claim",
"href": "/v1/work-items/7d1358c3-0859-415b-a7ad-adbcc4f56191/claim",
"ifMatch": "\"1\""
}
]
}

It is a different item, with its own identifier and revision 1, bound to the registry revision the approval produced. Claim it the same way:

Terminal window
item_id=$(python3 -c 'import json; items = json.load(open("tutorial-work/inbox.json"))["items"]; print(next(item["itemId"] for item in items if item["occurrenceKind"] == "application"))')
claim_match=$(python3 -c 'import json; items = json.load(open("tutorial-work/inbox.json"))["items"]; actions = next(item["actions"] for item in items if item["occurrenceKind"] == "application"); print(next(action["ifMatch"] for action in actions if action["operation"] == "claim"))')
curl --silent --show-error \
--header @tutorial-work/staff.header \
--header 'Registry-Casework-Profile: staff' \
--header 'Registry-Source-Profile: reviewer' \
--header "If-Match: $claim_match" \
--header 'Idempotency-Key: tutorial-claim-application' \
--header 'Content-Type: application/json' \
--data '{}' \
--output tutorial-work/claimed.json --write-out 'HTTP %{http_code}\n' \
"$casework_url/v1/work-items/$item_id/claim"
HTTP 200

The claim response offers apply beside release and delegate. Build the application from it and send it:

Terminal window
python3 -c 'import json; item = json.load(open("tutorial-work/claimed.json"))["item"]; json.dump({"displayedBinding": item["binding"], "sourceProfileId": "reviewer", "operation": "apply"}, open("tutorial-work/apply.json", "w"), indent=2)'
apply_match=$(python3 -c 'import json; actions = json.load(open("tutorial-work/claimed.json"))["item"]["actions"]; print(next(action["ifMatch"] for action in actions if action["operation"] == "apply"))')
curl --silent --show-error \
--header @tutorial-work/staff.header \
--header 'Registry-Casework-Profile: staff' \
--header 'Registry-Source-Profile: reviewer' \
--header "If-Match: $apply_match" \
--header 'Idempotency-Key: tutorial-apply' \
--header 'Content-Type: application/json' \
--data @tutorial-work/apply.json \
--output tutorial-work/applied.json --write-out 'HTTP %{http_code}\n' \
"$casework_url/v1/work-items/$item_id/decisions"
HTTP 200

Read the receipt:

Terminal window
python3 -c 'import json; receipt = json.load(open("tutorial-work/applied.json"))["attempt"]["receipt"]; print(json.dumps({key: receipt[key] for key in ("sourceRevision", "resultingState")}, indent=2)); print(json.loads(receipt["metadata"]["nativeReceipt"])["request"]["application"])'
{
"sourceRevision": "4",
"resultingState": "applied"
}
{'applicationId': '646bb184-848d-4222-a20e-8d7e1f8fec31', 'appliedAt': '2026-09-12T12:43:19.267466Z', 'effectDigest': 'sha256:3d36e5d90957f52d675844dd16bfd2f0d41156df43a233ba5fe11ee2d9068b6f', 'proposalVersion': 1}

"resultingState": "applied" is the registry reporting that it wrote the correction into the licence; applicationId and appliedAt are its record of that write. The effectDigest is the one the inbox item’s binding.integrity carried from the start: what was applied is what you were shown.

Casework’s receipts are the registry’s answers, and the registry holds the record. Ask it, through the retained example attempt, as the reviewer client:

Terminal window
bregctl examples run reviewed-change tutorial-work/registry --step inspect

The inspect result carries the request as the registry holds it. Among its lines, with identifiers and timestamps that vary per run:

"bregState": "applied",
"decisions": [
{
"decidedAt": "2026-09-12T12:42:56.622404Z",
"kind": "approve",
"reasonPresent": false,
"stageId": "review"
}
],
"resultLinks": [
{
"targetEntityId": "professional-license",
"targetRecordId": "f372d369-4823-4c93-beea-7975135d4235",
"targetRevision": 2
}
]

The request is applied, its one decision was an approve in stage review, and resultLinks names the licence revision the application wrote: revision 2 of the record first-record created. The report’s closing next command offers --step approve, which no longer applies to a request the registry has applied; the rest of the report is omitted here.

Read the licence’s history:

Terminal window
bregctl examples run reviewed-change tutorial-work/registry --step history

The history result lists two revisions of the licence, newest first. Revision 2 is a patch carrying "example-assessment" and "example-practical-services" under licensedActivities and an empty authorizationConditions; revision 1 is the create with the original single activity and condition. Each carries an actorReference, the registry’s opaque handle for who acted, and a requestReference: the correction is in the registry’s own history as an ordinary revision made through review, and nothing about Casework is in it.

Stop Casework first, then the registry, because Casework reconciles against the registry while it runs:

Terminal window
caseworkctl dev stop tutorial-work/casework
bregctl dev stop tutorial-work/registry

Each report’s status reads stopped. The containers and their volumes stay, with the licence, the request, the work items, and the attempts, and the two start commands from Start the registry and Start Casework, in that order, bring the same runtimes back.

Terminal window
caseworkctl dev stop tutorial-work/casework --remove
bregctl dev stop tutorial-work/registry --remove

Either way, keep tutorial-work/: the two projects inside it are what the configuration guides edit, and tutorial-work/casework/sources/ holds the connection source add wrote.

You generated a registry project and a Casework project that fit each other, connected them with one command, ran both as services sharing one token issuer, submitted a change request to the registry, approved and applied it from the Casework inbox as Staff, and read the applied revision from the registry’s own history. The registry decided what you were allowed to see and do; Casework coordinated who held the work and carried your decisions.

A local token lasts five minutes. Run the mint token command for the staff client again; it replaces the header file. If it fails, the registry session is stopped: start it again with the command from Start the registry, then start Casework, then renew.

SymptomNext move
bregctl, caseworkctl, or mint is not foundAdd the installer’s directory, ~/.local/bin unless you changed it, to PATH in this terminal.
caseworkctl dev refuses over a reported versionThe bregctl, casework, or mint it resolved comes from another release than caseworkctl. The refusal names both versions. Install both products from the same release, or put the matching build first on PATH.
caseworkctl dev refuses a project that declares a sourceIt was started without --source-project. A source binding needs a running registry session; pass --source-project tutorial-work/registry after bregctl dev reports ready.
bregctl dev refuses a portSomething else listens on 8090, 8091, or 55432. Pass --breg-port, --mint-port, or --database-port on the first start; later starts keep the ports you chose. With --mint-port, change the token_endpoint assignment in Start Casework.
caseworkctl dev refuses a portSomething else listens on 8092 or 55433. Set CASEWORKCTL_DEV_CASEWORK_PORT or CASEWORKCTL_DEV_DATABASE_PORT, or pass --casework-port or --database-port, on the first start; the casework_url assignment picks the first up. The session borrows the registry’s Mint, so its own Mint port is unused.
Either dev command fails before it reports readyRead the refusal: it names the check that failed. Otherwise check that Docker is running. bregctl dev events tutorial-work/registry and caseworkctl dev events tutorial-work/casework show the retained journals; do not share the credential files beside them.
The inbox loop runs its full minute with no itemCheck that the submit step reported "operation": "submit_request", and that the registry session is still running: bregctl dev tutorial-work/registry reuses a running session and reports status ready. Then read the Casework journal with caseworkctl dev events tutorial-work/casework for the source reconciliation it logged.
An inbox read returns 400 with code source-profile.requiredA source-backed inbox is read under a registry profile. Send Registry-Source-Profile: reviewer.
A request returns 401 with code authentication.refusedThe token expired, or the header file is stale. Mint it again, as Renew an expired token describes.
A request returns 400 with code request.invalidAnother required or structured input is missing or malformed. Every request carries Registry-Casework-Profile, and every write also carries Idempotency-Key; source-backed reads name a source profile as described above.
A decision returns 428 with code precondition.requiredThe If-Match header is missing. Take the ifMatch value from the actions entry for the operation you want in the claim response.
A claim or decision returns 412 with code precondition.failedThe item moved. Re-read it, take the ifMatch value from the actions entry for the operation you want, and send it again.
An inbox read or a claim returns 409The item is synchronizing with the registry, or another caller holds it. Read the inbox again a few seconds later; its actions array shows what remains open to you.
A decision returns 422 with code request.source-rejectedThe registry refused the promoted action body. Fix the decision request before trying again; re-reading the inbox does not fix this refusal.
A decision returns 502 with code source.bad-gatewayThe registry answered outside its registered contract. Check that the registry session is ready; if it is, inspect the Casework journal for the invalid response class.
You opened a fresh terminalRun the casework_url, token_endpoint, and credentials assignments from Start Casework again. The header file and tutorial-work directory are still there.