Loading…
Loading…
AgentGP can attach cryptographic governance proof (SHA-256 + Merkle/hash lineage) to governed executions. When policies, prompts, and tools are included in the governance call, the proof provides a verifiable record of the governed resources applied for audit and compliance workflows.
Integration time varies by stack and environment. Proof coverage depends on using governed SDK paths (govern / inject_governance) and configured audit/event pipelines.
Quickstart examples are available for Python, TypeScript/Node, Go, and Java. The integration path is the same: resolve policy + prompt + tools, then use full inject governance for Merkle/hash lineage proof.
pip install agentgpfrom agentgp import AgentGP
client = AgentGP(
"https://api.agentgp.io",
token="YOUR_API_TOKEN",
agent_id="agent.my-bot-v1",
)
# Fast path: policy + prompt + tools
bundle = client.govern(
policy_name="policy.trading-limits",
prompt_name="prompt.customer-support",
variables={"tier": "gold"},
tools=["tool.search"],
tool_policy_name="policy.tools",
tool_input={"q": "reset password"},
)
# Full proof path: inject_governance (governance + Merkle/hash lineage)
inject = client.inject_governance(
policy_variables={"policy.trading-limits": {"amount": 5000, "currency": "USD"}},
prompt_key="prompt.customer-support",
tool_keys=["tool.search"],
)
print(bundle.trace_id)
print(inject.merkle_root)
print(inject.allowed)SDK parity status (beta)
govern) and full proof inject (inject_governance / injectGovernance).Traditional governance relies on logs — text files that can be tampered with, deleted, or ignored. AIGP (AI Governance Proof) replaces logs with cryptographic proof.
Every time your AI agent requests a policy, prompt, or tool from AgentGP, the platform:
Not logs — PROOF
Logs can be tampered with, deleted, or ignored. AIGP produces cryptographic proof — immutable, verifiable, mathematically undeniable. If a regulator asks "did your agent use the approved policy?" — you hand them the Merkle root, not a log file.
Your AI Agent
@aigp(agent="agent.refund-bot-v1")
Delivers all approved Policies, Prompts & Tools linked to this agent
governance.merkle_root
"8dd464c..."
Proof that exact resources were delivered
AgentGP Platform
AIGP Event Bus (Kafka)
The @aigp decorator handles the entire governance pipeline automatically. Your function receives a GovernanceResult with the Merkle root, rendered policies, prompts, and tool permissions — all cryptographically proven.
When your agent requests multiple resources (e.g., 2 policies + 1 prompt + 1 tool), AgentGP builds a Merkle tree that proves all of them were delivered atomically in a single governance action:
Computing Merkle animation data...
Language note
A single Merkle tree proves what resources were used in one governance action. But production workflows rarely involve a single agent — an Orchestrator (GPT-4o) might triage a customer request, delegate to a Research Bot (Claude Sonnet) to look up order history, route through a Compliance Bot (GPT-4o-mini) for PII and refund-policy checks, then funnel the response back. Each agent makes its own governed call, often powered by a different LLM.
You need to prove the causal order of events across all agents and all LLM providers, and detect any tampering across the entire chain. AIGP solves this with three fields:
"Can I get a refund for order #4821?"
3 agents · 3 LLMs · 6 events · cryptographically chained
| Field | Scope | What it does |
|---|---|---|
| sequence_number | Per agent:trace | Monotonic counter — prevents replay and reordering within a single agent's event stream |
| causality_ref | Cross-agent | Event ID of the causally preceding event — links events across agent boundaries into a directed graph |
| parent_hash | Cross-agent | SHA-256 of the previous event's canonical JSON — any modification to an earlier event breaks all downstream hashes |
Each agent maintains a monotonic counter scoped to agent_id:trace_id. The first event in a trace gets sequence_number: 1, the second gets 2, and so on. This guarantees:
When an orchestrator delegates work to a specialist agent, the specialist's first event setscausality_ref to the orchestrator's delegation event ID. This creates a directed acyclic graph (DAG) across agents — you can trace any event back to the root cause, even across process boundaries.
In the diagram above, evt-e5f6 (Research Bot / Claude Sonnet — "Looked up order #4821") points back toevt-c3d4 (Orchestrator / GPT-4o — "Routed to Research Bot") — proving the research action was caused by the orchestrator's delegation, even though the two agents use different LLMs.
parent_hash is the SHA-256 of the previous event's canonical JSON (sorted keys, no whitespace, excluding the parent_hash field itself). This creates a hash chain like a blockchain — modifying any earlier event changes its hash, which breaks the parent_hash of every subsequent event.
import hashlib, json
def verify_chain(events: list[dict]) -> bool:
"""Verify the parent_hash chain across AIGP events."""
for i in range(1, len(events)):
prev = events[i - 1]
curr = events[i]
# Canonical JSON of previous event (excluding parent_hash)
prev_copy = {k: v for k, v in prev.items() if k != "parent_hash"}
canonical = json.dumps(prev_copy, sort_keys=True, separators=(",", ":"))
expected_hash = hashlib.sha256(canonical.encode()).hexdigest()
if curr["parent_hash"] != expected_hash:
print(f"CHAIN BROKEN at event {i}: {curr['event_id']}")
print(f" Expected parent_hash: {expected_hash[:16]}...")
print(f" Actual parent_hash: {curr['parent_hash'][:16]}...")
return False
print(f"Chain verified: {len(events)} events, all hashes match.")
return TrueBreak any link, break the chain
If an attacker modifies event 3 in a 6-event chain, events 4, 5, and 6 all have incorrectparent_hash values. The chain break is immediately detectable by any verifier — no trust in AgentGP required. Combined with JWS ES256 signatures, each event is both causally linked and individually authenticated.
Each leaf in the Merkle tree is a domain-separated SHA-256 hash. The domain separation prevents a policy named prompt.foo from colliding with a prompt named prompt.foo:
leaf_hash = SHA256(resource_type + ":" + resource_name + ":" + content)
# Example:
SHA256("policy:policy.refund-limits:Maximum refund amount is $500...")
→ "1138149eb8ee4355ad80ee3c02a93c46a2b82c8976eaa9ba7c304b740cc6921a"| Type | Example AGRN | What it governs |
|---|---|---|
| policy | policy.refund-limits | Jinja2-rendered policy guidance + OPA/Rego policy enforcement |
| prompt | prompt.support-system | Approved system prompt for the LLM |
| tool | tool.crm-api | Tool permissions and enforcement rules |
| lineage | lineage.trace-001 | Data lineage snapshots |
| context | context.session-abc | Conversation or session context |
| memory | memory.user-prefs | Agent memory and RAG context |
| model | model.gpt-4-turbo | Model selection and configuration |
For large resources that shouldn't be embedded in the event, use the Pointer Pattern. Instead of hashing the content directly, hash a reference URI:
# Instead of: SHA256("policy:policy.large-doc:...10MB of content...")
# Use: SHA256("policy:policy.large-doc:s3://bucket/sha256:abc123...")
#
# The content_ref URI includes a content hash, so the proof still
# covers the exact content — just stored externally.Every AIGP event is optionally signed with JWS Compact Serialization (RFC 7515) using ECDSA P-256 + SHA-256. The signature covers the canonical JSON of the event (sorted keys, no whitespace):
# Header
{"alg": "ES256", "typ": "JWT", "kid": "aigp-signing-key-001"}
# Payload = canonical JSON of the AIGP event
# (excluding event_signature and signature_key_id fields)
# Signature = ECDSA P-256 sign(SHA256(header.payload))
# Result: Header.Payload.Signature (Base64url encoded)
event["event_signature"] = "eyJhbGciOi....<payload>....<signature>"
event["signature_key_id"] = "aigp-signing-key-001"AgentGP automatically signs events server-side. The signing key is stored in a secrets vault (OpenBao) or configured via environment variable. You can verify signatures using the public key.
The real power of governance proof is atomic multi-resource governance. A single @aigp call delivers ALL approved resources linked to your agent — and the Merkle root proves they were ALL delivered in the same request:
# agent.trade-execution-bot has 5 resources linked in AgentGP:
# - policy.trading-limits (rendered with Jinja2)
# - policy.risk-controls (evaluated with OPA/Rego)
# - prompt.trader-instructions (approved system prompt)
# - tool.bloomberg-api (tool permissions)
# - tool.order-execution (tool permissions)
@aigp(agent="agent.trade-execution-bot")
def execute_trade(order: dict, governance: GovernanceResult = None):
# governance.merkle_root covers ALL 5 resources atomically.
# Changing any one resource changes the Merkle root.
# The proof is atomic — all or nothing.
print(f"5-resource proof: {governance.merkle_root}")Fail-open (default)
If the AgentGP backend is unavailable, the decorated function still executes.governance.merkle_root will be empty, and governance.allowed will be True. Good for non-critical agents where availability matters more than enforcement.
Fail-closed
If the backend is unavailable, the decorator raises GovernanceError. The function does NOT execute. Good for regulated agents (banking, healthcare) where governance is mandatory.
# Option 1: Global
configure(backend=..., fail_closed=True)
# Option 2: Per-function
@aigp(agent="agent.loan-approval-bot", fail_closed=True)
def approve_loan(application: dict, governance: GovernanceResult = None):
# This function will NEVER execute without governance
...This detailed walkthrough uses Python. For TypeScript, Go, and Java quickstart snippets, use the Choose your SDK section above.
pip install aigp agentgpThis installs two packages: aigp (the open AIGP standard — Merkle trees, event signing, OTel) and agentgp (the AgentGP backend adapter).
Standalone mode
pip install agentgp. The agentgp package includes fallback decorators that work identically.from aigp import configure
from agentgp import AgentGPBackend
configure(
backend=AgentGPBackend(
base_url="https://api.agentgp.io", # or your self-hosted URL
token="YOUR_API_TOKEN",
agent_id="agent.my-support-bot",
),
agent_id="agent.my-support-bot",
agent_name="Support Bot",
org_id="org.acme",
fail_closed=False, # True = raise error if backend unavailable
)configure() sets up the global governance backend. Every @aigp call after this will use it automatically.
You can also configure via environment variables (no code changes needed):
export AGENTGP_URL=https://api.agentgp.io
export AGENTGP_TOKEN=your_api_token
export AGENTGP_AGENT_ID=agent.my-support-botfrom aigp import aigp, GovernanceResult
@aigp(agent="agent.refund-bot-v1")
def handle_refund(request: dict, governance: GovernanceResult = None):
"""Handle a customer refund request — governed by AgentGP.
AgentGP delivers ALL approved policies, prompts, and tools
linked to agent.refund-bot-v1 — no need to list them individually.
"""
# 1. Check if governance approved the action
if governance.denied:
return {"error": governance.denial_reason}
# 2. Use the governed prompt (linked to this agent in AgentGP)
system_prompt = governance.get_prompt("prompt.support-system")
# 3. Use the rendered policy (Jinja2 with your variables)
policy_text = governance.get_rendered("policy.refund-limits")
# 4. Check tool permissions
if not governance.is_tool_allowed("tool.crm-api"):
return {"error": "CRM API not permitted for this agent"}
# 5. The Merkle root proves ALL of the above were delivered together
proof_hash = governance.merkle_root
print(f"Governance proof: {proof_hash}")
# 6. Proceed with your business logic
return {
"action": "refund_approved",
"proof": proof_hash,
"policy_version": governance.policies.get(
"policy.refund-limits", {}
).get("metadata", {}).get("version"),
}What happens behind the scenes
agent.refund-bot-v1)SHA256(resource_type:resource_name:content)GovernanceResult with the proofEvery governed action generates a trace. Open the proof detail page in AgentGP:
https://ui.agentgp.io/proof/{trace_id}Or via the API:
from agentgp import AgentGP
client = AgentGP(token="YOUR_API_TOKEN")
proof = client.get_proof("your-trace-id-here")
print(f"Merkle Root: {proof['governanceHash']}")
print(f"Leaf Count: {proof['merkleTree']['leafCount']}")
print(f"Verdict: {proof['verdict']}")
print(f"Duration: {proof['durationMs']}ms")
# Inspect each leaf in the Merkle tree
for leaf in proof["merkleTree"]["leaves"]:
print(f" {leaf['resource_type']:8s} | {leaf['resource_name']:30s} | {leaf['hash'][:16]}...")| Field | Description |
|---|---|
| governanceHash | Merkle root — single hash covering ALL resources |
| merkleTree.leaves[] | Per-resource hashes (policy, prompt, tool, etc.) |
| verdict | approved, denied, or partial |
| twoKeyStatus | Whether Jinja2 rendering and OPA/Rego policy enforcement were both applied |
| signatureCoverage | % of events signed with JWS ES256 |
| chainIntegrity | Whether the causal event chain is intact |
| events[] | All AIGP events in the trace, time-ordered |
The AgentGP UI provides an interactive Merkle tree visualizer at /proof/{traceId}/merkle-tree. You can see the tree structure, click individual leaves to see the resource content, and verify each hash.
The entire point of cryptographic proof is that anyone can verify it independently — no trust in AgentGP required.
import hashlib
def verify_merkle_root(leaves: list[dict], expected_root: str) -> bool:
"""Independently verify a governance Merkle root."""
# 1. Recompute each leaf hash
hashes = []
for leaf in sorted(leaves, key=lambda l: l["hash"]):
# Domain-separated hashing prevents cross-resource collisions
preimage = f"{leaf['resource_type']}:{leaf['resource_name']}:{leaf['content']}"
h = hashlib.sha256(preimage.encode()).hexdigest()
assert h == leaf["hash"], f"Leaf hash mismatch for {leaf['resource_name']}"
hashes.append(h)
# 2. Build the Merkle tree (odd promotion, not duplication)
level = hashes
while len(level) > 1:
next_level = []
i = 0
while i < len(level) - 1:
parent = hashlib.sha256(
(level[i] + level[i + 1]).encode()
).hexdigest()
next_level.append(parent)
i += 2
if i == len(level) - 1:
next_level.append(level[i]) # Odd promotion
level = next_level
# 3. Compare against the expected root
computed_root = level[0]
return computed_root == expected_root
# ── Usage ──
proof = client.get_proof("your-trace-id")
leaves = proof["merkleTree"]["leaves"]
# You need the actual content to verify (fetch from AgentGP or your cache)
# For leaf verification, add content to each leaf dict
verified = verify_merkle_root(leaves, proof["governanceHash"])
print(f"Proof verified: {verified}") # True = proof is validVerification is the whole point
When a regulator, auditor, or compliance officer asks "prove your agent used the approved policy" — you give them the Merkle root and the leaf hashes. They can independently recompute the tree and verify the proof. No trust in AgentGP required. No trust in your agent required. Mathematics verifies everything.
| Property / Method | Type | Description |
|---|---|---|
| allowed | bool | Whether the governance check passed |
| denied | bool | Inverse of allowed (convenience property) |
| denial_reason | str | None | Why it was denied (if denied) |
| merkle_root | str | SHA-256 Merkle root (governance hash) |
| policies | dict | Per-policy rendered content + metadata |
| prompts | dict | Per-prompt content |
| tools | dict | Per-tool enforcement results |
| get_rendered(name) | str | None | Get rendered policy content by AGRN |
| get_prompt(name) | str | None | Get prompt content by AGRN |
| get_tool(name) | dict | None | Get tool enforcement result by AGRN |
| is_tool_allowed(name) | bool | Check if tool is permitted (defaults True if not in result) |
Every governance action emits an AIGP event. Here's the schema for a multi-resource governance proof event:
{
"event_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"event_type": "POLICY_LOADED",
"event_category": "inject",
"event_time": "2026-02-18T14:30:00.123Z",
"spec_version": "0.11",
"agent_id": "agent.my-support-bot",
"agent_name": "Support Bot",
"org_id": "org.acme",
"trace_id": "550e8400-e29b-41d4-a716-446655440000",
"policy_name": "policy.refund-limits",
"policy_version": 4,
"prompt_name": "prompt.support-system",
"prompt_version": 2,
"governance_hash": "8dd464c1a2b3e4f5...",
"hash_type": "merkle-sha256",
"governance_merkle_tree": {
"algorithm": "sha256",
"leaf_count": 3,
"leaves": [
{
"resource_type": "policy",
"resource_name": "policy.refund-limits",
"content": "Maximum refund amount is $500. Refunds over $200 require manager approval. All refunds must be processed within 5 business days.",
"hash": "1138149eb8ee4355ad80ee3c02a93c46a2b82c8976eaa9ba7c304b740cc6921a"
},
{
"resource_type": "prompt",
"resource_name": "prompt.support-system",
"content": "You are a customer support agent. Be polite and professional. Never share sensitive account information without verification. Escalate disputes to compliance.",
"hash": "7a3bc4d5e6f78901234567890abcdef1234567890abcdef1234567890abcdef1"
},
{
"resource_type": "tool",
"resource_name": "tool.crm-api",
"content": {
"endpoint": "https://crm.acme.com/api/v2",
"methods": [
"GET",
"POST"
],
"scopes": [
"read:customer",
"write:refund"
],
"rate_limit": 100
},
"hash": "9d8e7f6a5b4c3d2e1f0a9b8c7d6e5f4a3b2c1d0e9f8a7b6c5d4e3f2a1b0c9d8"
}
]
},
"event_signature": "eyJhbGciOiJFUzI1NiI...",
"signature_key_id": "aigp-key-001",
"sequence_number": 1,
"causality_ref": ""
}Complete working example — from install to verified proof:
"""
AgentGP Governance Proof — Complete Example
Run: pip install aigp agentgp && python full_example.py
"""
from aigp import configure, aigp, GovernanceResult
from agentgp import AgentGPBackend
# ── 1. Configure ──
configure(
backend=AgentGPBackend(
base_url="https://api.agentgp.io",
token="YOUR_API_TOKEN",
agent_id="agent.refund-bot-v1",
),
agent_id="agent.refund-bot-v1",
agent_name="Refund Bot",
org_id="org.acme",
)
# ── 2. Define a governed function ──
# AgentGP delivers ALL approved policies, prompts, and tools
# linked to agent.refund-bot-v1 automatically.
@aigp(agent="agent.refund-bot-v1")
def process_refund(amount: float, governance: GovernanceResult = None):
if governance.denied:
return {"status": "denied", "reason": governance.denial_reason}
policy = governance.get_rendered("policy.refund-limits")
prompt = governance.get_prompt("prompt.support-system")
return {
"status": "approved",
"amount": amount,
"proof": governance.merkle_root,
"policy_used": policy[:50] + "..." if policy else None,
}
# ── 3. Call it ──
result = process_refund(amount=250.00)
print(f"Result: {result['status']}")
print(f"Proof: {result.get('proof', 'none')}")
# ── 4. The proof is now in AgentGP ──
# View it at: https://ui.agentgp.io/proof/{trace_id}
# Or query via API, A2A, or MCPWhen a single resource is governed, the leaf hash IS the governance hash (no Merkle tree needed). The hash_type will be sha256 instead of merkle-sha256. This is backward compatible with earlier AIGP versions.
Yes. Call the AgentGP REST API directly (POST /api/inject) and you'll receive the same Merkle root and leaf hashes in the response. The SDK just makes it easier.
AIGP events are stored across three tiers: Hot (ClickHouse, 6 months), Warm (OpenSearch, 1 year), and Cold (Iceberg on object storage, unlimited). The Merkle root and leaf hashes are part of every event, so proof is available as long as the event exists.
governance.allowed will be False and governance.denial_reason will contain the reason. A governance proof is still generated (type INJECT_DENIED) to prove the denial happened — proof covers approvals AND denials.
Yes. The GovernanceBackend is a Python Protocol (structural typing). Implement inject_governance(),log_activity(),audit(), and the agent_id property — no inheritance required.
Next steps