This tutorial takes you from a clean machine to a successful sandbox_script call wired through Claude Code in five copy-pasteable steps. By the end you will have demesne running as a stdio MCP server and Claude Code invoking it to run a shell command inside a disposable container.
demesne runs your workloads as local containers via OpenSandbox, so two host requirements come first — settle them before installing OpenSandbox:
- Platform — your host must be able to run
linux/amd64containers: native onlinux/amd64, or via a Docker/Podman Machine VM (Rosetta on Apple Silicon) on macOS and Windows. Onlylinux/amd64is actively tested. - Container runtime — install Docker or Podman. Rootless Podman, serving the Docker-compatible API, is supported and is the tested setup.
- Rootless Podman only — use cgroup v2, and set
fs.pipe-user-pages-soft=0(a fan-out of concurrent containers exceeds the default pipe-page cap):sudo sysctl -w fs.pipe-user-pages-soft=0.
See docs/reference/requirements.md for the full host checklist. Everything below assumes a working container runtime.
Pre-built binaries for linux/amd64, darwin/amd64, darwin/arm64, and windows/amd64 are published on the GitHub releases page. Download the archive for your platform, extract it, and place demesne-mcp (or demesne-mcp.exe on Windows) somewhere on your PATH. No Go toolchain required.
# Example for linux/amd64 — replace VERSION with the latest release tag
VERSION=v0.5.0
curl -L "https://github.com/jbeshir/demesne/releases/download/${VERSION}/demesne-mcp_${VERSION#v}_linux_amd64.tar.gz" \
| tar xz -C /usr/local/bin demesne-mcpgo install github.com/jbeshir/demesne/cmd/demesne-mcp@latestThe binary lands in $(go env GOPATH)/bin/demesne-mcp (typically ~/go/bin/demesne-mcp).
See CONTRIBUTING.md for the local make build development flow.
$ demesne-mcp --help
# (prints usage; exact text varies by release)
Demesne delegates container lifecycle to OpenSandbox. The reference server runs locally against Docker or Podman. Install it and generate a config:
pipx install uv
uvx opensandbox-server init-config ~/.sandbox.toml --example docker
The init-config defaults are too permissive to use as a security boundary, so edit these three settings in ~/.sandbox.toml before starting the server:
[server]
# Any non-empty value. Reuse it as OPEN_SANDBOX_API_KEY when you wire demesne in (Steps 3–4).
api_key = "your-secret-key"
[storage]
# Host paths demesne may bind-mount: the directories you want to share, plus
# demesne's output root (default ~/.demesne/out).
allowed_host_paths = ["/home/username/code", "/home/username/.demesne/out"]
[egress]
# Adds nftables IP-level filtering, so `egress: "none"` actually denies the network.
mode = "dns+nft"allowed_host_paths is the setting that bites if you skip it — bind mounts then fail with VOLUME::HOST_PATH_NOT_ALLOWED. See requirements.md for the full checklist.
Then start the server:
uvx opensandbox-server --config ~/.sandbox.toml
OpenSandbox is long-running — it must stay up for the entire demesne session. The remaining steps assume it is still listening on :8080. Pick whichever approach suits your workflow:
- (Recommended) Run it in its own dedicated terminal tab and leave it there.
- Background it (logs to a file):
nohup uvx opensandbox-server --config ~/.sandbox.toml >/tmp/opensandbox.log 2>&1 & # Follow logs with: tail -f /tmp/opensandbox.log
- Use tmux/screen (keeps it recoverable):
tmux new-session -d -s opensandbox 'uvx opensandbox-server --config ~/.sandbox.toml'
$ uvx opensandbox-server --config ~/.sandbox.toml
INFO Listening on :8080
At minimum you need the three required variables from the Configuration reference:
export OPEN_SANDBOX_DOMAIN=localhost:8080
export OPEN_SANDBOX_API_KEY=your-secret-key # the [server] api_key you set in Step 2
export DEMESNE_ALLOWED_PATHS=/home/username/codeOptionally verify the binary starts cleanly (this is a smoke-check — Ctrl-C to exit; the real run happens in Step 4 when Claude Code spawns it):
demesne-mcp# (demesne-mcp blocks, waiting for JSON-RPC on stdin)
No output on startup is correct — it is waiting for a client. This manual invocation is optional and just confirms the binary is functional. Step 4 (Claude Code) is what actually runs demesne-mcp for real.
Add demesne to your user-level Claude Code config (~/.claude.json, available in every project) with claude mcp add:
claude mcp add --transport stdio --scope user \
--env OPEN_SANDBOX_DOMAIN=localhost:8080 \
--env OPEN_SANDBOX_API_KEY=your-secret-key \
--env DEMESNE_ALLOWED_PATHS=/home/username/code \
demesne -- /usr/local/bin/demesne-mcpReplace /usr/local/bin/demesne-mcp with the actual path from Step 1 (e.g. ~/go/bin/demesne-mcp), and use the same OPEN_SANDBOX_API_KEY you set as [server] api_key in Step 2. Keep --transport stdio ahead of the server name demesne. Claude Code spawns demesne-mcp as a child process and talks to it over stdio.
demesne returns each run's output_dir under ~/.demesne/out; so your agent can open those files without a permission prompt on every read, grant it read access — in Claude Code, add ~/.demesne/out to permissions.additionalDirectories or start the session with --add-dir ~/.demesne/out. See Let your agent read demesne's output.
Using Codex (or another client)? See Wire demesne into your MCP client for the Codex config.toml block and Claude Desktop / VS Code pointers.
In Claude Code, after reloading the MCP config (restart Claude Code or run /mcp):
✓ demesne connected
In a Claude Code session, ask Claude to run a command:
Use the sandbox_script tool with command "echo hello && uname -a"
Or invoke the tool directly from the Claude Code developer console:
tools/call sandbox_script command="echo hello && uname -a"
exit_code: 0
output_dir: ~/.demesne/out/<job-id>
job_id: <uuid>
---
hello
Linux <container-hostname> 6.x.x ... x86_64 GNU/Linux
---stderr---
The command ran inside a disposable continuumio/anaconda3 container (the default image). The ~/.demesne/out/<job-id> directory on your host contains any files the command wrote to /out inside the sandbox.
- How-to guides —
../how-to/covers sharing host directories, egress control, spawning nested agents, and more. - Tool reference —
../reference/tools/has the full parameter tables, sample requests, and error tables for all 8 tools. - Concepts —
../explanation/explains the architecture, trust boundary, and key concepts in depth.