AI agent governance for Go. Policies, audit, and observability for tool calls. Works locally with no signup.
package main
import (
"fmt"
"log"
"controlzero.ai/sdk/go"
)
func main() {
cz, err := controlzero.New(controlzero.WithPolicy(map[string]any{
"rules": []any{
map[string]any{"deny": "delete_*", "reason": "Hello World: deletes are blocked"},
map[string]any{"allow": "*", "reason": "Hello World: everything else is fine"},
},
}))
if err != nil {
log.Fatal(err)
}
defer cz.Close()
d, _ := cz.Guard("delete_file", controlzero.GuardOptions{
Args: map[string]any{"path": "/tmp/foo"},
})
fmt.Println(d.Decision()) // "deny"
d, _ = cz.Guard("read_file", controlzero.GuardOptions{
Args: map[string]any{"path": "/tmp/foo"},
})
fmt.Println(d.Decision()) // "allow"
}No API key. No signup. Run it.
go get controlzero.ai/sdk/go@v1.7.6Pin to a specific version for reproducible builds. The version source of
truth is VERSION; bumping that file triggers an
auto-tagged release on the public mirror repo (handled by the monorepo
sync workflow).
go install controlzero.ai/sdk/go/cmd/controlzero@latest
controlzero init
controlzero validate
controlzero test delete_fileThe generated controlzero.yaml is the tutorial. It ships with annotated
rules covering allow lists, deny lists, wildcards, and the catch-all.
Templates available (controlzero init -t <name>):
generic- Hello World template (default)rag- RAG agent template (block exfiltration)mcp- MCP server templatecost-cap- model allow-listing and cost guardsclaude-code- Claude Code hook starterlangchain- LangChain tool guardrailscrewai- CrewAI starter policycursor- Cursor / editor hook starterautogen- AutoGen starter policy
Three ways:
// From a Go map
cz, _ := controlzero.New(controlzero.WithPolicy(map[string]any{
"rules": []any{
map[string]any{"deny": "delete_*"},
map[string]any{"allow": "read_*"},
},
}))
// From a YAML file
cz, _ := controlzero.New(controlzero.WithPolicyFile("./controlzero.yaml"))
// From an environment variable
// (set CONTROLZERO_POLICY_FILE=./controlzero.yaml)
cz, _ := controlzero.New()If ./controlzero.yaml exists in the current directory, it is picked up
automatically. No env var needed.
version: '1'
rules:
- deny: 'delete_*'
reason: 'Deletes need human approval'
- allow: 'search'
- allow: 'read_*'
- allow: 'github:list_*'
- deny: 'github:delete_repo'
- deny: '*'
reason: 'Default deny'Rules are evaluated top to bottom. The first match wins. If no rule matches, the call is denied (fail-closed).
The reason is plain UTF-8 text, so it can be written in any language. To
serve more than one language from one policy, add a reason_localized map and
select a locale with the CONTROLZERO_LOCALE environment variable (ko,
ko-KR, ...):
version: '1'
rules:
- deny: 'delete_*'
reason: 'Deletes need human approval' # English default
reason_localized:
ko: '삭제는 사람의 승인이 필요합니다'
- allow: '*'When CONTROLZERO_LOCALE is unset (or has no entry for the rule) the plain
reason is used, so existing behavior is unchanged. The SDK's built-in
no-rule-match and empty/observe-bundle messages also ship an English default
plus a Korean translation via in-binary maps (no runtime i18n dependency --
air-gap safe). Localization is display-only and never changes the decided
effect.
When running without an API key, every decision is written to ./controlzero.log.
Configure rotation via options:
cz, _ := controlzero.New(
controlzero.WithPolicyFile("./controlzero.yaml"),
controlzero.WithLogPath("./logs/controlzero.log"),
controlzero.WithLogRotation(10, 5, 30, true), // 10MB, 5 backups, 30 days, gzip
controlzero.WithLogFormat("json"), // or "pretty"
)When CONTROLZERO_API_KEY is set, audit ships to the remote dashboard and
log options are ignored with a warning.
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.
For prod, use WithStrictHosted() to return an error instead:
cz, err := controlzero.New(
controlzero.WithAPIKey("cz_live_..."),
controlzero.WithPolicy(localPolicy),
controlzero.WithStrictHosted(),
)
// err is ErrHybridModeHosted mode ships dashboard-managed signed policy bundles and remote audit.
Pass your project API key; New hits /v1/sdk/bootstrap, pulls the signed
.czpolicy bundle, verifies the signature, decrypts locally, and enforces
every call against the dashboard policy. Audit streams to the remote trail.
client, err := controlzero.New(controlzero.WithAPIKey("cz_live_..."))
if err != nil {
log.Fatal(err)
}
defer client.Close()Use NewWithContext(ctx, ...) to pass your own context. Bootstrap keys
and the bundle are cached under ~/.controlzero/cache/ so restarts work
offline.
Full integration guides at docs.controlzero.ai/sdk/integrations.
Apache 2.0