Metadata-Version: 2.4
Name: controlzero
Version: 1.4.0
Summary: AI agent governance: policies, audit, and observability for tool calls. Works locally with no signup.
Project-URL: Homepage, https://controlzero.ai
Project-URL: Documentation, https://docs.controlzero.ai
Project-URL: Repository, https://github.com/controlzero/controlzero
Project-URL: Examples, https://docs.controlzero.ai/sdk/integrations
Author-email: Control Zero <hello@controlzero.ai>
License: Apache-2.0
License-File: LICENSE
Keywords: agents,ai,audit,governance,guardrails,llm,mcp,policy
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Security
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.9
Requires-Dist: click>=8.1.0
Requires-Dist: loguru>=0.7.0
Requires-Dist: pydantic>=2.0.0
Requires-Dist: pyyaml>=6.0
Provides-Extra: dev
Requires-Dist: cryptography>=41.0.0; extra == 'dev'
Requires-Dist: httpx>=0.25.0; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
Requires-Dist: pytest>=7.0.0; extra == 'dev'
Requires-Dist: pyyaml>=6.0; extra == 'dev'
Requires-Dist: respx>=0.20.0; extra == 'dev'
Requires-Dist: zstandard>=0.22.0; extra == 'dev'
Provides-Extra: hosted
Requires-Dist: cryptography>=41.0.0; extra == 'hosted'
Requires-Dist: httpx>=0.25.0; extra == 'hosted'
Requires-Dist: zstandard>=0.22.0; extra == 'hosted'
Description-Content-Type: text/markdown

# control-zero

AI agent governance for Python. Policies, audit, and observability for tool calls.
Works locally with no signup.

