8.0 KiB
summary, title, read_when, status
| summary | title | read_when | status |
|---|---|---|---|
| Per-agent sandbox + tool restrictions, precedence, and examples | Multi-Agent Sandbox & Tools | You want per-agent sandboxing or per-agent tool allow/deny policies in a multi-agent gateway. | active |
Multi-Agent Sandbox & Tools Configuration
Overview
Each agent in a multi-agent setup can now have its own:
- Sandbox configuration (
agents.list[].sandboxoverridesagents.defaults.sandbox) - Tool restrictions (
tools.allow/tools.deny, plusagents.list[].tools)
This allows you to run multiple agents with different security profiles:
- Personal assistant with full access
- Family/work agents with restricted tools
- Public-facing agents in sandboxes
For how sandboxing behaves at runtime, see Sandboxing.
For debugging “why is this blocked?”, see Sandbox vs Tool Policy vs Elevated and clawdbot sandbox explain.
Configuration Examples
Example 1: Personal + Restricted Family Agent
{
"agents": {
"list": [
{
"id": "main",
"default": true,
"name": "Personal Assistant",
"workspace": "~/clawd",
"sandbox": { "mode": "off" }
},
{
"id": "family",
"name": "Family Bot",
"workspace": "~/clawd-family",
"sandbox": {
"mode": "all",
"scope": "agent"
},
"tools": {
"allow": ["read"],
"deny": ["exec", "write", "edit", "apply_patch", "process", "browser"]
}
}
]
},
"bindings": [
{
"agentId": "family",
"match": {
"provider": "whatsapp",
"accountId": "*",
"peer": {
"kind": "group",
"id": "120363424282127706@g.us"
}
}
}
]
}
Result:
mainagent: Runs on host, full tool accessfamilyagent: Runs in Docker (one container per agent), onlyreadtool
Example 2: Work Agent with Shared Sandbox
{
"agents": {
"list": [
{
"id": "personal",
"workspace": "~/clawd-personal",
"sandbox": { "mode": "off" }
},
{
"id": "work",
"workspace": "~/clawd-work",
"sandbox": {
"mode": "all",
"scope": "shared",
"workspaceRoot": "/tmp/work-sandboxes"
},
"tools": {
"allow": ["read", "write", "apply_patch", "exec"],
"deny": ["browser", "gateway", "discord"]
}
}
]
}
}
Example 3: Different Sandbox Modes per Agent
{
"agents": {
"defaults": {
"sandbox": {
"mode": "non-main", // Global default
"scope": "session"
}
},
"list": [
{
"id": "main",
"workspace": "~/clawd",
"sandbox": {
"mode": "off" // Override: main never sandboxed
}
},
{
"id": "public",
"workspace": "~/clawd-public",
"sandbox": {
"mode": "all", // Override: public always sandboxed
"scope": "agent"
},
"tools": {
"allow": ["read"],
"deny": ["exec", "write", "edit", "apply_patch"]
}
}
]
}
}
Configuration Precedence
When both global (agents.defaults.*) and agent-specific (agents.list[].*) configs exist:
Sandbox Config
Agent-specific settings override global:
agents.list[].sandbox.mode > agents.defaults.sandbox.mode
agents.list[].sandbox.scope > agents.defaults.sandbox.scope
agents.list[].sandbox.workspaceRoot > agents.defaults.sandbox.workspaceRoot
agents.list[].sandbox.workspaceAccess > agents.defaults.sandbox.workspaceAccess
agents.list[].sandbox.docker.* > agents.defaults.sandbox.docker.*
agents.list[].sandbox.browser.* > agents.defaults.sandbox.browser.*
agents.list[].sandbox.prune.* > agents.defaults.sandbox.prune.*
Notes:
agents.list[].sandbox.{docker,browser,prune}.*overridesagents.defaults.sandbox.{docker,browser,prune}.*for that agent (ignored when sandbox scope resolves to"shared").
Tool Restrictions
The filtering order is:
- Global tool policy (
tools.allow/tools.deny) - Agent-specific tool policy (
agents.list[].tools) - Sandbox tool policy (
tools.sandbox.toolsoragents.list[].tools.sandbox.tools) - Subagent tool policy (
tools.subagents.tools, if applicable)
Each level can further restrict tools, but cannot grant back denied tools from earlier levels.
If agents.list[].tools.sandbox.tools is set, it replaces tools.sandbox.tools for that agent.
Elevated Mode
tools.elevated is the global baseline (sender-based allowlist). agents.list[].tools.elevated can further restrict elevated for specific agents (both must allow).
Mitigation patterns:
- Deny
execfor untrusted agents (agents.list[].tools.deny: ["exec"]) - Avoid allowlisting senders that route to restricted agents
- Disable elevated globally (
tools.elevated.enabled: false) if you only want sandboxed execution - Disable elevated per agent (
agents.list[].tools.elevated.enabled: false) for sensitive profiles
Migration from Single Agent
Before (single agent):
{
"agents": {
"defaults": {
"workspace": "~/clawd",
"sandbox": {
"mode": "non-main"
}
}
},
"tools": {
"sandbox": {
"tools": {
"allow": ["read", "write", "apply_patch", "exec"],
"deny": []
}
}
}
}
After (multi-agent with different profiles):
{
"agents": {
"list": [
{
"id": "main",
"default": true,
"workspace": "~/clawd",
"sandbox": { "mode": "off" }
}
]
}
}
Legacy agent.* configs are migrated by clawdbot doctor; prefer agents.defaults + agents.list going forward.
Tool Restriction Examples
Read-only Agent
{
"tools": {
"allow": ["read"],
"deny": ["exec", "write", "edit", "apply_patch", "process"]
}
}
Safe Execution Agent (no file modifications)
{
"tools": {
"allow": ["read", "exec", "process"],
"deny": ["write", "edit", "apply_patch", "browser", "gateway"]
}
}
Communication-only Agent
{
"tools": {
"allow": ["sessions_list", "sessions_send", "sessions_history", "session_status"],
"deny": ["exec", "write", "edit", "apply_patch", "read", "browser"]
}
}
Common Pitfall: "non-main"
agents.defaults.sandbox.mode: "non-main" is based on session.mainKey (default "main"),
not the agent id. Group/channel sessions always get their own keys, so they
are treated as non-main and will be sandboxed. If you want an agent to never
sandbox, set agents.list[].sandbox.mode: "off".
Testing
After configuring multi-agent sandbox and tools:
-
Check agent resolution:
clawdbot agents list --bindings -
Verify sandbox containers:
docker ps --filter "label=clawdbot.sandbox=1" -
Test tool restrictions:
- Send a message requiring restricted tools
- Verify the agent cannot use denied tools
-
Monitor logs:
tail -f "${CLAWDBOT_STATE_DIR:-$HOME/.clawdbot}/logs/gateway.log" | grep -E "routing|sandbox|tools"
Troubleshooting
Agent not sandboxed despite mode: "all"
- Check if there's a global
agents.defaults.sandbox.modethat overrides it - Agent-specific config takes precedence, so set
agents.list[].sandbox.mode: "all"
Tools still available despite deny list
- Check tool filtering order: global → agent → sandbox → subagent
- Each level can only further restrict, not grant back
- Verify with logs:
[tools] filtering tools for agent:${agentId}
Container not isolated per agent
- Set
scope: "agent"in agent-specific sandbox config - Default is
"session"which creates one container per session