Skip to main content
reversi-sdk lets a Python agent journal its tool calls through a Reversi server, and lets any Python program verify ledgers and validate manifests offline. It needs Python 3.9 or later and has no runtime dependencies.
The package is not published to PyPI by this repository. Install it from the checkout, or publish it yourself. The test suite (npm run test:py, 55 tests) runs against a live Reversi server as well as fakes.

What it covers

It talks to the run, approval, halt and evidence endpoints. The newer governance endpoints (search, impact preview, simulate, replay, registry, holds, retention, keys and the rest of the admin API) are not wrapped yet: call them over HTTP, or use the CLI or TypeScript SDK.

Journal a call

call journals the intent before running your function and the outcome after. If the process dies in between, the server shows the step as unresolved rather than pretending it never happened. Raise EffectUncertain from your function when you cannot tell whether the effect happened (a timeout, a dropped connection): the step is recorded as uncertain and an operator settles it later. start_run(permissions, run_id=None, mode="enforce", reason=None) takes the run’s permission scope. mode is enforce (violations block), observe (violations are journaled and the call runs; the key needs allow_observe) or advise. Pass run_id to start a run with a name you choose, and client.run(run_id) to pick up an existing one. Lower-level methods exist when you do not want call to run the function for you: run.begin(tool, reason, args) returns the decision (allow or block) and a step number, and run.complete(step, status, result=…, error=…) records the outcome. run.state() returns the steps and their statuses. Decorate tools so every call goes through the run:

Approvals

When a call needs approval, Blocked.approval_id holds the request id. The approval is bound to the exact arguments and used once, so ask an approver to decide it, then retry the same call:
Routing to groups, quorum, delegation and break-glass are configured on the server; see approvals. The Python client decides as the identity of its key, so a key with the right groups is what satisfies a group-routed request.

Operator and admin methods

Each needs a key with the matching role. The rollback report lists compensated, failed, deferred, awaiting_approval and not_undoable steps. It does not run a manifest’s declared read-back verification: that happens on the server side (rollback). Errors: ReversiError (with status and message) for any refused or failed request, Blocked for a policy refusal of a call, EffectUncertain raised by you.

Offline tools (no server)

python -m reversi_sdk verify ledger.jsonl checks a ledger’s hash chain from a shell (exit 0 ok, 1 tampered, 64 usage). validate_manifest accepts the full manifest format (versions, status, risk, residue, verification, timeouts, retry, idempotency, approval rules) and returns the normalized manifest or a list of every error. It is tested against the TypeScript validator on shared fixtures, so the two agree. Two limits: manifests are read from JSON, so convert YAML first, and Ed25519 evidence bundles are not verified in Python (the standard library has no Ed25519). Verify bundles with reversi verify-bundle.
Canonical JSON for payload hashing rejects non-whole floats outside 1e-4 to 1e16, where Python and JavaScript format numbers differently. Pass such values as strings.

Webhook signatures

Pass the raw body before any JSON parsing. The header is sha256=<hex>, an HMAC-SHA256 of the body; the comparison is constant time and a missing or malformed header returns False. See notifications for the server side.

Pydantic models

reversi_sdk.pydantic_models has pydantic v2 ToolManifest, Compensation and Verification models for IDE types and JSON Schema generation. They run the standard library validator first, so the two cannot disagree.

Agent frameworks and Claude Code

Adapters for LangGraph, CrewAI, the OpenAI Agents SDK and FastMCP, and the reversi-claude-hook command for Claude Code hooks, are in integrations. They are tested against fakes of those frameworks’ interfaces, not against the frameworks themselves.