# Core Concepts (https://www.agentenvframework.com/docs/core-concepts)

> The concepts AgentEnv Framework is built from and why each one exists, starting with environments

AgentEnv Framework lets you build environments for AI agents, run agents in them and check what
they did. Seven concepts make it up: environments, artifacts, agents, tasks, the registry,
plugins and sandboxes. This page defines each one and why it exists, in that order.

## Environments

An environment is the world an agent acts in: usually a server that offers tools over the Model
Context Protocol (MCP) and holds the state they change, such as an email inbox that an agent can
search and send from. You write it as a subclass of `AgentEnvEnvironment`, like the `EmailEnv`
below, and it describes itself in an environment card. You register it as an env under an id, with
a new version on every registration, and each deployment of an env is an env instance. A `multi`
env composes several envs into one, such as the inbox and a contacts book.
[Environments](https://www.agentenvframework.com/docs/environments.md) follows `EmailEnv` through each of these.

1. Write the environment as an `AgentEnvEnvironment`.
2. Register it under an env id. Every put adds a new version.
3. Deploy a version on any sandbox. The deployment is an env instance.
4. The instance’s card lists its interfaces: MCP, the data plane, `/step` and a CLI.
5. The env’s `EnvironmentProvider` sets its topology: gateway, server, or your own.
6. Load and read data, and drive triggers, the clock and per-role tool access.
7. Tear it down and deploy again. Every deployment gets its own instance id.

Parts of the scene:

- **Your environment**: A subclass of `AgentEnvEnvironment`. Its `@tool` methods become the agent’s MCP tools, and `@reset_data`, `@add_data` and `@get_data` answer the data plane.
- **Versions**: Every `agent-env env mcp-server put --id email` stores a new version, and older versions never change. Pin one as `email:1`; a bare id means the latest when it is read.
- **One image, several envs**: The topology is recorded on the env when you register it, so the same server image registered under another environment provider becomes an env of its own.
- **Env instances**: Each deployment is an env instance, with its own id: the env id plus eight random characters. The record keeps the version it deployed, its URLs and its card; `agent-env env get-instance --id` reads it back.
- **Deploy as often as you like**: Every deployment gets a new instance id, and the env it came from never changes. Nothing tears an instance down on its own: close it from Python or with a `teardown_sandboxes` step, or let a remote sandbox reach its TTL.
- **Local**: Runs the instance as containers on your machine’s Docker. The local sandbox does not enforce the TTL, so close instances yourself.
- **Modal**: `modal` runs each server in a Modal container, and `modal_vm` runs the whole instance in one Modal VM. Credentials come from `MODAL_TOKEN_ID` and `MODAL_TOKEN_SECRET`, or `~/.modal.toml`.
- **Your sandbox provider**: Subclass `SandboxProvider` and register it under `[sandbox.providers.<name>]` in `config.toml`, or through the `agent_env.sandbox_providers` entry point. Then deploy with `--sandbox <name>`.
- **Gateway**: The default topology: one MCP endpoint in front of every server in the instance. It merges their tools into one list and adds `/step`, the virtual clock, triggers and per-role tool access.
- **Server**: One server with no gateway in front, so its own card is the env’s card. There is no `/step`, clock, triggers or tool access.
- **Your topology**: Subclass `EnvironmentProvider` and register it through the `agent_env.env_providers` entry point. The entry-point name is the topology your envs record.
- **MCP**: `/mcp` is where the agent calls the environment’s tools, over the Model Context Protocol.
- **Data plane**: `/agentenv` answers `data/reset`, `data/add` and `data/get` over JSON-RPC. The framework seeds an instance through it before a run and reads its state back after.
- **/step**: A REST endpoint on the gateway for listing and calling tools, filtered by the caller’s `AgentEnv-Role` header.
- **CLI**: `agent-env env mcp-server create-cli --id email` deploys the env, reads its live tools and stores a command-line tool for them as an artifact.
- **Virtual clock**: A `sync_env_clock` step sets the gateway’s clock and syncs every server that supports it, so a scenario that spans days can run in minutes.
- **Triggers**: A `register_env_triggers` step registers conditions on tool calls, state or time, and the actions to run when they fire.
- **Tool access**: A `modify_env_tool_access` step disables or enables tools for a role. The gateway hides them from that role’s tool list and refuses its calls.

## Artifacts

An artifact is data that the framework stores and versions, such as a set of emails, a file, a
Docker image or a VM image. You register artifacts on their own, independently of any environment, and every
registration adds a new version. That decoupling lets you take one set of artifacts, such as a
company's data, and load it into different environments, such as an MCP environment for
tool-calling agents and a computer-use (CUA) environment for agents that work in a browser. Because
the artifacts are separate, you can iterate on them without touching the environments that load
them. [Artifacts](https://www.agentenvframework.com/docs/artifacts.md) follows one company's data through each of these.

1. Collect a company’s data as files: its emails, its chat messages and its CRM as SQL.
2. Put each file as an environment artifact named for its environment: `email`, `chat`, `crm`.
3. Bundle the three into a universe, `acme`, which pins the version of each.
4. Load `acme` into a support desk: each server gets the artifact with its name.
5. Load the same `acme` into a sales office, and a browser office for computer-use agents.
6. Fix a refund in the emails: `acme-email` v2, and `acme` v2 to pin it. No env changes.
7. Load `acme` v2 into the same envs. `acme:1` is unchanged, so old runs reproduce.

Parts of the scene:

- **The company’s data**: Plain files, in whatever format each server reads: JSON for the inbox and the chat, a SQL dump for the CRM. The framework stores and moves them, and each server’s `@add_data` decides how to read its file.
- **Environment artifacts**: `agent-env artifact environment put --id acme-email --environment-name email --description "Acme inbox" emails.json` stores the file and wraps it with the name of the environment it targets. Every put of the same id adds a version, and older versions never change.
- **A universe**: `agent-env artifact environment-universe put --id acme`, with one `--environment-artifact` per artifact, bundles the three and pins each to the version that was latest at the put. After a fix, putting `acme` again makes version 2, which pins the new emails; version 1 still pins the old ones.
- **A loaded server**: Loading `acme` into an env instance hands each server the artifact with its name: the framework copies the file into the server’s container, then calls `data/reset` and `data/add` on its data plane. A `load_artifact` step does this in a task, and `agent-env env multi load-environment-universe-artifact` from the command line.
- **A support desk**: An instance of `support-desk`, a `multi` env whose `email`, `chat` and `crm` servers offer their tools to an agent over MCP. It takes all three of `acme`’s artifacts, and the env stays at version 1 while the data changes.
- **A sales office**: An instance of `sales-office`, another `multi` env, with `email`, `crm` and `calendar` servers. It loads the same `acme-email` and `acme-crm` as the support desk, and the env stays at version 1 while the data changes.
- **A browser office**: An instance of `browser-office`, a `multi` env of websites for computer-use agents, which work the apps through a browser instead of calling their tools. A website named `email` loads `acme-email` the same way the `email` server does.
- **Skipped**: The sales office has no `chat` server, so `acme-chat` has nowhere to go. Loading a universe skips an artifact whose name no server in the instance has.
- **Nothing for calendar**: `acme` has no `calendar` artifact, so the calendar server keeps the state it starts with. Add one to the universe, and the next load seeds it too.
- **The browser**: What a computer-use agent works the websites through. Every env with websites deploys it next to them, from the env that `agent-env env website-browser put` registers once.

## Agents

An agent is what acts in an environment: anything that speaks the agent-to-agent protocol (A2A),
written as an `AgentEnvAgent` or wrapping a harness such as [Claude Code, Codex, Gemini, Hermes
Agent, OpenCode, pi, OpenHands, Letta Code, Pydantic AI or Browser
Use](https://www.agentenvframework.com/docs/agents/supported-agents.md). Agents are separate from envs and tasks, so the same task runs
unchanged with any agent, and one task can deploy several agents that message each other.
[Agents](https://www.agentenvframework.com/docs/agents.md) covers how they plug in.

1. Write an agent as an `AgentEnvAgent`, or wrap a harness you already use. Either way it speaks A2A.
2. Register each one under an agent id with `agent-env a2a-agent put`. Every put adds a new version.
3. A task’s `deploy_agent` step deploys a version in a sandbox of its own. The deployment is an agent instance.
4. `deploy_agent` sends the agent the env’s MCP URL, so it can call `email_search` and `send`.
5. `prompt_agent` messages the agent over A2A, then records its reply and its trajectory.
6. Run the same task with another agent. Each run deploys a new instance of the same env version.
7. Deploy several agents in one task. `peer_agents` tells each where the others are, and they message each other over A2A.

Parts of the scene:

- **A harness you already use**: Anything that speaks A2A and serves an agent card can be an agent, so a harness such as Claude Code needs only a wrapper that runs it behind an A2A server. agent-env does not ship one.
- **Another harness**: The OpenAI Agents SDK, or any other harness, is wrapped the same way. Each wrapper is an agent of its own, with its own id and versions, and a task can use any of them.
- **Your own agent**: A subclass of `AgentEnvAgent` from `agentenv-framework-protocol[agent]`, declared with `@a2a_agent`. Its `run()` handles each A2A task, and the framework serves its agent card and the extensions it declares.
- **The same env**: Version 1 of the `email` env from Environments. Every run here deploys this version, whichever agent the run uses; a `deploy_env` step pins it with `env_version`.
- **Agent versions**: `agent-env a2a-agent put --id <id> --dockerfile <path>` builds the image, stores it as the artifact `a2a-agent-<id>` and adds a version of the agent. Older versions never change.
- **The commands**: `agent-env a2a-agent put` registers an agent, and `agent-env task run` runs a task. `--a2a-agent-id` replaces the agent that every `deploy_agent` step deploys, for that run only.
- **The task q3**: A DAG of steps: `deploy_env` starts the env, `load_artifact` loads Dana’s email into it, `deploy_agent` starts an agent and `prompt_agent` asks it to send Sam the numbers. No step depends on which agent it is.
- **Three agents in one task**: Three `deploy_agent` steps, each with its own `agent_name` and the same `env_ids`, so all three connect to one env instance. `peer_agents` then peers them, and `prompt_agent` prompts `my-agent`.
- **Runs**: Each `agent-env task run` is a run, with its own id, env instance and agent instances. Its record keeps the context every step wrote, including each prompt’s reply.
- **An agent instance**: One deployment of an agent version, in a sandbox of its own from `[sandbox] agent_default` unless the step sets `sandbox_type` or `sandbox_name`. Its id is the agent id plus eight random characters, and it serves the agent card that lists its extensions. An `install_agent` step can instead install the agent into a container the task already runs.
- **mcp-config**: `deploy_agent` posts each env’s MCP URL to this extension, as the URL reaches the env from the agent’s sandbox. The agent can then call the env’s tools.
- **agent-config**: Before each prompt, `prompt_agent` sends the settings the agent card says the agent accepts, such as `model` and `system_prompt`. The deploy gives the agent the model endpoint and key, from `[model]` or `LITELLM_BASE_URL` and `LITELLM_API_KEY`.
- **trajectory**: Once the agent replies, `prompt_agent` fetches its trajectory, the agent’s own record of what it did, and stores it with the run for grading.
- **peer-agents**: A `peer_agents` step posts each agent the name, A2A URL and card of each of its peers. It refuses an agent whose card does not list this extension.
- **The prompt**: `prompt_agent` sends it to the agent as an A2A message and waits for the reply. The reply is stored in the run’s context, where a verifier step can grade it.
- **The env instance**: Each run deploys its own instance of `email` version 1 and loads the same email into it, so every agent starts from the same state. In the three-agent task, all three share one instance.
- **MCP**: The env instance’s MCP URL. Whichever agent a run deploys connects here and gets the same tools, `email_search` and `send`.
- **A2A between agents**: Agents message each other over A2A, the protocol `prompt_agent` uses to message them. Here `my-agent` asks `claude-code` and `openai-agents` for help; how an agent uses its peers is up to the agent.

## Tasks

A task is a run you can repeat: a directed acyclic graph (DAG) of task steps that deploy envs and
agents, prompt the agents, grade their work with verifiers and collect what they made. The
framework ships nearly 50 built-in steps, and you can [register your own](https://www.agentenvframework.com/docs/tasks/more-steps.md#your-own-steps) as a
Python class. A task is a JSON list of steps, so you write one without code.
[Tasks and steps](https://www.agentenvframework.com/docs/tasks.md) covers how a run executes.

1. An analyst charts `q3.csv`; a judge agent deployed beside it grades the collected `q3.png`.
2. Deploy `email`, load its inbox while the agent deploys, then prompt it and check what it sent.
3. A second agent plays Dana, and `prompt_agent` runs their conversation for up to six turns.
4. Two envs with an agent on each, peered so the planner can hand the email to the writer.
5. A virtual clock runs a week in two minutes, and a trigger locks `send` at Friday’s deadline.
6. 25 agents in one `office`: 20 customers, 3 support agents and a world agent that runs triggers.

Parts of the scene:

- **The task**: A task is stored under an id, like an env. Every `agent-env task create` stores a new version and never changes an older one, and a run uses the latest unless you pass `--version`.
- **task.json**: Each box is one entry in the task’s JSON list: an `id`, a `type`, that type’s fields and `depends_on`, such as `{"id": "reply", "type": "prompt_agent", "agent_name": "assistant", "prompt": "Reply to Dana.", "depends_on": [{"task_step_id": "inbox"}, {"task_step_id": "assistant"}]}`. You write a task as JSON, not code.
- **Task instances**: Each run is a task instance, with its own id: the task id plus eight random characters. Its record keeps the task version, the run’s status and the context each step leaves. `agent-env task run --id reply-to-dana` starts one, and `--k 5` starts five at once.
- **depends_on**: Each line is a `depends_on` edge. A step starts once every step it depends on has finished, so steps with no path between them run at the same time. A step without `depends_on` waits for every step listed before it.
- **deploy_env**: Deploys an env as a new env instance, as `agent-env env deploy` does, and adds it to the run’s context, where later steps find it by `env_id`.
- **load_artifact**: Loads a pinned artifact into a running env through its data plane, `data/reset` and then `data/add`, or copies it into an agent’s sandbox as files, so every run starts from the same data.
- **deploy_agent**: Starts a registered agent in its own sandbox, under an `agent_name` that later steps use. `env_ids` points its MCP client at those env instances, which is when the agent gets their tools.
- **prompt_agent**: Sends a prompt to an agent by `agent_name` and waits for it to finish. The reply and the agent’s trajectory are stored under the step’s `prompt_id`, which is how verifiers find them.
- **collect_artifacts**: Copies the files an agent made out of its sandbox into the object store and records them in the run’s context, so they outlast the sandbox.
- **rubrics_verifier**: A judge model scores the agent’s trajectory for a `prompt_id` against weighted criteria written in plain language, and writes the score to the run’s context under `verifications`.
- **env_outcome_verifier**: Grades the end state rather than the transcript: it runs a `verify()` function, stored as a file artifact, against the env instance’s MCP URL. No model is involved.
- **A judge agent**: A second `deploy_agent`, under `agent_name: judge`: any registered agent, such as one on another model than the analyst’s. Its `depends_on` is empty, so it deploys at the same time as the analyst.
- **load_artifact from a step**: With `artifact_from_step_id: collect` and `agent_name: judge`, it copies the files the collect step stored, here `q3.png`, into the judge’s sandbox.
- **rubrics_verifier with a judge**: With `agent_name: judge`, the judge you deployed grades the analyst’s trajectory for its `prompt_id` against the criteria, and its prompt lists the files loaded into its sandbox, so it can open `q3.png` itself. Without `agent_name`, the verifier deploys `judge_a2a_agent_id`, or the default agent.
- **An agent that plays Dana**: A second `deploy_agent`, under `agent_name: dana`. It needs no env, so its `depends_on` is empty and it deploys at the same time as `email`.
- **A conversation**: With `user_agent_name: dana` and `max_conversation_turns: 6`, `prompt_agent` sends each of the assistant’s replies to Dana’s agent, and her answer back as the next turn.
- **peer_agents**: Pushes a routing table to each agent it names. The planner gets one naming the writer, so it can message the writer over A2A.
- **deploy_agent with a role**: Deploys the assistant with `role: assistant`. Its calls carry that role to the env’s gateway, which is what tool access and triggers act on.
- **register_env_triggers**: Registers rules on the env’s gateway: when a tool is called, the state changes or a time arrives, run an action. This one disables `send` for the assistant’s role at Friday 17:00, virtual time.
- **sync_env_clock**: Sets the env’s virtual clock and its speed, right before the prompt. At 3,600 virtual seconds a second, Monday 09:00 to Friday 17:00 passes in under two minutes.
- **20 customer agents**: Twenty `deploy_agent` steps in task.json, `customer-1` to `customer-20`, drawn as one stack, each with its own persona in `system_prompt`. None needs the env, so their `depends_on` is empty and all twenty deploy at the same time as `office`.
- **Three support agents**: Three `deploy_agent` steps on `office`, each with `role: support`. The gateway reads that role on every call they make, which is what the triggers watch.
- **The world agent**: An agent that plays the rest of the world. `register_env_triggers` names it as `executor_agent_name`, so it carries out the triggers’ plain-language actions, such as a supplier emailing about a delay. Its role, `world`, stays out of `watch_roles`, so its own calls fire nothing.
- **peer_agents**: Pushes each support agent a routing table naming the other two, so one can hand a customer to another over A2A.
- **register_env_triggers**: Registers the world’s rules on the gateway, watching `role: support`: when a support agent calls a tool, the state changes or a virtual time arrives, the world agent acts, a tool call runs, or a tool is disabled for a role.
- **20 conversations**: Twenty `prompt_agent` steps, each pairing a support agent with a customer through `user_agent_name`, for up to `max_conversation_turns: 8`. They share a wave, so all twenty run at once in the same env, on the same virtual clock.
- **20 rubrics**: One `rubrics_verifier` per conversation, each reading its own `prompt_id`, so every customer’s experience gets its own score under `verifications`.
- **snapshot_env**: Exports the env’s end state as a new environment universe artifact, which a later task can load, so next week can start where this one ended.

## The registry

The registry is where the framework keeps what you register and what your runs record, in [four
stores](https://www.agentenvframework.com/docs/registry.md): a document store for versioned records and run history, an object store for
the bytes they point at, an image store for container images, and a secret store for the `secret:`
references in your config. All four are local by default, and `config.toml` swaps any of them for AWS or Google Cloud stores, or a
store of your own.

**Register an env**: `agent-env env mcp-server put --id email --dockerfile Dockerfile`

1. `put` builds the image on your machine, and pushes it to the image store as `mcp-server-email:v1`.
2. It saves the same image as a tarball in the object store, with the files its build copied in.
3. A `docker_image` artifact, `mcp-server-email` version 1, records both in the document store.
4. The env `email` gets version 1 in `envs`, pinned to that image artifact. The next `put` adds version 2.

**Put an artifact**: `agent-env artifact environment put --id acme-email --environment-name email acme/emails.json`

1. The file goes to the object store, under its artifact’s type, id and version.
2. A `file` artifact, `acme-email-file` version 1, records where the bytes are.
3. The environment artifact `acme-email` pins that file for envs named `email`, and holds no bytes of its own.

**Deploy an env**: `agent-env env deploy --id email`

1. `deploy` reads `email`, the image artifact it pins, and the gateway and database envs it runs with.
2. The sandbox loads each image from its tarball in the object store. Providers that run containers, such as `modal`, pull from the image store.
3. The instance goes into `env_instances` as `email-mmtflq6t`, and its database state into `env_state_instances`.

**Run a task**: `agent-env task run --id q3-email`

1. `task run` reads `q3-email` from `tasks`, and records the run in `task_instances`, where each step’s progress lands.
2. `deploy_env` deploys `email` as in Deploy an env, and records the instance in `env_instances`.
3. `load_artifact` reads `acme-email`, and the sandbox copies its `emails.json` from the object store into the instance.
4. `deploy_agent` reads `my-agent`, pulls its image from the image store, and resolves the model key through the secret store.
5. `prompt_agent` asks the agent to answer Dana, and stores the agent’s trajectory in the object store.
6. `collect_artifacts` uploads the agent’s `q3-summary.md` to the object store, and records it in `artifacts` as a new artifact.

Parts of the scene:

- **The framework**: The `agent-env` CLI, or your code through the Python API. It reads and writes the document, object and secret stores itself, and pushes the images it builds to the image store.
- **The document store**: A versioned record of every env, artifact, agent and task, and a record of every instance and run: `envs`, `artifacts`, `a2a_agents`, `tasks`, `env_instances`, `task_instances` and more. Every put inserts a new version. Locally it is SQLite, at `~/.local/state/agent-env/document_store/documents.db`; MongoDB, DynamoDB and Firestore are built in too.
- **The object store**: The bytes that records point at, written once under keys such as `artifacts/<type>/<id>/<version>/`: artifact files, image tarballs, trajectories and snapshots. Locally it is a folder, `~/.local/state/agent-env/object_store/`; S3 and Cloud Storage are built in too.
- **The image store**: Container images, pushed as `<artifact id>:v<version>` when you put an env or an agent. Locally it is an OCI registry on `localhost:5000`, which the first push starts as the `agentenv-registry` container; ECR and any OCI registry, such as Artifact Registry, are built in too.
- **The secret store**: Resolves the `secret:KEY` references in your config file, such as `[model] api_key = "secret:MODEL_KEY"`, so no secret sits in the file. Locally it reads your environment variables; AWS Secrets Manager and Google Cloud Secret Manager are built in too.
- **The sandbox**: Where env instances and agents run: `local`, `modal`, `modal_vm` and `e2b` are built in. The `local` provider loads an env’s images from their tarballs in the object store, and pulls an agent’s image from the image store; `modal` pulls both from the image store.

## Plugins

The AgentEnv Framework comes with some strong building blocks, but you may find the need to add more.
Plugins let you write your own custom env types (e.g a CUA env), custom task steps for your DAG, your
own custom stores, custom env topologies, and more.

1. A plugin is a Python package whose `pyproject.toml` declares `agent_env.*` entry points: here an env, a task step and a CLI command.
2. `agent-env plugin add` installs it with the installer that owns agent-env’s environment, here a uv tool, and then checks it.
3. With no config to write, each entry point takes its place next to the built-ins, under its entry-point name.
4. `agent-env plugin list` shows each package, what it provides and whether all of it took effect.
5. A package whose task step is named `deploy_env`, a built-in’s name, fails the check that `plugin add` makes, so the add rolls the environment back.

Parts of the scene:

- **The plugin commands**: `agent-env plugin` lists, shows and checks what installed plugins contribute, and adds or removes them with the installer that owns the environment: `uv tool`, pipx, a uv project or a virtualenv.
- **A plugin package**: An installable Python distribution, such as `acme-agentenv-browser`. Uninstalling it removes the plugin, apart from any `[plugins.acme-agentenv-browser]` settings table in the config file.
- **Entry points**: Each `agent_env.*` group names a kind of addition: `envs`, `task_steps`, `artifacts`, sandbox, state and environment providers, `cli_plugins`, `cli_root_options`, `explorer_plugins` and `bundles`. The name is the registry key, and for an env or a step it must equal the class’s `type`.
- **agent-env’s environment**: Where agent-env is installed, here a uv tool. A plugin has to be installed into that same environment, and `plugin list` names it in its header.
- **The registries**: Where the framework looks a name up: an env or step type by the name its documents carry, a command by what you type. Each takes the built-ins first and then plugins, so a plugin can add a name but never replace a built-in.
- **plugin list**: One row per plugin package, with its version, what it provides and its status. agent-env’s own `hello` bundle is listed too. `--json` gives the same report in a format for scripts and CI.
- **The check**: After installing, `plugin add` checks the new package in a fresh interpreter: every contribution must be active, or replaced by config. A step skipped because a built-in owns its name fails it, so the add puts every package back as it was.

## Sandboxes

A sandbox is the isolated machine or container that your env or agent can run in. Your machine's
Docker is the default sandbox provider, but you may also choose to use a sandbox provider such as
[Modal's](https://www.agentenvframework.com/docs/sandboxes.md#modal), [CoreWeave](https://www.agentenvframework.com/docs/sandboxes.md#coreweave), [E2B](https://www.agentenvframework.com/docs/sandboxes.md#e2b), [Sail](https://www.agentenvframework.com/docs/sandboxes.md#sail),
[Tensorlake](https://www.agentenvframework.com/docs/sandboxes.md#tensorlake), [Tenki](https://www.agentenvframework.com/docs/sandboxes.md#tenki), [Vercel](https://www.agentenvframework.com/docs/sandboxes.md#vercel), [smol machines](https://www.agentenvframework.com/docs/sandboxes.md#smol), [boxd](https://www.agentenvframework.com/docs/sandboxes.md#boxd), [Daytona](https://www.agentenvframework.com/docs/sandboxes.md#daytona), [boat](https://www.agentenvframework.com/docs/sandboxes.md#boat), [Agent37](https://www.agentenvframework.com/docs/sandboxes.md#agent37), [Lume](https://www.agentenvframework.com/docs/sandboxes.md#lume), or a sandbox provider of your own creation via a
[plugin](https://www.agentenvframework.com/docs/plugins/sandbox-provider-plugins.md). View all available sandbox providers
[here](https://www.agentenvframework.com/docs/sandboxes.md#supported-providers).

1. `--sandbox local`, the default, runs the env and the agent as containers on your machine’s Docker.
2. `--sandbox modal` runs each of the env’s servers, and the agent, in a Modal container of its own.
3. `--sandbox modal_vm` boots a Modal VM for the env and one for the agent, and runs their containers inside.
4. `--sandbox coreweave` boots a CoreWeave Sandbox with Docker inside for the env, and runs the agent’s own image in a Sandbox of its own.
5. `--sandbox e2b` boots an E2B sandbox for the env and one for the agent, each a VM with Docker inside.
6. `--sandbox sail_vm` boots Sailboxes, root VMs with Docker inside, and keeps the agent’s model key out of them.
7. `--sandbox tensorlake` boots Tensorlake MicroVMs from a public image with Docker inside.
8. `--sandbox tenki` boots a Tenki VM for the env and one for the agent, each with Docker inside.
9. `--sandbox vercel` boots a Vercel Sandbox for the env and one for the agent, each with Docker inside.
10. `--sandbox smol_vm` boots a SmolVM microVM on your own machine for the env and one for the agent, each with Docker inside.
11. `--sandbox boxd` boots a boxd machine for the env and one for the agent, each a VM with Docker already running.
12. `--sandbox daytona` boots a Daytona sandbox for the env and one for the agent, each running a Docker daemon of its own.
13. `--sandbox boat` boots a boat sandbox for the env and one for the agent, each an Ubuntu VM with Docker already running.
14. `--sandbox agent37` boots an Agent37 VM for the env and one for the agent, each with Docker already running.
15. A provider of your own, installed as a plugin, takes the same env and agent with `--sandbox <name>`.

Parts of the scene:

- **Picking the sandbox**: `--sandbox` picks the provider for one run. `[sandbox] default` and `agent_default` in `config.toml` pick it for every run, separately for envs and agents, and a comma-separated list such as `sail_vm,e2b` is a fallback chain.
- **One env, one agent**: A registered version of the `email` env and of the `claude-code` agent. Neither names a sandbox, so the same versions deploy on every provider.
- **local**: The default: the env’s servers and the agent run as containers on your machine’s Docker. It needs no account, and it does not enforce a TTL, so close instances yourself.
- **modal**: Each of the env’s servers and the agent run in a Modal container of its own, reached through a `*.modal.host` URL. Credentials come from `MODAL_TOKEN_ID` and `MODAL_TOKEN_SECRET`, or `~/.modal.toml`.
- **modal_vm**: One Modal VM for the whole env instance, with Docker inside running its containers, and one for the agent. Same credentials as `modal`.
- **e2b**: E2B sandboxes used as VMs: Docker runs inside, and each exposed port gets a public `*.e2b.app` URL. Needs an E2B API key and a base template with Docker and Compose v2.
- **sail_vm**: Sail Sailboxes: root Linux VMs with Docker and Compose v2, a public `*.sail.box` URL per port, and egress allowlists. Sail adds the agent’s model key to its requests on the way out, so the key never enters the box.
- **tensorlake**: Tensorlake MicroVMs booted from `agentenv-dind-host-v1`, a public image with Docker and Compose v2, with a public `*.sandbox.tensorlake.ai` URL per port and egress allowlists. Needs the `tensorlake` extra and a Tensorlake API key.
- **tenki**: Tenki Sandbox VMs: one Linux VM each for the env and the agent, with Docker inside, a public `*.sb.tenki.sh` URL per port and egress allowlists. Needs the `tenki` extra and a Tenki API key; booting from a snapshot that already has Docker starts faster.
- **smol_vm**: smol machines’ SmolVM microVMs on your own machine: one each for the env and the agent, with Docker inside and a localhost port for each port it exposes. Needs the `smol` extra and Apple Silicon macOS or Linux with `/dev/kvm`, but no account or API key. It can’t enforce an egress allowlist, so a step with one is refused.
- **vercel**: Vercel Sandboxes: one each for the env and the agent, with Docker inside, a public `*.vercel.run` URL per port and egress allowlists. Needs the `vercel` extra and Vercel credentials, either the Vercel SDK’s own or a token, team and project in `config.toml`.
- **boxd**: boxd machines: one hardware-isolated Linux VM each for the env and the agent, with Docker already running, a public `*.boxd.sh` URL per port and egress allowlists. Needs the `boxd` extra and a boxd API key; booting from a snapshot that already holds the images starts faster.
- **daytona**: Daytona sandboxes: one each for the env and the agent, with a Docker daemon started inside, a public `*.daytonaproxy01.net` URL per port and egress allowlists on Tier 3 organizations. Needs the `daytona` extra and a Daytona API key; the first sandbox an organization creates builds the host image.
- **boat**: boat sandboxes: one Ubuntu VM each for the env and the agent, with Docker already running and a public `*.on.boat.dev` URL per port. They get none of the boat account’s secrets; boat has no egress allowlists. Needs the `boat` extra and a boat API key.
- **coreweave**: CoreWeave Sandboxes: the env’s gateway gets one with Docker inside, and the agent runs its own image in one of its own, pulled from its registry. Each port gets a public HTTPS URL, and egress allowlists take hostnames, IP addresses and CIDRs. Needs the `coreweave` extra and a CoreWeave or W&B API key.
- **agent37**: Agent37 VMs: one Ubuntu VM each for the env and the agent, with Docker already running and a public `*.agent37.app` URL per port. Egress is allow-all only: Agent37 restricts egress to IPv4 ranges, not hostnames, so an allowlist is refused. Needs an Agent37 API key, and no extra.
- **Your sandbox provider**: Subclass `SandboxProvider` and register it through the `agent_env.sandbox_providers` entry point, or under `[sandbox.providers.<name>]` in `config.toml`. Then deploy with `--sandbox <name>`.
- **Instances**: Every deploy starts an env instance and an agent instance, each with its own id, in sandboxes the provider creates. `local` and `modal` run their containers directly; the VM providers boot a VM for each and run the containers in it.