Gateway
Tool and agent permissions
Control plane tools
Two built-in tools remain control-plane sensitive:
gatewayreads config withconfig.schema.lookup/config.getand starts owner-requested OpenClaw updates withupdate.run. It has no config-write or standalone restart action; update restart and completion notices are automatic.croncreates scheduled jobs that keep running after the original chat/task ends.
The gateway tool stays owner-only because config reads can expose secrets and host topology and update.run changes the running installation. Updates require an explicit user request. Agents request other persistent config or lifecycle changes through the openclaw delegation tool. OpenClaw maps them to typed operations and applies the requesting run's effective permission policy: Full Access, including Default (Full Access), authorizes permitted changes without an approval prompt; restricted runs require human approval. Independent tool, filesystem, sandbox, and operation restrictions still apply, and the host revalidates live authority before execution. See Session permission modes and OpenClaw setup agent.
For any agent/surface handling untrusted content, deny these by default:
{ tools: { deny: ["gateway", "cron", "sessions_spawn", "sessions_send"], },}commands.restart=false disables /restart and external SIGUSR1 restart requests. The gateway agent tool has no restart action.
Node execution (system.run)
If a macOS node is paired, the Gateway can invoke system.run on it - this is remote code execution on that Mac.
- Requires node pairing (approval + token). Pairing establishes node identity/trust and token issuance; it is not a per-command approval surface.
- The Gateway applies a coarse global node command policy via
gateway.nodes.commands.allow/gateway.nodes.commands.deny. The deny list matches exact node command names only (for examplesystem.run), not shell text inside a command payload - a reconnecting node advertising a different command list is not, by itself, a vulnerability if the gateway global policy and the node's own exec approvals still enforce the boundary. - The per-node
system.runpolicy is the node's own exec approvals file (exec.approvals.node.*), controlled on the Mac via Settings -> Exec approvals (security + ask + allowlist); it can be stricter or looser than the gateway's global command-ID policy. - A node running
security="full"andask="off"follows the default trusted-operator model - expected behavior, not a bug, unless your deployment needs a tighter stance. - Approval mode binds exact request context and, when possible, one concrete local script/file operand. If OpenClaw cannot identify exactly one direct local file for an interpreter/runtime command, approval-backed execution is denied rather than promising full semantic coverage.
- For
host=node, approval-backed runs also store a canonical preparedsystemRunPlan; later approved forwards reuse that stored plan, and gateway validation rejects caller edits to command/cwd/session context after the approval request was created. - To disable remote execution entirely: set security to
denyand remove node pairing for that Mac.
Dynamic skills (watcher / remote nodes)
OpenClaw can refresh the skills list mid-session: the skills watcher updates the snapshot on the next agent turn when SKILL.md changes, and connecting a macOS node can make macOS-only skills eligible (based on bin probing). Treat skill folders as trusted code and restrict who can modify them.
Plugins
Plugins run in-process with the Gateway - treat them as trusted code.
- Only install from sources you trust; prefer explicit
plugins.allowallowlists; review plugin config before enabling. Restart the Gateway after plugin code, metadata, or discovery-root changes. With the default hybrid reload mode, ordinary config and enablement changes hot-reload unless the plugin declares a restart-triggering prefix. - Installing/updating plugins runs executable code:
- The install path is the per-plugin directory under the active plugin install root.
- ClawHub packages and OpenClaw's bundled/official catalog are trusted sources. A new arbitrary npm,
npm-pack:, git, local path/archive, or marketplace source warns before install; noninteractive installs require--forceafter you review and trust that source.--forceconfirms provenance and permits overwrite; it does not bypasssecurity.installPolicyor remaining install safety checks. Updates reuse the already selected source. - OpenClaw does not run built-in local dangerous-code blocking during install/update. Use
security.installPolicyfor operator-owned local allow/warn/block decisions andopenclaw security audit --deepfor diagnostic scanning. - npm and git plugin installs run package-manager dependency convergence only during the explicit install/update flow. Local paths and archives are treated as self-contained packages; OpenClaw copies/references them without running
npm install. - Prefer pinned exact versions (
@scope/pkg@1.2.3) and inspect the unpacked code before enabling. security.installPolicylets operators run a trusted local command to returnallow,warn, orblockfor skill and plugin installs. It runs after source material is staged but before install continues and applies to ClawHub skills too.- A
warnresult stops before commit. Interactive CLI commands ask the operator to type the plugin or skill name using the same wording as suspicious ClawHub releases, then re-evaluate policy before continuing. An over-4,000-character rendered review fails closed before prompting. Declined and non-interactive direct CLI commands can use--acknowledge-install-policy-warningas explicit approval after review for every warning in that command invocation. The Control UI exposes the same invocation-wide approval through Install anyway for plugin installs. Other Gateway-backed and automatic installs remain blocked when they have no operator-confirmation flow. Every approved warning is re-evaluated before continuing.blockand policy failures remain terminal. Neither--forcenor the deprecated plugin install/update flag--dangerously-force-unsafe-installapproves policy warnings.
Details: Plugins
Sandboxing
Dedicated doc: Sandboxing
Two complementary approaches:
- Full Gateway in Docker (container boundary): Docker
- Tool sandbox (
agents.defaults.sandbox; host gateway + sandbox-isolated tools; built-in Docker and Podman backends): Sandboxing
Agent workspace access inside the sandbox (agents.defaults.sandbox.workspaceAccess):
"none"(default): tools see a sandbox workspace under~/.openclaw/sandboxes; agent workspace is off-limits."ro": mounts the agent workspace read-only at/agent(disableswrite/edit/apply_patch)."rw": mounts the agent workspace read/write at/workspace.
Extra sandbox.docker.binds are validated against normalized, canonicalized source paths. A blocked-path denylist covers /etc, /private/etc, /proc, /sys, /dev, /root, /boot, and directories that commonly contain or alias the Docker socket (/run, /var/run, and docker.sock under them), plus HOME credential subpaths (.aws, .cargo, .config, .docker, .gnupg, .netrc, .npm, .ssh). Parent-symlink tricks and canonical home aliases are resolved through existing ancestors and re-checked, so they still fail closed if they resolve into a blocked root.
Sub-agent delegation guardrail
If you allow session tools, treat delegated sub-agent runs as another boundary decision:
- Deny
sessions_spawnunless the agent truly needs delegation. - Keep
agents.defaults.subagents.allowAgentsand any per-agentagents.entries.*.subagents.allowAgentsoverrides restricted to known-safe target agents. - For workflows that must remain sandboxed, call
sessions_spawnwithsandbox: "require"(default is"inherit");"require"fails fast when the target child runtime is not sandboxed.
Read-only mode
Build a read-only profile by combining agents.defaults.sandbox.workspaceAccess: "ro" (or "none" for no workspace access) with tool allow/deny lists that block write, edit, apply_patch, exec, process, etc.
tools.exec.applyPatch.workspaceOnly: true(default): keepsapply_patchfrom writing/deleting outside the workspace directory even with sandboxing off. Setfalseonly if you intentionally wantapply_patchto touch files outside the workspace.tools.fs.workspaceOnly: true(optional): restrictsread/write/edit/apply_patchpaths and native prompt image auto-load paths to the workspace directory.- Keep filesystem roots narrow - avoid broad roots like your home directory for agent/sandbox workspaces, which can expose sensitive local files (for example state/config under
~/.openclaw) to filesystem tools.
Per-agent access profiles (multi-agent)
Each agent can have its own sandbox + tool policy: full access, read-only, or no access. See Multi-Agent Sandbox & Tools for precedence rules.
Common patterns: personal agent (full access, no sandbox), family/work agent (sandboxed + read-only tools), public agent (sandboxed + no filesystem/shell tools).
Tool profiles do not narrow session-tool reach, and sandboxing only clamps the sandboxed caller to its spawn tree; an unsandboxed agent can still read a sandboxed agent's sessions. Session visibility is Gateway-wide and agent-to-agent messaging is on by default, so pair persona profiles with tools.sessions.visibility and tools.agentToAgent when agents on one Gateway should not see or message each other (see the last example below).
Full access (no sandbox)
{ agents: { entries: { personal: { default: true, workspace: "~/.openclaw/workspace-personal", sandbox: { mode: "off" }, }, }, },}Read-only tools + read-only workspace
{ agents: { entries: { family: { default: true, workspace: "~/.openclaw/workspace-family", sandbox: { mode: "all", scope: "agent", workspaceAccess: "ro" }, tools: { allow: ["read"], deny: ["write", "edit", "apply_patch", "exec", "process", "browser"], }, }, }, },}No filesystem/shell access (provider messaging allowed)
{ // The default "all" covers every session on the Gateway, including other agents' and other users' transcripts. // Explicit tree scope limits non-main callers to current + spawned sessions. // Use visibility: "self" for strict current-session access, including main. tools: { sessions: { visibility: "tree" } }, // self | tree | agent | all agents: { entries: { public: { default: true, workspace: "~/.openclaw/workspace-public", sandbox: { mode: "all", scope: "agent", workspaceAccess: "none" }, tools: { allow: [ "sessions_list", "sessions_history", "sessions_send", "sessions_spawn", "session_status", "discord", "slack", "telegram", "whatsapp", ], deny: [ "apply_patch", "browser", "canvas", "cron", "edit", "exec", "gateway", "image", "nodes", "process", "read", "write", ], }, }, }, },}