> **v1.0.0 is a complete rewrite.** If you depend on `control-zero<1.0.0`
> (the hosted-mode SDK), pin your requirement: `control-zero<1.0.0` to stay on
> the legacy v0.3.x. The new v1.0.0+ is a local-first SDK with a different
> API surface; see the [migration guide](https://docs.controlzero.ai/sdk/migrate-v1)
> for details.

## Hello World

```python
from controlzero import Client

cz = Client(policy={
    "rules": [
        {"deny":  "delete_*", "reason": "Hello World: deletes are blocked"},
        {"allow": "*",        "reason": "Hello World: everything else is fine"},
    ]
})

print(cz.guard("delete_file", {"path": "/tmp/foo"}).decision)  # "deny"
print(cz.guard("read_file",   {"path": "/tmp/foo"}).decision)  # "allow"
```

11 lines. No API key. No signup. Run it.

## Install

```bash
pip install control-zero
```

## Why

Your AI agents call tools. Some of those tools should never be called by an
agent without a human in the loop. `controlzero` is the policy layer between
the model's output and the tool execution. Decisions are fail-closed by default.

You can use it offline with a local YAML file or Python dict. When you want to
share policies across a team or get a hosted audit dashboard, sign up at
[controlzero.ai](https://controlzero.ai) and set `CONTROLZERO_API_KEY`.

## Quickstart with the CLI

```bash
# 1. Generate a starter policy file with examples and comments
controlzero init

# 2. Edit controlzero.yaml in your editor

# 3. Validate it
controlzero validate

# 4. Test a tool call against the policy
controlzero test delete_file
```

The generated `controlzero.yaml` is the tutorial. It ships with annotated
rules covering the common patterns: allow lists, deny lists, wildcards, and
the catch-all.

Templates available:

- `controlzero init` — Hello World template (default)
- `controlzero init -t rag` — RAG agent template (block exfiltration)
- `controlzero init -t mcp` — MCP server template
- `controlzero init -t cost-cap` — model allow-listing and cost guards

## Loading a policy

Three ways:

```python
from controlzero import Client

# From a Python dict
cz = Client(policy={
    "rules": [
        {"deny": "delete_*"},
        {"allow": "read_*"},
    ]
})

# From a YAML file
cz = Client(policy_file="./controlzero.yaml")

# From an environment variable
# (set CONTROLZERO_POLICY_FILE=./controlzero.yaml)
cz = Client()
```

If `./controlzero.yaml` exists in the current directory, it is picked up
automatically. No environment variable needed.

## Policy schema

```yaml
version: '1'
rules:
  # Block any tool whose name starts with "delete_"
  - deny: 'delete_*'
    reason: 'Deletes need human approval'

  # Allow specific known-good tools
  - allow: 'search'
  - allow: 'read_*'

  # tool:method syntax
  - allow: 'github:list_*'
  - deny: 'github:delete_repo'

  # Catch-all
  - deny: '*'
    reason: 'Default deny'
```

Rules are evaluated top to bottom. The first match wins. If no rule matches,
the call is denied (fail-closed).

## Tamper detection and quarantine

The policy YAML supports a `settings:` section that controls how the SDK
responds when it detects that the local policy file has been modified outside
of normal channels (manual edits, unexpected hash changes, etc.):

```yaml
version: '1'
settings:
  tamper_behavior: warn # Options: warn | deny | deny-all | quarantine
rules:
  - deny: 'delete_*'
  - allow: '*'
```

| Mode         | Behavior                                                                 |
| ------------ | ------------------------------------------------------------------------ |
| `warn`       | Log a warning but continue evaluating rules normally.                    |
| `deny`       | Deny the current tool call that triggered the tamper check.              |
| `deny-all`   | Deny all tool calls and place the machine in quarantine until recovered. |
| `quarantine` | Same as `deny-all`, plus report a tamper alert to the backend dashboard. |

**Quarantine recovery.** When a machine enters quarantine (`deny-all` or
`quarantine`), every tool call is denied until you re-establish trust with one
of these commands:

```bash
controlzero enroll
controlzero policy-pull
controlzero sign-policy
```

**Org-level policy signing.** When a machine is enrolled via `controlzero enroll`,
it receives the organization's signing public key. Policy bundles pulled from
the backend are cryptographically signed and verified by the SDK automatically.
No extra configuration is required.

**Tamper alert reporting.** In `quarantine` mode, the SDK reports a tamper alert
to the Control Zero backend so your team can see it on the dashboard.

## Local audit log

When running without an API key, every decision is written to `./controlzero.log`
with daily rotation and 30-day retention. Tail it:

```bash
controlzero tail
```

Configure rotation via the client:

```python
cz = Client(
    policy_file="./controlzero.yaml",
    log_path="./logs/controlzero.log",
    log_rotation="10 MB",        # rotate at 10 MB, or "daily", or "1 hour"
    log_retention="30 days",
    log_compression="gz",        # gzip rotated files
    log_format="json",           # or "pretty"
)
```

When `CONTROLZERO_API_KEY` is set, audit ships to the remote dashboard and
these `log_*` options are ignored with a warning.

## Hybrid mode

If you set both an API key AND pass a local policy, the local policy
**overrides** the dashboard policy and you get a loud WARN log on init:

```
WARNING: controlzero: manual policy override detected. ...
```

This is intentional: it makes accidental prod bypass impossible to miss.
For prod environments, opt into strict mode to raise instead:

```python
cz = Client(api_key="cz_live_...", policy=local_policy, strict_hosted=True)
# RuntimeError: manual policy override detected ...
```

## Framework examples

Full integration guides at [docs.controlzero.ai/sdk/integrations](https://docs.controlzero.ai/sdk/integrations):

- LangChain
- LangGraph
- CrewAI
- OpenAI Agents SDK
- Anthropic tool use
- Pydantic AI
- AutoGen
- MCP servers
- Raw HTTP / no framework

## Hosted mode

When you want a dashboard, audit search, team policies, and approval workflows,
sign up at [controlzero.ai](https://controlzero.ai) and set the API key:

```python
import os
os.environ["CONTROLZERO_API_KEY"] = "cz_live_..."

from controlzero import Client
cz = Client()  # picks up the API key from env, audit ships remote
```

## License

Apache 2.0
