Bench sandboxes: where candidate code actually runs

June 24, 2026 · View on GitHub

Twin: examples/bench_sandboxes.py · two code-exec lanes · offline by default, no credentials, no container launched. A coding agent can complete this page from the frontmatter alone.

1. What you are testing

The unified bench harness scores a coding suite by actually executing candidate code against a held-out oracle. The one dimension this page covers is where that execution happens — the sandbox argument to run_bench(...):

  • subprocess (default) — a fresh interpreter in a throwaway tempdir, with a scrubbed environment (no harness secrets cross in) and a hard wall-clock timeout. It is credential-free, runs anywhere, and is the lane the release gate uses on trusted, shipped reference code. It is not a security boundary against deliberately hostile code: there is no real filesystem or network isolation, so a candidate could read the host or reach the network.

  • docker (opt-in) — per-task, ephemeral, OS-level isolation for untrusted agent output. The container drops everything the subprocess lane cannot: --network none, --cap-drop ALL, --security-opt no-new-privileges, a --read-only rootfs, a nosuid size-capped tmpfs as the only writable surface, a non-root user (65534), and PID / memory / CPU caps. It is --rm, so the container is killed and removed after the run.

The failure classes this page targets: an untrusted candidate that escapes a weak sandbox, a runner that silently downgrades to a weaker sandbox when Docker is absent (mislabelling isolation), and a missing daemon read as an agent failure instead of an honest void.

Honesty. A real Docker run of untrusted code is a genuine live event, so the harness stamps those rows evidence_class=live_lane (never captured_fixture) and never downgrades the label. The subprocess lane on trusted shipped code is a local_gate event. If the daemon is unreachable, the lane is recorded void — it never ran, so it is neither a pass nor a fail.

2. Run it

Score the shipped coding_starter suite in the subprocess lane (deterministic, credential-free), report whether Docker is reachable, and capture the exact hardened docker run argv without launching a container — then write the artifact:

python examples/bench_sandboxes.py artifacts/bench-sandboxes.json

The same convenience-lane scoring from the CLI, against the suite's own references:

agent-learn bench examples/bench_suites/coding_starter.json --mode artifact_in --reference --sandbox subprocess

To run untrusted output under OS-level isolation, opt into the Docker lane on a host with a daemon (it stamps the rows live_lane):

agent-learn bench examples/bench_suites/coding_starter.json --mode artifact_in --reference --sandbox docker --evidence-class live_lane

In Python, the sandbox is one argument; the example inspects the hardened argv via the pure builder, so it never contacts a daemon:

from fi.alk import bench
from fi.alk.bench import _docker

# Convenience lane: trusted shipped code, scored in a scrubbed subprocess.
suite = bench.load_coding_suite("examples/bench_suites/coding_starter.json")
result = bench.run_bench(
    "examples/bench_suites/coding_starter.json",
    control_mode="artifact_in",
    submission=bench.reference_submission(suite),
    sandbox="subprocess",
    evidence_class="local_gate",
)
print(result["aggregate"]["pass_rate"], result["aggregate"]["scored"])

# Report the hardened lane without starting anything: probe the daemon, then
# build the EXACT argv a Docker run would launch (no container is created).
print("docker reachable:", _docker.docker_available())
argv = _docker._build_docker_argv(
    name="demo", image=_docker.DEFAULT_IMAGE,
    memory=_docker._DEFAULT_MEMORY, cpus=_docker._DEFAULT_CPUS, bootstrap="...",
)
print("--network" in argv and argv[argv.index("--network") + 1] == "none")

A daemon that is missing or unreachable makes every Docker row void (never silently passed, never re-run in the weaker lane). pass_rate is computed over scored tasks only, so a host with no Docker does not read as "0% passed".

3. What you built

Run the postcondition verbatim:

python -c "import json; p=json.load(open('artifacts/bench-sandboxes.json')); s=p['subprocess_lane']; assert s['aggregate']['pass_rate']==1.0, s; assert s['aggregate']['scored']==3, s; d=p['docker_lane']; assert d['launched_container'] is False, d; assert d['all_hardening_present'] is True, d; assert d['isolation_flags']['--network none'] is True, d; print('ok')"

The artifact records two lanes. subprocess_lane carries the real run — the aggregate (count, scored, void, passed, pass_rate, mean_score, the by_* rollups, and the honesty block stamped local_gate) plus one row per task. docker_lane carries the audit of the hardened lane without running it: docker_available, launched_container: false, the chosen image / memory / cpus / pids_limit, the exact hardened_argv from the pure builder, an isolation_flags map auditing each control flag, and all_hardening_present.

4. When it fails

SymptomFirst-mile classDoctor check
Docker rows show verdict: void with infra: docker unavailableno reachable daemon — the lane never ran, so it is void, not failmissing_public_modules
docker run failed (exit ...) recorded as voidthe image is not pulled or the daemon erred — infra, not the agentmissing_public_modules
all_hardening_present is false in the artifactan isolation flag was dropped from the argv — file a bug; never weaken the untrusted lanemissing_public_modules
an untrusted candidate "passes" the Docker lanestructural hold-out only defends accidental gaming, not a candidate that attacks the harness protocol — do not treat as authoritativemissing_public_modules
BenchError: unknown sandbox '...'sandbox must be subprocess or dockermissing_public_modules

5. Prove it / keep it

The subprocess lane and the held-out-oracle scoring model are covered in benchmark-coding. For the forge- and oracle-read-resistant grading model — a held-out grader that runs after the candidate is killed, which the Docker lane composes with for untrusted output — see benchmark-command-graded. The harness, its three control modes, and the unified Result are covered in benchmark-overview.