Exec approvals
When an agent tries to run a shell command that its exec policy does not already permit, WednesdayAI pauses the command and asks a human to approve it. This page documents that approval flow end to end: what triggers a prompt, where it is delivered, how decisions are made, and what happens when nobody answers. The policy definitions themselves (tools.exec.security, ask, safe bins, the approvals file format) are covered in Tool policy — this page covers the approval lifecycle on top of them.
When approval is required
Key:tools.exec.security — default "deny"; key: tools.exec.ask — default "on-miss". Both take effect immediately — no gateway restart needed (tool policy is read per turn).
An approval prompt is raised when:
With the defaults (
security: "deny"), exec is fully blocked and no approval flow exists — approvals only come into play once exec is enabled. The readonly, minimal, and coding presets (tools.exec.preset) all run allowlist security with on-miss asking, so they are approval-friendly baselines.
Exec policy presets
Key:tools.exec.preset — no default (unset; explicit tools.exec.* fields govern). Takes effect immediately — tool policy is read per turn.
Explicit
tools.exec.security / ask / safeBins fields beat preset values (agent-level config beats global config), and the allowlist pack comes only from a preset. An unknown preset id is warned about and ignored — explicit fields still apply.
The approval lifecycle
- Request. The exec tool registers a pending approval with the gateway (
exec.approval.request) and immediately returns anapproval-pendingresult to the agent. The prompt includes the full command, working directory, host, agent, and the effective security mode. Pending approvals expire after 120 seconds by default (timeoutMson the RPC; the exec tool uses the fixed default). - Delivery. The gateway broadcasts
exec.approval.requestedto connected approval-capable clients (control UI, CLI) and optionally forwards the prompt to chat channels — see Forwarding approvals to chat. - Decision. An operator resolves the approval with one of three decisions:
- Outcome. The agent receives system events as the command proceeds: an “Exec running” notice if it runs longer than
tools.exec.approvalRunningNoticeMs(default10000; per-agent only;0disables), and an “Exec finished” summary with exit code and output tail when it completes — or an “Exec denied” event with the reason.
allow-always persistence only happens in allowlist security mode, and it writes to the exec approvals file (~/.openclaw/exec-approvals.json) — the same file whose own security/ask defaults can only make the effective policy stricter, never looser (see Tool policy: exec approvals file).
Approving from chat
In any paired chat channel, an authorised sender can decide a pending approval:allow / once for allow-once, always for allow-always, and reject / block for deny. The <id> is the approval id shown in the prompt (forwarded chat prompts carry the full id; the agent-facing pending result shows a short 8-character form of the same id).
- Only authorised senders (allowlisted or paired) may decide approvals; messages from anyone else are ignored.
- Approvals arriving over the gateway’s internal message channels additionally require the
operator.approvals(oroperator.admin) scope. - You should see a confirmation reply (
✅ Exec approval ... submitted for <id>); the “Exec denied” / “Exec finished” events follow in the conversation.
Forwarding approvals to chat
By default, prompts reach operators connected to the gateway (control UI, CLI). Two config surfaces forward them into chat channels — both are read when an approval is raised, so changes take effect on the next approval with no restart.Any channel
Key:approvals.exec.enabled — default false.
mode: "session" sends the prompt to the chat the triggering conversation came from; "targets" sends to the explicit targets list; "both" does both. Text prompts are answered with /approve.
Discord (buttons)
Key:channels.discord.execApprovals.enabled — default false; requires approvers to be set.
target: "channel" or "both", buttons only work for configured approvers — everyone else gets an ephemeral denial. A per-account override is supported via channels.discord.accounts.<id>.execApprovals with the same shape.
Timeouts and fail-closed defaults
The
askFallback and autoAllowSkills keys live in the defaults block of ~/.openclaw/exec-approvals.json (askFallback default "deny", autoAllowSkills default false). See Tool policy: exec approvals file for the file format.
Node exec approvals
Withtools.exec.host: "node", commands run on a paired remote node, and approvals carry an exact binding of the argv, cwd, agent, session, and a hash of any env overrides — the approved decision is only valid for that exact command shape. Per-node approvals files are managed through RPC:
The prompt flow (request,
/approve, timeout, fallback) is identical to gateway-host exec.
Managing the approvals file
The host approvals file can be read and replaced over RPC (exec.approvals.get / exec.approvals.set, both operator.admin, baseHash-guarded), or from the CLI (openclaw approvals, alias openclaw exec-approvals):
Troubleshooting
Related
- Tool policy — exec security modes, safe bins, and the approvals file format
- Config writes — how config changes are written and applied
- Security hardening
- Exec (developers) — building exec-aware plugin tools