Skip to content

Remote and Cloud Environments

Chimera agents do not care where their tools run. Every file read, file write, glob, and shell command goes through one interface — chimera.env.base.Environment — and swapping the implementation moves the whole agent onto another machine without touching the agent, the loop, or the tools.

This guide covers the backends that run somewhere other than your laptop:

Backendcreate_environment(...)Runs onExtraCredentials
SSH"ssh"Any host you can ssh tonone (stdlib)your SSH config
SSH (async)"ssh-async"Any host you can ssh tochimera-run[ssh]your SSH config
Docker"docker"Local containerchimera-run[docker]Docker daemon
Modal"modal"Modal cloud sandboxchimera-run[modal-sandbox]~/.modal.toml
E2B"e2b"E2B microVMchimera-run[e2b]E2B_API_KEY
Daytona"daytona"Daytona sandboxchimera-run[daytona]DAYTONA_API_KEY
Remote HTTP"remote"A Chimera workspace serverchimera-run[remote]bearer token
Cloud HTTP"cloud"A provisioning APIchimera-run[remote]bearer token

create_environment("local") and "git" round out the set; both are stdlib only and need no credentials.

from chimera.env.factory import available_providers, create_environment
print(available_providers())
# ['cloud', 'daytona', 'docker', 'e2b', 'git', 'local', 'modal',
# 'remote', 'ssh', 'ssh-async']
with create_environment("daytona", image="python:3.11-slim") as env:
env.write_file("main.py", "print('hello from the cloud')")
print(env.run_command("python main.py").stdout)

Every backend implements the same nine methods — setup, cleanup, read_file, write_file, list_files, run_command, run_tests, checkpoint, restore — plus an optional clone(). Anything that accepts an Environment accepts all of them. checkpoint / restore are real on the SSH backends (tar snapshots of the remote workdir) and on local / git; the ephemeral cloud sandboxes and an attached Docker container raise NotImplementedError instead of pretending.

Register your own with register_environment("my-cloud", MyEnvironment).

list_files(pattern) follows pathlib.Path.glob rules on every backend: a single * stops at the path separator, ** spans segments. Backends that enumerate paths remotely filter through chimera.env.base.glob_match so a pattern selects the same files no matter what is mounted — otherwise a benchmark’s file counts would drift with its sandbox.

env.list_files("*.py") # top-level only
env.list_files("**/*.py") # recursive

Cloud sandboxes fail loudly, never quietly

Section titled “Cloud sandboxes fail loudly, never quietly”

Every managed-sandbox backend refuses to construct when its SDK or its credentials are missing:

>>> create_environment("daytona")
ValueError: DaytonaEnvironment requires a Daytona API key. Pass api_key=... or
set $DAYTONA_API_KEY. Refusing to continue: a cloud sandbox backend must never
silently fall back to local execution, because results produced locally would
be indistinguishable from results produced in the cloud.

This is deliberate and load-bearing. Chimera exists to compare agents under controlled variables; a sandbox that quietly degraded to your laptop would silently invalidate every result it touched, and nothing downstream could tell. The same rule holds at the CLI, which exits 2 rather than running.

Terminal window
pip install 'chimera-run[e2b]'
export E2B_API_KEY=e2b_... # https://e2b.dev/dashboard
from chimera.env.factory import create_environment
with create_environment(
"e2b",
template="base", # E2B template name
working_dir="/home/user",
timeout=300, # sandbox lifetime; E2B reaps it after this
) as env:
env.write_file("app.py", "print(21 * 2)")
print(env.run_command("python app.py").stdout) # -> 42

Notes:

  • sandbox_id="sbx-..." reconnects to an existing sandbox instead of creating one; keep_alive=True leaves it running at cleanup() (it keeps billing).
  • checkpoint() / restore() raise NotImplementedError — E2B sandboxes are ephemeral. Use "docker" or "ssh" when a benchmark needs snapshots.
Terminal window
pip install 'chimera-run[daytona]'
export DAYTONA_API_KEY=dtn_... # https://app.daytona.io
# optional:
export DAYTONA_API_URL=https://app.daytona.io/api
export DAYTONA_TARGET=us
from chimera.env.factory import create_environment
with create_environment(
"daytona",
image="python:3.11-slim", # or snapshot="my-snapshot" (not both)
working_dir="/home/daytona",
env_vars={"PYTHONUNBUFFERED": "1"},
) as env:
env.write_file("app.py", "print(21 * 2)")
print(env.run_command("python app.py").stdout) # -> 42

Notes:

  • image= and snapshot= are mutually exclusive; supplying neither uses the account default.
  • Daytona returns a command’s output as one consolidated stream, so CommandResult.stdout carries everything and stderr is usually empty.
  • checkpoint() / restore() raise NotImplementedError.
  • keep_alive=True skips deletion at cleanup() for post-mortem debugging.

