Skip to main content

ccs-pydantic-ai

License: MIT Python Conformance

The first official framework integration for CCS (Capability Compliance System). Add cryptographically-verifiable runtime receipts to every tool call in a Pydantic AI agent with 2 lines of code.

Every tool call emits a signed, 30-field L1 action receipt plus a linked ccs.behavior_evidence.v1 receipt. Both verify independently with the closed-core ccs-verifier==1.3.0 package, and the L1 to behavior link is bound by a SHA-256 digest over JCS-canonical JSON.

from ccs_pydantic_ai import CCSCapability, CCSConfig
from pydantic_ai import Agent

agent = Agent(
    "openai:gpt-4o",
    capabilities=[CCSCapability(CCSConfig(seed=b"my-app-seed"))],
)

No changes to your tools, agent structure, or prompts are required.


Installation

pip install ccs-pydantic-ai

# Optional: add the closed-core verifier to independently validate receipts:
pip install "ccs-pydantic-ai[verify]"   # pulls in ccs-verifier==1.3.0

The adapter itself is MIT licensed. The core ccs-verifier dependency is ELv2 and is only needed for verifying receipts, not for producing them.

Quick start

Recommended: CCSCapability (covers local + MCP tools)

import asyncio
from pydantic_ai import Agent
from ccs_pydantic_ai import CCSCapability, CCSConfig

def search(query: str) -> str:
    """Search the knowledge base."""
    return f"Results for {query!r}"

async def main():
    agent = Agent(
        "openai:gpt-4o",
        tools=[search],
        capabilities=[CCSCapability(CCSConfig(
            deployment_mode="in-process",
            seed=b"my-app-seed",
            issuer="my-app/ccs",
            audience="my-audience",
        ))],
    )
    result = await agent.run("Search for X")
    print(result.output)
    # Receipts are printed to stdout as JSON lines by default.

asyncio.run(main())

CCSCapability wraps the agent's assembled combined toolset each run via AbstractCapability.get_wrapper_toolset(...), so every tool -- local function tools and MCP tools alike -- is intercepted. This is the interception point confirmed by a Pydantic AI maintainer in pydantic/pydantic-ai#4262.

Explicit: wrap a single toolset

If you only want receipts for a specific toolset (e.g. one MCP server), wrap it directly:

from pydantic_ai import FunctionToolset
from ccs_pydantic_ai import CCSToolset, CCSConfig

my_tools = FunctionToolset(tools=[search, calculate])
agent = Agent(
    "openai:gpt-4o",
    toolsets=[CCSToolset(my_tools, CCSConfig(seed=b"my-app-seed"))],
)

Run the bundled example

pip install -e ".[dev]"
python examples/basic_agent.py   # uses TestModel -- no API key needed

Configuration

CCSConfig(
    deployment_mode="in-process",     # "in-process" | "sidecar"
    seed=b"my-app-seed",              # required for in-process key derivation
    # sidecar_url="http://localhost:9100",  # sidecar signing endpoint
    # public_key="...base64 Ed25519...",     # trusted key for sidecar mode
    # signer=my_custom_signer,              # override with any CCSSigner
    rule_version="1.3.0",
    rule_summary="no_rules_matched",
    issuer="my-app/ccs",
    audience="my-audience",
    trace_id=None,                     # fixed trace id; auto per run if None
    receipt_ttl_seconds=300.0,
    max_clock_skew=0.0,
    verifier_source_class="PydanticAIAdapter",
    sink=my_callable,                  # ReceiptRecord -> None; default stdout
    include_behavior_receipts=True,
    action_suffix="execute",           # action field becomes "<tool>.execute"
)

Deployment modes

Mode Private key location Reproducible Forgeable on process compromise
in-process Inside the agent process (derived from seed via Ed25519PrivateKey.from_private_bytes(sha256(seed))) Yes Yes
sidecar Outside the process (held by the CCS sidecar) No No -- only the public key is embedded

In sidecar mode the adapter never holds the private key. It POSTs the canonical payload to {sidecar_url}/sign, receives a base64 Ed25519 signature, and verifies that signature locally against the configured public key before attaching it to the receipt.

Receipt sink

By default each receipt pair is printed to stdout as one JSON line. Pass any callable (ReceiptRecord) -> None:

receipts = []
config = CCSConfig(seed=b"x", sink=receipts.append)

ReceiptRecord exposes .l1 (dict), .behavior (dict or None), .trace_id, .tool_call_id, and .verdict.

Verifying receipts

from ccs_verifier.ccs_verifier_l1 import L1Receipt
from ccs_pydantic_ai import linked_l1_digest, verify_ed25519

# L1: strict parse (rejects unknown/tampered fields) + signature
l1 = L1Receipt.from_dict(record["l1"], strict=True)
assert l1.verify_signature() is True

# Behavior evidence: signature + linkage to the L1 receipt
beh = record["behavior"]
assert verify_ed25519(beh["public_key"], beh, beh["signature"])
assert beh["linked_l1_receipt_digest"] == linked_l1_digest(record["l1"])

If any L1 field is modified after signing, verify_signature() returns False and the behavior receipt's linked_l1_receipt_digest no longer matches.

Architecture

Tool call (local fn or MCP)
        |
        v
