Skip to main content

scopeblind-pydantic-ai

PyPI version License: MIT Python 3.10+

Ed25519 signed receipts and (optional) Cedar policy enforcement for Pydantic AI agents. Every tool call produces a cryptographic receipt that anyone can verify offline with npx @veritasacta/verify.

Why

Pydantic AI agents call tools. Most teams log those calls to stdout, a SaaS dashboard, or nothing at all. When an auditor or regulator asks "prove what the agent did on April 12", logs aren't proof — they're mutable text files held by the operator.

This package wraps every tool call as a Pydantic AI Capability. The capability:

  1. Evaluates a policy (optional — Cedar, custom function, or allow-all)
  2. Signs the decision with Ed25519 (RFC 8032)
  3. Canonicalizes with JCS (RFC 8785) so the signature verifies deterministically across implementations
  4. Hash-chains each receipt to the previous one so insertion/deletion is detectable
  5. Exports to JSONL compatible with @veritasacta/verify — no ScopeBlind account, no network call, works air-gapped

Install

pip install scopeblind-pydantic-ai

Quick start

from pydantic_ai import Agent
from scopeblind_pydantic_ai import ScopeBlindSigning, ReceiptSigner

# Generate or load an Ed25519 signing key
signer = ReceiptSigner.generate()
signer.save_key("./keys/agent.json")

# Attach the capability to your agent
signing = ScopeBlindSigning(signer=signer, agent_name="weather-bot")

agent = Agent(
    "anthropic:claude-sonnet-4-6",
    capabilities=[signing],
    tools=[get_weather, get_forecast],
)

result = await agent.run("What's the weather in Sydney?")

# Inspect and export receipts
print(f"{signing.receipt_count} tool calls signed")
print(f"Public key for verification: {signing.public_key_hex}")
signing.export_receipts("./receipts.jsonl")

Verify the output:

npx @veritasacta/verify receipts.jsonl --key $(jq -r .public_key keys/agent.json)

Exit 0 = all receipts valid. Exit 1 = tampered. Exit 2 = malformed.

With a policy

The policy argument is a callable (tool_name, args) -> (allowed, deny_reason):

def no_production_writes(tool_name: str, args: dict) -> tuple[bool, str | None]:
    if tool_name == "database_write" and args.get("env") == "production":
        return (False, "writes to production require human approval")
    return (True, None)

signing = ScopeBlindSigning(signer=signer, policy=no_production_writes)

Denied calls raise SkipToolExecution, and a decision="deny" receipt is still signed — the record survives the block.

With Cedar

For declarative policy, run Cedar evaluation in your policy callable. Two options:

Option 1 — use protect-mcp's Cedar evaluator (shells out to the CLI):

import subprocess, json

def cedar_gate(tool_name: str, args: dict) -> tuple[bool, str | None]:
    result = subprocess.run(
        ["npx", "protect-mcp", "evaluate",
         "--policy", "./protect.cedar",
         "--tool", tool_name,
         "--input", json.dumps(args)],
        capture_output=True,
    )
    if result.returncode == 0:
        return (True, None)
    return (False, result.stderr.decode().strip() or "Cedar deny")

Option 2 — use the Cedar WASM bindings from cedar-policy/cedar-for-agents (merged #64) through a Python binding. Native Python Cedar bindings are on the way.

Receipt format

Every receipt is a JSON object following IETF draft-farley-acta-signed-receipts:

{
  "payload": {
    "type": "protectmcp:decision",
    "spec": "draft-farley-acta-signed-receipts-01",
    "tool_name": "get_weather",
    "tool_input_hash": "sha256:a3f8...",
    "decision": "allow",
    "issued_at": "2026-04-15T10:30:00.000Z",
    "issuer_id": "scopeblind-pydantic-ai",
    "session_id": "sess_3a8b2c",
    "sequence": 1,
    "previousReceiptHash": null,
    "agent_name": "weather-bot",
    "output_hash": "sha256:b7e2..."
  },
  "signature": {
    "alg": "EdDSA",
    "kid": "sb:pydai:bd84c36b620c",
    "sig": "4cde814b..."
  }
}

The signature covers the JCS-canonicalized payload, so any edit to any payload field breaks verification.

Ecosystem

Cross-compatible with the rest of the ScopeBlind / Veritas Acta stack. A receipt signed by this package verifies identically to one signed by:

  • protect-mcp (Node, Claude Code hooks)
  • protect-mcp-adk (Google ADK)
  • The Rust gateway (APS ProxyGateway)
  • The Microsoft Agent Governance Toolkit adapter (PR #667, merged)

Compatibility

Pydantic AI Status
>= 1.0.0 Supported — uses the Capability API (wrap_tool_execute hook)
< 1.0 Not supported — the hook API landed in the 1.0 release

Development

git clone https://github.com/ScopeBlind/scopeblind-gateway
cd scopeblind-gateway/packages/scopeblind-pydantic-ai
pip install -e ".[dev]"
pytest tests/ -v

Standards

License

MIT. See LICENSE.

Release files for scopeblind-pydantic-ai 0.1.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 scopeblind-pydantic-ai 0.1.0
File Size Uploaded
scopeblind_pydantic_ai-0.1.0.tar.gz 11.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for scopeblind-pydantic-ai 0.1.0
File Interpreter ABI Platform
scopeblind_pydantic_ai-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 22.8 kB

Release files / scopeblind_pydantic_ai-0.1.0.tar.gz

Download URL scopeblind_pydantic_ai-0.1.0.tar.gz
Size 11.0 kB
Tags Source
SHA-256 checksum
How to use checksums
af20953e07ad39575166a505344ee4eecf0991590d76cad5c1dee556bb97697c
BLAKE2b-256 checksum
How to use checksums
d4403aac7c877ddb8f78a5b63dce5ae6f0be452fee9856f9e834b36b450cefea
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.6

Release files / scopeblind_pydantic_ai-0.1.0-py3-none-any.whl

Download URL scopeblind_pydantic_ai-0.1.0-py3-none-any.whl
Size 11.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
1607383a065d07ddd2737c6c73a81841caf8f79708992f133273ab33b773170c
BLAKE2b-256 checksum
How to use checksums
6bba7d0e4efebe623c052659c3447409cb9e032bf28882fb4a7e5ac476c7dd26
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.6

Release history Release notifications | RSS feed

This release

0.1.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