Skip to content

Managing Exec Approvals for Safe Command Execution

We have all been there: you are building an automated agent and you want it to actually do things, but giving a sandboxed process free reign over your terminal feels risky. You want the productivity of automation without the constant worry that a script might execute something unexpected on your machine.

I find that the best way to handle this is with a proper safety interlock. Exec approvals act as a guardrail for your host, ensuring that commands only run when your policy, allowlist, and manual approval are all in sync.

  • An execution host (either a gateway or a node).
  • The openclaw process or the macOS companion app.

Exec approvals are enforced locally on your execution host. If you are on a gateway, the openclaw process handles it. If you are on macOS, the node host service sends the request to your macOS app to show the UI.

To get started, you need to configure the local JSON file where these settings live.

  1. Locate your config file: Open ~/.openclaw/exec-approvals.json on your host.
  2. Define your policy: Use the schema below to set your defaults and agent-specific rules.
{
"version": 1,
"socket": {
"path": "~/.openclaw/exec-approvals.sock",
"token": "base64url-token"
},
"defaults": {
"security": "deny",
"ask": "on-miss",
"askFallback": "deny",
"autoAllowSkills": false
},
"agents": {
"main": {
"security": "allowlist",
"ask": "on-miss",
"askFallback": "deny",
"autoAllowSkills": true,
"allowlist": [
{
"id": "B0C8C0B3-2C2D-4F8A-9A3C-5A4B3C2D1E0F",
"pattern": "~/Projects/**/bin/rg",
"lastUsedAt": 1737150000000,
"lastUsedCommand": "rg -n TODO",
"lastResolvedPath": "/Users/user/Projects/.../bin/rg"
}
]
}
}
}

I recommend setting security to allowlist for specific agents. This ensures that only known patterns can run without you manually clicking “approve” every single time.

If the macOS companion app UI is unavailable, any command that requires a prompt will fail. The system uses the askFallback setting (which defaults to deny) to resolve the request safely.

The system binds the execution context, including the file path and script content. If a script changes after you approve it but before it executes, the run is denied to prevent “drifted content” from running on your machine.

If you have more questions about setting this up, check out the AI Setup Assistant.

I’ve spent a lot of time worrying about what happens when an automated agent tries to run commands on my local machine. It is a stressful balance: you want the agent to be helpful, but you don’t want to give it total control over your terminal. I often find myself hovering over the keyboard, wondering if I should let a process execute or block it entirely to stay safe.

Setting up the right boundaries makes a huge difference in how much you can trust your tools. I’ll show you how to use policy knobs to get that control back.

  • A running agent instance (macOS app or headless node).
  • Access to your agent configuration settings.
  • Binary paths for the tools you want to allowlist.
  • Gateway RPC connection (if using skill CLIs).

You can get your security policy running in about five minutes by following these steps:

  1. Set your security level: Choose between deny, allowlist, or full in your exec.security settings.
  2. Define prompt behavior: Set exec.ask to decide when you want to be notified.
  3. Map your binaries: Add specific binary paths to your allowlist using glob patterns.
  4. Handle fallbacks: Configure askFallback for situations where the UI isn’t available to show a prompt.

I recommend looking at these three main categories to define how your agent behaves.

This is your primary toggle for host execution:

  • deny: This blocks all host exec requests immediately.
  • allowlist: Only commands you have specifically approved will run.
  • full: This allows everything, which is the same as elevated access.

This controls the frequency of prompts:

  • off: You will never see a prompt.
  • on-miss: You only get a prompt if the command isn’t in your allowlist.
  • always: You get a prompt for every single command.

If the system needs to prompt you but can’t reach a UI, it looks at this setting:

  • deny: It blocks the command.
  • allowlist: It only allows the command if it matches your allowlist.
  • full: It allows the execution.

Allowlists are specific to each agent. If you have several agents, I suggest switching between them in the macOS app to edit their individual lists.

The patterns use case-insensitive glob matches. A very important detail: these must resolve to binary paths. If you only enter a basename (like just rg), it will be ignored. If you are moving from older versions, agents.default entries are migrated to agents.main when you load them.

