Skip to main content

vouchwell (Python)

Tamper-evident receipts for what your AI agents do, from Python. This is the Python SDK for Vouchwell, and it writes exactly the format the vouchwell CLI reads:

  • a log written here passes vw verify;
  • a bundle exported here passes vw check;
  • logs and bundles written by the JavaScript side open and verify here.

CI runs both directions against the real CLI on every commit.

pip install "vouchwell @ git+https://github.com/vouchwell/vouchwell#subdirectory=sdk/python"

That installs straight from this repository. The package is not on PyPI yet; once it is, pip install vouchwell will do the same.

Python 3.9+. The one dependency is cryptography, for Ed25519.

Record an agent's tool calls

from vouchwell import ProofLog, Recorder

log = ProofLog.open_or_create(".vouchwell")
rec = Recorder(log, agent="support-bot", principal="ops@acme.com", namespace="stripe")

@rec.tool("refund", metrics=lambda order_id, amount_cents: {"amount_usd": amount_cents / 100})
def refund(order_id: str, amount_cents: int):
    return stripe.Refund.create(payment_intent=order_id, amount=amount_cents)

refund("pi_123", 4200)

Each call leaves two signed receipts:

  1. An intent receipt, before the call runs. A crash mid-call still leaves evidence it was attempted.
  2. An outcome receipt, when it returns or raises, linked to the intent.

Async functions work the same. So does a context manager:

with rec.call("crm.update", params={"id": 42}) as call:
    call.result = crm.update(42)

Arguments and results are committed to, not stored. A receipt holds a salted hash plus a preview with secrets and personal data masked (API keys, card numbers, emails and so on). The salts stay in the log's private salts.jsonl, so you can later prove what a value was: log.reveal(seq, "params", value). Or you can destroy the salts, and the value becomes unprovable forever while the receipt still verifies: log.shred(predicate).

Check calls before they run

def decide(target, params):
    if target.endswith(".delete"):
        return {"outcome": "deny", "rules": ["no-deletes"], "reason": "agents may not delete"}
    return {"outcome": "allow", "policy": "v3"}

rec = Recorder(log, agent="support-bot", principal="ops@acme.com", decide=decide)

A refused call never runs. It gets one receipt, and PolicyDenied is raised. An escalate outcome is a denial here, recorded as policy:no-approver, because nobody in the process can approve it.

The full policy language (budgets, rate limits, escalation to a person, Slack approvals, monitor mode) lives in the vw proxy MCP proxy and in the JavaScript Recorder in @vouchwell/core. For an agent that uses MCP, wrap its servers with the proxy instead. A Python port of the policy engine is planned.

Use it with LangChain or the OpenAI Agents SDK

Wrap the agent's tools once; use the wrapped ones in their place. Each call is checked with decide, recorded as intent and outcome, and a refused call never runs: the model is told why in the tool's reply, as the MCP proxy does, instead of the agent crashing.

from vouchwell.integrations.langchain import record_tools        # pip install "vouchwell[langchain]"
agent = create_react_agent(model, record_tools(rec, [search, refund]))

from vouchwell.integrations.openai_agents import record_tools    # pip install "vouchwell[openai-agents]"
agent = Agent(name="Support", tools=record_tools(rec, [lookup_order, refund]))

Hosted OpenAI tools (web search and the like) run on OpenAI's side, so they are passed through unrecorded. Both adapters need Python 3.10+, and are tested in CI against the real packages.

Find calls that never finished

Each call's intent is on disk before the tool runs. If the process is killed while a call is out, the intent has no outcome, and that is worth knowing:

from vouchwell import find_unfinished

found = find_unfinished(log.entries)
for u in found["unfinished"]:
    print(u["seq"], u["ts"], u["target"], u["principal"])

inFlight holds calls from the last five minutes that may still be running, and abandoned the ones a recorder gave up on while shutting down. vw verify reports the same list, and vw verify --fail-on-unfinished exits 3 when there is one.

Hand over evidence

import json
from vouchwell import verify_bundle

log.checkpoint()                                   # sign the current state
open("evidence.json", "w").write(json.dumps(log.bundle()))

# Anyone can verify it, here or with `vw check evidence.json`:
verify_bundle(json.load(open("evidence.json")))
# -> {"ok": True, "issues": [], "checked": 12}

To require an independent witness, pin its key. Get the key from somewhere the bundle can't influence:

verify_bundle(bundle, min_witnesses=1, trusted_witnesses={"pw1…": "<public key>"})

vw report (from the npm CLI) turns a log written here into an evidence pack for auditors.

Ship to a hub

from vouchwell import push

push(log, "https://hub.acme.com", token=os.environ["VOUCHWELL_TOKEN"], name="support-bot")

It sends whatever the hub doesn't have yet, and is safe to call again. Plain http:// is refused unless the hub is on this machine, so the API key never crosses a network unencrypted.

What's here

Module
canonical RFC 8785 canonical JSON: UTF-16 key order, ECMAScript number formatting
merkle RFC 6962 trees, inclusion and consistency proofs
unfinished find_unfinished: calls sent and never answered
integrations langchain.record_tools, openai_agents.record_tools
keys Ed25519 identities, canonical base64url
receipt Build, seal, sign and verify receipts
checkpoint Signed tree heads, witness countersignatures
log ProofLog, the on-disk log the CLI shares, and verify_bundle
record Recorder, for Python agents
remote push to a hub

Tested against the RFC 6962 Certificate Transparency reference tree and the RFC 8032 Ed25519 vectors, plus byte-level canonicalization parity with the JavaScript implementation over hundreds of awkward values.

License: Apache-2.0.

Metadata

Release files for vouchwell 0.6.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for vouchwell 0.6.0
File Size Uploaded
vouchwell-0.6.0.tar.gz 43.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for vouchwell 0.6.0
File Interpreter ABI Platform
vouchwell-0.6.0-py3-none-any.whl Python 3 none any Details

Total release size: 78.9 kB

Release files / vouchwell-0.6.0.tar.gz

Download URL vouchwell-0.6.0.tar.gz
Size 43.7 kB
Tags Source
SHA-256 checksum
How to use checksums
bd7cfde00e16fee58aba5ac5a0dda09294959979f73b481ad084a60bfb959aec
BLAKE2b-256 checksum
How to use checksums
eda5d277863134145c6695156d81d3057c2a8a71c8214a9f63c1d8abdcbfa1fb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 12, 2026.

Transparency log

Release files / vouchwell-0.6.0-py3-none-any.whl

Download URL vouchwell-0.6.0-py3-none-any.whl
Size 35.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
7b001a3ac0cb9fce01095ef693e6a65e5615a543136ff4a8087aa2fd64043fc0
BLAKE2b-256 checksum
How to use checksums
ac20a5f9b975a4641ac40f36d4a26e5f08e1e16e163e12aada2cb513949b2e59
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 12, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.6.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page