An AI agent is a program that decides what to do next based on text it was given, and then does it. If that program can read files and execute commands, then anyone who can influence the text can influence what runs on your machine. This is not a hypothetical failure mode โ it is the normal operating condition of every agent runtime.
ZeroClaw's answer is layered: several independent controls, each of which limits the damage the others fail to prevent. This guide covers what each one does, how to configure it, and โ the part usually missing โ what it does not protect you from.
The threat model
Before configuring anything, be clear about what you are defending against. There are three distinct risks, and they need different controls.
The agent makes a mistake. The model misreads an instruction and deletes the wrong directory. No malice, just a wrong inference acted on with real permissions. This is the most likely failure by a wide margin.
Someone reaches your agent who should not have. Your bot has a public endpoint. Anyone who finds it can issue instructions unless something stops them.
Prompt injection. The agent reads a file, a web page or a message containing text crafted to look like instructions. The model cannot reliably distinguish data it was asked to process from commands it was asked to follow. This is an unsolved problem across the entire industry, and it is why the controls below are containment rather than prevention.
The realistic goal is not an agent that cannot be tricked. It is an agent whose worst possible day is survivable.
Control 1: Workspace scoping
Workspace scoping restricts all file operations to a single directory tree. The agent cannot read, write or list anything outside it โ not because it is instructed not to, but because the path is rejected before the operation happens.
This is the single most valuable control, because it converts the worst case from "read my SSH keys and browser cookies" into "corrupt some files in one folder I chose".
[security]
workspace = "/home/user/agent-workspace"
Getting this right in practice:
- Give the agent a purpose-built directory. Not your home directory, not your main projects folder, not
/. Create a directory that exists solely for the agent. - Copy data in, do not point at originals. If the agent processes invoices, copy them into the workspace. A read-only reference is still a reference the agent can be tricked into modifying if write access is enabled.
- Watch for symlinks. A symlink inside the workspace pointing outside it is the obvious escape. Do not create them, and be careful with archives you extract into the workspace.
- Separate workspaces per agent. If two agents share a directory, a compromise of the weaker one reaches the stronger one's data.
What it does not protect against: anything that is not a file operation. An agent that can make network requests can exfiltrate the workspace contents regardless of scoping. Scoping bounds what can be read, not where it can be sent.
Control 2: Command allowlists
Shell execution is deny-by-default. The agent cannot run an executable unless you have explicitly named it.
[security]
allowed_commands = ["git", "cargo", "ls", "cat"]
If the agent attempts rm, the call is refused because rm is not on the list. There is no "allow everything" mode reachable by accident.
The subtlety that catches people out is that allowlisting a command allowlists everything that command can do. Some entries are far more dangerous than they look:
gitcan execute arbitrary code through hooks, andgit configcan install those hooks. Allowinggitis closer to allowing shell access than it appears.cargo,npm,makeand every other build tool execute code from the project they build. Abuild.rsor apostinstallscript runs whatever it likes.curlandwgetare exfiltration channels and a way to fetch a payload that a later allowed command executes.findhas-exec.awkandsedcan write files. Interpreters likepythonorbashobviously grant everything.
Practical guidance: start with an empty list and add entries only when a task actually fails without them. Prefer the narrowest tool that does the job. If you must allow a build tool, run the agent as a user with minimal privileges and accept that the allowlist is no longer your main defence โ the workspace boundary and the OS user are.
Control 3: Channel pairing
Any new connection to the agent through a messaging channel must present a pairing code before it is trusted. Discovering your bot's address is not the same as being able to command it.
This closes the most embarrassing failure mode: a Telegram bot that anyone who finds the username can instruct. Without pairing, bot discovery is authorisation.
Operational notes:
- Complete pairing over a channel that is already private. Do not paste a pairing code into a public group.
- Re-pair after rotating credentials or moving the agent to a new host.
- Treat a pairing prompt you did not initiate as a signal that someone is trying to reach your agent.
Pairing authenticates the connection, not each individual instruction. Once a channel is paired, everything arriving on it is trusted โ so if the paired account is compromised, the agent is compromised.
Control 4: Encrypted secrets
Provider API keys are encrypted at rest against a local key file rather than stored as plaintext in the configuration. Running onboarding stores credentials this way by default:
zeroclaw onboard --api-key sk-... --provider openrouter
This protects against the ordinary accidents that leak keys: committing a config file, sharing a directory, restoring a backup somewhere less private.
Its limit is worth stating plainly. The key file sits on the same machine as the encrypted secrets, because the runtime must be able to decrypt them unattended. Anyone with read access to that machine as that user can obtain the keys. Encryption at rest defends against copies of the config leaving the machine, not against an attacker who is already on it.
Consequently: set restrictive file permissions on the config directory, keep the key file out of version control and backups, and use provider keys scoped to the minimum necessary โ with a spending cap where the provider supports one. A leaked key with a $20 monthly ceiling is an annoyance rather than an incident.
Control 5: Plugin sandboxing
ZeroClaw plugins are WebAssembly components built for wasm32-wasip2. They run sandboxed and deny-by-default: the host grants only the capabilities the plugin's manifest.toml declares.
This is a meaningfully stronger boundary than the usual plugin model. A native plugin is a shared library running with the host's full privileges โ installing one is trusting its author completely. A WASM component has no ambient authority at all. It cannot open a socket or touch a file unless that capability was explicitly granted, and the sandbox enforces this at the runtime level rather than by convention.
Installation verifies integrity and authenticity separately: the CLI checks the downloaded archive's sha256 for transport integrity, and the host then enforces an Ed25519 signature for authenticity. The distinction matters โ a hash proves the bytes arrived intact, a signature proves who produced them.
When installing a plugin, read the declared capabilities before you accept them. A text-processing plugin requesting network access is telling you something.
Control 6: Supervised autonomy
Autonomy is a spectrum, not a switch. Supervised modes require you to approve actions before they execute, which is the correct setting for any agent that is new, recently reconfigured, or operating on data you cannot afford to lose.
The realistic pattern is to start supervised, watch what the agent actually chooses to do for a week, and relax only the specific approvals that turn out to be routine and safe. Approval fatigue is real โ if you are approving forty actions an hour you will start clicking through them โ so narrow the allowlist until the approval volume is low enough that you still read them.
A baseline configuration
For an agent handling anything that matters:
[security]
workspace = "/home/agent/workspace"
allowed_commands = ["ls", "cat"]
require_pairing = true
Then, outside ZeroClaw:
- Run it as a dedicated unprivileged OS user that owns nothing but the workspace. This is your real backstop, and it is the one control the agent cannot reason its way around.
- Restrict outbound network access with a firewall rule if the agent only needs to reach one provider endpoint. This is the control that actually addresses exfiltration.
- Keep backups outside the workspace, on storage the agent user cannot write to.
- Read the logs. Containment fails silently if nobody looks.
What none of this fixes
Prompt injection remains unsolved. If your agent processes untrusted input โ web pages, incoming email, user-submitted files โ assume that input can eventually make the agent attempt any action available to it. Every control above is designed on that assumption: they limit the blast radius rather than preventing the trigger.
The practical implication is a rule of thumb: do not give an agent a capability whose worst-case use you could not tolerate. Not "would not expect" โ could not tolerate. If the agent can send email, assume it will eventually send the wrong email to the wrong person. If that is survivable, proceed. If it is not, do not grant the capability and do not rely on instructions telling the agent to be careful.
Related reading
- The ZeroClaw config.toml reference โ every configuration key, including the full
[security]section - ZeroClaw skills and plugins โ how the permission model applies to extensions
- ZeroClaw web UI, gateway and tunnel setup โ the risks specific to exposing an endpoint
- Installing ZeroClaw โ verifying what you downloaded before you run it