Command rules
For: tenant admins and governance reviewers.
Command rules decide the shell commands a harness asks to run. Add a tenant rule below, then review the matching and parser limits before allowing a broader class of commands. Keep the sandbox enabled: these rules cover commands the harness reports to the platform.
A harness agent runs commands of its own: Claude's Bash, a Codex, Gemini CLI, OpenCode or goose session's execute calls and the terminals it opens through the client, and a remote session's bash. These are not functions of the tenant's plugins, so the function rules do not decide them. Command rules do. They live in the same tool policy, beside the function rules, with the same three outcomes, the same reasons, ends, shadow mode, four eyes, export and apply, and the same audit trail.
On every node, for every tenant, with nothing installed and no model asked, each command a harness asks to run is read as a syntax tree and decided before it runs:
| Outcome | What happens |
|---|---|
| Allow | The command goes on, as the job's hooks and approvals say. |
| Ask | A person decides, on an approval that names the rule and its reason. Their no reaches the agent as a refused call. |
| Refuse | The command is not run, and the agent reads the rule's reason. |
The platform's default rules only ask; they never refuse on their own. Only the tenant's own Refuse refuses.
The default rules
These ask a person about catastrophic commands, on by default. Each has an ID a tenant's rules name, a reason the approval card shows, and examples that are checked whenever the rules load: each match example must still be found and each not_match example must not, or the node asks a person about every command rather than decide by rules that no longer say what they did.
| ID | It asks about | Such as |
|---|---|---|
cmd.delete-home | Deleting a home, the root, a top-level folder, a folder above the working directory, or a path it cannot resolve, recursively | rm -rf ~, rm -rf /*, cd ~ && rm -rf *, rm -rf "$DIR"/, find ~ -delete, find / -exec rm {} + |
cmd.disk-erase | Erasing a disk | mkfs.ext4 /dev/sdb1, wipefs -a /dev/sda, dd of=/dev/sda, > /dev/nvme0n1, /usr/bin/time -o /dev/sda, shred /dev/sda, sgdisk --zap-all, diskutil eraseDisk |
cmd.chmod-root | Changing permissions or owners of the root recursively | chmod -R 777 /, chown -R nobody /usr |
cmd.fork-bomb | A function that calls itself | :(){ :|:& };: |
cmd.download-run | Running what it downloads, unread | curl ... | sh, wget -qO- ... | sudo bash, bash <(curl ...), sh -c "$(curl ...)" |
cmd.default-branch | Rewriting or deleting a default branch | git push --force origin main (and master, trunk, develop, or the current branch when none is named), git push origin --delete main |
cmd.git-clean-home | Deleting what git does not track in a home | cd ~ && git clean -fdx |
cmd.cloud-delete | Deleting cloud resources | gh repo delete, gh release delete, kubectl delete namespace, kubectl delete ... --all -A, terraform destroy and apply -destroy, aws s3 rb --force, aws s3 rm s3://bucket --recursive, docker system prune -a --volumes |
cmd.unreadable | A command with a construct the reader does not model exactly: the floor | an unclosed quote, eval, a loop, a pattern (ls *.txt), $(...), $HOME, sh -c '...', xargs, git -c ..., and everything else outside what the reader reads |
oz0 policy show lists them with their version; Govern › Policy › Commands shows them with their reasons. Each default is found by one or more checks (rm-recursive, find-delete, download-to-shell and the others); the card names both, such as "cmd.delete-home (rm-recursive)". A home spelt in another case (rm -rf /USERS/kevin) is the home, since macOS's file system ignores case by default.
Your own rules
Add a rule as you add a function rule, with --command and the command's pattern instead of a function:
oz0 policy refuse --command 'git push --force * [default-branch]' --reason "nobody rewrites main"
oz0 policy ask --command 'kubectl delete **' --reason "deletes in the cluster need a person"
oz0 policy allow --command 'terraform destroy -target=module.test.*' --agent infra --reason "test modules only"
oz0 policy allow --command 'curl -fsSL https://get.k3s.io | sh' --agent installer --expires 72h --reason "the k3s bootstrap"
A rule over a default names it by its ID instead of a pattern, to turn it off (Allow), make it refuse, or either for some agents, callers or target classes. It is stored by the ID, so a later version of the defaults never undoes it:
oz0 policy allow --default cmd.download-run --agent installer --reason "the installer runs vendor scripts"
oz0 policy refuse --default cmd.cloud-delete --reason "agents never delete cloud resources"
oz0 policy refuse --default cmd.delete-home --target home --reason "homes are never deleted"
oz0 policy refuse --default cmd.unreadable --agent nightly --reason "unattended jobs run only what can be read"
--agent (repeatable) narrows a rule to those agents' jobs, --caller to calls by some plugins, --target a rule over a default to what it finds of these classes: home, root, outside-workdir, unresolvable, device, default-branch. An Allow narrowed so allows a command only when every path the default found in it is of those classes: a delete of an unresolvable path and a home together still asks under an Allow for unresolvable. A Refuse or an Ask narrowed so decides when any of them is.
--shadow, --expires and --id work as for function rules, and so do oz0 policy edit and remove by the rule's ID. --agent on a function rule is the condition caller.agent == '<name>'.
The --reason is what the agent reads when the rule refuses a command, and what the approval card says when it asks, so write it for them: "homes are never deleted" rather than "per review".
Related tasks and reference
- Command rule patterns
- What the command reader understands
- Create a command rule from an approval
- Command rule evaluation and limits
Previous section links
allowlist a command from its card: Read the section.
governed audited exported: Read the section.
patterns: Read the section.
what it sees and what it does not: Read the section.
what the reader reads: Read the section.
which rule decides: Read the section.
which rules a running job uses: Read the section.