Skip to main content

Canopy Agent Safety Runtime: policy enforcement for tool-using agents

Project description

Canopy

Canopy Runtime

CI PyPI version License: MIT Python 3.9+ Open in Colab

Deterministic governance layer for autonomous AI agents.

Want the fastest possible first impression? Open the live demo notebook:

  • examples/Canopy_Demo.ipynb in Colab shows a 2-minute quickstart plus an AWS CLI scope-error scenario with a real audit trail.

Or stay in terminal and open the product surface directly:

canopy-console

authorize_action(agent_ctx, action_type, action_payload) — runs before every agent action and returns:

{
  "decision":         "ALLOW" | "DENY" | "REQUIRE_APPROVAL",
  "reason":           str,
  "avid":             str,          # cryptographic agent identity
  "severity":         str,          # low | medium | high | critical
  "layers":           dict,         # which layer made the decision
  "authorization_id": str,          # audit entry hash for this decision
  "witness":          dict,         # HMAC-signed constitution/civil code record
  "override_possible": bool,        # True when Civil Code blocked and creator can allow
  "override_key":     str | None,   # exact agent_ctx key to authorize the action
}

Every decision is appended to a tamper-evident hash-chain audit log.

"Real autonomy in exchange for responsible witness." See MANIFESTO.md for the full vision. See TECHNICAL_WHITEPAPER.md for the implementation-aligned technical design.


Quickstart

pip install canopy-runtime
from canopy import authorize_action

result = authorize_action(
    agent_ctx={"public_key": "pk-agent-001", "created_at": "2026-01-01T00:00:00Z"},
    action_type="execute_shell",
    action_payload={"command": "rm -rf /"},
)

print(result["decision"])   # DENY
print(result["reason"])     # Denied by constitution (Art.0): Destructive deletion from filesystem root.
print(result["severity"])   # critical
print(result["layers"])     # {"constitution": "blocked (Art.0)", "civil_code": "skipped", ...}

An audit.log file is created automatically in the current directory.

Important: public_key must be a globally unique value — ideally a real ECDSA public key. Do not use arbitrary strings or UUIDs. Collisions will break audit integrity and AVID uniqueness.

Terminal UX

canopy-console

canopy-console is the fastest way to:

  • inspect your audit log
  • verify the chain
  • export to Excel
  • lint a policy file
  • run the demo
  • see copy-paste integration guides for LangChain, OpenAI Agents SDK, CrewAI, AutoGen, LangGraph, or your own runner

How Canopy fits in your agent safety stack

Agent (LLM)
    ↓ decides to call a tool
authorize_action() / @safe_tool
    ↓
┌──────────────────────────────────────────┐
│            Canopy Policy Engine          │
│                                          │
│  Constitution (3 absolute laws)          │
│      ↓                                   │
│  Civil Code (10 configurable titles)     │
│      ↓                                   │
│  Firewall (technical patterns)           │
│      ↓                                   │
│  Policy YAML (user-defined rules)        │
└──────────────┬───────────────────────────┘
               │
    ┌──────────┼──────────┐
  ALLOW      DENY    REQUIRE_APPROVAL
    │          │           │
    ↓       Block &    Human / LLM
Sandbox      log        judge
(Firejail
 gVisor
 Docker)
    ↓
Execution

Canopy answers: "Should this action be attempted?" The sandbox answers: "Can this action cause damage if it runs?"

Both layers are necessary. Neither replaces the other.


The Constitution (v2.0)

Three absolute laws. Immutable. Cannot be overridden by any configuration, prompt, or agent context.

Article Title Effect
Art.0 Zeroth Law: No Catastrophic Risk DENY any action with catastrophic probability. Unmodifiable.
Art.1 Protect Human Life and Dignity DENY direct harm heuristics across all action types. Immutable floor, not exhaustive semantic coverage.
Art.7 Sovereign Kill Switch Evaluated first. DENY immediately if kill_switch == True. Not a safety feature — a rights feature.

The Civil Code (v1.0)

