Files
zeekayandHanzo Dev 337b7156eb exec with no sandbox is host exec held to tools.exec.security; deny holds everywhere; elevated never raises it
- The default host with no sandbox for the session runs as gateway exec: tools.exec.security and
  ask apply, with the approvals file, and an unset security is DEFAULT_SECURITY deny.
- An explicit security deny refuses exec in a real sandbox too, as OpenClaw does.
- deny is checked before elevated; elevated full skips approvals only where the policy and the
  approvals file are already full/off, and never raises security.
- doctor names each agent this denies (no sandbox, no tools.exec.security).
- The OpenClaw guide, exec and elevated docs say so.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-26 13:32:19 -07:00

9.4 KiB

summary, read_when, title
summary read_when title
Exec tool usage, stdin modes, and TTY support
Using or modifying the exec tool
Debugging stdin or TTY behavior
Exec Tool

Exec tool

Run shell commands in the workspace. Supports foreground + 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

  • command (required)
  • workdir (defaults to cwd)
  • env (key/value overrides)
  • yieldMs (default 10000): auto-background after delay
  • background (bool): background immediately
  • timeout (seconds, default 1800): kill on expiry
  • pty (bool): run in a pseudo-terminal when available (TTY-only CLIs, coding agents, terminal UIs)
  • host (sandbox | gateway | node): where to execute
  • security (deny | allowlist | full): enforcement mode for gateway/node
  • ask (off | on-miss | always): approval prompts for gateway/node
  • node (string): node id/name for host=node
  • elevated (bool): request elevated mode (gateway host); it never raises security

Notes:

  • host defaults to sandbox.
  • elevated is ignored when sandboxing is off (exec already runs on the host).
  • gateway/node approvals are controlled by ~/.bot/exec-approvals.json.
  • node requires a paired node (companion app or headless node host).
  • If multiple nodes are available, set exec.node or tools.exec.node to select one.
  • On non-Windows hosts, exec uses SHELL when set; if SHELL is fish, it prefers bash (or sh) from PATH to avoid fish-incompatible scripts, then falls back to SHELL if neither exists.
  • On Windows hosts, exec prefers PowerShell 7 (pwsh) discovery (Program Files, ProgramW6432, then PATH), then falls back to Windows PowerShell 5.1.
  • Host execution (gateway/node) rejects env.PATH and loader overrides (LD_*/DYLD_*) to prevent binary hijacking or injected code.
  • HanzoBot sets BOT_SHELL=exec in the spawned command environment (including PTY and sandbox execution) so shell/profile rules can detect exec-tool context.
  • Important: sandboxing is off by default. If sandboxing is off and host=sandbox is explicitly configured/requested, exec now fails closed instead of silently running on the gateway host. Enable sandboxing or use host=gateway with approvals.
  • With the default host and no sandbox for the session, exec runs on the host as host=gateway does: held to tools.exec.security and ask and to ~/.bot/exec-approvals.json, and denied when tools.exec.security is unset. Set it to allowlist or full to allow host exec; hanzo-bot doctor names each agent it denies. Cloud agents set full themselves.
  • security: "deny" refuses exec on every host, in a sandbox too, and with elevated.
  • Script preflight checks (for common Python/Node shell-syntax mistakes) only inspect files inside the effective workdir boundary. If a script path resolves outside workdir, preflight is skipped for that file.

Config

  • tools.exec.notifyOnExit (default: true): when true, backgrounded exec sessions enqueue a system event and request a heartbeat on exit.
  • tools.exec.approvalRunningNoticeMs (default: 10000): emit a single “running” notice when an approval-gated exec runs longer than this (0 disables).
  • tools.exec.host (default: sandbox)
  • tools.exec.security (default when unset: deny for the default host, sandboxed or not; allowlist for an explicit gateway or node host)
  • tools.exec.ask (default: on-miss)
  • tools.exec.node (default: unset)
  • tools.exec.pathPrepend: list of directories to prepend to PATH for exec runs (gateway + sandbox only).
  • tools.exec.safeBins: stdin-only safe binaries that can run without explicit allowlist entries. For behavior details, see Safe bins.
  • tools.exec.safeBinTrustedDirs: additional explicit directories trusted for safeBins path checks. PATH entries are never auto-trusted. Built-in defaults are /bin and /usr/bin.
  • tools.exec.safeBinProfiles: optional custom argv policy per safe bin (minPositional, maxPositional, allowedValueFlags, deniedFlags).

Example:

{
  tools: {
    exec: {
      pathPrepend: ["~/bin", "/opt/oss/bin"],
    },
  },
}

PATH handling

  • host=gateway: merges your login-shell PATH into the exec environment. env.PATH overrides are rejected for host execution. The daemon itself still runs with a minimal PATH:
    • macOS: /opt/homebrew/bin, /usr/local/bin, /usr/bin, /bin
    • Linux: /usr/local/bin, /usr/bin, /bin
  • host=sandbox: runs sh -lc (login shell) inside the container, so /etc/profile may reset PATH. HanzoBot prepends env.PATH after profile sourcing via an internal env var (no shell interpolation); tools.exec.pathPrepend applies here too.
  • host=node: only non-blocked env overrides you pass are sent to the node. env.PATH overrides 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):

hanzo-bot config get agents.list
hanzo-bot 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=gateway security=allowlist ask=on-miss node=mac-1

Authorization model

/exec is only honored for authorized senders (channel allowlists/pairing plus commands.useAccessGroups). It updates session state only and does not write config. 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 system events (Exec finished / Exec denied). If the command is still running after tools.exec.approvalRunningNoticeMs, a single Exec running notice is emitted.

Allowlist + safe bins

Manual allowlist enforcement matches resolved binary paths only (no basename matches). 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.

autoAllowSkills is a separate convenience path in exec approvals. It is 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. hanzo-bot security audit warns when interpreter/runtime safeBins entries are missing explicit profiles, and hanzo-bot doctor --fix can scaffold missing custom safeBinProfiles entries.

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>"}

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 (experimental)

apply_patch is a subtool of exec for structured multi-file edits. Enable it explicitly:

{
  tools: {
    exec: {
      applyPatch: { enabled: true, workspaceOnly: true, allowModels: ["gpt-5.2"] },
    },
  },
}

Notes:

  • Only available for OpenAI/OpenAI Codex models.
  • Tool policy still applies; allow: ["exec"] implicitly allows apply_patch.
  • Config lives under tools.exec.applyPatch.
  • tools.exec.applyPatch.workspaceOnly defaults to true (workspace-contained). Set it to false only if you intentionally want apply_patch to write/delete outside the workspace directory.