🧰 Tooling Guide

OpenClaw Exec Approvals: An Allowlist You Can Leave Running Overnight

•7 min read

On a gateway host, the documented default for exec security is full. Your agent can run shell commands on that machine and nobody gets asked.

Most people find this out the first time they read an approvals file closely, usually after an agent did something with rm that was technically what they asked for. Exec approvals are the interlock that sits on the machine actually running the command (the gateway, or a paired node), and a command runs only when the policy, the allowlist and, if configured, a human all agree. The search results for this topic are, as of today, the official docs and copies of the official docs. They explain every key. What they leave out is what happens to an approval prompt at 3 a.m. when nobody is awake to tap it, and what your allowlist looks like after six months of tapping "allow always". Those two things are where this setup breaks in practice.

The Shape

Three settings decide every exec call. security is the ceiling: deny blocks host exec, allowlist runs only matched commands, full runs anything. ask decides when a human gets prompted: off, on-miss (only when the allowlist does not match), or always. And askFallback decides what happens to a prompt when no approval UI is reachable. Omit it and it is deny.

{
  "version": 1,
  "defaults": {
    "security": "allowlist",
    "ask": "on-miss",
    "askFallback": "deny"
  },
  "agents": {
    "repo-janitor": {
      "allowlist": [
        { "pattern": "/opt/homebrew/bin/rg" },
        { "pattern": "git", "argPattern": "^(status|diff|log)( .*)?$" }
      ]
    }
  }
}

Where this lives depends on your release. Older builds kept it in ~/.openclaw/exec-approvals.json and newer ones keep it in the state database under ~/.openclaw/state, so do not hand-edit a file you found by searching. Read and write it through the CLI:

openclaw approvals get
openclaw exec-policy show --agent repo-janitor --verbose
openclaw approvals set --stdin < approvals.json

exec-policy show is the one I run most. It prints the effective policy after config, session and host settings have been merged, which is the only version of the truth the gateway cares about. A host-local ask: "always" keeps prompting even when openclaw.json asks for on-miss, and you will not see that by reading either file alone.

Patterns Match Binaries

