controlzero 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- controlzero-1.0.0/.gitignore +220 -0
- controlzero-1.0.0/Dockerfile.test +24 -0
- controlzero-1.0.0/LICENSE +17 -0
- controlzero-1.0.0/PKG-INFO +232 -0
- controlzero-1.0.0/README.md +194 -0
- controlzero-1.0.0/controlzero/__init__.py +41 -0
- controlzero-1.0.0/controlzero/_internal/__init__.py +1 -0
- controlzero-1.0.0/controlzero/_internal/dlp_scanner.py +777 -0
- controlzero-1.0.0/controlzero/_internal/enforcer.py +210 -0
- controlzero-1.0.0/controlzero/_internal/types.py +19 -0
- controlzero-1.0.0/controlzero/audit_local.py +128 -0
- controlzero-1.0.0/controlzero/audit_remote.py +221 -0
- controlzero-1.0.0/controlzero/cli/__init__.py +1 -0
- controlzero-1.0.0/controlzero/cli/main.py +1177 -0
- controlzero-1.0.0/controlzero/cli/templates/autogen.yaml +79 -0
- controlzero-1.0.0/controlzero/cli/templates/claude-code.yaml +85 -0
- controlzero-1.0.0/controlzero/cli/templates/codex-cli.yaml +80 -0
- controlzero-1.0.0/controlzero/cli/templates/cost-cap.yaml +64 -0
- controlzero-1.0.0/controlzero/cli/templates/crewai.yaml +83 -0
- controlzero-1.0.0/controlzero/cli/templates/cursor.yaml +86 -0
- controlzero-1.0.0/controlzero/cli/templates/gemini-cli.yaml +85 -0
- controlzero-1.0.0/controlzero/cli/templates/generic.yaml +57 -0
- controlzero-1.0.0/controlzero/cli/templates/langchain.yaml +89 -0
- controlzero-1.0.0/controlzero/cli/templates/mcp.yaml +79 -0
- controlzero-1.0.0/controlzero/cli/templates/rag.yaml +63 -0
- controlzero-1.0.0/controlzero/client.py +398 -0
- controlzero-1.0.0/controlzero/enrollment.py +493 -0
- controlzero-1.0.0/controlzero/errors.py +60 -0
- controlzero-1.0.0/controlzero/policy_loader.py +245 -0
- controlzero-1.0.0/controlzero/tamper.py +337 -0
- controlzero-1.0.0/examples/hello_world.py +20 -0
- controlzero-1.0.0/pyproject.toml +65 -0
- controlzero-1.0.0/tests/conftest.py +30 -0
- controlzero-1.0.0/tests/test_audit_remote.py +532 -0
- controlzero-1.0.0/tests/test_audit_sink_isolation.py +67 -0
- controlzero-1.0.0/tests/test_cli_hook.py +296 -0
- controlzero-1.0.0/tests/test_cli_init.py +43 -0
- controlzero-1.0.0/tests/test_cli_init_templates.py +24 -0
- controlzero-1.0.0/tests/test_cli_tail.py +33 -0
- controlzero-1.0.0/tests/test_cli_test.py +59 -0
- controlzero-1.0.0/tests/test_cli_validate.py +37 -0
- controlzero-1.0.0/tests/test_dlp_scanner.py +1063 -0
- controlzero-1.0.0/tests/test_enrollment.py +392 -0
- controlzero-1.0.0/tests/test_fail_closed_eval.py +19 -0
- controlzero-1.0.0/tests/test_glob_matching.py +118 -0
- controlzero-1.0.0/tests/test_hybrid_mode_strict.py +53 -0
- controlzero-1.0.0/tests/test_hybrid_mode_warn.py +30 -0
- controlzero-1.0.0/tests/test_install_hooks.py +1175 -0
- controlzero-1.0.0/tests/test_local_mode_dict.py +98 -0
- controlzero-1.0.0/tests/test_local_mode_file_json.py +33 -0
- controlzero-1.0.0/tests/test_local_mode_file_yaml.py +62 -0
- controlzero-1.0.0/tests/test_log_fallback_stderr.py +34 -0
- controlzero-1.0.0/tests/test_log_options_ignored_hosted.py +35 -0
- controlzero-1.0.0/tests/test_log_rotation.py +66 -0
- controlzero-1.0.0/tests/test_no_policy_no_key.py +28 -0
- controlzero-1.0.0/tests/test_package_rename_shim.py +46 -0
- controlzero-1.0.0/tests/test_policy_freshness.py +278 -0
- controlzero-1.0.0/tests/test_tamper.py +204 -0
- controlzero-1.0.0/tests/test_tamper_hook.py +309 -0
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
# Worktrees
|
|
2
|
+
.worktrees/
|
|
3
|
+
|
|
4
|
+
# Dependencies
|
|
5
|
+
node_modules/
|
|
6
|
+
vendor/
|
|
7
|
+
|
|
8
|
+
# Build outputs
|
|
9
|
+
dist/
|
|
10
|
+
build/
|
|
11
|
+
.next/
|
|
12
|
+
out/
|
|
13
|
+
*.egg-info/
|
|
14
|
+
# Go binaries
|
|
15
|
+
apps/control-zero-platform/backend/server
|
|
16
|
+
|
|
17
|
+
# Test & Coverage
|
|
18
|
+
coverage/
|
|
19
|
+
htmlcov/
|
|
20
|
+
.coverage
|
|
21
|
+
*.lcov
|
|
22
|
+
.pytest_cache/
|
|
23
|
+
.vitest/
|
|
24
|
+
test-results/
|
|
25
|
+
playwright-report/
|
|
26
|
+
.playwright/
|
|
27
|
+
__pycache__/
|
|
28
|
+
*.pyc
|
|
29
|
+
*.pyo
|
|
30
|
+
|
|
31
|
+
# Environment and secrets (deny-all, allow explicitly)
|
|
32
|
+
.env*
|
|
33
|
+
production-secrets*
|
|
34
|
+
vault-backup.key
|
|
35
|
+
*.pem
|
|
36
|
+
*.key
|
|
37
|
+
*.p12
|
|
38
|
+
*.pfx
|
|
39
|
+
id_rsa
|
|
40
|
+
id_ed25519
|
|
41
|
+
*.keystore
|
|
42
|
+
# Firebase credentials and config (contains API keys / service account)
|
|
43
|
+
firebase.js
|
|
44
|
+
firebase.json
|
|
45
|
+
firebase-service-account*.json
|
|
46
|
+
*-firebase-adminsdk-*.json
|
|
47
|
+
firebase-adminsdk-*.json
|
|
48
|
+
serviceAccountKey.json
|
|
49
|
+
cz-service-account.json
|
|
50
|
+
firebase-credentials.json/
|
|
51
|
+
google-services.json
|
|
52
|
+
GoogleService-Info.plist
|
|
53
|
+
!*.pub
|
|
54
|
+
!.env.example
|
|
55
|
+
!.env.local.example
|
|
56
|
+
!.env.production.template
|
|
57
|
+
!.env.production.example
|
|
58
|
+
!.env.test
|
|
59
|
+
|
|
60
|
+
# Explicitly block files that contain or may contain real credentials (override allowlist above)
|
|
61
|
+
.env.dev
|
|
62
|
+
.env.production
|
|
63
|
+
.env.production.complete
|
|
64
|
+
.env.production.local
|
|
65
|
+
.env.local
|
|
66
|
+
.env.*.local
|
|
67
|
+
*.env.real
|
|
68
|
+
*.env.secret
|
|
69
|
+
.env.vercel
|
|
70
|
+
|
|
71
|
+
# IDE & Editors
|
|
72
|
+
.idea/
|
|
73
|
+
.vscode/
|
|
74
|
+
*.swp
|
|
75
|
+
*.swo
|
|
76
|
+
*.sublime-workspace
|
|
77
|
+
*.sublime-project
|
|
78
|
+
|
|
79
|
+
# OS files
|
|
80
|
+
.DS_Store
|
|
81
|
+
Thumbs.db
|
|
82
|
+
*.log
|
|
83
|
+
|
|
84
|
+
# Go
|
|
85
|
+
*.exe
|
|
86
|
+
*.exe~
|
|
87
|
+
*.dll
|
|
88
|
+
*.so
|
|
89
|
+
*.dylib
|
|
90
|
+
|
|
91
|
+
# Docker
|
|
92
|
+
docker-compose.override.yml
|
|
93
|
+
.docker/
|
|
94
|
+
|
|
95
|
+
# Production logs and certificates
|
|
96
|
+
logs/
|
|
97
|
+
letsencrypt/
|
|
98
|
+
*.log
|
|
99
|
+
|
|
100
|
+
# DLP test reports (generated, not committed)
|
|
101
|
+
reports/
|
|
102
|
+
|
|
103
|
+
# MkDocs
|
|
104
|
+
site/
|
|
105
|
+
|
|
106
|
+
# Turbo (if adopted)
|
|
107
|
+
.turbo/
|
|
108
|
+
|
|
109
|
+
# Changeset
|
|
110
|
+
.changeset/*.md
|
|
111
|
+
!.changeset/config.json
|
|
112
|
+
!.changeset/README.md
|
|
113
|
+
|
|
114
|
+
# Vercel
|
|
115
|
+
.vercel/
|
|
116
|
+
|
|
117
|
+
# Rust
|
|
118
|
+
target/
|
|
119
|
+
Cargo.lock
|
|
120
|
+
*.rlib
|
|
121
|
+
|
|
122
|
+
# Python virtual environments
|
|
123
|
+
venv/
|
|
124
|
+
.venv/
|
|
125
|
+
*.egg-info/
|
|
126
|
+
|
|
127
|
+
# Gateway
|
|
128
|
+
apps/control-zero-gateway/.venv/
|
|
129
|
+
apps/control-zero-gateway/__pycache__/
|
|
130
|
+
|
|
131
|
+
# Selenium test outputs
|
|
132
|
+
tests/selenium/screenshots/
|
|
133
|
+
tests/selenium/report.html
|
|
134
|
+
|
|
135
|
+
# Miscellaneous
|
|
136
|
+
*.bak
|
|
137
|
+
*.tmp
|
|
138
|
+
*.temp
|
|
139
|
+
.cache/
|
|
140
|
+
.vercel
|
|
141
|
+
# Internal documentation (sensitive -- do not track)
|
|
142
|
+
docs/internal/
|
|
143
|
+
|
|
144
|
+
# Playwright MCP
|
|
145
|
+
.playwright-mcp/
|
|
146
|
+
|
|
147
|
+
# Session artifacts (prevent future accumulation)
|
|
148
|
+
*_SUMMARY.md
|
|
149
|
+
*_STATUS.md
|
|
150
|
+
*_COMPLETE.md
|
|
151
|
+
|
|
152
|
+
# E2E test screenshots
|
|
153
|
+
e2e-*.png
|
|
154
|
+
cz-*.png
|
|
155
|
+
.superpowers/
|
|
156
|
+
|
|
157
|
+
# AI tooling directories
|
|
158
|
+
.gemini/
|
|
159
|
+
.claude/launch.json
|
|
160
|
+
|
|
161
|
+
# Docusaurus build cache (should not be tracked)
|
|
162
|
+
docs-site/.docusaurus/
|
|
163
|
+
|
|
164
|
+
# Test/governance tester scripts (generated during testing sessions)
|
|
165
|
+
*_tester.py
|
|
166
|
+
verify_and_screenshot.js
|
|
167
|
+
|
|
168
|
+
# HTML reports generated during testing sessions
|
|
169
|
+
*_AUDIT.html
|
|
170
|
+
*_REPORT.html
|
|
171
|
+
*_ANALYSIS.html
|
|
172
|
+
e2e-report.html
|
|
173
|
+
sit-uat-report.html
|
|
174
|
+
UAT_COMPREHENSIVE_REPORT.html
|
|
175
|
+
uat-screenshots/
|
|
176
|
+
|
|
177
|
+
# Offensive summary reports
|
|
178
|
+
SUMMARY_OFFENSIVE_*.md
|
|
179
|
+
.gstack/
|
|
180
|
+
|
|
181
|
+
# Deployment docs (contain server-specific credentials)
|
|
182
|
+
docs/deployment/
|
|
183
|
+
docs/SSH_RECOVERY_GUIDE.md
|
|
184
|
+
docs/HETZNER_OS_INSTALLATION.md
|
|
185
|
+
docs/BACKUP_RECOVERY.md
|
|
186
|
+
docs/VERCEL_SETUP_WEBAPPS.md
|
|
187
|
+
scripts/fix-ssh-config.sh
|
|
188
|
+
scripts/hetzner-post-install.sh
|
|
189
|
+
docs/knowledge-base/
|
|
190
|
+
docs/superpowers/
|
|
191
|
+
scripts/doppler/doppler-env.values.sh
|
|
192
|
+
|
|
193
|
+
# Air-gap build artifacts (generated tarballs and package directories)
|
|
194
|
+
cz-air-gap-*/
|
|
195
|
+
cz-air-gap-*.tar.gz
|
|
196
|
+
cz-support-*.tar.gz
|
|
197
|
+
|
|
198
|
+
# SSL Proxy -- customer CA certs and generated certs (never track secrets)
|
|
199
|
+
services/ssl-proxy/certs/
|
|
200
|
+
|
|
201
|
+
# UAT screenshot artifacts
|
|
202
|
+
uat-*.png
|
|
203
|
+
|
|
204
|
+
# Stray HTML reports in docs/. These are large exported artifacts
|
|
205
|
+
# (SIT/UAT reports, factsheet, launch readiness) that accumulate
|
|
206
|
+
# untracked and risk being swept up by `git add .`. The
|
|
207
|
+
# sit-uat-report-2026-03-22.html in particular is 128MB and would
|
|
208
|
+
# explode the repo. Real docs go under docs/knowledge-base/ or
|
|
209
|
+
# docs/designs/ instead.
|
|
210
|
+
docs/sit-uat-report-*.html
|
|
211
|
+
docs/launch-readiness-report-*.html
|
|
212
|
+
docs/control-zero-factsheet.html
|
|
213
|
+
docs/control-zero-review.html
|
|
214
|
+
|
|
215
|
+
# Reference screenshots (keep local, not in repo)
|
|
216
|
+
agentdefenders-full.png
|
|
217
|
+
cz-revamp-live.png
|
|
218
|
+
|
|
219
|
+
# Agent worktrees (created by Claude Code during parallel work)
|
|
220
|
+
.claude/worktrees/
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Test image for the controlzero Python SDK.
|
|
2
|
+
# Builds the package, installs it, runs the test suite.
|
|
3
|
+
FROM python:3.12-slim
|
|
4
|
+
|
|
5
|
+
WORKDIR /app
|
|
6
|
+
|
|
7
|
+
# Install build deps
|
|
8
|
+
RUN pip install --no-cache-dir hatchling pytest pytest-asyncio pytest-cov
|
|
9
|
+
|
|
10
|
+
# Copy the package
|
|
11
|
+
COPY pyproject.toml README.md LICENSE /app/
|
|
12
|
+
COPY controlzero /app/controlzero
|
|
13
|
+
COPY tests /app/tests
|
|
14
|
+
COPY examples /app/examples
|
|
15
|
+
|
|
16
|
+
# Install the package + dev extras
|
|
17
|
+
RUN pip install --no-cache-dir -e ".[dev]"
|
|
18
|
+
|
|
19
|
+
# Smoke test: import and run the example
|
|
20
|
+
RUN python -c "from controlzero import Client, __version__; print(f'controlzero {__version__} installed')"
|
|
21
|
+
RUN python examples/hello_world.py
|
|
22
|
+
|
|
23
|
+
# Run the full test suite by default
|
|
24
|
+
CMD ["pytest", "-v", "--tb=short", "tests/"]
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
Copyright 2026 Control Zero, Inc.
|
|
6
|
+
|
|
7
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
8
|
+
you may not use this file except in compliance with the License.
|
|
9
|
+
You may obtain a copy of the License at
|
|
10
|
+
|
|
11
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
+
|
|
13
|
+
Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
See the License for the specific language governing permissions and
|
|
17
|
+
limitations under the License.
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: controlzero
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: AI agent governance: policies, audit, and observability for tool calls. Works locally with no signup.
|
|
5
|
+
Project-URL: Homepage, https://controlzero.ai
|
|
6
|
+
Project-URL: Documentation, https://docs.controlzero.ai
|
|
7
|
+
Project-URL: Repository, https://github.com/controlzero/controlzero
|
|
8
|
+
Project-URL: Examples, https://docs.controlzero.ai/sdk/integrations
|
|
9
|
+
Author-email: Control Zero <hello@controlzero.ai>
|
|
10
|
+
License: Apache-2.0
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: agents,ai,audit,governance,guardrails,llm,mcp,policy
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Topic :: Security
|
|
23
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
24
|
+
Requires-Python: >=3.9
|
|
25
|
+
Requires-Dist: click>=8.1.0
|
|
26
|
+
Requires-Dist: loguru>=0.7.0
|
|
27
|
+
Requires-Dist: pydantic>=2.0.0
|
|
28
|
+
Requires-Dist: pyyaml>=6.0
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
|
|
31
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
|
|
32
|
+
Requires-Dist: pytest>=7.0.0; extra == 'dev'
|
|
33
|
+
Requires-Dist: pyyaml>=6.0; extra == 'dev'
|
|
34
|
+
Provides-Extra: hosted
|
|
35
|
+
Requires-Dist: cryptography>=41.0.0; extra == 'hosted'
|
|
36
|
+
Requires-Dist: httpx>=0.25.0; extra == 'hosted'
|
|
37
|
+
Description-Content-Type: text/markdown
|
|
38
|
+
|
|
39
|
+
# control-zero
|
|
40
|
+
|
|
41
|
+
AI agent governance for Python. Policies, audit, and observability for tool calls.
|
|
42
|
+
Works locally with no signup.
|
|
43
|
+
|
|
44
|
+
> **v1.0.0 is a complete rewrite.** If you depend on `control-zero<1.0.0`
|
|
45
|
+
> (the hosted-mode SDK), pin your requirement: `control-zero<1.0.0` to stay on
|
|
46
|
+
> the legacy v0.3.x. The new v1.0.0+ is a local-first SDK with a different
|
|
47
|
+
> API surface; see the [migration guide](https://docs.controlzero.ai/sdk/migrate-v1)
|
|
48
|
+
> for details.
|
|
49
|
+
|
|
50
|
+
## Hello World
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
from controlzero import Client
|
|
54
|
+
|
|
55
|
+
cz = Client(policy={
|
|
56
|
+
"rules": [
|
|
57
|
+
{"deny": "delete_*", "reason": "Hello World: deletes are blocked"},
|
|
58
|
+
{"allow": "*", "reason": "Hello World: everything else is fine"},
|
|
59
|
+
]
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
print(cz.guard("delete_file", {"path": "/tmp/foo"}).decision) # "deny"
|
|
63
|
+
print(cz.guard("read_file", {"path": "/tmp/foo"}).decision) # "allow"
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
11 lines. No API key. No signup. Run it.
|
|
67
|
+
|
|
68
|
+
## Install
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
pip install control-zero
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Why
|
|
75
|
+
|
|
76
|
+
Your AI agents call tools. Some of those tools should never be called by an
|
|
77
|
+
agent without a human in the loop. `controlzero` is the policy layer between
|
|
78
|
+
the model's output and the tool execution. Decisions are fail-closed by default.
|
|
79
|
+
|
|
80
|
+
You can use it offline with a local YAML file or Python dict. When you want to
|
|
81
|
+
share policies across a team or get a hosted audit dashboard, sign up at
|
|
82
|
+
[controlzero.ai](https://controlzero.ai) and set `CONTROLZERO_API_KEY`.
|
|
83
|
+
|
|
84
|
+
## Quickstart with the CLI
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
# 1. Generate a starter policy file with examples and comments
|
|
88
|
+
controlzero init
|
|
89
|
+
|
|
90
|
+
# 2. Edit controlzero.yaml in your editor
|
|
91
|
+
|
|
92
|
+
# 3. Validate it
|
|
93
|
+
controlzero validate
|
|
94
|
+
|
|
95
|
+
# 4. Test a tool call against the policy
|
|
96
|
+
controlzero test delete_file
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
The generated `controlzero.yaml` is the tutorial. It ships with annotated
|
|
100
|
+
rules covering the common patterns: allow lists, deny lists, wildcards, and
|
|
101
|
+
the catch-all.
|
|
102
|
+
|
|
103
|
+
Templates available:
|
|
104
|
+
|
|
105
|
+
- `controlzero init` — Hello World template (default)
|
|
106
|
+
- `controlzero init -t rag` — RAG agent template (block exfiltration)
|
|
107
|
+
- `controlzero init -t mcp` — MCP server template
|
|
108
|
+
- `controlzero init -t cost-cap` — model allow-listing and cost guards
|
|
109
|
+
|
|
110
|
+
## Loading a policy
|
|
111
|
+
|
|
112
|
+
Three ways:
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
from controlzero import Client
|
|
116
|
+
|
|
117
|
+
# From a Python dict
|
|
118
|
+
cz = Client(policy={
|
|
119
|
+
"rules": [
|
|
120
|
+
{"deny": "delete_*"},
|
|
121
|
+
{"allow": "read_*"},
|
|
122
|
+
]
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
# From a YAML file
|
|
126
|
+
cz = Client(policy_file="./controlzero.yaml")
|
|
127
|
+
|
|
128
|
+
# From an environment variable
|
|
129
|
+
# (set CONTROLZERO_POLICY_FILE=./controlzero.yaml)
|
|
130
|
+
cz = Client()
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
If `./controlzero.yaml` exists in the current directory, it is picked up
|
|
134
|
+
automatically. No environment variable needed.
|
|
135
|
+
|
|
136
|
+
## Policy schema
|
|
137
|
+
|
|
138
|
+
```yaml
|
|
139
|
+
version: '1'
|
|
140
|
+
rules:
|
|
141
|
+
# Block any tool whose name starts with "delete_"
|
|
142
|
+
- deny: 'delete_*'
|
|
143
|
+
reason: 'Deletes need human approval'
|
|
144
|
+
|
|
145
|
+
# Allow specific known-good tools
|
|
146
|
+
- allow: 'search'
|
|
147
|
+
- allow: 'read_*'
|
|
148
|
+
|
|
149
|
+
# tool:method syntax
|
|
150
|
+
- allow: 'github:list_*'
|
|
151
|
+
- deny: 'github:delete_repo'
|
|
152
|
+
|
|
153
|
+
# Catch-all
|
|
154
|
+
- deny: '*'
|
|
155
|
+
reason: 'Default deny'
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Rules are evaluated top to bottom. The first match wins. If no rule matches,
|
|
159
|
+
the call is denied (fail-closed).
|
|
160
|
+
|
|
161
|
+
## Local audit log
|
|
162
|
+
|
|
163
|
+
When running without an API key, every decision is written to `./controlzero.log`
|
|
164
|
+
with daily rotation and 30-day retention. Tail it:
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
controlzero tail
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Configure rotation via the client:
|
|
171
|
+
|
|
172
|
+
```python
|
|
173
|
+
cz = Client(
|
|
174
|
+
policy_file="./controlzero.yaml",
|
|
175
|
+
log_path="./logs/controlzero.log",
|
|
176
|
+
log_rotation="10 MB", # rotate at 10 MB, or "daily", or "1 hour"
|
|
177
|
+
log_retention="30 days",
|
|
178
|
+
log_compression="gz", # gzip rotated files
|
|
179
|
+
log_format="json", # or "pretty"
|
|
180
|
+
)
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
When `CONTROLZERO_API_KEY` is set, audit ships to the remote dashboard and
|
|
184
|
+
these `log_*` options are ignored with a warning.
|
|
185
|
+
|
|
186
|
+
## Hybrid mode
|
|
187
|
+
|
|
188
|
+
If you set both an API key AND pass a local policy, the local policy
|
|
189
|
+
**overrides** the dashboard policy and you get a loud WARN log on init:
|
|
190
|
+
|
|
191
|
+
```
|
|
192
|
+
WARNING: controlzero: manual policy override detected. ...
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
This is intentional: it makes accidental prod bypass impossible to miss.
|
|
196
|
+
For prod environments, opt into strict mode to raise instead:
|
|
197
|
+
|
|
198
|
+
```python
|
|
199
|
+
cz = Client(api_key="cz_live_...", policy=local_policy, strict_hosted=True)
|
|
200
|
+
# RuntimeError: manual policy override detected ...
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## Framework examples
|
|
204
|
+
|
|
205
|
+
Full integration guides at [docs.controlzero.ai/sdk/integrations](https://docs.controlzero.ai/sdk/integrations):
|
|
206
|
+
|
|
207
|
+
- LangChain
|
|
208
|
+
- LangGraph
|
|
209
|
+
- CrewAI
|
|
210
|
+
- OpenAI Agents SDK
|
|
211
|
+
- Anthropic tool use
|
|
212
|
+
- Pydantic AI
|
|
213
|
+
- AutoGen
|
|
214
|
+
- MCP servers
|
|
215
|
+
- Raw HTTP / no framework
|
|
216
|
+
|
|
217
|
+
## Hosted mode
|
|
218
|
+
|
|
219
|
+
When you want a dashboard, audit search, team policies, and approval workflows,
|
|
220
|
+
sign up at [controlzero.ai](https://controlzero.ai) and set the API key:
|
|
221
|
+
|
|
222
|
+
```python
|
|
223
|
+
import os
|
|
224
|
+
os.environ["CONTROLZERO_API_KEY"] = "cz_live_..."
|
|
225
|
+
|
|
226
|
+
from controlzero import Client
|
|
227
|
+
cz = Client() # picks up the API key from env, audit ships remote
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
## License
|
|
231
|
+
|
|
232
|
+
Apache 2.0
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
# control-zero
|
|
2
|
+
|
|
3
|
+
AI agent governance for Python. Policies, audit, and observability for tool calls.
|
|
4
|
+
Works locally with no signup.
|
|
5
|
+
|
|
6
|
+
> **v1.0.0 is a complete rewrite.** If you depend on `control-zero<1.0.0`
|
|
7
|
+
> (the hosted-mode SDK), pin your requirement: `control-zero<1.0.0` to stay on
|
|
8
|
+
> the legacy v0.3.x. The new v1.0.0+ is a local-first SDK with a different
|
|
9
|
+
> API surface; see the [migration guide](https://docs.controlzero.ai/sdk/migrate-v1)
|
|
10
|
+
> for details.
|
|
11
|
+
|
|
12
|
+
## Hello World
|
|
13
|
+
|
|
14
|
+
```python
|
|
15
|
+
from controlzero import Client
|
|
16
|
+
|
|
17
|
+
cz = Client(policy={
|
|
18
|
+
"rules": [
|
|
19
|
+
{"deny": "delete_*", "reason": "Hello World: deletes are blocked"},
|
|
20
|
+
{"allow": "*", "reason": "Hello World: everything else is fine"},
|
|
21
|
+
]
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
print(cz.guard("delete_file", {"path": "/tmp/foo"}).decision) # "deny"
|
|
25
|
+
print(cz.guard("read_file", {"path": "/tmp/foo"}).decision) # "allow"
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
11 lines. No API key. No signup. Run it.
|
|
29
|
+
|
|
30
|
+
## Install
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pip install control-zero
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Why
|
|
37
|
+
|
|
38
|
+
Your AI agents call tools. Some of those tools should never be called by an
|
|
39
|
+
agent without a human in the loop. `controlzero` is the policy layer between
|
|
40
|
+
the model's output and the tool execution. Decisions are fail-closed by default.
|
|
41
|
+
|
|
42
|
+
You can use it offline with a local YAML file or Python dict. When you want to
|
|
43
|
+
share policies across a team or get a hosted audit dashboard, sign up at
|
|
44
|
+
[controlzero.ai](https://controlzero.ai) and set `CONTROLZERO_API_KEY`.
|
|
45
|
+
|
|
46
|
+
## Quickstart with the CLI
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
# 1. Generate a starter policy file with examples and comments
|
|
50
|
+
controlzero init
|
|
51
|
+
|
|
52
|
+
# 2. Edit controlzero.yaml in your editor
|
|
53
|
+
|
|
54
|
+
# 3. Validate it
|
|
55
|
+
controlzero validate
|
|
56
|
+
|
|
57
|
+
# 4. Test a tool call against the policy
|
|
58
|
+
controlzero test delete_file
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
The generated `controlzero.yaml` is the tutorial. It ships with annotated
|
|
62
|
+
rules covering the common patterns: allow lists, deny lists, wildcards, and
|
|
63
|
+
the catch-all.
|
|
64
|
+
|
|
65
|
+
Templates available:
|
|
66
|
+
|
|
67
|
+
- `controlzero init` — Hello World template (default)
|
|
68
|
+
- `controlzero init -t rag` — RAG agent template (block exfiltration)
|
|
69
|
+
- `controlzero init -t mcp` — MCP server template
|
|
70
|
+
- `controlzero init -t cost-cap` — model allow-listing and cost guards
|
|
71
|
+
|
|
72
|
+
## Loading a policy
|
|
73
|
+
|
|
74
|
+
Three ways:
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
from controlzero import Client
|
|
78
|
+
|
|
79
|
+
# From a Python dict
|
|
80
|
+
cz = Client(policy={
|
|
81
|
+
"rules": [
|
|
82
|
+
{"deny": "delete_*"},
|
|
83
|
+
{"allow": "read_*"},
|
|
84
|
+
]
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
# From a YAML file
|
|
88
|
+
cz = Client(policy_file="./controlzero.yaml")
|
|
89
|
+
|
|
90
|
+
# From an environment variable
|
|
91
|
+
# (set CONTROLZERO_POLICY_FILE=./controlzero.yaml)
|
|
92
|
+
cz = Client()
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
If `./controlzero.yaml` exists in the current directory, it is picked up
|
|
96
|
+
automatically. No environment variable needed.
|
|
97
|
+
|
|
98
|
+
## Policy schema
|
|
99
|
+
|
|
100
|
+
```yaml
|
|
101
|
+
version: '1'
|
|
102
|
+
rules:
|
|
103
|
+
# Block any tool whose name starts with "delete_"
|
|
104
|
+
- deny: 'delete_*'
|
|
105
|
+
reason: 'Deletes need human approval'
|
|
106
|
+
|
|
107
|
+
# Allow specific known-good tools
|
|
108
|
+
- allow: 'search'
|
|
109
|
+
- allow: 'read_*'
|
|
110
|
+
|
|
111
|
+
# tool:method syntax
|
|
112
|
+
- allow: 'github:list_*'
|
|
113
|
+
- deny: 'github:delete_repo'
|
|
114
|
+
|
|
115
|
+
# Catch-all
|
|
116
|
+
- deny: '*'
|
|
117
|
+
reason: 'Default deny'
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Rules are evaluated top to bottom. The first match wins. If no rule matches,
|
|
121
|
+
the call is denied (fail-closed).
|
|
122
|
+
|
|
123
|
+
## Local audit log
|
|
124
|
+
|
|
125
|
+
When running without an API key, every decision is written to `./controlzero.log`
|
|
126
|
+
with daily rotation and 30-day retention. Tail it:
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
controlzero tail
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Configure rotation via the client:
|
|
133
|
+
|
|
134
|
+
```python
|
|
135
|
+
cz = Client(
|
|
136
|
+
policy_file="./controlzero.yaml",
|
|
137
|
+
log_path="./logs/controlzero.log",
|
|
138
|
+
log_rotation="10 MB", # rotate at 10 MB, or "daily", or "1 hour"
|
|
139
|
+
log_retention="30 days",
|
|
140
|
+
log_compression="gz", # gzip rotated files
|
|
141
|
+
log_format="json", # or "pretty"
|
|
142
|
+
)
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
When `CONTROLZERO_API_KEY` is set, audit ships to the remote dashboard and
|
|
146
|
+
these `log_*` options are ignored with a warning.
|
|
147
|
+
|
|
148
|
+
## Hybrid mode
|
|
149
|
+
|
|
150
|
+
If you set both an API key AND pass a local policy, the local policy
|
|
151
|
+
**overrides** the dashboard policy and you get a loud WARN log on init:
|
|
152
|
+
|
|
153
|
+
```
|
|
154
|
+
WARNING: controlzero: manual policy override detected. ...
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
This is intentional: it makes accidental prod bypass impossible to miss.
|
|
158
|
+
For prod environments, opt into strict mode to raise instead:
|
|
159
|
+
|
|
160
|
+
```python
|
|
161
|
+
cz = Client(api_key="cz_live_...", policy=local_policy, strict_hosted=True)
|
|
162
|
+
# RuntimeError: manual policy override detected ...
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Framework examples
|
|
166
|
+
|
|
167
|
+
Full integration guides at [docs.controlzero.ai/sdk/integrations](https://docs.controlzero.ai/sdk/integrations):
|
|
168
|
+
|
|
169
|
+
- LangChain
|
|
170
|
+
- LangGraph
|
|
171
|
+
- CrewAI
|
|
172
|
+
- OpenAI Agents SDK
|
|
173
|
+
- Anthropic tool use
|
|
174
|
+
- Pydantic AI
|
|
175
|
+
- AutoGen
|
|
176
|
+
- MCP servers
|
|
177
|
+
- Raw HTTP / no framework
|
|
178
|
+
|
|
179
|
+
## Hosted mode
|
|
180
|
+
|
|
181
|
+
When you want a dashboard, audit search, team policies, and approval workflows,
|
|
182
|
+
sign up at [controlzero.ai](https://controlzero.ai) and set the API key:
|
|
183
|
+
|
|
184
|
+
```python
|
|
185
|
+
import os
|
|
186
|
+
os.environ["CONTROLZERO_API_KEY"] = "cz_live_..."
|
|
187
|
+
|
|
188
|
+
from controlzero import Client
|
|
189
|
+
cz = Client() # picks up the API key from env, audit ships remote
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## License
|
|
193
|
+
|
|
194
|
+
Apache 2.0
|