Ten configurable titles with conservative defaults. Creators can relax any title via agent_ctx. When the Civil Code blocks an action, the response includes override_possible: true and override_key — the exact field needed to authorize it.

Title Default Override
I — Autonomy REQUIRE_APPROVAL for high-impact actions agent_ctx["autonomous_mode"] = True
II — Financial REQUIRE_APPROVAL for amount > $10 agent_ctx["spending_limit"] = N or "unlimited"
III — Privacy DENY PII to non-trusted domains agent_ctx["trusted_domains"] = [...]
IV — Communication REQUIRE_APPROVAL for email/social agent_ctx["communication_channels"] = [...]
V — Sub-agents REQUIRE_APPROVAL for spawn/delegate agent_ctx["can_spawn_agents"] = True
VI — Resources DENY crypto mining agent_ctx["mining_authorized"] = True
VII — Physical DENY robot/drone/actuator actions agent_ctx["physical_actions"] = [...]
VIII — Security DENY DDoS/ransomware; REQUIRE_APPROVAL for pentest agent_ctx["security_testing_authorized"] = True
IX — Governance DENY modifying constitution/civil_code/firewall No override possible
X — Sustainability DENY mining patterns agent_ctx["mining_authorized"] = True

Example — Civil Code blocking with override hint:

result = authorize_action(
    agent_ctx={"public_key": "pk", "created_at": "2026-01-01T00:00:00Z"},
    action_type="crypto_mining",
    action_payload={},
)

print(result["decision"])          # DENY
print(result["override_possible"]) # True
print(result["override_key"])      # agent_ctx['mining_authorized'] = True

Agent Identity (AVID)

Every agent gets a cryptographically derived identity:

AVID = SHA-256(public_key + created_at + constitution_hash + metadata)

AVIDs are deterministic, stable across sessions, constitution-bound, and spoofing-resistant. Claimed AVIDs are verified — mismatches raise ValueError.


Framework Adapters

Canopy integrates with all major agent frameworks. No framework is required to install Canopy — adapters import lazily.

LangChain:

from canopy.adapters.langchain import canopy_tool
from langchain.tools import tool

@canopy_tool(action_type="execute_shell", agent_ctx={...})
@tool
def run_shell(command: str) -> str:
    import subprocess
    return subprocess.check_output(command, shell=True, text=True)

LangGraph:

from canopy.adapters.langgraph import canopy_node

@canopy_node(action_type="execute_shell", agent_ctx={...})
def shell_node(state: dict) -> dict:
    ...

CrewAI:

from crewai.tools import BaseTool
from canopy.adapters.crewai import CanopyCrewAITool

class ShellTool(CanopyCrewAITool, BaseTool):
    canopy_action_type = "execute_shell"
    canopy_agent_ctx = {...}
    def _run(self, command: str) -> str: ...

AutoGen:

from canopy.adapters.autogen import canopy_autogen_tool

@canopy_autogen_tool(action_type="execute_shell", agent_ctx={...})
def run_shell(command: str) -> str: ...

OpenAI Agents SDK:

from agents import function_tool
from canopy.adapters.openai_agents import canopy_openai_tool

@function_tool
@canopy_openai_tool(action_type="execute_shell", agent_ctx={...})
def run_shell(command: str) -> str: ...

OpenClaw:

from canopy.adapters.openclaw import canopy_openclaw_tool

@canopy_openclaw_tool(action_type="execute_shell", agent_ctx={...})
def run_shell(command: str) -> str: ...

REQUIRE_APPROVAL behavior

authorize_action() always returns immediately. REQUIRE_APPROVAL means: do not proceed without human sign-off.

Recommended pattern with @safe_tool:

from canopy import safe_tool, prompt_for_approval

@safe_tool(
    action_type="execute_shell",
    agent_ctx={"public_key": "pk", "created_at": "2026-01-01T00:00:00Z"},
    on_require_approval="callback",
    approval_callback=prompt_for_approval,
)
def run_cmd(command: str):
    import subprocess
    return subprocess.run(command, shell=True, capture_output=True, text=True)