An allowlist pattern is either a resolved path glob like /opt/homebrew/bin/rg or ~/.local/bin/*, or a bare command name. The bare name is narrower than it looks. rg matches when the agent types rg and PATH resolves it, and it does not match ./rg or /tmp/rg, which is exactly the protection you want against a downloaded binary sitting in the workspace.

argPattern is an ECMAScript regex over the arguments, joined with single spaces, not counting the binary itself. Use it on anything with a destructive subcommand. Allowing git outright means allowing git push --force. The entry in the example above lets the janitor read history all day and sends anything else to a prompt.

Chains get checked segment by segment. rg TODO && curl ... needs both halves on the list, so an allowed first command cannot carry an unlisted second one through.

The 3 a.m. Problem

This is the angle the docs leave for you to work out, and it is the one I would put money on biting anyone who runs scheduled jobs. With ask: on-miss the chat session feels safe, because every unusual command pops a prompt in Telegram with reactions for allow once, allow always and deny (or a plain /approve if your channel has no reactions). Then the nightly backup cron calls tar with a flag it has never used, the prompt goes nowhere because there is no live session to deliver it to, askFallback resolves it as deny, and the job fails at 3:12 a.m. with an exec error you will read at breakfast, if you read it at all.

Leave askFallback on deny. Give unattended agents their own agent ID and an allowlist that covers the job completely, written from what the job actually runs. The allow and deny list guide has the sessions_history prompt for pulling a week of tool calls, and it works the same way for exec commands: ask for every command line, not every tool. Then run the job once by hand, in a session where you can see the prompts, and add whatever it asks for. A cron agent should never need a human. If it does, the list is wrong.

Keep the chat agent on on-miss. That one has you.

Allow-Always Is a Ratchet

Every time you tap allow always, OpenClaw writes a new allowlist entry with source: "allow-always". Nothing ever removes one. Six months in, the list is a record of every Tuesday afternoon you were too busy to read a prompt, and on-miss has quietly become something close to full.

The entries carry the evidence you need to undo that. Each one records lastUsedAt in Unix milliseconds, plus lastUsedCommand and lastResolvedPath. Dump the document and sort:

openclaw approvals get --json > approvals-$(date +%F).json

jq -r '.agents | to_entries[] | .key as $a
  | .value.allowlist[]?
  | [$a, .source // "manual", ((.lastUsedAt // 0) / 1000 | todate), .pattern, (.lastUsedCommand // "")]
  | @tsv' approvals-$(date +%F).json | sort -k3

Flag names on get vary by release, so drop --json if yours already prints JSON. Read the top of that output, the oldest entries. Anything with source set to allow-always that has not been used in 30 days comes off. Anything that has been used but whose lastResolvedPath is somewhere you do not recognize gets read very carefully before it comes off. Manual entries stay unless you know why they are there and the reason is gone.

MCP tools get the same treatment under mcpTools, and they are listed and revoked separately:

openclaw approvals grants list
openclaw approvals grants revoke <grant-id>

An MCP grant covers that tool with any arguments, which makes it broader than most exec entries. Give those the 30-day rule too.

Commit the dated JSON file somewhere private before you prune. If you cut something a monthly job needed, the diff tells you what to put back, and the backup and migration guide covers where the rest of the state directory should live.

Inline Eval

Turn on tools.exec.strictInlineEval. It treats python -c, node -e, osascript -e and the inline forms of tools like find -exec and xargs as approval-only, regardless of full or off, and allow always will not turn them into permanent entries. An allowlisted python3 is otherwise a full shell with extra steps.

Two Canaries

Same rule as every other policy on this site: watch it fail once. Restart the gateway, open a fresh session with the cron agent, and ask it to run curl -s https://example.com. Pass is a denial that names the approval policy. Then ask the chat agent the same thing. Pass there is a prompt landing on your phone. If either agent just returns HTML, run exec-policy show for it and look for a host-level full overriding what you set.

Put both canaries on the checklist in the upgrade guide. Approval storage has moved at least once already.

Common Failure Modes

A cron job fails with an exec denial that never prompted anyone

No approval UI was reachable and askFallback resolved to deny. Widen that agent's allowlist to cover the job; do not loosen the fallback.

The agent runs things you never approved

Check exec-policy show for security: full, which is the gateway default. Then check for autoAllowSkills, which trusts binaries referenced by installed skills.

It keeps prompting after you set on-miss

A host-local ask: always wins. Fix it on the execution host, not in openclaw.json.

An approved script was denied anyway

OpenClaw binds the approval to the file. If the script changed between the prompt and the run, it refuses to execute the new contents, which is correct.

Final Verdict

Run allowlist with on-miss and a deny fallback everywhere, split anything unattended into its own agent with a complete list, and turn on strict inline eval. Then put a monthly reminder on the calendar to sort the allowlist by lastUsedAt and cut the stale allow-always entries, because the policy you configured in an afternoon is not the policy you will be running by spring. The security best practices guide tells you to read every approval prompt. This is how you keep the number of prompts low enough that you actually do.

⚡

Ready to build?

Get the OpenClaw Starter Kit — config templates, 5 production-ready skills, deployment checklist. Go from zero to running in under an hour.

$14 $6.99

Get the Starter Kit →

Also in the OpenClaw store

🗂️
Executive Assistant Config
Buy
Calendar, email, daily briefings on autopilot.
$6.99
🔍
Business Research Pack
Buy
Competitor tracking and market intelligence.
$5.99
⚡
Content Factory Workflow
Buy
Turn 1 post into 30 pieces of content.
$6.99
📬
Sales Outreach Skills
Buy
Automated lead research and personalized outreach.
$5.99

Get the free OpenClaw quickstart guide

Step-by-step setup. Plain English. No jargon.