bash or zsh.
Scratch files go in try/ (git-ignored); delete it any time. Use separate terminals where it says so.
This guide covers what is built. Things listed under “Not built” in the roadmap have nothing to test yet.
0. Prerequisites and build
You need Node 22.13 or newer (node -v), Python 3.9+ for the SDK, curl, and openssl. Docker is optional (sections 8 and 12).
node -v shows an older version (nvm: nvm use 22), nothing else will work. The one-line helpers used below:
1. The automated suites
Test Files … passed and OK. PostgreSQL tests are skipped unless you point them at a throwaway database
(section 8): REVERSI_TEST_PG=postgres://… npm test.
2. The built-in demo and the viewer
delete_customer is blocked (“permission
customers:admin is not in the run’s scope”), the plan lists the email as unrecoverable, rollback compensates steps 2 and 1,
and the last line says chain intact.
Then open viewer/index.html in a browser and choose examples/order-agent/out/ledger.jsonl. You should see the timeline and a green
“chain verified”. Open the file in an editor, change one letter inside a record, reload it in the viewer: it must flag the first bad record.
3. Manifests
4. Policy as code
expect: require_approval to expect: allow and run policy test again. You should see FAIL … expected allow, got require_approval and exit code 1.
5. The server, end to end
5.1 Create keys and start it
reversi-server listening on http://127.0.0.1:7421 (7 tools, files store).
Terminal B (all the rest of section 5):
5.2 An agent works; policy, approvals, content checks, scope
"amount":900 after a fresh approval for 500 and see it blocked again.
Content check and scope:
5.3 Reconcile, roll back, verify
blocked, approved (by alice, from the approval decision), approved (the engine consuming it),
intended, succeeded, uncertain, failed (resolved by dana: …), compensating/compensated (actor cli).
5.4 Evidence
5.5 Metrics, kill switch, logs
method, path, status, ms, principal) and no bodies or keys.
5.6 The console (browser)
Open http://127.0.0.1:7421/console/login, paste$APPR (print it with echo $APPR), sign in. Then:
- Runs:
try-1is listed. Use the filter “needs attention”. - Run detail: steps with statuses, the rollback plan, the event trail, the report link and signed-bundle link.
- Approvals: ask for another big charge in a new run from the agent, then approve it with the form (add a note). Try Deny on another.
- Tools: manifests with effect, permission and compensation; “called without a manifest”.
- Sign out, sign in with
$AUD(the auditor): the approve buttons are gone, and a forged POST is refused. - Sign in with
$ROOTand use Evidence → Block all new calls, then resume. The red banner shows while halted.
HttpOnly; SameSite=Strict; a form POST without the CSRF field returns 403.
Stop the server (Ctrl-C in terminal A) when done.
6. The MCP gateway
The gateway fronts an MCP server; here the upstream is the built-in filesystem connector. Create the config and manifests:--run gw-run-1 makes every one-shot invocation continue the same run, as a real agent’s session would):
reversi.halt (admin token: $ROOT) then an agent call; reversi.export_evidence; and deny_action instead of approve.
Observe mode: stop the gateway, change mode: observe in try/gateway.yaml, start it again, and call nope: it is no longer
blocked, so the call goes on to the upstream (which has no such tool, so it fails), and the ledger shows would_block then intended then failed:
rv export -d try/gw-data | grep -E 'would_block|failed'. With a tool the upstream does have but that has no manifest, the call runs.
stdio mode (no listen:, no auth:: one unauthenticated session, as an MCP client like Claude Code would launch it):
7. Connectors
The filesystem connector is covered above. For the others you need sandbox credentials; use throwaway accounts, never production. List and compensate by hand:github:* permissions in max_permissions; see each tool’s permission in the manifests.) Slack:
slack.post_message {"channel":"C…","text":"hi"} then rollback deletes the message (SLACK_BOT_TOKEN). Stripe:
stripe.create_customer {"email":"t@example.com"} then rollback deletes it (STRIPE_SECRET_KEY = a sk_test_… key);
stripe.create_refund needs approval and cannot be undone: check that the plan says so.
The connectors are tested against fakes of these APIs; this is the first time they meet the real ones.
8. SQLite and PostgreSQL
SQLite (a single file, Node 22.13+): restart the server from 5.1 withREVERSI_STORE=sqlite REVERSI_DATA=try/sqldata, repeat a few calls,
then:
rejected: ledger is append-only.
PostgreSQL with Docker:
localhost:7423 as in 5.2, then:
rv verify --store postgres:
the chain stays intact.
9. Authentication
Roles. With the keys from 5.1, repeat these and check the status codes: an agent key on/v1/approvals (403), an approver key on
/v1/runs/try-1/plan (200) but on /v1/runs/try-1/steps/0/compensation (403), an operator key on /v1/halt (403), the admin key on it (200).
Key rotation. Add "expires_at": "2000-01-01T00:00:00Z" to one key in try/keys.json and wait two seconds (or kill -HUP the server): that key now
returns 401, others keep working. Set "disabled": true for the same effect. Break the JSON on purpose: the server logs
key reload failed, keeping the old keys and nothing changes.
OIDC with the bundled mock provider. Terminal D: node examples/oidc/mock-idp.mjs. Then:
sub (bot-1). You can also paste a token into the console login.
10. Approval notifications
Terminal D:node examples/webhook-sink.mjs. Start any server or gateway with
REVERSI_APPROVAL_WEBHOOK=http://127.0.0.1:9100/hook REVERSI_CONSOLE_URL=http://127.0.0.1:7421, trigger an approval (5.2 or 6) and watch the sink print a
Slack-style text plus the structured request. It never contains argument values. Stop the sink and trigger another: the call is still blocked
normally and the server logs approval webhook failed.
11. The Python SDK
created …, a charge, and blocked: … for the out-of-scope email. See the SDK page for approvals and rollback calls
(Blocked.approval_id, run.rollback(...)).
12. Docker
docker ps shows healthy after ~10 s.) The Compose stack on PostgreSQL:
follow the comments at the top of deploy/docker-compose.yml.
13. Docs and CI
.github/workflows/ci.yml) runs the test suites with a PostgreSQL
service, the fixture check, npm audit, an SBOM and a secret scan, and builds the Docker image.

