Forkd Sandbox Setup
July 14, 2026 ยท View on GitHub
Forkd support lets Odin run agent CLIs inside forkd microVM sandboxes. Agents can use allow-all flags inside the VM, while Odin stages only the task workspace and selected auth/config from the host.
This setup is experimental and machine-sensitive. Treat it as a reviewer checklist, not a zero-config install.
What Forkd Provides
- Per-task microVM execution for supported CLI harnesses.
- Host workspace staged into
/tmp/odin-workspace. - Selected credentials/config copied into the guest.
- TaskIt MCP proof/comments through a forkd-local shim.
- Chrome DevTools proof when the selected snapshot includes Chromium.
- Final workspace result, mapped outputs, stdout, and stderr uploaded after the agent exits.
- Live stdout trace upload while the agent runs.
Prerequisites
- Linux host with sudo access.
- Python, Node/npm, and normal Harness Kit dev dependencies.
- Built or installed forkd CLI and controller.
- A forkd kernel image.
- forkd scripts directory containing
host-tap.sh. - Agent CLIs installed on the host:
claude,codex,gemini,opencode, etc. - Agent auth already initialized on the host.
Common discovery paths used by dev.sh:
~/forkd-poc/bin/forkd
~/forkd-poc/bin/forkd-controller
~/forkd-poc/vmlinux
~/forkd-poc/forkd/scripts
/usr/local/share/forkd/scripts
/opt/forkd/scripts
Environment
Set these when auto-discovery is not enough:
export FORKD_BIN="$HOME/forkd-poc/bin/forkd"
export FORKD_CONTROLLER_BIN="$HOME/forkd-poc/bin/forkd-controller"
export FORKD_KERNEL="$HOME/forkd-poc/vmlinux"
export FORKD_SCRIPTS_DIR="$HOME/forkd-poc/forkd/scripts"
export FORKD_CONTROLLER_URL="http://127.0.0.1:8889"
export FORKD_TAP="forkd-tap0"
export FORKD_MEM_SIZE_MIB="4096"
Optional switches:
export FORKD_SETUP_TAP=0 # skip host tap setup
export FORKD_PROVISION_BROWSER=0 # skip browser snapshot provisioning
export FORKD_START_CONTROLLER=0 # skip controller startup
dev.sh also reads:
export FORKD_CONTROLLER_BIND="127.0.0.1:8889"
export FORKD_SNAPSHOT_ROOT="/var/lib/forkd/snapshots"
export FORKD_AUDIT_LOG="/tmp/odin-forkd-controller-audit.log"
export FORKD_BROWSER_SNAPSHOT_TAG="odin-node22-4g-cli-browser"
export FORKD_BROWSER_ROOTFS_SIZE_MIB="8192"
export FORKD_IMAGE="node:22-slim"
export FORKD_CACHE_DIR="$HOME/.cache/odin/forkd"
Agent Auth
Configure auth on the host before running forkd tasks:
- Claude: install/login with the normal
claudeCLI flow on the host. Odin stages~/.claudeand~/.claude.jsonwhen present, then runs Claude inside the VM with--dangerously-skip-permissions. - Codex: install/login with the normal
codexCLI flow on the host. Odin stages~/.codex/auth.jsonwhen present. - Gemini: run
geminiinteractively once so~/.geminihas valid OAuth credentials, or configure API-key auth intentionally. Odin refreshes expiring OAuth on the host before copying credentials into forkd. - MiniMax: set
MINIMAX_API_KEYor configure opencode/kilo auth files. Odin stages opencode/kilo auth/config files when present. - GLM: set
ZAI_API_KEYor configure opencode auth files. Odin stages opencode auth/config files when present.
Odin copies selected auth/config into the VM. It does not mount the whole home directory, and refreshed/modified credentials inside the VM are not copied back to the host.
Config
Start from odin/config/config.sample.yaml and copy relevant values into your local .odin/config.yaml or ~/.odin/config.yaml.
Minimum forkd fields per sandboxed agent:
run_in_forkd: true
forkd_bin: ${FORKD_BIN}
forkd_kernel: ${FORKD_KERNEL}
forkd_scripts_dir: ${FORKD_SCRIPTS_DIR}
forkd_mode: controller
forkd_controller_url: http://127.0.0.1:8889
forkd_snapshot_tag: odin-node22-4g-cli-browser
forkd_mem_size_mib: 4096
forkd_per_child_netns: true
Full Odin forkd settings reference:
| Setting | Default | Purpose |
|---|---|---|
run_in_forkd | false | Enables forkd wrapping for that agent. |
forkd_bin | auto-discover forkd | Path to forkd CLI. |
forkd_kernel | auto-discover common paths | Kernel image used by forkd. Required for forkd runs. |
forkd_scripts_dir | auto-discover common paths | Directory containing forkd helper scripts such as host-tap.sh. |
forkd_use_sudo | false | Prefixes forkd CLI calls with sudo in CLI mode. Controller startup already uses sudo in dev.sh. |
forkd_mode | cli | Use controller for this branch's expected path. |
forkd_controller_url | http://127.0.0.1:8889 | Controller API URL. |
forkd_snapshot_tag | derived from image | Snapshot tag to fork. Browser proof expects odin-node22-4g-cli-browser. |
forkd_per_child_netns | true | Requests per-child network namespaces where supported. |
forkd_image | node:22-slim | Base image used when building rootfs/snapshot. |
forkd_extra | python3, ca-certificates, git, chromium | Extra packages included in rootfs builds. |
forkd_cache_dir | ~/.cache/odin/forkd | Rootfs cache directory. |
forkd_rootfs_size_mib | 4096 | Default rootfs size; browser provisioning uses FORKD_BROWSER_ROOTFS_SIZE_MIB and defaults to 8192. |
forkd_mem_size_mib | 4096 | Guest memory size. Keep at least 4096 for browser/CLI tasks. |
forkd_tap | forkd-tap0 | Host tap device name. Must match setup script/controller use. |
forkd_init_git | true | Initializes a baseline git commit inside the staged guest workspace. |
forkd_workspace_excludes | repo defaults | Paths excluded from workspace staging/restoration, including local config/auth directories. |
Recommended per-agent config for first validation:
agents:
gemini:
cli_command: gemini
run_in_forkd: true
forkd_bin: ${FORKD_BIN}
forkd_kernel: ${FORKD_KERNEL}
forkd_scripts_dir: ${FORKD_SCRIPTS_DIR}
forkd_mode: controller
forkd_controller_url: http://127.0.0.1:8889
forkd_snapshot_tag: odin-node22-4g-cli-browser
forkd_mem_size_mib: 4096
forkd_per_child_netns: true
After Gemini works, enable the same forkd block for Claude, Codex, MiniMax, and GLM.
Agent-Friendly Setup Sequence
Use this exact order on a fresh machine:
- Clone the Harness Kit branch.
- Install/build forkd so
forkd,forkd-controller,vmlinux, andforkd/scriptsexist. - Export
FORKD_BIN,FORKD_CONTROLLER_BIN,FORKD_KERNEL, andFORKD_SCRIPTS_DIRif they are not in the common discovery paths. - Run host CLI auth for the agents you plan to test.
- Copy
odin/config/config.sample.yamlinto.odin/config.yamlor~/.odin/config.yamland verify the forkd fields. - Run
./dev.shand enter sudo password when asked. - Confirm frontend/backend/controller are up before assigning tasks.
- Run the smoke tests below in order.
Startup
Run:
./dev.sh
On a forkd-capable machine, dev.sh will:
- create Python/frontend dependencies as usual;
- create the forkd tap if
host-tap.shis available and the tap is missing; - provision the browser rootfs/snapshot unless disabled;
- start
forkd-controllerunless disabled; - start TaskIt backend, frontend, and Celery.
The browser snapshot provisioning script is:
scripts/provision_forkd_browser.sh
It builds/reuses a Chromium-capable rootfs and creates/registers the default snapshot tag:
odin-node22-4g-cli-browser
Smoke Tests
Use a real TaskIt board and run these in order:
- Simple forkd task that edits a small file.
- Gemini forkd task.
- TaskIt MCP status/proof comment task.
- Chrome DevTools task with screenshot proof.
- Reflection on a completed forkd task.
- Rerun a completed/failed task.
- Live trace check during a longer task.
- Two parallel forkd tasks only after single-task flow is stable.
Focused local tests:
python3 -m pytest odin/tests/mock/test_forkd_harness.py -q
Troubleshooting
Triage order for agents or humans:
- Check
git status --shortso local scratch files are not confused with setup issues. - Check forkd binaries and kernel paths.
- Check tap creation and sudo.
- Check controller health at
$FORKD_CONTROLLER_URL/v1/snapshots. - Check browser snapshot availability if Chrome DevTools is enabled.
- Check host agent auth.
- Check TaskIt MCP proof/comment output.
- Only then debug Odin task logic.
Common failures:
forkd-controller not found: setFORKD_CONTROLLER_BINor install/build forkd.forkd binary/kernel not found: setFORKD_BINandFORKD_KERNEL.- tap errors: verify sudo,
FORKD_SCRIPTS_DIR/host-tap.sh, and thatFORKD_TAPis not already in a bad state. - browser proof fails preflight: selected snapshot/rootfs does not contain Chromium; rerun browser provisioning.
- controller does not know snapshot: stop the old controller and rerun
./dev.shso/var/lib/forkd/state.jsonis reloaded. - Gemini auth fails: run
geminion the host interactively, then retry. - GLM exits without final
ODIN-STATUS: forkd now synthesizes success only if the raw trace proves a TaskIt proof comment was successfully created; otherwise the task fails with the raw GLM tail.
Current Limitations
- This is not yet a clean-machine one-command setup.
- forkd install/build is not vendored by Harness Kit.
- sudo and
/var/lib/forkdaccess are required for tap/snapshot/controller workflows. - Networking and tap behavior can vary by host.
- Browser proof depends on a prebuilt Chromium snapshot.
- Parallel forkd execution should be treated as experimental until validated on the target machine.