Metadata-Version: 2.4
Name: canopy-runtime
Version: 0.3.0
Summary: Canopy Agent Safety Runtime: policy enforcement for tool-using agents
Author: Canopy
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: rich>=13.0
Provides-Extra: excel
Requires-Dist: openpyxl>=3.1; extra == "excel"
Provides-Extra: http
Requires-Dist: requests>=2.25; extra == "http"
Provides-Extra: gateway
Requires-Dist: fastapi>=0.100; extra == "gateway"
Requires-Dist: pydantic>=2; extra == "gateway"
Requires-Dist: uvicorn[standard]>=0.23; extra == "gateway"
Provides-Extra: dev
Requires-Dist: pytest>=7; extra == "dev"
Requires-Dist: httpx>=0.24; extra == "dev"
Requires-Dist: requests>=2.25; extra == "dev"
Requires-Dist: fastapi>=0.100; extra == "dev"
Requires-Dist: pydantic>=2; extra == "dev"
Requires-Dist: uvicorn[standard]>=0.23; extra == "dev"
Requires-Dist: openpyxl>=3.1; extra == "dev"
Dynamic: license-file

<p align="center">
  <img src="Logos/canopy_logo_with_text.svg" alt="Canopy" width="420" />
</p>

# Canopy Runtime
[![CI](https://github.com/Mavericksantander/Canopy/actions/workflows/ci.yml/badge.svg)](https://github.com/Mavericksantander/Canopy/actions/workflows/ci.yml)
[![PyPI version](https://badge.fury.io/py/canopy-runtime.svg)](https://pypi.org/project/canopy-runtime/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Python 3.9+](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org/downloads/)
[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/Mavericksantander/Canopy/blob/main/examples/Canopy_Demo.ipynb)

**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:

```bash
canopy-console
```

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

```python
{
  "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](MANIFESTO.md) for the full vision. See [TECHNICAL_WHITEPAPER.md](TECHNICAL_WHITEPAPER.md) for the implementation-aligned technical design.

---

## Quickstart

```bash
pip install canopy-runtime
```

```python
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

```bash
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:

```python
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:**
```python
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:**
```python
from canopy.adapters.langgraph import canopy_node

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

**CrewAI:**
```python
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:**
```python
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:**
```python
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:**
```python
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`:

```python
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:

```python
from canopy.audit import HashChainAuditLog
ok = HashChainAuditLog("audit.log").verify_integrity()
```

```bash
canopy-verify audit.log
```

Human-readable report:

```bash
canopy-report audit.log
```

Excel export for management / incident review:

```bash
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.

```yaml
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](POLICY_COOKBOOK.md) for production-ready examples including CI/CD agents, SQL injection prevention, and role-based deployment controls.

Policy linting:

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

## Production policy pack

```bash
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

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

---

## Development

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

---

## Learn more

- [MANIFESTO.md](MANIFESTO.md) — the foundational vision
- [TECHNICAL_WHITEPAPER.md](TECHNICAL_WHITEPAPER.md) — implementation-aligned technical design
- [WHITEPAPER.md](WHITEPAPER.md) — archived v0.2-era narrative whitepaper
- [POLICY_COOKBOOK.md](POLICY_COOKBOOK.md) — production-ready policy examples
- [INVESTOR_WHITEPAPER.md](INVESTOR_WHITEPAPER.md) — market context and business model

---

## License

MIT
