docs/book/src/security/model.md
ZeroClaw's security model gates what the agent is allowed to do at runtime. There are six layers. From outer to inner:
Before a message from a channel reaches the agent, the channel's pairing and allow-list are checked. allowed_users, allowed_chats, IP allowlists for webhooks, all enforced at the channel adapter, before the runtime sees the event.
Docs: each channel's page under Channels.
The coarse-grained knob. Three settings:
workspace_only is implicitly disabled. forbidden_paths, forbidden_commands, and the OS sandbox still enforce.Docs: Autonomy levels.
The agent operates within a configured workspace directory. file_read, file_write, and shell (for commands that touch the filesystem) refuse paths outside it unless workspace_only = false.
Per-session sandbox roots (ACP and gateway WebSocket): When a session is opened via ACP (session/new with a cwd parameter) or via the gateway WebSocket (connect-time cwd parameter), that path becomes the SecurityPolicy workspace boundary for all file and shell tools for the lifetime of the session. The daemon's global workspace_dir remains the data directory for memory, identity, cron, and other persistent state. The model is: session cwd = project boundary the agent can touch; workspace_dir = where ZeroClaw stores its own files. Note: the agent's system prompt currently reflects the daemon's workspace_dir rather than the session cwd; enforcement is correct but the model's self-reported location may differ.
Important: the cwd parameter changes which directory on the ZeroClaw host the agent is sandboxed to, it does not affect which machine tools run on. Tool use (shell commands, file reads/writes) always executes on the machine running ZeroClaw. If you connect to a remote ZeroClaw instance over the gateway WebSocket, tool calls operate on the remote machine's filesystem, not on your local machine. For localhost-only deployments this distinction does not matter, but remote setups should account for it.
Beyond the workspace, a forbidden_paths list (default: /etc, /sys, /boot, ~/.ssh, …) is always blocked regardless of workspace setting.
For shell invocations:
allowed_commands: if non-empty, shell only runs commands whose basename is in this listforbidden_commands: explicit denylist (rm -rf /, shutdown, kernel operations)validate_command_execution: a pattern-matching pass that looks for dangerous flags, pipelines, and argument shapesThe validator runs before the command hits the shell. A blocked command surfaces as a tool error the model sees and can react to.
When a sandbox backend is available, tool invocations run inside it:
| Platform | Default backend |
|---|---|
| Linux | Landlock (kernel) / Bubblewrap / Firejail / Docker, auto-detected |
| macOS | Seatbelt (native) |
| Windows | AppContainer (experimental) |
| Any | Docker (if the daemon is reachable) |
The sandbox confines filesystem access to the workspace, drops network reachability except what the tool explicitly needs, and removes access to the parent process's secrets.
Docs: Sandboxing.
Tool receipts provide HMAC evidence that a successful tool call and its result passed through the runtime. When receipts are enabled, successful tool outputs receive an HMAC-SHA256 receipt over the call and result, and the receipt is fed back into the conversation with the tool result.
Receipts help catch fabricated tool claims. They are not a chained or durable audit log today: receipt keys are ephemeral, receipts are not cross-signed with the conversation hash, and persistent receipt storage is still future work.
Docs: Tool receipts.
Beyond the six layers:
[security.otp] gated_actions = ["shell", "browser", "file_write"] requires a one-time code before each listed action. Useful for remote-access scenarios.zeroclaw estop halts all in-flight tool calls. With [security.estop] enabled = true, resuming requires an OTP.Configure outbound leak detection in its own TOML section:
[security.leak_detection]
enabled = true
sensitivity = 0.7
high_entropy_tokens = true
enabled = false disables the entire outbound leak detector.
high_entropy_tokens = false disables only the standalone entropy heuristic;
deterministic credential patterns still run. sensitivity accepts 0.0
through 1.0; higher values are more aggressive.
The complete field table and defaults are in the Config reference.
A blocked tool call doesn't silently fail:
ToolResult::Err and hands it back to the modelrm -rf /" and can retry, apologise, or ask the userIf a tool is excluded from the channel via [autonomy].non_cli_excluded_tools (which gates non-CLI channels as a group), it simply isn't advertised to the model on those channels. Model never sees a tool it can't use.
Out of the box:
Supervisedtruefalse (enable explicitly)falsefalseThis is a reasonable middle ground, safe enough for a laptop, permissive enough to not frustrate. Crank it up for production (OTP, audit, restricted tools) or down to YOLO for a dev box.