Exec Approvals: Host Command Guardrails and Policy
Learn how exec approvals gate commands on live hosts via policy, allowlists, and user approval. Essential for operators configuring secure agent execution.
Read this when
- Configuring exec approvals or allowlists
- Inspecting or revoking durable MCP tool grants
- Implementing exec approval UX in the macOS app
- Reviewing sandbox-escape prompts and their implications
Exec approvals act as the companion app / node host guardrail that lets a sandboxed agent execute commands on a live host (gateway or node). A command only runs when policy, allowlist, and (if applicable) user approval all line up. These approvals sit on top of tool policy and elevated gating, and elevated full bypasses them entirely.
For a mode-first walkthrough of deny, allowlist, ask, auto, full, Codex Guardian mapping, and ACPX harness permissions, check out Permission modes.
Note
The effective policy is the more restrictive of
tools.exec.*and the approvals defaults: approvals can only make config-derived security or ask stricter, never more lenient. When an approvals field is left out, thetools.execvalue applies. Host exec also relies on the local approvals state of that machine, so a host-localask: "always"in the execution host approvals document keeps prompting active even if session or config defaults ask forask: "on-miss".
Where it applies
Enforcement of exec approvals happens locally on the execution host:
- Gateway host -> the
openclawprocess running on the gateway machine. - Node host -> the node runner, either the macOS companion app or a headless node host.
Trust model
- Callers authenticated through the Gateway count as trusted operators for that Gateway.
- Paired nodes extend that trusted operator capability to the node host.
- Approvals lower the odds of accidental execution, but they are not a per-user auth boundary or a filesystem read-only policy.
- After approval, a command can modify files according to the permissions of the chosen host or sandbox filesystem.
- Approved runs on node hosts bind the canonical execution context: cwd, exact argv, env binding when present, and the pinned executable path when applicable.
- For shell scripts and direct interpreter or runtime file invocations, OpenClaw additionally tries to bind one concrete local file operand. If that file changes after approval but before execution, the run is denied instead of executing drifted content.
- File binding is best-effort and does not fully model every interpreter or runtime loader path. When exactly one concrete local file cannot be identified, OpenClaw refuses to mint an approval-backed run rather than claiming complete coverage.
macOS split
- The node host service passes
system.runto the macOS app through local IPC. - The macOS app enforces approvals and runs the command within the UI context.
Inspecting the effective policy
| Command | What it shows |
|---|---|
openclaw approvals get / --gateway / --node <id|name|ip> | The requested policy, host policy sources, and the effective result. |
openclaw exec-policy show | A merged view for the local machine. |
openclaw exec-policy set / preset | Syncs the local requested policy with the local host approvals document in a single step. |
Note
Per-session
/execoverrides are excluded. Run/execin the relevant session to see its current defaults. Refer to session overrides.
The full CLI reference, covering flags, JSON output, and allowlist add/remove, is at Approvals CLI.
When a local scope requests host=node, exec-policy show reports that scope as node-managed at runtime, rather than treating the local approvals file as the authoritative source.
If the companion app UI is unavailable, any request that would normally prompt is handled by the ask fallback, which defaults to deny.
Tip
Native chat approval clients can seed channel-specific affordances on the pending approval message. Matrix seeds reaction shortcuts (
✅allow once,♾️allow always,❌deny) while still leaving/approve ...in the message as a fallback.
Settings and storage
Approvals are stored in the shared SQLite state database on the execution host. When OPENCLAW_STATE_DIR is set, the database follows that state directory; otherwise it falls back to the default OpenClaw state directory:
$OPENCLAW_STATE_DIR/state/openclaw.sqlite#exec_approvals_config
# otherwise
~/.openclaw/state/openclaw.sqlite#exec_approvals_config
The #exec_approvals_config suffix is a display locator for the singleton SQLite row, not part of the database filename. That row holds the JSON document shown below as its authoritative value, so CLI and Gateway compare-and-swap hashes stay stable.
The default approval socket follows the same root: $OPENCLAW_STATE_DIR/exec-approvals.sock, or ~/.openclaw/exec-approvals.sock when the variable is unset.
State directories act as independent trust scopes. When OPENCLAW_STATE_DIR points elsewhere, OpenClaw never imports or archives approvals from the default state directory; you must configure approvals separately for the custom state directory. After upgrading from a file-backed release, stop the Gateway and run openclaw doctor --fix once to import the active state directory's retired exec-approvals.json. Doctor also imports legacy plugin-binding-approvals.json only when it belongs to the active state directory.
Example schema:
{
"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",
"argPattern": "sha256:argv:...",
"source": "allow-always",
"lastUsedAt": 1737150000000,
"lastResolvedPath": "/Users/user/Projects/.../bin/rg"
},
{
"pattern": "~/Projects/**/bin/git"
}
]
}
}
}
Policy knobs
tools.exec.mode
tools.exec.mode is the preferred normalized policy surface for host exec:
| Value | Behavior |
|---|---|
deny | Block host exec. |
allowlist | Run only allowlisted commands without asking. |
ask | Use allowlist policy and ask on misses. |
auto | Use allowlist policy, run deterministic matches directly, and send approval misses through OpenClaw's native auto reviewer before falling back to a human approval route. |
full | Run host exec without approval prompts. |
Doctor migrates the retired persisted tools.exec.security / tools.exec.ask pair to tools.exec.mode.
exec.security
security(deny" | "allowlist" | "full), -deny- reject every host exec request. -allowlist- permit only commands on the allowlist. -full- permit all commands (same as elevated). By default,fullapplies to gateway and node hosts; asandboxhost instead usesdenyas its default.
exec.ask
ask(off" | "on-miss" | "always), Defines the ask policy for host exec. It sets the default approval prompt behavior used bytools.exec.askand host approval settings. The default isoff. The per-callasktool parameter (see Exec tool) can only tighten this baseline; model calls originating from a channel ignore it when the effective host ask equalsoff. -off- do not prompt. -on-miss- prompt only when the allowlist fails to match. -always- prompt for every command. Withallow-always, durable trust does not suppress prompts when the effective ask mode isalways.
askFallback
askFallback(deny" | "allowlist" | "full), What happens when a prompt is required but no UI is available (or the prompt times out). If omitted, it defaults todeny. -deny- deny. -allowlist- allow only when the allowlist matches. -full- allow.
tools.exec.strictInlineEval
strictInlineEval(boolean), When set totrue, inline code-eval forms are treated as approval-only even if the interpreter binary itself is on the allowlist. This adds defense-in-depth for interpreter loaders that do not map cleanly to a single stable file operand.
Cases caught by strict mode include: python -c, node -e/--eval/-p,
ruby -e, perl -e/-E, php -r, lua -e, osascript -e (also awk,
sed, make, find -exec, and xargs inline forms).
Under strict mode, these commands require reviewer or explicit approval. When tools.exec.mode: "auto" is active, the reviewer may grant one low-risk execution if the command has an enforceable plan; otherwise OpenClaw asks a human. Codex app-server command approvals that reach the reviewer fallback ask a human because their approval requests do not expose an enforceable resolved executable. allow-always does not persist new allowlist entries for inline-eval commands.
tools.exec.commandHighlighting
commandHighlighting(boolean, default: false), Display only: when enabled, OpenClaw may attach parser-derived command spans so Web approval prompts can highlight command tokens. It does not altersecurity,ask, allowlist matching, strict inline-eval behavior, approval forwarding, or command execution.
Set it globally under tools.exec.commandHighlighting or per agent under agents.entries.*.tools.exec.commandHighlighting.
YOLO mode (no-approval)
To run host exec without approval prompts, open both policy layers: the requested exec policy in OpenClaw config (tools.exec.*) and the host-local approvals policy in the execution host approvals document.
When askFallback is omitted, it defaults to deny. Set host askFallback to full explicitly if a no-UI approval prompt should fall back to allow.
| Layer | YOLO setting |
|---|---|
tools.exec.mode | full on gateway/node |
Host askFallback | full |
Warning
Key differences to keep in mind:
- Where exec runs is decided by
tools.exec.host=auto: a sandbox is used when one exists, otherwise the gateway takes over.- How host exec gets approved is determined by YOLO:
security=fullcombined withask=off.- YOLO never layers an extra heuristic gate for command obfuscation or a script-preflight rejection step on top of the host exec policy you have configured.
- Gateway routing cannot be freely overridden from a sandboxed session via
auto. A per-callhost=noderequest is permitted fromauto;host=gatewayis only permitted fromautowhen no sandbox runtime is running. To get a stable non-auto default, configuretools.exec.hostor explicitly use/exec host=....
Providers that back their CLI with their own noninteractive permission mode
can adopt this policy. When OpenClaw's effective exec
policy is YOLO, Claude CLI adds
--permission-mode bypassPermissions. For OpenClaw-managed Claude live sessions, OpenClaw's
effective exec policy takes precedence over Claude's native permission mode:
YOLO brings live launches to --permission-mode bypassPermissions, and
a restrictive effective exec policy brings live launches to
--permission-mode default, even when raw Claude backend args specify a different
mode.
For a more cautious setup, pull OpenClaw exec policy back to
allowlist / on-miss or deny.
Persistent gateway-host "never prompt" setup
Set the requested config policy
openclaw config set tools.exec.host gateway
openclaw config set tools.exec.mode full
openclaw gateway restart
Match the host approvals document
openclaw approvals set --stdin <<'EOF'
{
version: 1,
defaults: {
security: "full",
ask: "off",
askFallback: "full"
}
}
EOF
Local shortcut
openclaw exec-policy preset yolo
Both the local tools.exec.host/security/ask and the local approvals
file defaults (which include askFallback: "full") get updated. This action is
deliberately local-only. To update gateway-host or node-host approvals from a distance, go through
openclaw approvals set --gateway or openclaw approvals set --node <id|name|ip>.
Other presets that ship with the product: cautious (host=gateway, security=allowlist,
ask=on-miss, askFallback=deny) and deny-all (host=gateway,
security=deny, ask=off, askFallback=deny). They are applied in the same manner:
openclaw exec-policy preset cautious.
When you want to set individual fields rather than a whole preset, run
openclaw exec-policy set --host <auto|sandbox|gateway|node> --security <deny|allowlist|full> --ask <off|on-miss|always> --ask-fallback <deny|allowlist|full> with any subset of those flags.
Node host
Put the same approvals document on the node instead:
openclaw approvals set --node <id|name|ip> --stdin <<'EOF'
{
version: 1,
defaults: {
security: "full",
ask: "off",
askFallback: "full"
}
}
EOF
Note
What stays local:
- Node approvals are not synced by
openclaw exec-policy.openclaw exec-policy set --host nodegets turned down.- Node exec approvals are pulled from the node at runtime, so node-targeted changes have to go through
openclaw approvals --node ....
Session-only shortcut
- Only the current session is affected by
/exec security=full ask=off. - As a break-glass shortcut,
/elevated fullbypasses exec approvals only when the requested policy and the host approvals document both resolve tosecurity: "full"andask: "off". A stricter host file, for instanceask: "always", still triggers a prompt.
When the host approvals document is stricter than config, the stricter host policy remains in control.
Allowlist (per agent)
Allowlists apply per agent. With multiple agents present, pick the agent you want to edit in the macOS app. Patterns are matched as globs.
Patterns can be expressed either as globs for resolved binary paths or as globs for bare command names. Bare names only match commands invoked through PATH, meaning rg can match /opt/homebrew/bin/rg when the command is rg, but not ./rg or /tmp/rg. To trust a single specific binary location, use a path glob instead.
On load, legacy agents.default entries are converted into agents.main. Shell chains like echo ok && pwd still require every top-level segment to comply with allowlist rules.
Examples:
rg~/Projects/**/bin/peekaboo~/.local/bin/*/opt/homebrew/bin/rg
Restricting arguments with argPattern
Add argPattern when an allowlist entry needs to match both a binary and a particular argument structure. OpenClaw applies ECMAScript (JavaScript) regular expression semantics across all hosts, evaluating the expression against the parsed command arguments while excluding the executable token (argv[0]). For manually written entries, arguments are joined using a single space, so anchor the pattern when an exact match is required.
{
"version": 1,
"agents": {
"main": {
"allowlist": [
{
"pattern": "python3",
"argPattern": "^safe\\.py$"
}
]
}
}
}
That entry permits python3 safe.py; python3 other.py does not pass the allowlist. If a path-only entry for the same binary also exists, unmatched arguments can still fall back to that path-only entry. When the goal is to confine the binary to the declared arguments, leave out the path-only entry.
Entries generated by approval flows use an internal separator format for exact argv matching. Prefer the UI or approval flow to recreate those entries rather than manually editing the encoded value. If OpenClaw cannot parse argv for a command segment, entries with argPattern will not match.
Generated allow-always entries are bound to argv. Newly generated entries include argPattern; older generated path-only entries are disregarded and require a fresh approval. For a manual path-only rule, exclude both source and argPattern.
Each allowlist entry supports:
| Field | Meaning |
|---|---|
pattern | Resolved binary path glob or bare command-name glob |
argPattern | ECMAScript argv regex or generated exact-argv hash; omitted is path-only |
id | Stable opaque ID; generated as a UUID when absent |
source | Generated entry source, such as allow-always; omit for manual entries |
commandText | Legacy plaintext input; discarded during load |
lastUsedAt | Last-used timestamp |
lastUsedCommand | Last command that matched; omitted for generated hashed argv entries |
lastResolvedPath | Last resolved binary path |
Auto-allow skill CLIs
When Auto-allow skill CLIs (autoAllowSkills) is turned on, executables referenced by known skills are considered allowlisted on nodes (macOS node or headless node host). This relies on skills.bins over the Gateway RPC to obtain the skill bin list. Turn this off for strict manual allowlists.
Warning
- This is an implicit convenience allowlist, separate from manual path allowlist entries.
- It is intended for trusted operator environments where Gateway and node are in the same trust boundary.
- If you require strict explicit trust, keep
autoAllowSkills: falseand use manual path allowlist entries only.
Safe bins and approval forwarding
For safe bins (the stdin-only fast-path), interpreter binding details, and forwarding approval prompts to Slack/Discord/Telegram (or running them as native approval clients), refer to Exec approvals - advanced.
Control UI editing
Use the Control UI -> Nodes -> Exec approvals card to edit defaults, per-agent overrides, and allowlists. Choose a scope (Defaults or an agent), adjust the policy, add/remove allowlist patterns, then Save. The UI displays last-used metadata per pattern to help keep the list organized.
The target selector picks Gateway (local approvals) or a Node. Nodes must advertise system.execApprovals.get/set (macOS app or headless node host). If a node does not advertise exec approvals yet, edit its local approvals document directly.
Some node hosts, including the Windows companion, use a different approval policy format. Control UI shows these host-native policies read-only. Use the companion app or openclaw approvals set --node <id|name|ip> with the native policy shape to edit them; see Approvals CLI.
CLI: openclaw approvals supports gateway or node editing, see Approvals CLI.
Approval flow
When a prompt is required, the gateway broadcasts exec.approval.requested to operator clients. The Control UI and macOS app resolve it via exec.approval.resolve, then the gateway forwards the approved request to the node host.
For host=node, approval requests include a canonical systemRunPlan payload. The gateway uses that plan as the authoritative command/cwd/session context when forwarding approved system.run requests:
- The node exec path prepares one canonical plan up front.
- The approval record stores that plan and its binding metadata.
- Once approved, the final forwarded
system.runcall reuses the stored plan instead of trusting later caller edits. - If the caller changes
command,rawCommand,cwd,agentId, orsessionKeyafter the approval request was created, the gateway rejects the forwarded run as an approval mismatch.
System events and denials
Exec lifecycle sends an Exec finished system message to the agent's session once the node signals completion. OpenClaw can additionally issue an in-progress notification when an approval is granted, after tools.exec.approvalRunningNoticeMs passes (default 10000, 0 turns it off). A denied exec approval is final for the host command: the command never executes.
- For main-agent async approvals tied to an originating session, OpenClaw routes the denial back into that session as an internal followup, letting the agent stop waiting on the async command and sidestep a missing-result repair.
- When no session exists or resuming it isn't possible, OpenClaw can still deliver a brief denial to the operator or a direct chat route.
- Subagent and cron session denials aren't posted back into their sessions.
Gateway-host exec approvals trigger the identical completion lifecycle event. Approval-gated execs reuse the approval id to tie the pending request to its completion or denial message (Exec finished (gateway id=...) / Exec denied (gateway id=...)).
Implications
fullcarries significant power; prefer allowlists whenever feasible.askkeeps you informed while still permitting rapid approvals.- Per-agent allowlists stop one agent's approvals from spilling into another's.
- Approvals apply solely to host exec requests from authorized senders. Unauthorized senders can't issue
/exec. /exec security=fullserves as a session-level convenience for authorized operators and deliberately bypasses approvals. To hard-block host exec, set approvals security todenyor deny theexectool via tool policy.
Related
-
Exec approvals - advanced, Safe bins, interpreter binding, and approval forwarding to chat.
-
Exec tool, Shell command execution tool.
-
Elevated mode, Break-glass path that also skips approvals.
-
Sandboxing, Sandbox modes and workspace access.
-
Security, Security model and hardening.
-
Sandbox vs tool policy vs elevated, When to reach for each control.
-
Skills, Skill-backed auto-allow behavior.