> ## Documentation Index
> Fetch the complete documentation index at: https://docs.e2b.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Sandcastle

> Run Sandcastle coding agents such as Claude Code and Codex in E2B sandboxes, with their commits coming back to your local repository.

[Sandcastle](https://github.com/mattpocock/sandcastle) is a TypeScript library for orchestrating coding agents such as Claude Code and Codex on a git repository. Its built-in E2B provider runs each agent in its own E2B sandbox. Sandcastle copies your repository in, runs the agent there, brings its commits back to a branch on your machine, and kills the sandbox.

## Prerequisites

* Node.js 22.19 or later, and `git`
* An [E2B API key](https://console.e2b.dev/?tab=keys)
* A key for your agent, for example `ANTHROPIC_API_KEY` for Claude Code

## Install

The E2B provider uses the `e2b` package as an optional peer dependency, so install both:

```bash theme={"theme":"css-variables"}
npm install @ai-hero/sandcastle e2b
```

Set your E2B API key in the shell where you run Sandcastle, or pass it as the `apiKey` option:

```bash theme={"theme":"css-variables"}
export E2B_API_KEY="e2b_***"
```

> [!WARNING]
>
> Don't put `E2B_API_KEY` in `.sandcastle/.env`. Sandcastle forwards every key declared there into the sandbox, where the agent could read it.

## Run Claude Code

E2B's default template has no agent CLI. Use the prebuilt `claude` template, which ships Claude Code:

```typescript run.mts theme={"theme":"css-variables"}
import { run, claudeCode } from "@ai-hero/sandcastle";
import { e2b } from "@ai-hero/sandcastle/sandboxes/e2b";

const result = await run({
  agent: claudeCode("claude-opus-4-8", {
    env: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY! },
  }),
  sandbox: e2b({ template: "claude" }),
  prompt: "Fix the failing test in src/slugify.js and commit the fix.",
});

console.log(result.commits);
```

Run it from the root of your git repository with `npx tsx run.mts`. The agent's commits land on your current branch; pass `branchStrategy: { type: "branch", branch: "agent/fix" }` to put them on a separate branch instead.

Only the environment variables you give Sandcastle reach the sandbox: keys declared in `.sandcastle/.env`, and the `env` of the agent and sandbox providers. That's why the example passes `ANTHROPIC_API_KEY` explicitly.

## Run Codex

Use the prebuilt `codex` template and pass your OpenAI key as `CODEX_API_KEY`, which `codex exec` reads. `OPENAI_API_KEY` alone isn't enough.

```typescript theme={"theme":"css-variables"}
import { run, codex } from "@ai-hero/sandcastle";
import { e2b } from "@ai-hero/sandcastle/sandboxes/e2b";

await run({
  agent: codex("gpt-5.4", {
    env: { CODEX_API_KEY: process.env.OPENAI_API_KEY! },
  }),
  sandbox: e2b({ template: "codex" }),
  prompt: "Fix the failing test in src/slugify.js and commit the fix.",
});
```

For another agent, use a [custom template](https://docs.e2b.dev/template/quickstart.md) with its CLI installed, or install it in a Sandcastle `onSandboxReady` hook.

## Run agents in parallel

Each `run()` creates its own sandbox, so you can start several agents at once with `Promise.allSettled`, each on its own branch. The [cookbook example](https://github.com/e2b-dev/e2b-cookbook/tree/main/examples/sandcastle-parallel-agents-js) runs two Claude Code agents on two tasks in parallel, then merges both branches and runs the tests locally.

## Configure the sandbox

`e2b()` accepts the options of the SDK's `Sandbox.create()` and passes them through:

```typescript theme={"theme":"css-variables"}
e2b({
  template: "claude",        // E2B template to create the sandbox from
  timeoutMs: 60 * 60_000,    // sandbox lifetime; defaults to 30 minutes
  metadata: { team: "ai" },  // shows up when you list sandboxes
  env: { NODE_ENV: "test" }, // extra environment variables for the sandbox
})
```

The provider sets `envs` and `lifecycle` itself, so you can't override them. `env` is merged with the rest of Sandcastle's environment, and `maxOutputTailChars` limits how much streamed output Sandcastle keeps for the result (64 KiB by default).

## Lifecycle

* `run()` creates a new sandbox for each call and kills it when the run finishes. `createSandbox()` keeps one sandbox for several runs until you close it.
* The repository is copied to `$HOME/workspace` in the sandbox.
* The sandbox is always killed, never paused, when `timeoutMs` runs out. Make the timeout long enough for setup, the agent run, and syncing the commits back. E2B's Hobby plan caps a sandbox at 1 hour.
* If you stop Sandcastle with Ctrl+C, it doesn't kill the sandbox; E2B kills it when `timeoutMs` runs out.

## Limitations

* `interactive()` isn't supported with the E2B provider.
* `sandcastle init` doesn't scaffold E2B; configure `e2b()` in your script as shown above.

## Learn more

* [Sandcastle sandbox providers](https://github.com/mattpocock/sandcastle/blob/main/docs/guide/sandbox-providers.md)
* [Sandcastle on GitHub](https://github.com/mattpocock/sandcastle)

## Related guides

> [card] [Claude Code](https://docs.e2b.dev/agents/claude-code.md)
>
> The prebuilt `claude` template

> [card] [Codex](https://docs.e2b.dev/agents/codex.md)
>
> The prebuilt `codex` template

> [card] [Templates](https://docs.e2b.dev/template/quickstart.md)
>
> Build a template with your own agent CLI