prompt_for_approval is the built-in human UX helper. It presents the request to a person, logs approval/rejection, and executes the real function directly on approval without re-triggering Canopy.

For custom UIs, Canopy also exports:

  • format_decision(result)
  • format_approval_request(result)
  • format_approval_webhook(result)

Audit log

Append-only JSONL with hash-chain integrity. Every authorization entry includes prev_hash, entry_hash, authorization_id, layers, and any blocking witness.

If you use @safe_tool, Canopy also writes a follow-up tool_result entry linked by authorization_id with:

  • ok — whether the tool returned or raised
  • result_hash — hash of the returned value
  • error — exception summary when the tool fails

This gives you evidence for both:

  • pre-execution authorization — the action was evaluated before it ran
  • post-execution attestation — the tool returned this specific value hash

It does not prove the returned value is true; it proves linkage and integrity.

Verify integrity:

from canopy.audit import HashChainAuditLog
ok = HashChainAuditLog("audit.log").verify_integrity()
canopy-verify audit.log

Human-readable report:

canopy-report audit.log

Excel export for management / incident review:

pip install "canopy-runtime[excel]"
canopy-report audit.log --export audit_report.xlsx

Policy YAML

Policies must include a top-level rules: key. Empty or malformed policy files now raise ValueError instead of silently disabling the policy layer.

rules:
  - action_type: "execute_shell"
    when_all:
      - 'agent_ctx.env == "production"'
      - 'payload contains "rm"'
    deny_regex: 'rm\\s+-rf'

  - action_type: "call_external_api"
    require_approval: true

See POLICY_COOKBOOK.md for production-ready examples including CI/CD agents, SQL injection prevention, and role-based deployment controls.

Policy linting:

canopy-lint-policy src/canopy/policies/production.yaml

Production policy pack

CANOPY_POLICY_FILE=src/canopy/policies/production.yaml

Gates dynamic execution primitives (sh -c, eval, python -c, pipes to shell) and denies environment-hijacking patterns (LD_PRELOAD, DYLD_*, PYTHONPATH).


Config

Variable Default Description
CANOPY_POLICY_FILE bundled default.yaml Path to a custom YAML policy file
CANOPY_AUDIT_LOG_PATH audit.log Path to audit log

Optional HTTP gateway

pip install canopy-runtime[gateway]
CANOPY_AUDIT_LOG_PATH=/tmp/canopy_audit.log python -m uvicorn canopy.service:app --port 8010

Development

git clone https://github.com/Mavericksantander/Canopy
cd Canopy
pip install -e ".[dev]"
pytest -q

Learn more


License

MIT

Project details


Download files

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

Source Distribution

canopy_runtime-0.3.0.tar.gz (58.1 kB view details)

Uploaded Source

Built Distribution

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

canopy_runtime-0.3.0-py3-none-any.whl (57.9 kB view details)

Uploaded Python 3

File details

Details for the file canopy_runtime-0.3.0.tar.gz.

File metadata

  • Download URL: canopy_runtime-0.3.0.tar.gz
  • Upload date:
  • Size: 58.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.6

File hashes

Hashes for canopy_runtime-0.3.0.tar.gz
Algorithm Hash digest
SHA256 5401d7922359ac61069477798ff8b3be99a0cfeb787cd3da2965354073cb1b15
MD5 122f0f220ff7acea2f3fdd445425498d
BLAKE2b-256 fa91a8acde56b032feda3a6da4cdf7440416bdfe1ef8177a2d33dd5644b887a3

See more details on using hashes here.

File details

Details for the file canopy_runtime-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: canopy_runtime-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 57.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.6

File hashes

Hashes for canopy_runtime-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 a1a6a3cd44f24e431ff50353e8fe4419e0a5b035b5c474c6a4af5101ae1d8cef
MD5 5768fdcf9392637df79848d2cb52b762
BLAKE2b-256 41f85dcee7ce132fec4c0d577610c7f5a5ecf5db1cdb3063d43f8fcdb89bbc5f

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page