+----------------------+    Ed25519 over JCS (RFC 8785)
|   CCSToolset         |    (private key never leaves the signer)
|   .call_tool()       |
|                      |
|  (1) record request  |
|  (2) invoke tool     |
|  (3) record response |
|  (4) build L1 (30)   |----> sign ----> L1 receipt
|  (5) build behavior  |----> sign ----> behavior receipt
|  (6) emit to sink    |
+----------------------+
        |
        v
   ReceiptSink (stdout / callback / file / queue ...)
  • Hashing -- args_digest, params_hash, request_hash, response_hash, runtime_context_hash, and config_hash are all SHA-256 over JCS-canonical JSON.
  • Signing -- Ed25519; the signature field is excluded from the signed payload, while signing_algorithm and public_key_fingerprint are included to prevent algorithm/key substitution.
  • Linkage -- linked_l1_receipt_digest = "sha256:" + sha256(JCS(L1 minus signature)), matching the CCS v1.3.1 paired conformance vectors.

Receipt structure

L1 action receipt (30 fields)

trace_id, receipt_version ("1.1"), verdict ("allow"|"block"), timestamp,
tool, tool_call_id, params_hash, args_digest, rule_summary, rule_version,
request_hash, response_hash, runtime_context_hash, config_hash,
verifier_source_class, deployment_mode, issuer, audience,
nonce, sequence, issued_at, expires_at, max_clock_skew, action,
signature, signing_algorithm ("Ed25519"),
public_key_fingerprint, public_key, verified_at, latency_us

Behavior evidence receipt (ccs.behavior_evidence.v1)

receipt_type, trace_id, tool_call_id, sequence,
linked_l1_receipt_digest, behavior_evidence_verdict
  ("not_observed" | "observed_and_rejected" | "observed_and_allowed"),
evidence_ref, issuer, audience, issued_at, deployment_mode,
signing_algorithm, public_key_fingerprint, public_key, signature

Conformance

The adapter's key derivation, JCS canonicalization, field set, and linkage algorithm are verified against the published CCS conformance vectors (v1.3.0 / v1.3.1):

  • In-process seed b"ccs-verifier/in-process-test/v1" reproduces the vector public key 6PPlM1taN/Ws4SnxaypgY2CGcKvGPw/eC54cUNesSb8= (fingerprint bbca301d8848dfdb).
  • All generated L1 receipts pass L1Receipt.from_dict(data, strict=True) and verify_signature() against ccs-verifier==1.3.0.
  • Behavior receipts are signed and linked exactly as the independent verify_v131.py conformance checker requires.

Development

cd ccs-integrations/adapters/ccs-pydantic-ai
pip install -e ".[dev]"
pytest

The test suite covers:

  • signer -- deterministic key derivation, cross-key rejection, tamper detection, JCS canonicalization, sidecar signature verification.
  • receipt chain -- strict 30-field L1 parse through ccs-verifier, Ed25519 verification, behavior linkage, per-field tamper detection, sequence ordering.
  • toolset -- allow/block verdicts through real Pydantic AI agents (TestModel, no API key), CCSToolset and CCSCapability integration, per-run trace isolation, config validation.

Security notes

  • The adapter never modifies ccs-verifier or the 30-field L1 structure.
  • In sidecar mode the private key is never present in the agent process; the adapter only stores the trusted public key.
  • Receipt generation failures are logged to stderr and never break the agent run -- the tool result/exception is always propagated unchanged.
  • For production in-process deployments, supply a high-entropy seed via a secret manager; treat it as a signing key (process compromise enables forgery, per the CCS trust model).

License

MIT -- see LICENSE. The adapter depends on ccs-verifier (ELv2) only as an optional [verify] extra.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ccs_pydantic_ai-0.1.1.tar.gz (25.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ccs_pydantic_ai-0.1.1-py3-none-any.whl (25.4 kB view details)

Uploaded Python 3

File details

Details for the file ccs_pydantic_ai-0.1.1.tar.gz.

File metadata

  • Download URL: ccs_pydantic_ai-0.1.1.tar.gz
  • Upload date:
  • Size: 25.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.12

File hashes

Hashes for ccs_pydantic_ai-0.1.1.tar.gz
Algorithm Hash digest
SHA256 703fc14ef4f9868c689283a410cd319c5ba6526e9e9610f21cf25dc10ad1f157
MD5 c8be1aca82b66f18a3560287f21bb7cb
BLAKE2b-256 657e22a5453bafaa9b7a4ea51a350ccadffa1f992222a2e88671ff064f1557cd

See more details on using hashes here.

File details

Details for the file ccs_pydantic_ai-0.1.1-py3-none-any.whl.

File metadata

File hashes

Hashes for ccs_pydantic_ai-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 a55adc28186bcd8f6b647024f6295b95bdafc88ccc26fafac0e786f6ba932313
MD5 989e28cc7754fb2d09c0f502449e5a8e
BLAKE2b-256 b6fabd82046dd0cd476cdaf9e05df1b625bae4ca1a379f4f7237243e3667179d

See more details on using hashes here.

Release history Release notifications | RSS feed

0.1.2

1 file

This release

0.1.1 This release

2 files

0.1.0

2 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