Source-grounded rewrite of 529 published docs pages with per-unit information-loss verification: 1,713 factual corrections cited to src/**, generated surfaces regenerated, frontmatter titles preserved for i18n, release notes pages untouched. All docs gates green. Closes #100141
21 KiB
summary, read_when, title
| summary | read_when | title | ||
|---|---|---|---|---|
| Exec tool usage, stdin modes, and TTY support |
|
Exec tool |
Run shell commands in the workspace. exec is a mutating shell surface: commands can create, edit, or delete files wherever the selected host or sandbox filesystem permits. Disabling OpenClaw filesystem tools such as write, edit, or apply_patch does not make exec read-only.
Supports foreground and background execution via process. If process is disallowed, exec runs synchronously and ignores yieldMs/background. Background sessions are scoped per agent; process only sees sessions from the same agent.
Parameters
Shell command to run. Working directory for the command. Key/value environment overrides merged on top of the inherited environment. Auto-background the command after this delay (ms). Background the command immediately instead of waiting for `yieldMs`. Override the configured exec timeout for this call, in seconds. Applies to foreground, background, `yieldMs`, gateway, sandbox, and node `system.run` execution. `timeout: 0` disables the exec process timeout for that call. Run in a pseudo-terminal when available. Use for TTY-only CLIs, coding agents, and terminal UIs. Where to execute. `auto` resolves to `sandbox` when a sandbox runtime is active and `gateway` otherwise. Ignored for normal tool calls. `gateway`/`node` security is controlled by `tools.exec.security` and the host approvals file; elevated mode can force `security=full` only when the operator explicitly grants elevated access. The baseline ask mode comes from `tools.exec.ask` and host approvals. For channel-origin model calls, per-call `ask` is ignored when the effective host ask is `off`; otherwise it can only harden to a stricter mode. Trusted internal/API callers that construct exec tools with an explicit `ask` value are unchanged. Node id/name when `host=node`. Request elevated mode: escape the sandbox onto the configured host path. `security=full` is forced only when elevated resolves to `full`.Notes:
hostonly acceptsauto,sandbox,gateway, ornode. It is not a hostname selector; hostname-like values are rejected before the command runs.- Per-call
host=nodeis allowed fromauto; per-callhost=gatewayis only allowed when no sandbox runtime is active. - With no extra config,
host=autostill "just works": no sandbox means it resolves togateway; a live sandbox means it stays in the sandbox. elevatedescapes the sandbox onto the configured host path:gatewayby default, ornodewhentools.exec.host=node(or the session default ishost=node). It is only available when elevated access is enabled for the current session/provider.gateway/nodeapprovals are controlled by the host approvals file.noderequires a paired node (companion app or headless node host). If multiple nodes are available, setexec.nodeortools.exec.nodeto select one.exec host=nodeis the only shell-execution path for nodes; the legacynodes.runwrapper has been removed.- On non-Windows hosts, exec uses
SHELLwhen set; ifSHELLisfish, it prefersbash(orsh) fromPATHto avoid fish-incompatible bashisms, then falls back toSHELLif neither exists. - On Windows hosts, exec prefers PowerShell 7 (
pwsh) discovery (Program Files, ProgramW6432, then PATH), then falls back to Windows PowerShell 5.1. - On non-Windows gateway hosts, bash and zsh exec commands use a startup snapshot. OpenClaw captures sourceable aliases/functions and a small safe environment set from shell startup files into
$OPENCLAW_STATE_DIR/cache/shell-snapshots/, then sources that snapshot before each exec command. Secret-looking variables are excluded; sandbox and node exec do not use this snapshot. SetOPENCLAW_EXEC_SHELL_SNAPSHOT=0in the Gateway process environment to disable this snapshot path. - Host execution (
gateway/node) rejectsenv.PATHand loader overrides (LD_*/DYLD_*) to prevent binary hijacking or injected code. - OpenClaw sets
OPENCLAW_SHELL=execin the spawned command environment (including PTY and sandbox execution) so shell/profile rules can detect exec-tool context. - For channel-origin runs, OpenClaw also exposes a narrow sender/chat identity JSON payload in
OPENCLAW_CHANNEL_CONTEXTwhen the channel provided those ids. execcannot runopenclaw channels loginor/approveshell commands:openclaw channels loginis an interactive channel-auth flow, and/approveneeds to go through the approval command handler, not a shell. Run channel login in a terminal on the gateway host, or use a channel-specific login agent tool when one exists (for examplewhatsapp_login).- Important: sandboxing is off by default. If sandboxing is off, implicit
host=autoresolves togateway. Explicithost=sandboxstill fails closed instead of silently running on the gateway host. Enable sandboxing or usehost=gatewaywith approvals. - Script preflight checks (for common Python/Node shell-syntax mistakes) only inspect files inside the effective
workdirboundary. If a script path resolves outsideworkdir, preflight is skipped for that file. Preflight also skips entirely whenhost=gatewayand the effective policy issecurity=fullwithask=off. - For long-running work that starts now, start it once and rely on automatic completion wake when it is enabled and the command emits output or fails. Use
processfor logs, status, input, or intervention; do not emulate scheduling with sleep loops, timeout loops, or repeated polling. - For work that should happen later or on a schedule, use cron instead of
execsleep/delay patterns.
Config
| Key | Default | Notes |
|---|---|---|
tools.exec.timeoutSec |
1800 |
Default per-command exec timeout in seconds. Per-call timeout overrides it; per-call timeout: 0 disables the exec process timeout. |
tools.exec.host |
auto |
Resolves to sandbox when a sandbox runtime is active, gateway otherwise. |
tools.exec.security |
deny for sandbox, full for gateway/node when unset |
|
tools.exec.ask |
off |
|
tools.exec.mode |
unset | Normalized policy knob. See Modes below. Cannot be combined with tools.exec.security/tools.exec.ask. |
tools.exec.node |
unset | |
tools.exec.notifyOnExit |
true |
When true, backgrounded exec sessions enqueue a system event and request a heartbeat on exit. |
tools.exec.approvalRunningNoticeMs |
10000 |
Emit a single "running" notice when an approval-gated exec runs longer than this (0 disables). |
tools.exec.strictInlineEval |
false |
See Inline eval. |
tools.exec.commandHighlighting |
false |
When true, approval prompts can highlight parser-derived command spans in the command text. Set globally or per agent; does not change approval policy. |
tools.exec.pathPrepend |
unset | List of directories to prepend to PATH for exec runs (gateway + sandbox only). |
tools.exec.safeBins |
unset | Stdin-only safe binaries that can run without explicit allowlist entries. See Safe bins. |
tools.exec.safeBinTrustedDirs |
/bin, /usr/bin |
Additional explicit directories trusted for safeBins path checks. PATH entries are never auto-trusted. |
tools.exec.safeBinProfiles |
unset | Optional custom argv policy per safe bin (minPositional, maxPositional, allowedValueFlags, deniedFlags). |
No-approval host exec is the default for gateway and node (security=full, ask=off) — this comes from the host-policy defaults, not from host=auto. If you want approvals/allowlist behavior, tighten both tools.exec.* and the host approvals file; see Exec approvals. To force gateway or node routing regardless of sandbox state, set tools.exec.host or use /exec host=....
Example:
{
tools: {
exec: {
pathPrepend: ["~/bin", "/opt/oss/bin"],
},
},
}
Modes
tools.exec.mode is the normalized policy knob. Setting it derives security/ask and cannot be combined with explicit tools.exec.security/tools.exec.ask.
| Mode | security | ask | Behavior |
|---|---|---|---|
deny |
deny |
off |
Exec is denied. |
allowlist |
allowlist |
off |
Only allowlisted/safe-bin commands run; nothing else is asked. |
ask |
allowlist |
on-miss |
Allowlist matches run directly; everything else asks a human. |
auto |
allowlist |
on-miss |
Allowlist/safe-bin matches run directly; everything else routes through OpenClaw's native auto reviewer before asking a human. |
full |
full |
off |
No approval gate. |
ask/ask=always still asks a human every time regardless of mode.
Inline eval (strictInlineEval)
When tools.exec.strictInlineEval is true, inline interpreter-eval forms require reviewer or explicit approval: python -c, node -e, ruby -e, perl -e, php -r, lua -e, osascript -e, and similar forms across other supported interpreters and command carriers (awk, find -exec, make, sed, xargs, and more). In mode=auto, the normal exec approval path may let the native auto reviewer allow a clearly low-risk one-off command; direct node-host system.run calls still require an explicit approval because they cannot hand the command to a human approval route. If the reviewer asks, the request goes to a human. allow-always can still persist benign interpreter/script invocations, but inline-eval forms do not become durable allow rules.
PATH handling
host=gateway: merges your login-shellPATHinto the exec environment.env.PATHoverrides are rejected for host execution. The daemon itself still runs with a minimalPATH:- macOS:
/opt/homebrew/bin,/usr/local/bin,/usr/bin,/bin - Linux:
/usr/local/bin,/usr/bin,/bin - To prevent user shell configuration (like
~/.zshenvor/etc/zshenv) from overriding priority paths during startup,tools.exec.pathPrependentries are securely prepended to the finalPATHinside the shell command right before execution.
- macOS:
host=sandbox: runssh -lc(login shell) inside the container, so/etc/profilemay resetPATH. OpenClaw prependsenv.PATHafter profile sourcing via an internal env var (no shell interpolation);tools.exec.pathPrependapplies here too.host=node: only non-blocked env overrides you pass are sent to the node.env.PATHoverrides are rejected for host execution and ignored by node hosts. If you need additional PATH entries on a node, configure the node host service environment (systemd/launchd) or install tools in standard locations.
Per-agent node binding (use the agent list index in config):
openclaw config get agents.list
openclaw config set 'agents.list[0].tools.exec.node' "node-id-or-name"
Control UI: the Nodes tab includes a small "Exec node binding" panel for the same settings.
Session overrides (/exec)
Use /exec to set per-session defaults for host, security, ask, and node. Send /exec with no arguments to show the current values.
Example:
/exec host=auto security=allowlist ask=on-miss node=mac-1
/exec is only honored for authorized senders (channel allowlists/pairing plus commands.useAccessGroups). It updates session state only and does not write config. Authorized external channel senders may set these session defaults. Internal gateway/webchat clients need operator.admin to persist them.
To hard-disable exec, deny it via tool policy (tools.deny: ["exec"] or per-agent). Host approvals still apply unless you explicitly set security=full and ask=off.
Exec approvals (companion app / node host)
Sandboxed agents can require per-request approval before exec runs on the gateway or node host. See Exec approvals for the policy, allowlist, and UI flow.
When approvals are required, the exec tool returns immediately with status: "approval-pending" and an approval id. Once approved (or denied / timed out), the Gateway emits command progress and completion system events only for approved runs (Exec running / Exec finished). Denied or timed-out approvals are terminal and do not wake the agent session with a denial system event.
On channels with native approval cards/buttons, the agent should rely on that native UI first and only include a manual /approve command when the tool result explicitly says chat approvals are unavailable or manual approval is the only path.
Allowlist + safe bins
Manual allowlist enforcement matches resolved binary path globs and bare command-name globs. Bare names match only commands invoked through PATH, so rg can match /opt/homebrew/bin/rg when the command is rg, but not ./rg or /tmp/rg.
When security=allowlist, shell commands are auto-allowed only if every pipeline segment is allowlisted or a safe bin. Chaining (;, &&, ||) and redirections are rejected in allowlist mode unless every top-level segment satisfies the allowlist (including safe bins). Redirections remain unsupported. Durable allow-always trust does not bypass that rule: a chained command still requires every top-level segment to match.
autoAllowSkills is a separate convenience path in exec approvals, not the same as manual path allowlist entries. For strict explicit trust, keep autoAllowSkills disabled.
Use the two controls for different jobs:
tools.exec.safeBins: small, stdin-only stream filters.tools.exec.safeBinTrustedDirs: explicit extra trusted directories for safe-bin executable paths.tools.exec.safeBinProfiles: explicit argv policy for custom safe bins.- allowlist: explicit trust for executable paths.
Do not treat safeBins as a generic allowlist, and do not add interpreter/runtime binaries (for example python3, node, ruby, bash). If you need those, use explicit allowlist entries and keep approval prompts enabled.
openclaw security audit warns when interpreter/runtime safeBins entries are missing explicit profiles, and openclaw doctor --fix can scaffold missing custom safeBinProfiles entries. openclaw security audit and openclaw doctor also warn when you explicitly add broad-behavior bins such as jq back into safeBins (jq supports broad programs and builtins, so prefer explicit allowlist entries or approval-gated runs instead). If you explicitly allowlist interpreters, enable tools.exec.strictInlineEval so inline code-eval forms still require reviewer or explicit approval.
For full policy details and examples, see Exec approvals and Safe bins versus allowlist.
Examples
Foreground:
{ "tool": "exec", "command": "ls -la" }
Background + poll:
{"tool":"exec","command":"npm run build","yieldMs":1000}
{"tool":"process","action":"poll","sessionId":"<id>"}
Polling is for on-demand status, not waiting loops. If automatic completion wake is enabled, the command can wake the session when it emits output or fails.
Send keys (tmux-style):
{"tool":"process","action":"send-keys","sessionId":"<id>","keys":["Enter"]}
{"tool":"process","action":"send-keys","sessionId":"<id>","keys":["C-c"]}
{"tool":"process","action":"send-keys","sessionId":"<id>","keys":["Up","Up","Enter"]}
Submit (send CR only):
{ "tool": "process", "action": "submit", "sessionId": "<id>" }
Paste (bracketed by default):
{ "tool": "process", "action": "paste", "sessionId": "<id>", "text": "line1\nline2\n" }
apply_patch
apply_patch is a subtool of exec for structured multi-file edits. It is enabled by default and available to any model provider; allowModels can restrict it. Use config only when you want to disable it or restrict it to specific models:
{
tools: {
exec: {
applyPatch: { workspaceOnly: true, allowModels: ["gpt-5.5"] },
},
},
}
Notes:
- Tool policy still applies;
allow: ["write"]implicitly allowsapply_patch. deny: ["write"]does not denyapply_patch; denyapply_patchexplicitly or usedeny: ["group:fs"]when patch writes should also be blocked.- Config lives under
tools.exec.applyPatch. tools.exec.applyPatch.enableddefaults totrue; set it tofalseto disable the tool.tools.exec.applyPatch.workspaceOnlydefaults totrue(workspace-contained). Set it tofalseonly if you intentionally wantapply_patchto write/delete outside the workspace directory.tools.exec.applyPatch.allowModelsis an optional allowlist of model ids (raw, likegpt-5.4, or full, likeopenai/gpt-5.4). When set, only matching models get the tool; when unset, all models get it.
Related
- Exec Approvals — approval gates for shell commands
- Sandboxing — running commands in sandboxed environments
- Background Process — long-running exec and process tool
- Security — tool policy and elevated access