- Evaluate any agent, such as Claude Code, OpenHands, or Codex CLI, against curated datasets like Terminal-Bench, SWE-Bench, and Aider Polyglot
- Build and share your own benchmarks
- Run thousands of trials in parallel across cloud providers
- Generate rollouts for reinforcement learning (RL) optimization
--env flag. Tensorlake is one of those providers, alongside other sandbox vendors and local Docker. The same Harbor commands run on Tensorlake sandboxes without changes to your tasks, agents, or evaluators.
This guide covers command-line interface (CLI) agent evaluations against benchmarks like Terminal-Bench. Harbor also generates rollouts for RL optimization. We’ll cover those workflows in follow-up guides.
Quickstart
Install Harbor with the Tensorlake extra, set 2 API keys, and run 1 Terminal-Bench task.1
Get a Tensorlake API key
Create one in the Tensorlake Dashboard. You also need an API key for the agent provider you want to evaluate, such as Anthropic.
2
Install Harbor with the Tensorlake provider
The
harbor[tensorlake] extra installs the TensorLakeEnvironment provider alongside Harbor.- uv
- pip
3
Set your environment variables
4
Run a Terminal-Bench task
Run a single Terminal-Bench task on Tensorlake with Claude Code as the agent:Remove
--include-task-name to run the full Terminal-Bench 2.1 suite. --ae forwards an environment variable from your shell into the sandbox where the agent runs. Add more --ae flags for any other secrets the agent needs.Why Tensorlake for Harbor
Harbor’s value comes from running large fleets of environments in parallel and trusting the results. Tensorlake’s runtime targets that workload:- Per-trial sandboxes: Each task starts on a clean sandbox, which Tensorlake destroys at the end. Trials share no kernel state, which matters for both eval reproducibility and RL reward integrity.
- Full task-environment support: Tensorlake imports a task’s real Docker image and converts it into a sandbox image that boots directly. Every trial runs the exact environment the benchmark defines, not an approximation built by replaying a Dockerfile. That closes the environment gap that otherwise skews results.
- Pre-warmed snapshots: Build environments with heavy
aptorpipinstalls, such as PyTorch, CUDA toolchains, or full Linux desktops, once. Snapshot them, then restore the snapshot for every later trial or rollout. - Independent verification: Harbor’s test script runs inside the sandbox and writes
1.0or0.0toreward.txt. The agent never sees or touches the verifier, so you never confuse “the agent said it worked” with “the tests pass”. - Parallel scale: Tensorlake schedules thousands of sandboxes concurrently, which is what RL rollout generation and full benchmark sweeps need.
Anatomy of a Harbor task
Harbor expects each task to use the layout below. gcode-to-text is an example:environment/Dockerfiledefines the base image and any setup steps.instruction.mdis the prompt the agent receives.solution/is a reference solution that validates the environment itself.tests/test.shruns after the agent finishes and producesreward.txt.
Tune sandbox resources
Each task’stask.toml controls the sandbox Harbor provisions on Tensorlake. Set resources in the [environment] block:
task.toml
Tensorlake requires between 1024 MB and 8192 MB of
memory_mb per CPU core.Harbor deprecated
allow_internet in favor of network_mode. Harbor still accepts it and maps true and false to network_mode = "public" and "no-network". Set network_mode directly in new tasks.- Large or heavy images: If your
environment/Dockerfilepulls in big toolchains, such as PyTorch, CUDA, full Linux desktops, or large datasets, raisecpusandmemory_mbso the build and runtime have headroom. Raisestorage_mbpast the image size plus working-set room. Underprovisioned sandboxes fail with build timeouts or out-of-memory (OOM) errors mid-trial. - Lock down
network_mode: Setnetwork_mode = "no-network"to stop the agent from searching the web for answers. Or set"allowlist"withallowed_hoststo permit only specific destinations.
Dynamic network policy
Different phases of a trial need different network access. Setup may need the open internet to install dependencies. The agent and verifier need tighter limits so the agent can’t search the web for answers and nobody can tamper with the verifier’s result. Tensorlake sandboxes switch the network policy on a running sandbox, so Harbor can scopenetwork_mode per phase in task.toml:
task.toml
allowed_hosts accepts exact hostnames, leading-wildcard hostnames such as *.example.com, IPv4 literals, and IPv4 CIDR ranges.
allowlist and per-phase overrides apply to single-container tasks only. Docker Compose tasks support public and no-network alone. That section explains why.Image build and caching
Each trial boots from an image. Harbor uses a prebuilt image when the task declares one, and otherwise builds the task’s Dockerfile:
Either way, Harbor builds or imports the image once and reuses it. You pay the cost on the first trial only. Every later trial boots directly from the cached image. If a task sets both and the prebuilt image exists, Harbor uses the prebuilt image.
Prebuilt image
If a task declares adocker_image in task.toml, Harbor boots directly from that image and skips the Dockerfile:
task.toml
TENSORLAKE_API_KEY for image lookup and import. The API key selects the project, so you don’t need separate organization or project environment variables. If Harbor reports that image lookup requires organization and project context, upgrade its Tensorlake SDK integration. That message comes from an older SDK contract.
Dockerfile
If a task has nodocker_image, Harbor builds its environment/Dockerfile once with Tensorlake’s image builder and caches the result. Every later trial boots directly from the cached image, with no per-trial apt or pip work. The cache key covers the Dockerfile and every file in the build context, so editing a requirements.txt pin or any COPY’d file triggers a rebuild.
RUN and COPY steps on each trial. Every trial still runs, but slower. You can also request the fallback explicitly while you iterate on a Dockerfile:
Dockerfile requirements
The image builder is stricter than a localdocker build, so 3 Docker conventions need adjustments:
COPYdoes not auto-create parent directories:COPY x /a/b/cfails if/a/bdoesn’t exist yet. AddRUN mkdir -p /a/bbefore theCOPY.- Don’t pin exact apt versions (
apt-get install curl=8.5.0-2ubuntu10.6): Drop the pin or pick a version that exists in the target distro. - Use a FROM image that ships the Python you need, such as
python:3.10-bookworm, rather than fetching a non-native version at build time.
--force-build. This applies to that run only and leaves the cache for later runs intact.
Sharing images publicly
By default, images Harbor builds or imports are private to your organization. Only your org can boot from them. Add--ek is_public=true to register a freshly built or imported image as public, so any organization with Tensorlake access can reuse it:
docker_image imports. Another organization boots a public image through the prebuilt docker_image path. The published Terminal-Bench images work this way. To publish an environment that others boot directly, use a docker_image reference.
Publishing public images requires an allowlist entry. If your account isn’t on it, Harbor ignores the flag and the image stays private. Contact Tensorlake to be added.
is_public takes effect only when Harbor registers a new image. If an image with the same name already exists, either a private copy from an earlier run or an existing public one, Harbor boots it as-is and won’t republish it. To turn an already-private image public, delete it first, then rerun with --ek is_public=true. Changing the build context so the image gets a new name also works.
Ad-hoc native dependencies
If a task needs extra apt packages and you don’t want to edit the Dockerfile or maintain a snapshot, usepreinstall_packages:
Docker Compose (multi-container) tasks
If a task needs more than one container, such as a database or sidecar service alongside the agent’s environment, add anenvironment/docker-compose.yaml. Harbor detects it and boots a docker-capable sandbox instead of a single container. You don’t need an extra flag:
dockerd and docker compose run on the sandbox. Your main service, where the agent and verifier operate, runs alongside any other services you declare.
Compose tasks differ from single-container tasks in 4 ways:
- No snapshots: Compose tasks don’t support
snapshot_id. The compose project builds inside a fresh DinD host on every run. preinstall_packageshas no effect: Put packages in the compose service images instead.- Network policy is coarser: Only
publicandno-networkare available (see the Dynamic network policy note above).allowlistand per-phase switching require host-level firewall control that a nested DinD host doesn’t have, so Tensorlake enforcesno-networkisolation inside the virtual machine (VM) instead. - Size storage generously: Compose pulls and builds layers on top of the DinD host’s own rootfs, so
storage_mbneeds headroom beyond what a single-container task needs for the same images.
--ek dind_image=your_dind_image_reference. It must ship dockerd, docker compose, and a systemd-managed docker.service.
Interactive debugging
When a trial fails and you want to inspect the live environment, attach to the session:Structured logs
Each trial produces structured artifacts:- The agent’s actions and outputs
- What the verifier checked
- Why the trial passed or failed
Next steps
Snapshots
Build an environment once, snapshot it, and restore it for every trial.
Reproducible RL Environments
Use sandboxes as a deterministic reward oracle for RL training loops.