chimera bench-matrix --env selects the per-task environment. Each task gets a fresh sandbox, so no task can contaminate the next:

Terminal window
# Modal (auth via `modal setup`, which writes ~/.modal.toml)
chimera bench-matrix --agents react --benchmarks mbpp --env modal --limit 20
# E2B
export E2B_API_KEY=e2b_...
chimera bench-matrix --agents react --benchmarks mbpp \
--env e2b --sandbox-image base --limit 20
# Daytona
export DAYTONA_API_KEY=dtn_...
chimera bench-matrix --agents react --benchmarks mbpp \
--env daytona --sandbox-image python:3.11-slim --limit 20

--sandbox-image is the E2B template name or the Daytona image; omit it for the service default. Without credentials the command prints which variable is missing and exits 2.

Cost. Every task provisions a billable sandbox. Start with --limit 5 and a budget (--max-cost, --max-tool-calls) before running a full column.

SSH is the backend for machines you already own — a build box, a GPU host, a staging server. Two implementations share one interface:

"ssh" (default)"ssh-async"
Dependencynone — drives the system ssh/scpchimera-run[ssh] (asyncssh)
Connectionone process per call (or ControlMaster)one persistent connection
File I/Ossh cat / ssh tee — text onlynative SFTP — binary safe
Bastionsyour ~/.ssh/confignative ProxyJump chains
Passwordsssh-add firstpassword= / passphrase= accepted
Concurrencyserialrun_bash_many / upload_files, bounded

Both implement the full ABC including checkpoint() / restore(), which tar the remote workdir into $HOME/.chimera/ssh-checkpoints.

from chimera.env.factory import create_environment
with create_environment(
"ssh-async",
host="build.example.com",
username="deploy",
workdir="/srv/app",
proxy_jump="bastion.example.com", # or "j1,j2" for a chain
client_keys=["/home/me/.ssh/id_ed25519"],
retries=3, # exponential backoff on connect
max_concurrency=5,
) as env:
env.write_file("patch.py", "...")
print(env.run_bash("pytest -q").stdout)
results = env.run_bash_many(["ruff check .", "mypy .", "pytest -q"])

Relative paths resolve against workdir; absolute paths are used as-is. SFTP has no notion of a remote cd, so the async backend materialises absolute paths itself and prefixes cd <workdir> && onto every shell command.

Terminal window
# Subprocess backend — no extra deps.
chimera mink --remote ssh://deploy@build.example.com:/srv/app -p "ls -la"
# asyncssh backend — SFTP, persistent connection, ProxyJump.
pip install 'chimera-run[ssh]'
CHIMERA_SSH_BACKEND=async chimera mink \
--remote ssh://deploy@build.example.com:/srv/app -p "ls -la"

docs/mink/remote.md documents the URL grammar, the authentication matrix, and the containerised live-test fixture in full.

Reach for "ssh-async" when the workload is chatty (many small reads and writes), binary-safe transfer matters, you cross a bastion, or the session is long enough that per-call handshakes dominate. Stay on "ssh" for one-off invocations and anywhere installing asyncssh is not worth it.

Unit tests never touch the network. Each backend is driven through a fake SDK or transport injected at the module boundary, so the whole suite runs in CI where no optional extra is installed:

BackendFake injected atTests
E2Bchimera.env.e2b.Sandboxtests/env/test_e2b.py
Daytonachimera.env.daytona._sdktests/env/test_daytona.py
SSH (async)chimera.env.ssh.asyncsshtests/env/test_ssh_contract.py
Glob parity— (real LocalEnvironment)tests/env/test_glob_match.py

Live checks are opt-in and cost money or need infrastructure:

Terminal window
# SSH against a throwaway containerised sshd (needs Docker).
uv run pytest -m live_ssh
# E2B — one real sandbox.
export E2B_API_KEY=e2b_...
uv run python -c "
from chimera.env.factory import create_environment
with create_environment('e2b') as env:
env.write_file('t.py', \"print('e2b ok')\")
print(env.run_command('python t.py').stdout)
"
# Daytona — one real sandbox.
export DAYTONA_API_KEY=dtn_...
uv run python -c "
from chimera.env.factory import create_environment
with create_environment('daytona', image='python:3.11-slim') as env:
env.write_file('t.py', \"print('daytona ok')\")
print(env.run_command('python t.py').stdout)
"

Each smoke provisions one billable sandbox and deletes it on exit.

  • docs/mink/remote.md — the --remote ssh://… CLI surface in depth.
  • docs/specs/agent-benchmark-matrix.md — the matrix runner these backends feed.
  • chimera/env/factory.py — the provider registry.