Policies & Safety¶
Four layers decide what the agent may do, evaluated in a fixed order for every tool call. This page shows each layer procedurally: what to write, where to put it, and what happens at runtime.
Order of authority¶
1. permission_request hooks (harness policies / plugins: can veto anything)
2. exec-policy rules (your YAML: allow / deny / ask for shell commands)
3. permission manager (built-in: dangerous-command guards, tool config)
4. ask flow (TUI prompt, or pause in harness runs)
Two invariants no layer can break:
- A hard deny (dangerous-command guard like
rm -rf, deny pattern, explicit DENY config) always wins: a permissive hook, rule, or session grant cannot override it. - A user
askrule always forces a prompt, even for commands that would auto-allow.
Exec policy: your allow/deny/ask rules¶
Write declarative rules for shell commands. Project rules take precedence over user rules:
# .superqode/execpolicy.yaml (project) or ~/.superqode/execpolicy.yaml (user)
rules:
- pattern: "pytest*" # glob against the full command
action: allow # skip the prompt
- pattern: "git push*"
action: ask # always confirm, even if auto-allowed
- pattern: "re:^rm\\s+-rf\\s+/" # 're:' prefix = regex
action: deny
reason: "refuse rm -rf on absolute paths"
- pattern: "npm publish*"
action: deny
reason: "no publishing from agents"
First matching rule decides; no match means no opinion. SUPERQODE_EXEC_POLICY=<path> prepends an explicit file (CI, tests). A denied command returns Command blocked by exec policy: <reason> to the model.
Try it:
mkdir -p .superqode
cat > .superqode/execpolicy.yaml <<'EOF'
rules:
- pattern: "pytest*"
action: allow
EOF
superqode -p "run the test suite" # pytest now runs without a prompt
Shell env policy: keep secrets out of spawned commands¶
By default spawned commands inherit your full environment. Opt in to secret filtering and a prompt-injected printenv exfiltrates nothing:
export SUPERQODE_SHELL_ENV_POLICY=filter-secrets
export SUPERQODE_SHELL_ENV_ALLOW=OPENAI_API_KEY # exceptions, comma-separated
Variables whose names match *KEY*, *TOKEN*, *SECRET*, *PASSWORD*, *CREDENTIAL*, *AUTH* are dropped from bash and shell_session children. SSH_AUTH_SOCK is always kept (it's a socket path, not a secret).
request_permissions: escalation with consent¶
When the agent keeps hitting ask-prompts for the same tool, it can make one explicit, justified request:
agent โ request_permissions(tools=["web_fetch"],
justification="need to read the upstream changelog for the fix")
you โ approve / decline (normal approval prompt, justification shown)
Approval upgrades those tools from ask-each-time to allowed for this session only; :permissions state resets with the session, and explicit denies remain unbreakable. A declined request tells the model not to retry.
The built-in permission manager¶
Configure per-tool and per-group permissions in superqode.yaml (see Safety & Permissions for the full schema):
permissions:
default: ask
tools:
read_file: allow
bash: ask
deny_patterns: ["bash:sudo *"]
The manager also auto-allows known read-only commands (ls, git status, and similar) and trusted-registry network commands when the policy would otherwise prompt, cutting prompt fatigue without weakening safety. Dangerous commands are hard-denied regardless of any other setting.
OS sandbox¶
With SUPERQODE_SANDBOX set, even auto-approved commands are confined by the OS through macOS Seatbelt (sandbox-exec) or Linux Bubblewrap (bwrap), so they cannot write outside the project. Harness specs select sandbox modes per run (execution_policy.sandbox); superqode harness doctor verifies the backend is available.
Loop-level guards¶
Two more protections live in the agent loop itself, documented in Inside the Agent Loop:
- the doom-loop guard (repeated identical tool calls intercepted, runs that refuse to move on are stopped), and
- mutation-safe parallelism (concurrent execution only for all-read-only batches).