Skip to content

Commit 3b075df

Browse files
committed
feat: add per-session agent sandbox
1 parent 7bad9f3 commit 3b075df

20 files changed

Lines changed: 1134 additions & 36 deletions

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
- Config: expose schema + UI hints for generic config forms (Web UI + future clients).
1717
- Skills: add blogwatcher skill for RSS/Atom monitoring — thanks @Hyaxia.
1818
- Discord: emit system events for reaction add/remove with per-guild reaction notifications (off|own|all|allowlist) (#140) — thanks @thewilloftheshadow.
19+
- Agent: add optional per-session Docker sandbox for tool execution (`agent.sandbox`) with allow/deny policy and auto-pruning.
1920

2021
### Fixes
2122
- Auto-reply: drop final payloads when block streaming to avoid duplicate Discord sends.
@@ -53,6 +54,7 @@
5354
- Gateway: document config hot reload + reload matrix.
5455
- Onboarding/Config: add protocol notes for wizard + schema RPC.
5556
- Queue: clarify steer-backlog behavior with inline commands and update examples for streaming surfaces.
57+
- Sandbox: document per-session agent sandbox setup, config, and Docker build.
5658

5759
## 2.0.0-beta5 — 2026-01-03
5860

Dockerfile.sandbox

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
FROM debian:bookworm-slim
2+
3+
ENV DEBIAN_FRONTEND=noninteractive
4+
5+
RUN apt-get update \
6+
&& apt-get install -y --no-install-recommends \
7+
bash \
8+
ca-certificates \
9+
curl \
10+
git \
11+
jq \
12+
python3 \
13+
ripgrep \
14+
&& rm -rf /var/lib/apt/lists/*
15+
16+
CMD ["sleep", "infinity"]

docs/configuration.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ better forms without hard-coding config knowledge.
3333
}
3434
```
3535

36+
Build the default image once with:
37+
```bash
38+
scripts/sandbox-setup.sh
39+
```
40+
3641
## Self-chat mode (recommended for group control)
3742

3843
To prevent the bot from responding to WhatsApp @-mentions in groups (only respond to specific text triggers):
@@ -323,6 +328,9 @@ Default: `~/clawd`.
323328
}
324329
```
325330

331+
If `agent.sandbox` is enabled, non-main sessions can override this with their
332+
own per-session workspaces under `agent.sandbox.workspaceRoot`.
333+
326334
### `messages`
327335

328336
Controls inbound/outbound prefixes and timestamps.
@@ -435,6 +443,50 @@ Z.AI models are available as `zai/<model>` (e.g. `zai/glm-4.7`) and require
435443
execute in parallel across sessions. Each session is still serialized (one run
436444
per session key at a time). Default: 1.
437445

446+
### `agent.sandbox`
447+
448+
Optional per-session **Docker sandboxing** for the embedded agent. Intended for
449+
non-main sessions so they cannot access your host system.
450+
451+
Defaults (if enabled):
452+
- one container per session
453+
- Debian bookworm-slim based image
454+
- workspace per session under `~/.clawdis/sandboxes`
455+
- auto-prune: idle > 24h OR age > 7d
456+
- tools: allow only `bash`, `process`, `read`, `write`, `edit` (deny wins)
457+
458+
```json5
459+
{
460+
agent: {
461+
sandbox: {
462+
mode: "non-main", // off | non-main | all
463+
perSession: true,
464+
workspaceRoot: "~/.clawdis/sandboxes",
465+
docker: {
466+
image: "clawdis-sandbox:bookworm-slim",
467+
containerPrefix: "clawdis-sbx-",
468+
workdir: "/workspace",
469+
readOnlyRoot: true,
470+
tmpfs: ["/tmp", "/var/tmp", "/run"],
471+
network: "bridge",
472+
user: "1000:1000",
473+
capDrop: ["ALL"],
474+
env: { LANG: "C.UTF-8" },
475+
setupCommand: "apt-get update && apt-get install -y git curl jq"
476+
},
477+
tools: {
478+
allow: ["bash", "process", "read", "write", "edit"],
479+
deny: ["browser", "canvas", "nodes", "cron", "discord", "gateway"]
480+
},
481+
prune: {
482+
idleHours: 24, // 0 disables idle pruning
483+
maxAgeDays: 7 // 0 disables max-age pruning
484+
}
485+
}
486+
}
487+
}
488+
```
489+
438490
### `models` (custom providers + base URLs)
439491

440492
Clawdis uses the **pi-coding-agent** model catalog. You can add custom providers

docs/docker.md

Lines changed: 131 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,27 @@ read_when:
99

1010
Docker is **optional**. Use it only if you want a containerized gateway or to validate the Docker flow.
1111

12-
## Quick start (recommended)
12+
This guide covers:
13+
- Containerized Gateway (full Clawdis in Docker)
14+
- Per-session Agent Sandbox (host gateway + Docker-isolated agent tools)
1315

14-
From the repo root:
16+
## Requirements
17+
18+
- Docker Desktop (or Docker Engine) + Docker Compose v2
19+
- Enough disk for images + logs
20+
21+
## Containerized Gateway (Docker Compose)
22+
23+
### Quick start (recommended)
24+
25+
From repo root:
1526

1627
```bash
1728
./docker-setup.sh
1829
```
1930

2031
This script:
21-
- builds the image
32+
- builds the gateway image
2233
- runs the onboarding wizard
2334
- runs WhatsApp login
2435
- starts the gateway via Docker Compose
@@ -27,7 +38,7 @@ It writes config/workspace on the host:
2738
- `~/.clawdis/`
2839
- `~/clawd`
2940

30-
## Manual flow (compose)
41+
### Manual flow (compose)
3142

3243
```bash
3344
docker build -t clawdis:local -f Dockerfile .
@@ -36,14 +47,126 @@ docker compose run --rm clawdis-cli login
3647
docker compose up -d clawdis-gateway
3748
```
3849

39-
## E2E smoke test (Docker)
50+
### Health check
51+
52+
```bash
53+
docker compose exec clawdis-gateway node dist/index.js health --token "$CLAWDIS_GATEWAY_TOKEN"
54+
```
55+
56+
### E2E smoke test (Docker)
4057

4158
```bash
4259
scripts/e2e/onboard-docker.sh
4360
```
4461

45-
## Notes
62+
### Notes
4663

4764
- Gateway bind defaults to `lan` for container use.
48-
- Health check:
49-
`docker compose exec clawdis-gateway node dist/index.js health --token "$CLAWDIS_GATEWAY_TOKEN"`
65+
- The gateway container is the source of truth for sessions (`~/.clawdis/sessions`).
66+
67+
## Per-session Agent Sandbox (host gateway + Docker tools)
68+
69+
### What it does
70+
71+
When `agent.sandbox` is enabled, **non-main sessions** run tools inside a Docker
72+
container. The gateway stays on your host, but the tool execution is isolated:
73+
- one container per session (hard wall)
74+
- per-session workspace folder mounted at `/workspace`
75+
- allow/deny tool policy (deny wins)
76+
77+
### Default behavior
78+
79+
- Image: `clawdis-sandbox:bookworm-slim`
80+
- One container per session
81+
- Workspace per session under `~/.clawdis/sandboxes`
82+
- Auto-prune: idle > 24h OR age > 7d
83+
- Default allow: `bash`, `process`, `read`, `write`, `edit`
84+
- Default deny: `browser`, `canvas`, `nodes`, `cron`, `discord`, `gateway`
85+
86+
### Enable sandboxing
87+
88+
```json5
89+
{
90+
agent: {
91+
sandbox: {
92+
mode: "non-main", // off | non-main | all
93+
perSession: true,
94+
workspaceRoot: "~/.clawdis/sandboxes",
95+
docker: {
96+
image: "clawdis-sandbox:bookworm-slim",
97+
workdir: "/workspace",
98+
readOnlyRoot: true,
99+
tmpfs: ["/tmp", "/var/tmp", "/run"],
100+
network: "bridge",
101+
user: "1000:1000",
102+
capDrop: ["ALL"],
103+
env: { LANG: "C.UTF-8" },
104+
setupCommand: "apt-get update && apt-get install -y git curl jq"
105+
},
106+
tools: {
107+
allow: ["bash", "process", "read", "write", "edit"],
108+
deny: ["browser", "canvas", "nodes", "cron", "discord", "gateway"]
109+
},
110+
prune: {
111+
idleHours: 24, // 0 disables idle pruning
112+
maxAgeDays: 7 // 0 disables max-age pruning
113+
}
114+
}
115+
}
116+
}
117+
```
118+
119+
### Build the default sandbox image
120+
121+
```bash
122+
scripts/sandbox-setup.sh
123+
```
124+
125+
This builds `clawdis-sandbox:bookworm-slim` using `Dockerfile.sandbox`.
126+
127+
### Custom sandbox image
128+
129+
Build your own image and point config to it:
130+
131+
```bash
132+
docker build -t my-clawdis-sbx -f Dockerfile.sandbox .
133+
```
134+
135+
```json5
136+
{
137+
agent: {
138+
sandbox: { docker: { image: "my-clawdis-sbx" } }
139+
}
140+
}
141+
```
142+
143+
### Tool policy (allow/deny)
144+
145+
- `deny` wins over `allow`.
146+
- If `allow` is empty: all tools (except deny) are available.
147+
- If `allow` is non-empty: only tools in `allow` are available (minus deny).
148+
149+
### Pruning strategy
150+
151+
Two knobs:
152+
- `prune.idleHours`: remove containers not used in X hours (0 = disable)
153+
- `prune.maxAgeDays`: remove containers older than X days (0 = disable)
154+
155+
Example:
156+
- Keep busy sessions but cap lifetime:
157+
`idleHours: 24`, `maxAgeDays: 7`
158+
- Never prune:
159+
`idleHours: 0`, `maxAgeDays: 0`
160+
161+
### Security notes
162+
163+
- Hard wall only applies to **tools** (bash/read/write/edit).
164+
- Host-only tools like browser/camera/canvas are blocked by default.
165+
- Allowing `browser` in sandbox **breaks isolation** (browser runs on host).
166+
167+
## Troubleshooting
168+
169+
- Image missing: build with `scripts/sandbox-setup.sh` or set `agent.sandbox.docker.image`.
170+
- Container not running: it will auto-create per session on demand.
171+
- Permission errors in sandbox: set `docker.user` to a UID:GID that matches your
172+
mounted workspace ownership (or chown the workspace folder).

docs/security.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,12 @@ services:
9999
network_mode: bridge # Limited network
100100
```
101101
102+
### Per-session sandbox (Clawdis-native)
103+
104+
Clawdis can also run **non-main sessions** inside per-session Docker containers
105+
(`agent.sandbox`). This keeps the gateway on your host while isolating agent
106+
tools in a hard wall container. See `docs/configuration.md` for the full config.
107+
102108
Expose only the services your AI needs:
103109
- ✅ GoWA API (for WhatsApp)
104110
- ✅ Specific HTTP APIs

scripts/sandbox-setup.sh

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
IMAGE_NAME="clawdis-sandbox:bookworm-slim"
5+
6+
docker build -t "${IMAGE_NAME}" -f Dockerfile.sandbox .
7+
echo "Built ${IMAGE_NAME}"

0 commit comments

Comments
 (0)