Bundled Policies¶
The exact verbs and subverbs allowed or blocked by each bundled policy in read mode.
Bundled policies live in internal/embed/policies/ and are compiled into the binary. They are always the first layer in the chain (evaluated before user and repo policies). In read & write mode, no bundled block rule fires. Mode is checked as the first condition in every rule.
Policy packages follow the naming convention guard.bundled.<tool>.
kubectl: guard.bundled.kubectl¶
Source: internal/embed/policies/kubectl.rego
Read-mode allowed verbs (the read_verbs set):
get describe logs exec port-forward top
version cluster-info config explain
api-resources api-versions auth diff wait
Special carve-out for apply --dry-run: kubectl apply with --dry-run=client, --dry-run=server, or --dry-run=true is allowed even in read mode.
All other verbs are blocked with reason: "kubectl <verb> blocked in read mode".
Examples of blocked verbs: delete, scale, patch, drain, cordon, uncordon, taint, create, replace, rollout, label, annotate, expose, set, run, cp, attach.
Full rule (exact source):
read_verbs := {
"get", "describe", "logs", "exec", "port-forward", "top",
"version", "cluster-info", "config", "explain",
"api-resources", "api-versions", "auth", "diff", "wait",
}
block contains {"reason": sprintf("kubectl %s blocked in read mode", [input.verb])} if {
input.mode == "read"
input.tool == "kubectl"
input.verb != ""
not input.verb in read_verbs
not allowed_dry_run
}
allowed_dry_run if {
input.verb == "apply"
input.flags["dry-run"] in {"client", "server", "true"}
}
helm: guard.bundled.helm¶
Source: internal/embed/policies/helm.rego
Read-mode allowed verbs (the read_verbs set):
repo verb, special subverb handling: helm repo list is allowed; any other helm repo <subverb> (e.g. add, remove, update) is blocked with reason "helm repo <subverb> blocked in read mode".
All other verbs are blocked with reason "helm <verb> blocked in read mode".
Examples of blocked verbs: install, upgrade, uninstall, rollback, push, package, dependency.
Full rule (exact source):
read_verbs := {"list", "get", "status", "show", "search", "version", "history", "template"}
block contains {"reason": sprintf("helm %s blocked in read mode", [input.verb])} if {
input.mode == "read"
input.tool == "helm"
input.verb != ""
input.verb != "repo"
not input.verb in read_verbs
}
block contains {"reason": sprintf("helm repo %s blocked in read mode", [input.subverb])} if {
input.mode == "read"
input.tool == "helm"
input.verb == "repo"
input.subverb != "list"
}
terraform / tofu: guard.bundled.terraform¶
Source: internal/embed/policies/terraform.rego
Both terraform and tofu binaries are matched by the same tool parser and evaluated against this policy (input.tool == "terraform" for both).
Read-mode allowed verbs (the read_verbs set):
state verb, special subverb handling: terraform state list and terraform state show are allowed; any other subverb (e.g. mv, rm, pull, push) is blocked with reason "terraform state <subverb> blocked in read mode".
All other verbs are blocked with reason "terraform <verb> blocked in read mode".
Examples of blocked verbs: apply, destroy, import, init, refresh, taint, untaint, workspace (non-read subverbs).
Full rule (exact source):
read_verbs := {"plan", "show", "output", "validate", "fmt", "providers", "version", "graph"}
block contains {"reason": sprintf("terraform %s blocked in read mode", [input.verb])} if {
input.mode == "read"
input.tool == "terraform"
input.verb != ""
input.verb != "state"
not input.verb in read_verbs
}
block contains {"reason": sprintf("terraform state %s blocked in read mode", [input.subverb])} if {
input.mode == "read"
input.tool == "terraform"
input.verb == "state"
not input.subverb in {"list", "show"}
}
aws: guard.bundled.aws¶
Source: internal/embed/policies/aws.rego
The AWS CLI uses a <service> <operation> pattern, parsed as verb (service) + subverb (operation).
Always allowed (no block rule matches):
- Any
aws stscommand (all subverbs). aws s3 ls.- Any operation whose name starts with
describe-,list-, orget-on any service. - A bare service invocation with no operation (e.g.
aws ec2,aws --help).
Blocked in read mode:
aws s3 <subverb>wheresubverbis notlsand not empty. Reason:"aws s3 <subverb> blocked in read mode".- Any
aws <service> <operation>where service is notstsors3, operation is not empty, and operation does not start withdescribe-,list-, orget-. Reason:"aws <service> <operation> blocked in read mode".
Examples of blocked operations: aws s3 rm, aws ec2 run-instances, aws ec2 terminate-instances, aws eks create-cluster, aws eks delete-cluster, aws iam create-role, aws ecr batch-delete-image.
Full rule (exact source):
block contains {"reason": sprintf("aws s3 %s blocked in read mode", [input.subverb])} if {
input.mode == "read"
input.tool == "aws"
input.verb == "s3"
input.subverb != ""
input.subverb != "ls"
}
block contains {"reason": sprintf("aws %s %s blocked in read mode", [input.verb, input.subverb])} if {
input.mode == "read"
input.tool == "aws"
input.verb != ""
input.verb != "sts"
input.verb != "s3"
input.subverb != ""
not is_read_action(input.subverb)
}
is_read_action(action) if startswith(action, "describe-")
is_read_action(action) if startswith(action, "list-")
is_read_action(action) if startswith(action, "get-")
git: guard.bundled.git¶
Source: internal/embed/policies/git.rego
The bundled git policy is intentionally empty: it declares no block rules. Git is allowed by default. Individual repos enforce their own constraints (force-push guards, branch protections) via .failsafe.rego.
The file exists so that failsafe policies list includes git in the bundled set, making the coverage explicit.
failsafe: guard.bundled.failsafe¶
Source: internal/embed/policies/failsafe.rego
This policy guards the failsafe binary itself (dogfood). Rules fire regardless of mode: the read/read & write distinction does not apply.
| Verb | Decision | Reason |
|---|---|---|
toggle |
BLOCK | "failsafe toggle is user-only: toggle from your terminal (hotkey or failsafe toggle typed yourself), never via Claude" |
hook |
BLOCK | "failsafe hook is a lifecycle subprocess started by Claude Code; not for direct tool invocation" |
mcp |
BLOCK | "failsafe mcp is a lifecycle subprocess started by Claude Code; not for direct tool invocation" |
All other failsafe subcommands (explain, report, audit, trust, validate, tools, policies, mode, test) are not blocked by this policy.
Full rule (exact source):
block contains {"reason": "failsafe toggle is user-only — toggle from your terminal (hotkey or `failsafe toggle` typed yourself), never via Claude"} if {
input.tool == "failsafe"
input.verb == "toggle"
}
block contains {"reason": sprintf("failsafe %s is a lifecycle subprocess started by Claude Code; not for direct tool invocation", [input.verb])} if {
input.tool == "failsafe"
input.verb in {"hook", "mcp"}
}