Here are the path formats you should use:

  • ~/Projects/**/bin/peekaboo
  • ~/.local/bin/*
  • /opt/homebrew/bin/rg

Each entry in your list tracks the stable UUID (id), the last used timestamp, the last used command, and the last resolved path.

If you enable Auto-allow skill CLIs, the system treats executables from known skills as if they were already allowlisted. This happens on your macOS node or headless node host. It uses skills.bins over the Gateway RPC to get the list.

I suggest disabling this if you prefer a strict manual setup. It is a convenience feature for environments where the Gateway and the node share the same trust boundary. If you need explicit trust, set autoAllowSkills: false and stick to manual path entries.

The allowlist is ignoring my entries Check if you are using basename-only entries. The system requires full paths or glob patterns that resolve to binary paths. Basenames are ignored.

Commands are failing when I’m away from my computer This usually happens when a prompt is required but the UI is unreachable. Check your askFallback setting. If it is set to deny, the command will fail whenever the prompt cannot be displayed.


If you need help configuring these knobs for your specific setup, check out the AI Setup Assistant.

I’ve been there: you’re in the middle of a task, and you just want to pipe some output through jq or head to see what’s going on. But instead of getting your data, you’re hit with a security prompt asking for permission to run a basic command. It breaks your flow and makes simple text processing feel like a chore.

I want to show you how to use Safe Bins. These are stdin-only binaries that can run in allowlist mode without needing explicit entries for every single variation. Think of it as a fast-path for stream filters.

  • Access to your tools.exec configuration files.
  • The openclaw CLI tool for security audits.
  • Binaries located in trusted system paths like /bin or /usr/bin.

You can get started with safe bins by identifying which tools you use most for stream processing. By default, the system trusts a specific set of tools when they are used strictly as filters.

  1. Check the defaults: The following tools are already configured as safe bins: jq, cut, uniq, head, tail, tr, and wc.
  2. Verify the path: Ensure your binaries live in /bin or /usr/bin. If you use Homebrew or other managers, you’ll need to add those paths to tools.exec.safeBinTrustedDirs.
  3. Run a command: Try piping data into a safe bin. For example: cat data.json | jq .. It should run without a manual approval prompt.

Safe bins are restricted to stdin-only operations. They reject positional file arguments and path-like tokens. This means they can only work on the data coming through the pipe, not files on your disk.

I recommend treating this as a narrow trust list for text transforms. Do not add interpreters or runtimes like python3, node, ruby, or bash to your safeBins. If a tool can execute code or read files by design, stick to the explicit allowlist.

Validation is deterministic based on the shape of the command arguments. The system doesn’t check the host filesystem; it just looks at the flags you are trying to use. If a flag allows a tool to read a file or write output, it is denied.

Here are the flags that will cause a safe bin command to be rejected:

  • grep: --dereference-recursive, --directories, --exclude-from, --file, --recursive, -R, -d, -f, -r
  • jq: --argfile, --from-file, --library-path, --rawfile, --slurpfile, -L, -f
  • sort: --compress-program, --files0-from, --output, --random-source, --temporary-directory, -T, -o
  • wc: --files0-from

Safe bins also force tokens to be treated as literal text. This means no globbing with * and no $VARS expansion. This prevents anyone from smuggling file reads into a simple filter command.

Shell chaining using &&, ||, or ; is allowed as long as every individual segment satisfies the allowlist requirements. However, redirections (like > or <) are not supported in allowlist mode.

If you use command substitution like $() or backticks, the allowlist parser will reject it. If you actually need the literal text $(), use single quotes.

If you have a custom tool that acts as a filter, you can define a profile for it. Here is how you would configure a custom filter in your JSON config:

{
tools: {
exec: {
safeBins: ["jq", "myfilter"],
safeBinProfiles: {
myfilter: {
minPositional: 0,
maxPositional: 0,
allowedValueFlags: ["-n", "--limit"],
deniedFlags: ["-f", "--file", "-c", "--command"],
},
},
},
},
}

It helps to know when to use a safe bin versus a standard allowlist entry.

Topictools.exec.safeBinsAllowlist (exec-approvals.json)
GoalAuto-allow narrow stdin filtersExplicitly trust specific executables
Match typeExecutable name + safe-bin argv policyResolved executable path glob pattern
Argument scopeRestricted by profile and literal rulesPath match only; args are your responsibility
Typical examplesjq, head, tail, wcpython3, node, ffmpeg, custom CLIs
Best useLow-risk text transforms in pipelinesAny tool with broader behavior or side effects

If things aren’t working as expected, check these common scenarios:

  • Interpreter Warnings: If you put an interpreter in safeBins without a profile, openclaw security audit will warn you with tools.exec.safe_bins_interpreter_unprofiled.
  • Missing Profiles: If you added a custom bin but forgot the profile, run openclaw doctor --fix. It can scaffold the missing safeBinProfiles.<bin> entries as {} for you to fill in later.
  • Grep or Sort issues: Remember that grep and sort are not in the default list. If you opt-in, use -e or --regexp for grep patterns, as positional patterns are rejected to prevent file smuggling.
  • Path issues: If your binary is in /usr/local/bin or /opt/homebrew/bin, it won’t be trusted by default. Add these to tools.exec.safeBinTrustedDirs.

If you need help getting your configuration just right, check out the AI Setup Assistant.

I’ve often felt that slight hesitation before letting an automated tool run commands on my machine. You want the speed of automation, but you also want to know exactly what is happening in your terminal. It is a common balance between moving fast and staying safe.

Setting up execution approvals helps you find that balance. Instead of giving an agent a blank check, you can decide exactly which commands are safe and which ones need a manual thumbs-up.

  • Access to the Control UI.
  • Nodes that advertise system.execApprovals.get/set (this includes the macOS app or headless node hosts).
  • The openclaw CLI if you prefer terminal-based management.

You can manage everything through the Control UI → Nodes → Exec approvals card. Here is how I usually set it up:

  1. Pick a scope: Choose between Defaults or a specific agent.
  2. Tweak the policy: Adjust your settings and add or remove allowlist patterns.
  3. Check metadata: The UI shows last used metadata for each pattern, which helps you identify and remove patterns you no longer use.
  4. Save: Apply your changes.

If you are working with a node that doesn’t show up in the UI yet, you can use the CLI:

Terminal window
openclaw approvals

This command supports editing for both the gateway and specific nodes.

When an agent needs to run a command, the gateway sends an exec.approval.requested signal. You will see a confirmation dialog that includes:

  • The command and its arguments.
  • The cwd (current working directory).
  • The agent ID and the resolved executable path.
  • Host and policy metadata.

You have three choices when the dialog pops up:

  • Allow once: Runs the command this one time.
  • Always allow: Runs the command and adds it to your allowlist.
  • Deny: Blocks the execution.

If a node does not advertise system.execApprovals.get/set yet, you won’t be able to edit it through the UI. In this case, I recommend editing the local configuration file directly at: ~/.openclaw/exec-approvals.json

When approvals are required, the tool returns an approval ID immediately. If you don’t make a decision before the timeout, the system treats it as an approval timeout. This will be surfaced in your logs as a denial reason. You can correlate these events by looking for the Exec finished or Exec denied system events using that specific ID.

If you need more help getting this configured, check out the AI Setup Assistant.

I hate being tethered to a specific terminal window just to wait for an execution approval. It is a common frustration: you start a task, it hits a security gate, and then you have to hunt down the right tab to click “Allow.” I prefer getting those notifications where I already spend my time—in chat.

If you are tired of context-switching to manage permissions, you can route these prompts directly to your team’s communication tools. It keeps the workflow moving without sacrificing security.

  • Authorized senders for host exec requests.
  • A configured chat channel (Slack, Telegram, Discord, or a plugin channel).
  • Access to your configuration file.

You can forward exec approval prompts to any chat channel and handle them with the /approve command. This setup uses the normal outbound delivery pipeline to keep things consistent.

Add the following block to your configuration. You can filter by agent or session and define specific targets like Slack or Telegram.

{
approvals: {
exec: {
enabled: true,
mode: "session", // "session" | "targets" | "both"
agentFilter: ["main"],
sessionFilter: ["discord"], // substring or regex
targets: [
{ channel: "slack", to: "U12345678" },
{ channel: "telegram", to: "123456789" },
],
},
},
}

When a prompt appears in your channel, you can respond with one of these three commands:

/approve <id> allow-once
/approve <id> allow-always
/approve <id> deny

For those on macOS, the system follows a specific IPC (Inter-Process Communication) flow to ensure the UI and the background services stay in sync:

Gateway -> Node Service (WS)
| IPC (UDS + token + HMAC + TTL)
v
Mac App (UI + approvals + system.run)

I want to highlight the security measures here. The Unix socket uses mode 0600, and the token is stored in exec-approvals.json. The system performs a same-UID peer check and uses a challenge/response mechanism with a short TTL to keep things tight.

The exec lifecycle is surfaced as system messages. These are posted to the agent’s session after the node reports the event:

  • Exec running: This appears if the command exceeds the running notice threshold.
  • Exec finished: Sent when the task completes.
  • Exec denied: Sent if the request is rejected.

If you are using approval-gated execs, the system reuses the approval ID as the runId in these messages, which makes correlation much easier.

I have a few recommendations for keeping your environment secure while using these features:

  • Prefer allowlists: The full security mode is powerful. I recommend using allowlists whenever you can to limit exposure.
  • Use “ask” mode: This keeps you in the loop for manual verification while still allowing for fast approvals through chat.
  • Isolate agents: Use per-agent allowlists. This prevents approvals meant for one agent from leaking into others.
  • Understand /exec security=full: This is a session-level convenience for authorized operators. It skips approvals by design, so use it carefully.

Unauthorized Senders If someone tries to run /exec and nothing happens, check their authorization. Approvals only apply to host exec requests from authorized senders. Unauthorized users cannot issue these commands.

Hard-Blocking Host Exec If you need to completely disable host exec, you have two options. You can set approvals security to deny, or you can deny the exec tool entirely via your tool policy.

If you need help with your specific configuration, check out the AI Setup Assistant.

OpenClaw

OpenClaw Expert

Still stuck?

If this page didn't answer your case, ask OpenClaw Expert for step-by-step guidance.