Skip to content
Registry StackDocsDevelopment (unreleased)

Run a citizen chat assistant locally

For the operator

View as Markdown

A chat host, the application a citizen chats with, can help that citizen correct their own record in a Base Registry Engine (BReg) without ever holding the power to change it. Two supporting services make that possible: breg-mcp, the Model Context Protocol (MCP) gateway the chat host calls, and breg-review, the page where the citizen reads and submits what the assistant prepared. In this tutorial you run both against a local registry and watch one address correction travel from a chat tool call to an applied change.

Outcome
One citizen address correction drafted over MCP, submitted on the review page, approved, and applied.
Time
About 5 to 15 minutes, most of it the first Rust build
Level
Local evaluation with synthetic data
Prerequisites
macOS on Apple Silicon or LinuxGitRust 1.95.0 through rustupRunning DockerBashAbout 10 GB of free disk for the build

The run is maintained as a script in the repository. It is verified on macOS on Apple Silicon; no continuous integration job runs it.

Clone the repository and confirm the script is present:

Terminal window
git clone --depth 1 --quiet https://github.com/registrystack/registry-stack.git
cd registry-stack
test -x products/breg/acceptance/citizen-address-correction/run-local.sh
printf 'checkout ready\n'

The last command reports:

checkout ready

The repository pins its Rust toolchain in rust-toolchain.toml, so rustup installs Rust 1.95.0 the first time cargo runs in the checkout. If another cargo comes first on your PATH, such as one from Homebrew, put ~/.cargo/bin ahead of it for the commands in this tutorial.

The script starts five parties on loopback addresses. Three of them are real, one is a test server, and one is a stub:

PartyWhat runs
Base Registry EngineThe synthetic citizen address correction project on PostgreSQL 17
Authorization serverA test server that issues the chat host’s token, performs the gateway’s token exchange, and signs the citizen in to the review page
GatewayThe breg-mcp binary
Review pageThe breg-review binary
Review authorityA stub standing in for Registry Casework, which serves one approval

The driver program plays the remaining roles: the chat host calling MCP tools, the citizen’s browser on the review page, and the registry staff member who applies the approved change. All names, identifiers, and addresses in the project are invented.

From the repository root, run the script:

Terminal window
products/breg/acceptance/citizen-address-correction/run-local.sh

The script starts a disposable postgres:17 container on a loopback port, builds breg-mcp, breg-review, and the driver, and then runs the journey. The first build compiles the workspace dependencies and takes most of the run. Cargo’s progress lines appear between the build: line and the registry: line; they are left out here. Ports and identifiers differ on every run:

postgres: a disposable postgres:17 container is ready on 127.0.0.1:<port>
build: breg-mcp, breg-review, and the local run driver
registry: the acceptance project is served on PostgreSQL at http://127.0.0.1:<port>
authorization server: the test server issues at http://127.0.0.1:<port>
breg-mcp: started, logging to <work-directory>/breg-mcp.log
breg-review: started, logging to <work-directory>/breg-review.log
breg-mcp: ready at http://127.0.0.1:<gateway-port>/ready
breg-review: ready at http://127.0.0.1:<page-port>/ready
registry: seeded a person, their address, and their self-service link
gateway: the chat host connected over MCP at http://127.0.0.1:<gateway-port>/mcp
tool describe_service: answered
tool get_my_details: the current address is 4 Mill Street
tool start_application: application <application-id> is prepared
tool update_application: the locality is now Old Town
tool prepare_review: the review link is http://127.0.0.1:<page-port>/requests/<application-id>
review page: the citizen signed in through the authorization server
review page: GET http://127.0.0.1:<page-port>/requests/<application-id> 200 OK, showing 4 Mill Street beside Old Town
review page: POST http://127.0.0.1:<page-port>/requests/<application-id>/submit 200 OK
tool get_application_status: submitted
registry: the stored address is still 4 Mill Street
review authority (stub standing in for Casework): approved
tool get_application_status: submitted (the agent profile reads no review outcome)
registry: the applier applied the approved proposal
tool get_application_status: applied
tool get_my_details: the current address is 2 Quay, Old Town
registry: the stored address is 2 Quay, Old Town, PS-200
local run: the citizen journey reached applied

The script exits with status 0 after the last line. It prints no credential.

The chat host never held a registry credential. It sent its own access token to the gateway, and for every tool call the gateway exchanged that token at the authorization server for a delegated registry token naming the citizen as subject and the gateway as actor. The chat host’s token never reached the registry.

The chat host never named the record it wanted to change. start_application took only the proposed address fields; the gateway found the citizen’s own linked address through the registry and wrote the application’s target and owner itself.

The chat host could draft, but not decide. The gateway’s registry access profile is a standing agent, and the registry compiler refuses such a profile any submit, direct write, or immediate action. prepare_review returned a link and nothing more; the submission happened on the review page, under the citizen’s own sign-in, after the page read the current address beside the proposed one.

The review authority decided, and registry staff applied. Between the approval and the apply, get_application_status still answered submitted: the agent profile cannot read the review outcome, so the chat host learns the result only once the change is applied.

The script removes what it created when it exits. On success it removes the PostgreSQL container and the temporary work directory that held both runtime configurations, their generated keys, and the two logs. On failure it still removes the container, keeps the work directory for diagnosis, and prints its path on standard error:

the run failed; the binaries' logs and configuration stay in <work-directory>

Remove that directory when you are done with it. The build output stays in target/; run cargo clean to reclaim the disk space.

To run the journey against a PostgreSQL 17 server you already have, set BREG_TEST_DATABASE_URL before you run the script. The script then creates a fresh database on that server and drops it at the end, and starts no container.