PIC Standard: Provenance & Intent Contracts
The Open Protocol for Causal Governance in Agentic AI.
PIC closes the causal gap: when untrusted inputs (prompt injection, user text, web pages) influence high‑impact side effects (payments, exports, infra changes), PIC forces a machine‑verifiable contract between what the agent claims and what evidence actually backs it.
Pick the extras you need:
# core (schema + verifier + CLI)
pip install pic-standard
# LangGraph integration
pip install "pic-standard[langgraph]"
# MCP integration
pip install "pic-standard[mcp]"
# Signature evidence (Ed25519)
pip install "pic-standard[crypto]"
Verify an example proposal (schema + verifier):
pic-cli verify examples/financial_irreversible.json
Expected output:
✅ Schema valid
✅ Verifier passed
Validate schema only:
pic-cli schema examples/financial_irreversible.json
Expected output:
✅ Schema valid
git clone https://github.com/madeinplutofabio/pic-standard.git
cd pic-standard
pip install -e .
pip install -r sdk-python/requirements-dev.txt
Run tests:
pytest -q
Run the CLI:
pic-cli verify examples/financial_irreversible.json
If your shell still uses an old pic-cli after editable installs:
python -m pic_standard.cli verify examples/financial_hash_ok.json --verify-evidence
PIC uses an Action Proposal JSON (protocol: PIC/1.0). The agent emits it right before executing a tool:
money, privacy, compute, irreversible, …)The reference verifier is intentionally minimal and fail‑closed:
privacy (same gating class as money and irreversible).This closes a spec↔code gap: privacy is treated as a first‑class “must be trusted” side‑effect category.
Tool binding is an integration‑time check: integrations that know the actual runtime tool call enforce
proposal.action.toolviaActionProposal.verify_with_context(expected_tool=...).
PIC supports deterministic evidence verification that can upgrade provenance trust in-memory (fail‑closed).
provenance[].trust → trusted for matching provenance IDs.type="hash")PIC v0.3 adds deterministic evidence verification: evidence IDs can point to a real artifact and be validated via SHA‑256.
What this gives you:
evidence[].id is no longer just a label — it can be resolved to a file (file://...) and verified.provenance[].trust → trusted before the verifier runs.Verify evidence only:
pic-cli evidence-verify examples/financial_hash_ok.json
Expected output:
✅ Schema valid
✅ Evidence invoice_123: sha256 verified
✅ Evidence verification passed
Fail (expected):
pic-cli evidence-verify examples/failing/financial_hash_bad.json
Expected output:
✅ Schema valid
❌ Evidence invoice_123: sha256 mismatch (expected ..., got ...)
❌ Evidence verification failed
Gate the verifier on evidence (schema → evidence verify → provenance upgrade → verifier):
pic-cli verify examples/financial_hash_ok.json --verify-evidence
Fail‑closed:
pic-cli verify examples/failing/financial_hash_bad.json --verify-evidence
Hash evidence references (file://)
file://artifacts/invoice_123.txt is resolved relative to the JSON proposal directory:
examples/financial_hash_ok.json → examples/artifacts/invoice_123.txtEvidence is sandboxed: the resolved path must stay under the configured evidence_root_dir (default: the proposal directory / server-configured root).
On Windows, recompute SHA‑256 with:
Get-FileHash .\examples\artifacts\invoice_123.txt -Algorithm SHA256
type="sig")PIC v0.4 adds signature verification so approvals can be endorsed by trusted signers (CFO, internal service, billing system) without shipping the raw artifact.
How it works
payload (the exact bytes-to-verify, as UTF‑8 string)signature (base64 Ed25519 signature)key_id (public key identifier)key_id against a trusted keyring (not inside the proposal).Canonicalization is the caller’s responsibility. If you change whitespace, ordering, or separators in
payload, signatures will fail.
Install
pip install "pic-standard[crypto]"
Signature evidence is verified against a local keyring file.
PIC loads keys from:
PIC_KEYS_PATH (if set), otherwise./pic_keys.json (if present), otherwisepic-cli keys
To generate an editable starter file:
pic-cli keys --write-example > pic_keys.json
PowerShell example:
$env:PIC_KEYS_PATH=".\pic_keys.json"
pic-cli keys
Recommended:
{
"trusted_keys": {
"demo_signer_v1": "u1esUbs/ZYS3PTPMIxiwsh47pyCUAv5VgzrmjEKbw6k=",
"cfo_key_v2": {
"public_key": "<base64-or-hex-or-PEM Ed25519 public key>",
"expires_at": "2026-12-31T23:59:59Z"
}
},
"revoked_keys": ["cfo_key_v1"]
}
Supported key encodings for public_key:
64 hex chars or 0x...)-----BEGIN PUBLIC KEY----- ...) (requires cryptography)A signature key_id is treated as inactive if any of the following are true:
revoked_keysexpires_at and it is expired (UTC)Evidence verification distinguishes these cases for operator clarity (e.g., “revoked”, “expired”).
Key rotation guidance (practical)
cfo_key_v2) to the keyring.key_id="cfo_key_v2".revoked_keys (or remove it) when you want to revoke/retire it.expires_at to force rotation hygiene.Signed example:
pic-cli evidence-verify examples/financial_sig_ok.json
Expected output:
✅ Schema valid
✅ Evidence approval_123: signature verified (key_id='demo_signer_v1')
✅ Evidence verification passed
Tampered example (expected fail):
pic-cli evidence-verify examples/failing/financial_sig_bad.json
Expected output:
✅ Schema valid
❌ Evidence approval_123: signature invalid (key_id='demo_signer_v1')
❌ Evidence verification failed
PIC can be enforced at the tool boundary using a LangGraph‑compatible tool execution node.
This repo provides:
pic_standard.integrations.PICToolNode: a drop‑in ToolNode wrapper that:args["__pic"])ActionProposal.verify_with_context(expected_tool=...)ToolMessage outputs (LangGraph state)Run the demo:
pip install -r sdk-python/requirements-langgraph.txt
python examples/langgraph_pic_toolnode_demo.py
Expected output:
✅ blocked as expected (untrusted money)
✅ allowed as expected (trusted money)
Tool‑call contract (PIC proposal is attached under __pic):
{
"name": "payments_send",
"args": {
"amount": 500,
"__pic": {
"protocol": "PIC/1.0",
"intent": "Send payment",
"impact": "money",
"provenance": [{"id": "invoice_123", "trust": "trusted", "source": "evidence"}],
"claims": [{"text": "Pay $500", "evidence": ["invoice_123"]}],
"action": {"tool": "payments_send", "args": {"amount": 500}}
}
},
"id": "tool_call_1"
}
Tool binding is enforced:
proposal.action.toolmust match the actual tool name.
PIC can also be enforced at the MCP tool boundary with a small wrapper:
pic_standard.integrations.mcp_pic_guard.guard_mcp_tool(...)This integration is designed for production defaults:
PIC_DEBUG gating)request_id / __pic_request_id appears in audit logs)ActionProposal.verify_with_context(expected_tool=...)Install demo deps:
pip install -r sdk-python/requirements-mcp.txt
Run the client (it spawns the server via stdio):
python -u examples/mcp_pic_client_demo.py
Expected output (high level):
1) untrusted money -> should be BLOCKED
✅ blocked as expected
2) trusted money -> should be ALLOWED
TEXT: sent $500
1) Debug gating (no leakage by default)
PIC_DEBUG unset/0): error payloads include only code + minimal message.PIC_DEBUG=1): error payloads may include diagnostic details.Windows PowerShell:
$env:PIC_DEBUG="0"
python -u examples/mcp_pic_client_demo.py
$env:PIC_DEBUG="1"
python -u examples/mcp_pic_client_demo.py
2) Request tracing
If your tool call includes:
__pic_request_id="abc123" (recommended reserved key), orrequest_id="abc123"…the guard logs a single structured line with that correlation ID.
3) Limits / DoS hardening
evidence_root_dir (prevents path escape)max_file_bytes (default 5MB)PICEvaluateLimits(max_eval_ms=...) blocks if enforcement work exceeds the budgetTool execution timeouts are an executor concern (sync Python can’t reliably kill a running function). PIC protects the policy enforcement path.
PIC/1.0 refers to the proposal protocol (schema).Guardrails constrain what the model says. PIC constrains what the agent is allowed to do (side effects) based on verifiable provenance + evidence.
graph TD
A[Untrusted Input] --> B{AI Agent / Planner}
C[Trusted Data/DB] --> B
B --> D[Action Proposal JSON]
D --> E[PIC Verifier Middleware]
E --> F{Valid Contract?}
F -- Yes --> G[Tool Executor]
F -- No --> H[Blocked / Alert Log]
We’re actively seeking:
Maintained by
@fmsalvadori
MadeInPluto