When an agent calls a tool, the tool runs code on your machine. That code came indirectly from a language model that read user input. The threat model is obvious; the defence is platform-specific isolation.
openclaw-rs ships three backends behind one Rust API.
The policy abstraction
pub enum SandboxLevel {
None, // No sandbox. Tools run with full process privileges.
Relaxed, // Workspace-only writes, network allowed.
Strict, // Workspace-only writes, no network, isolated namespace.
}
pub struct SandboxConfig {
pub level: SandboxLevel,
pub allowed_paths: Vec<PathBuf>,
pub network_access: bool,
pub max_cpu_time: Option<Duration>,
pub max_memory: Option<u64>,
}
This is the policy. The implementation differs per OS; the policy doesn’t.
Linux: bubblewrap
bubblewrap (bwrap) is the gold-standard Linux user-namespace sandbox. It’s what Flatpak uses.
bwrap \
--ro-bind /usr /usr \
--ro-bind /lib /lib \
--ro-bind /lib64 /lib64 \
--ro-bind /etc /etc \
--bind ${WORKSPACE} ${WORKSPACE} \
--unshare-net \
--unshare-pid \
--die-with-parent \
--new-session \
-- ${TOOL_COMMAND}
bwrap builds a fresh mount namespace, gives the tool a read-only view of the system, a read-write view of the workspace, no network, and a private PID namespace. The tool can’t see or signal anything outside that namespace.
Install: sudo apt install bubblewrap (Debian/Ubuntu) or your distro’s equivalent.
macOS: sandbox-exec
macOS ships an undocumented but stable sandbox interpreter at /usr/bin/sandbox-exec. The policy is a small Scheme-ish language called Seatbelt.
(version 1)
(deny default)
(allow process-exec)
(allow signal (target self))
(allow sysctl-read)
(allow mach-lookup)
(allow file-read* (subpath "/usr"))
(allow file-read* (subpath "/System"))
(allow file-read* (subpath "/Library/Frameworks"))
(allow file-read* file-write* (subpath "{WORKSPACE}"))
(deny network*)
We render the profile from SandboxConfig, write it to a temp file, and invoke sandbox-exec -f profile.sb -- ${TOOL_COMMAND}. No external dependency; macOS ships it.
Windows: Job Objects
Windows has no namespace sandbox in the Unix sense, but it has Job Objects, which let you cap CPU + memory and prevent child processes from escaping the job.
We create the job, set JOBOBJECT_BASIC_LIMIT_INFORMATION:
JOB_OBJECT_LIMIT_PROCESS_TIME— CPU time cap.JOB_OBJECT_LIMIT_JOB_MEMORY— memory cap.JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE— kill all processes if parent dies.JOB_OBJECT_LIMIT_BREAKAWAY_OKdisabled — child can’t escape.
Filesystem and network restrictions aren’t free here; the Strict policy on Windows is “weaker” than on Linux/macOS, and we document that honestly in docs/SECURITY.md. If your threat model demands the strongest filesystem sandbox, run on Linux.
A single Rust API
let config = SandboxConfig {
level: SandboxLevel::Strict,
allowed_paths: vec![workspace.clone()],
network_access: false,
max_cpu_time: Some(Duration::from_secs(30)),
max_memory: Some(512 * 1024 * 1024),
};
let result = sandbox::execute(&config, &tool_command).await?;
sandbox::execute dispatches to the right backend by cfg!(target_os = ...). The policy crosses platforms; the implementation differs.
What the sandbox doesn’t do
Be honest about scope. The sandbox is the last line of defence, not the only one.
- Input validation comes first.
openclaw-core::validationcaps message sizes (100 KB), JSON depth (32), attachment sizes (50 MB), attachment counts (10), and rejects null bytes. - Tool registration comes second. The agent runtime only invokes tools registered in the
ToolRegistry. The LLM can’t conjure a tool that wasn’t there. - The sandbox is what runs the registered tool with restricted privileges.
If an attacker can prompt-inject the LLM into calling a tool that does something destructive, but the sandbox stops the tool from touching anything outside the workspace, the attacker accomplished nothing.
Picking your level
| Level | Best for | What it gives up |
|---|---|---|
| None | Trusted internal tools you’ve reviewed | Process isolation entirely |
| Relaxed | Tools that genuinely need network access | Network-level isolation |
| Strict | Untrusted-looking tools, default for unknown plugins | Network access |
Set the default to Strict in production. Whitelist looser policies per tool.
Further reading
docs/SECURITY.mdin the repo for the full threat model.zero-trust-agent-runtimefor how the sandbox fits the broader defence-in-depth story.