Sandbox
What agents and solutions can write, read and reach on your machine, and how to see it for yourself.
hillclimb starts two kinds of process that run code nobody has read. The agents work with
their permission prompts off, and the verifier runs the solution.py an agent wrote. Both run on
your machine, as you.
So both run inside a sandbox. It is on by default, it is the operating system's own, and it needs no container.
See it for yourself
hillclimb sandbox check runs a script that behaves like a hostile solution, inside the same
sandbox a search uses, and lists what happened to every attempt:
$ hillclimb sandbox check A hostile script, run inside the sandbox (sandbox-exec) files✓ write to its own candidate folder allowed✓ write to your home folder blocked✓ write to the hillclimb dir blocked secrets✓ read the search's journal (it holds holdout scores) blocked✓ read ~/.ssh blocked✓ read Claude Code's login and history (~/.claude) blocked network✓ connect to the internet (1.1.1.1:443) blocked✓ look up a name (example.com) blocked processes✓ signal a process outside the sandbox blocked agent without internet✓ reach a host that is not allowed (example.org) blocked✓ connect past the proxy blocked✓ reach an allowed host (example.com) allowed The sandbox holds: 10 attempts blocked, and its own folder stays writable.
It exits with code 1 if one attempt got through. Whatever the script managed to write outside its
folder is removed again. hillclimb connect shows in one line whether the sandbox starts on your
machine.
What a sandboxed process can do
| allowed | blocked | |
|---|---|---|
| write | its candidate's folder, the temp dirs, the caches of huggingface, torch and matplotlib | everything else: your home folder, your repositories, the search's journal and best/, the Python environments solutions run in |
| read | most of the disk | ~/.ssh, ~/.aws, ~/.gnupg, cloud and GitHub logins, browser profiles, shell history, .env files, the other agents' logins, the search's journal and its holdout runs, the sockets of Docker and other container runtimes |
| network, agents | everything; with allow_internet_for_agents: false only their model provider | the rest |
| network, verifier | nothing; with the problem's allow_internet_during_solution: true everything | the rest, name lookups included |
| processes | its own children | signalling anything outside |
An agent also writes to its own state: ~/.claude and ~/.claude.json for Claude Code, an
isolated home and its session folder for pi.
How it is enforced
The engine stays outside. It is the only process that writes the journal and best/. Every agent
call and every verifier run is started inside a sandbox of its own, and everything that process
starts stays inside with it:
your machine
│
├── engine (hillclimb run) outside: writes the journal and best/
│ └── proxy the one way out for agents without internet
│
├── sandbox ── agent (claude-code, pi) writes its candidate's folder
│ └── every tool and script it starts
│
└── sandbox ── verifier.sh writes its candidate's folder, no network
└── solution.pyhillclimb does not implement the confinement. It writes down the rules and hands them to the
mechanism the operating system already has:
| OS | what enforces it | install |
|---|---|---|
| macOS | sandbox-exec (Seatbelt), the mechanism Chrome and Claude Code use | nothing, it is part of macOS |
| Linux | bubblewrap, the mechanism Flatpak uses | sudo apt install bubblewrap (dnf, pacman alike) |
| Windows | none exists | hillclimb runs unsandboxed and says so at every start |
The rules are readable. On macOS they are one Seatbelt profile per process, on Linux one list of
bubblewrap arguments, both written by
harness/sandbox.py.
Per agent
| agent | how it is confined |
|---|---|
claude-code | the whole process runs inside the sandbox |
pi | the whole process runs inside the sandbox |
codex | by its own sandbox (--sandbox workspace-write): writes stay in the candidate's folder and its commands have no network. macOS allows no sandbox inside a sandbox, so hillclimb does not put a second one round it, and codex can still read files the table above lists as blocked |
dummy | runs no model and no tools |
Agents without internet
By default an agent may use the internet while it works. To take it away:
allow_internet_for_agents: falseThe agents then reach their model provider and nothing else. Everything they and their tools send goes through a small proxy inside the engine, which lets HTTPS through to the provider's hosts and refuses the rest. The engine's log names every host it refused. Claude Code's web search, web fetch and MCP servers are off, codex's web search is off, and the draft prompt no longer asks for research on the web.
This needs the sandbox. With sandbox: off, or on Windows, a search with claude-code or pi
does not start.
When it does not start
A search does not start when the sandbox is on and cannot be started. The message says why:
| what it says | what to do |
|---|---|
| bubblewrap is not installed | install it with your package manager |
| it does not start, on Ubuntu 24.04 or later | Ubuntu keeps the kernel feature bubblewrap uses behind AppArmor: sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 |
| it does not start, inside a container | the container has to allow user namespaces. If it does not, the container is the sandbox: set sandbox: off |
| it does not start, on macOS | hillclimb was started inside another sandbox, a coding agent's for example. Run it from a plain terminal |
Settings
sandbox: off # run everything unsandboxedsandbox: # or adjust it
write: [scratch] # more writable paths, relative to the hillclimb dir
deny_read: [~/private] # more unreadable paths
allow_hosts: [bedrock-runtime.eu-north-1.amazonaws.com] # more hosts for agents without internet
local_ports: [8000] # localhost ports left open when the network is offHILLCLIMB_SANDBOX=off in the environment switches it off for one command.
What it does not protect
It is a sandbox, not a virtual machine
It stops writes outside the candidate's folder, reads of your keys and traffic to hosts you did not allow. It does not protect against a flaw in the operating system's kernel.
These stay open:
- An agent can read its own login, because it needs it.
- A sandboxed process can still read most of your files. Add what is private to
sandbox.deny_read. - An agent without internet still talks to its model provider, so what it can read can reach that provider.
- codex is confined by its own sandbox, which does not hide your keys from it.
- On Windows nothing is confined.