|
| 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