OpenClaw Exec Approvals: Controlling What Your AI Agent Can Run
Your AI agent just ran rm -rf / on your production server. Okay, probably not — but the fact that it could is the kind of thing that keeps operators up at night. OpenClaw's exec approval system exists to make sure you sleep well.
Exec approvals are the guardrail between your agent's intent and your host machine's shell. They sit on top of tool policy and sandboxing, adding a final "are you sure?" layer before any command touches real hardware. Think of it as a safety interlock: commands only run when policy, allowlist, and (optionally) your explicit approval all agree.
Where Exec Approvals Apply
Exec approvals are enforced locally on the execution host — not in the cloud, not in the agent's reasoning loop, but right where the command would actually run:
- Gateway host — the machine running your OpenClaw gateway process
- Node host — a paired device (macOS companion app, headless Linux node, etc.)
This matters because trust is local. Your gateway machine trusts the gateway process. A paired node extends that trust to whatever device it runs on. Exec approvals reduce accidental execution risk within that trust boundary — they're not a per-user auth system.
On macOS specifically, the node host service forwards system.run requests to the macOS companion app over local IPC. The app enforces approvals and executes commands in the UI context, so you see exactly what's running.
The Three Policy Knobs
Exec approvals boil down to three settings that work together. Understanding these is the whole game.
Security Mode
Controls what can run:
deny— block all host exec requests. Nothing gets through.allowlist— only commands matching your allowlist can run.full— allow everything. Equivalent to elevated mode.
Ask Mode
Controls when you get prompted:
off— never prompt. Policy decides silently.on-miss— prompt only when the allowlist doesn't match. This is the sweet spot for most setups.always— prompt on every single command, even allowlisted ones.
Ask Fallback
What happens when a prompt is required but nobody's home to answer it (no UI reachable):
deny— block the command. Safe default.allowlist— allow only if the allowlist matches.full— allow everything even without a human. Use with extreme caution.
The effective policy is always the stricter of your OpenClaw config (tools.exec.*) and the host-local approvals file. If you set ask: "off" in config but the host file says ask: "always", the host wins.
The Approvals File
Everything lives in one JSON file on the execution host:
~/.openclaw/exec-approvals.json Here's what a real setup looks like:
{\n "version": 1,\n "defaults": {\n "security": "allowlist",\n "ask": "on-miss",\n "askFallback": "deny"\n },\n "agents": {\n "main": {\n "security": "allowlist",\n "ask": "on-miss",\n "autoAllowSkills": true,\n "allowlist": [\n {\n "pattern": "/opt/homebrew/bin/rg",\n "lastUsedAt": 1737150000000,\n "lastUsedCommand": "rg -n TODO"\n },\n {\n "pattern": "~/.local/bin/*"\n }\n ]\n }\n }\n} Key things to notice:
- Per-agent allowlists. Each agent gets its own. One agent's approvals don't leak into another's.
- Glob patterns for allowlist entries. Case-insensitive. Must resolve to binary paths — basename-only entries are ignored.
- Usage tracking. Each entry records when it was last used and what command triggered it. Helps you audit and prune.
You can edit this file directly, but the cleaner approach is the CLI:
# Inspect current policy
openclaw approvals get
# Check a specific node
openclaw approvals get --node my-macbook
# Set policy via stdin
openclaw approvals set --stdin <<'EOF'
{\n "version": 1,\n "defaults": {\n "security": "allowlist",\n "ask": "on-miss",\n "askFallback": "deny"\n }\n}\nEOF Or use the Control UI → Nodes → Exec approvals card to manage everything visually — pick a scope (defaults or a specific agent), adjust policies, add or remove allowlist patterns, and save.
Want the complete security configuration guide for production agents?
Safe Bins: Auto-Allowing Harmless Tools
Not every command needs an allowlist entry. Some tools are so narrowly scoped — stdin in, stdout out — that gating them just creates friction. That's what safe bins are for.
OpenClaw ships with a small default set: cut, uniq, head, tail, tr, wc. These can run in allowlist mode without explicit entries because they're restricted to stdin-only operation.
The restrictions are real:
- Positional file arguments are rejected — no sneaking in file reads.
- Argv tokens are treated as literal text at execution time — no glob expansion, no
$VARS. - Binaries must resolve from trusted directories (default:
/bin,/usr/bin). - Specific flags that break stdin-only behavior are denied per-binary (e.g.,
sort -o,grep -r,jq -f).
You can add more safe bins in config, but be deliberate about it:
{\n "tools": {\n "exec": {\n "safeBins": ["jq", "myfilter"],\n "safeBinTrustedDirs": ["/opt/homebrew/bin"],\n "safeBinProfiles": {\n "myfilter": {\n "minPositional": 0,\n "maxPositional": 0,\n "allowedValueFlags": ["-n", "--limit"],\n "deniedFlags": ["-f", "--file", "-c", "--command"]\n }\n }\n }\n }\n} Never add interpreters to safe bins. python3, node, ruby, bash — these can evaluate arbitrary code, execute subcommands, and read files by design. Use explicit allowlist entries for those, and consider enabling strictInlineEval (more on that below).
Inline Eval Hardening
Even if you allowlist python3, you probably don't want your agent running python3 -c "import os; os.system('rm -rf /')" without a prompt. That's what strictInlineEval is for.
{\n "tools": {\n "exec": {\n "strictInlineEval": true\n }\n }\n} When enabled, inline eval forms like python -c, node -e, ruby -e, perl -e, php -r, lua -e, and osascript -e always require explicit approval — even if the interpreter binary is allowlisted. The "allow always" action won't auto-persist these entries either.
This is defense-in-depth for interpreter commands that don't map cleanly to one stable file. If the agent runs python3 myscript.py, that's fine — the file can be bound and verified. But python3 -c '...' is arbitrary code with no file to check, so it gets extra scrutiny.
Auto-Allow Skill CLIs
If you're using OpenClaw skills, you can automatically allowlist the executables those skills need. Set autoAllowSkills: true in your approvals config and OpenClaw will fetch the skill bin list via the Gateway RPC, treating those binaries as implicitly allowlisted on nodes.
This is a convenience for trusted environments where your gateway and nodes share a trust boundary. If you need strict explicit trust, keep it disabled and add manual allowlist entries instead.
The YOLO Mode Escape Hatch
Sometimes you just want everything to work without prompts. Development machines, throwaway environments, personal setups where you trust the agent completely — YOLO mode is for you.
To go fully permissive, open both policy layers:
# Config layer
openclaw config set tools.exec.host gateway
openclaw config set tools.exec.security full
openclaw config set tools.exec.ask off
openclaw gateway restart
# Host approvals layer
openclaw approvals set --stdin <<'EOF'
{\n "version": 1,\n "defaults": {\n "security": "full",\n "ask": "off",\n "askFallback": "full"\n }\n}\nEOF Both layers must agree. If you set YOLO in config but the host approvals file is stricter, the host wins. This is intentional — local machine policy always has final say.
For one-off sessions where you don't want to change persistent config:
/exec security=full ask=off— changes only the current session/elevated full— break-glass shortcut that also skips exec approvals
Approval Forwarding to Chat
Here's where it gets interesting for teams. Instead of requiring someone to sit at the macOS app clicking "Allow," you can forward approval prompts to any chat channel — Slack, Telegram, Discord — and approve them with /approve.
{\n "approvals": {\n "exec": {\n "enabled": true,\n "mode": "targets",\n "agentFilter": ["main"],\n "targets": [\n { "channel": "slack", "to": "U12345678" },\n { "channel": "telegram", "to": "123456789" }\n ]\n }\n }\n} When the agent needs approval, the prompt shows up in your Slack DM (or wherever you configured). You see the full command, working directory, agent ID, and resolved executable path. Then reply:
/approve <id> allow-once # Run this one time
/approve <id> allow-always # Add to allowlist and run
/approve <id> deny # Block it Same-chat approvals also work: if the exec request originated from a Slack conversation, you can /approve right there without needing a separate forwarding target. Pending approvals expire after 30 minutes by default.
For Discord and Telegram, native approval delivery adds DM routing and interactive approval buttons on top of the text-based /approve flow. Configure via channels.discord.execApprovals or channels.telegram.execApprovals.
What Happens When Approval Is Denied
When you deny an exec request, OpenClaw doesn't just block the command — it prevents the agent from reusing output from any earlier run of the same command in the session. The denial includes explicit guidance that no command output is available, which stops the agent from hallucinating results or acting on stale data from a prior successful run.
This is a subtle but important detail. Without it, an agent could run cat /etc/passwd once (approved), get denied on a second attempt, and still reference the first run's output as if it were current.
Recommended Setups
Personal Dev Machine
You trust the agent, but want a safety net for destructive commands:
defaults:
security: "allowlist"
ask: "on-miss"
askFallback: "deny" Build up your allowlist organically — approve commands as they come, use "allow always" for tools you trust, and review the list occasionally.
Production Server
Lock it down. Only pre-approved commands, always prompt on misses, deny when nobody's watching:
defaults:
security: "allowlist"
ask: "always"
askFallback: "deny" Combine with strictInlineEval: true and forward approvals to your ops channel so the on-call engineer can approve time-sensitive commands from their phone.
Multi-Agent Setup
Different agents get different trust levels. Your main orchestrator might have broad access while a content-writing agent gets almost none:
"agents": {\n "main": { "security": "allowlist", "ask": "on-miss" },\n "writer": { "security": "deny" }\n} Per-agent allowlists keep boundaries clean. See building a team of AI agents for the full multi-agent architecture.
Debugging Approvals
If a command isn't running and you're not sure why, start with:
openclaw approvals get This shows the requested policy, host policy sources, and the effective result after both layers are merged. For nodes:
openclaw approvals get --node <id|name|ip> Common gotchas:
- Host is stricter than config. The host always wins. If your config says
fullbut the host file saysallowlist, you getallowlist. - Basename-only allowlist entries are ignored. Use full paths or glob patterns:
/opt/homebrew/bin/rg, not justrg. - Shell chains need every segment allowed. Running
echo ok && pwdrequires bothechoandpwdto satisfy allowlist rules. - Redirections aren't supported in allowlist mode. Shell chaining (
&&,||,;) works when every segment passes, but>and<don't.
Also check openclaw security audit — it warns about things like interpreters in your safe bins list without explicit profiles.
The Security Stack in Context
Exec approvals are one layer in OpenClaw's defense-in-depth approach:
- Tool policy — controls which tools the agent can use at all
- Sandboxing — isolates exec to a sandbox environment by default
- Elevated mode — controlled breakout from sandbox to host
- Exec approvals — per-command gatekeeping on the host
- Allowlists + safe bins — granular trust for specific binaries
Each layer catches what the others miss. An agent could have exec tool access (policy), break out of sandbox (elevated), but still be stopped by an allowlist miss (approvals). That's the point — no single layer is the whole story.
For the full picture of how these layers interact, the security safety rails guide covers the complete model.
Ready to lock down your agent's exec access?
The OpenClaw Playbook covers exec approvals, sandboxing, elevated mode, and 30+ production security workflows.