Skip to content

Commit 1dea914

Browse files
committed
v0.5.6: Configurable sandbox + Dockerfile.kode + SANDBOXING.md
Full sandbox configurability across files, env vars, and CLI: --sandbox-image Docker image (alpine:latest default) --sandbox-network Network mode: bridge (default) | none | host --sandbox-readonly Mount working directory read-only --sandbox-memory Memory limit (e.g. 512m, 2g) --sandbox-cpus CPU limit (e.g. 0.5, 2) --sandbox-user Container user (uid:gid) sandbox_env Extra env vars (config-file only) sandbox_volumes Extra volume mounts (config-file only) Dockerfile.kode auto-detection → content-hash cached builds. Default network changed from 'none' to 'bridge' (internet by default). New SANDBOXING.md with full reference, security docs, and use-case patterns. 17 new tests across config loader (sandbox fields) and cmd/kode (sandbox CLI + resolveSandboxImage + env var + file config integration).
1 parent b20b3db commit 1dea914

4 files changed

Lines changed: 851 additions & 18 deletions

File tree

SANDBOXING.md

Lines changed: 271 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,271 @@
1+
# Sandboxing
2+
3+
kode runs agent shell commands inside an **isolated Docker container** when `--sandbox` is active. This document covers all configuration options, the `Dockerfile.kode` build system, security guarantees, and best practices.
4+
5+
## Quick start
6+
7+
```bash
8+
# Enable sandbox with internet access (default network: bridge)
9+
kode run --sandbox "npm install && npm test"
10+
11+
# Use a specific base image
12+
kode run --sandbox --sandbox-image node:20-alpine "echo hello"
13+
14+
# Custom Dockerfile for project-specific tooling
15+
echo 'FROM golang:1.24-alpine
16+
RUN apk add --no-cache protobuf
17+
WORKDIR /workspace' > Dockerfile.kode
18+
kode run --sandbox "protoc --version"
19+
```
20+
21+
## Config reference
22+
23+
All sandbox settings are available in `~/kode/config.json`, `./kode.json`, `KODE_*` env vars, and CLI flags, following the same [priority chain](README.md#configuration).
24+
25+
### Config file fields
26+
27+
```json
28+
{
29+
"sandbox": true,
30+
"sandbox_image": "node:20-alpine",
31+
"sandbox_network": "bridge",
32+
"sandbox_readonly": false,
33+
"sandbox_memory": "512m",
34+
"sandbox_cpus": "2",
35+
"sandbox_user": "1000:1000",
36+
"sandbox_env": {
37+
"HOME": "/home/node",
38+
"NODE_ENV": "development"
39+
},
40+
"sandbox_volumes": [
41+
"/home/user/.npm:/root/.npm"
42+
]
43+
}
44+
```
45+
46+
### Field reference
47+
48+
| Field | Env var | CLI flag | Type | Default | Description |
49+
|-------|---------|----------|------|---------|-------------|
50+
| `sandbox` | `KODE_SANDBOX` | `--sandbox` | bool | `false` | Enable/disable sandbox isolation |
51+
| `sandbox_image` | `KODE_SANDBOX_IMAGE` | `--sandbox-image` | string | `alpine:latest` | Docker image for the sandbox container |
52+
| `sandbox_network` | `KODE_SANDBOX_NETWORK` | `--sandbox-network` | string | `bridge` | Docker network mode |
53+
| `sandbox_readonly` | `KODE_SANDBOX_READONLY` | `--sandbox-readonly` | bool | `false` | Mount working directory read-only |
54+
| `sandbox_memory` | `KODE_SANDBOX_MEMORY` | `--sandbox-memory` | string | `""` | Memory limit (e.g. `512m`, `2g`) |
55+
| `sandbox_cpus` | `KODE_SANDBOX_CPUS` | `--sandbox-cpus` | string | `""` | CPU limit (e.g. `0.5`, `2`) |
56+
| `sandbox_user` | `KODE_SANDBOX_USER` | `--sandbox-user` | string | `""` | Run as user (`uid:gid` or name) |
57+
| `sandbox_env` ||| object | `{}` | Extra env vars injected into container |
58+
| `sandbox_volumes` ||| array | `[]` | Extra volume mounts (`host:container`) |
59+
60+
> **Note:** `sandbox_env` and `sandbox_volumes` are config-file-only — they're too complex for flat env vars or CLI flags. For all other fields, env vars and CLI flags follow the standard `KODE_*` pattern.
61+
62+
### Env var examples
63+
64+
```bash
65+
KODE_SANDBOX=true \
66+
KODE_SANDBOX_IMAGE=python:3.12-slim \
67+
KODE_SANDBOX_NETWORK=none \
68+
KODE_SANDBOX_READONLY=true \
69+
KODE_SANDBOX_MEMORY=1g \
70+
KODE_SANDBOX_CPUS=4 \
71+
KODE_SANDBOX_USER=1000:1000 \
72+
kode run "process untrusted data"
73+
```
74+
75+
### CLI flag examples
76+
77+
```bash
78+
kode run \
79+
--sandbox \
80+
--sandbox-image node:20-alpine \
81+
--sandbox-network bridge \
82+
--sandbox-readonly \
83+
--sandbox-memory 512m \
84+
--sandbox-cpus 2 \
85+
--sandbox-user 1000:1000 \
86+
"run build"
87+
```
88+
89+
## Docker image control
90+
91+
kode provides two ways to control the sandbox environment:
92+
93+
### 1. `sandbox_image` (simple)
94+
95+
Pick any public or private Docker image:
96+
97+
```bash
98+
# Node.js
99+
kode run --sandbox --sandbox-image node:20-alpine "npm run build"
100+
101+
# Python
102+
kode run --sandbox --sandbox-image python:3.12-slim "pytest"
103+
104+
# Go
105+
kode run --sandbox --sandbox-image golang:1.24-alpine "go test ./..."
106+
107+
# GPU workload
108+
kode run --sandbox --sandbox-image nvidia/cuda:12.2-runtime "nvidia-smi"
109+
```
110+
111+
Image resolution follows the Docker CLI convention — any valid `docker run IMAGE` argument works.
112+
113+
### 2. `Dockerfile.kode` (advanced)
114+
115+
Place a `Dockerfile.kode` in your working directory for **project-specific, pre-baked tooling**. kode auto-detects it and builds an image with a content-hash tag.
116+
117+
```dockerfile
118+
# Dockerfile.kode
119+
FROM node:20-alpine
120+
121+
# Pre-install project dependencies
122+
RUN apk add --no-cache git openssh
123+
RUN npm install -g typescript tsx prettier
124+
125+
# Set up any user-level config
126+
ENV NODE_ENV=development
127+
WORKDIR /workspace
128+
```
129+
130+
Build behavior:
131+
132+
- kode checks for `Dockerfile.kode` in the working directory
133+
- If found and no explicit `sandbox_image` is configured, kode builds it
134+
- The image is tagged as `kode-sandbox:<sha256[:12]>` based on file content hash
135+
- **Cached:** the image is only rebuilt when `Dockerfile.kode` changes
136+
- First build takes ~5–30s depending on the image; subsequent runs are instant
137+
138+
**Priority:**
139+
1. `sandbox_image` config field → use that image directly (explicit wins)
140+
2. `Dockerfile.kode` exists → build and use it
141+
3. Neither → `alpine:latest`
142+
143+
## Network modes
144+
145+
| Mode | Internet | Host access | Use case |
146+
|------|----------|-------------|----------|
147+
| `bridge` (default) | ✅ Yes | ❌ No | `npm install`, `go mod download`, `git clone`, API calls |
148+
| `none` | ❌ No | ❌ No | Fully isolated — untrusted code, malware scans |
149+
| `host` | ✅ Yes | ✅ Yes | Debugging, local services, port sniffing |
150+
151+
**Security note:** `bridge` gives the container internet access but isolates it from the host's network stack (no access to `localhost:port` on the host, no access to your LAN). `host` mode removes that isolation — use only when you need to connect to a service on the host.
152+
153+
## Read-only mode
154+
155+
When `sandbox_readonly` is `true`, the working directory is mounted **read-only** inside the container:
156+
157+
```bash
158+
kode run --sandbox --sandbox-readonly "ls -la /workspace" # can read
159+
kode run --sandbox --sandbox-readonly "touch /workspace/x" # fails
160+
```
161+
162+
The agent can still write to `/tmp` (which is a writable tmpfs mount) — useful for temporary files and build artifacts that don't need to persist.
163+
164+
> **Why `false` by default?** Most agent tasks involve editing files — fixing bugs, writing code, refactoring. Read-only mode blocks those workflows. Enable it when running untrusted or exploratory tasks.
165+
166+
## Security guarantees
167+
168+
kode's sandbox follows the principle of **least privilege with progressive opt-in**. Every hardening measure can be relaxed if the task requires it.
169+
170+
### Default (no sandbox config overrides)
171+
172+
| Hardening | How it's enforced |
173+
|-----------|------------------|
174+
| **No capabilities** | `--cap-drop ALL` — even root has zero Linux capabilities |
175+
| **No privilege escalation** | `--security-opt no-new-privileges``setuid` binaries can't escalate |
176+
| **No executable /tmp** | `--tmpfs /tmp:noexec` — can't download+run binaries from temp |
177+
| **Auto-cleanup** | `--rm` — container is destroyed on exit, no state persists |
178+
| **Isolated process** | Detached `sleep infinity` — agent commands run via `docker exec` |
179+
| **Ephemeral** | Container destroyed when agent finishes or is interrupted |
180+
181+
### With `--sandbox-network none`
182+
183+
Adds to the defaults above:
184+
185+
| Hardening | How it's enforced |
186+
|-----------|------------------|
187+
| **No network** | `--network none` — container cannot reach internet or LAN |
188+
189+
### With `--sandbox-readonly`
190+
191+
Adds to the defaults above:
192+
193+
| Hardening | How it's enforced |
194+
|-----------|------------------|
195+
| **Read-only workspace** | `-v $PWD:/workspace:ro` — agent can read but not modify project files |
196+
197+
### What you give up by relaxing defaults
198+
199+
| Relaxation | You lose |
200+
|------------|----------|
201+
| `network: bridge` (default) | Full network isolation |
202+
| `network: host` | All network isolation — container can access `localhost` services |
203+
| `readonly: false` (default) | Protection against accidental file modification |
204+
| `memory: ""` (default) | Protection against OOM on the host |
205+
206+
## Use case patterns
207+
208+
### Maximum security (untrusted code analysis)
209+
210+
```json
211+
{
212+
"sandbox": true,
213+
"sandbox_image": "alpine:latest",
214+
"sandbox_network": "none",
215+
"sandbox_readonly": true
216+
}
217+
```
218+
219+
### Development sandbox (Node.js project)
220+
221+
```json
222+
{
223+
"sandbox": true,
224+
"sandbox_image": "node:20-alpine",
225+
"sandbox_network": "bridge",
226+
"sandbox_readonly": false,
227+
"sandbox_memory": "2g",
228+
"sandbox_env": {
229+
"NODE_ENV": "development",
230+
"NPM_CONFIG_CACHE": "/tmp/.npm"
231+
},
232+
"sandbox_volumes": [
233+
"/root/.npm:/root/.npm"
234+
]
235+
}
236+
```
237+
238+
### CI-style sandbox (Go project)
239+
240+
```json
241+
{
242+
"sandbox": true,
243+
"sandbox_image": "golang:1.24-alpine",
244+
"sandbox_network": "bridge",
245+
"sandbox_readonly": false,
246+
"sandbox_memory": "4g",
247+
"sandbox_cpus": "4",
248+
"sandbox_env": {
249+
"GOMAXPROCS": "4",
250+
"GOCACHE": "/tmp/go-cache"
251+
}
252+
}
253+
```
254+
255+
### GPU workload
256+
257+
```json
258+
{
259+
"sandbox": true,
260+
"sandbox_image": "nvidia/cuda:12.2-runtime",
261+
"sandbox_network": "bridge"
262+
}
263+
```
264+
265+
Run with `--gpus all` (currently requires manual `docker run` args — see [limitations](#limitations) below).
266+
267+
## Limitations
268+
269+
- **GPU passthrough** is not yet configurable via kode flags — use `Dockerfile.kode` with `nvidia/cuda` images and run the agent without sandbox mode for now
270+
- **Docker-in-Docker** requires special volume mounts (`/var/run/docker.sock`) — not recommended with sandbox mode
271+
- **Windows containers** are not supported (tested on Linux only)

0 commit comments

Comments
 (0)