Skip to content

E2B.Sandbox reference

Source: packages/e2b/src/ConnectSandbox.ts

Work with one Sandbox declared in the stack, from a Worker, Function or any other host: run commands, read and write files, open terminals, call its ports, snapshot or fork it. The handle has the same operations as the sandboxes CreateSandbox returns; its sandboxId and url(port) are Effects, read at runtime.

A declared sandbox pauses when it is idle. With autoResume (the default), E2B resumes it on the next request, to one of its ports or from any call here. With autoResume: false, calls inside it (exec, start, files, pty) fail with SandboxNotRunning until you call resume().

The host gets its own E2B API key, minted at deploy and revoked when the host is destroyed. E2B keys are not scoped, so the key reaches the whole team; it is never the deployer’s own.

ConnectSandbox: Running commands in a dev box

Section titled “ConnectSandbox: Running commands in a dev box”
const devBox = yield* E2B.ConnectSandbox(DevBox);
return {
fetch: Effect.gen(function* () {
yield* devBox.resume({ timeout: "10 minutes" });
const test = yield* devBox.exec("git pull && npm test", { cwd: "/home/user/repo" });
return yield* HttpServerResponse.json(test);
}),
};
const build = yield* devBox.start("npm run build", { cwd: "/home/user/repo" });
const output = build.output.pipe(Stream.map((chunk) => chunk.data));

ConnectSandbox: Calling a program in the sandbox

Section titled “ConnectSandbox: Calling a program in the sandbox”

Call port 3000

const port = yield* api.getTcpPort(3000);
const response = yield* port.fetch(HttpClientRequest.get("/health"));

Proxy the Worker’s request to port 3000

const port = yield* api.getTcpPort(3000);
return yield* port.fetch(yield* HttpServerRequest);
yield* devBox.pause();

Source: packages/e2b/src/ConnectSandboxHttp.ts Kind: Layer · Provides: E2B.ConnectSandbox

HTTP implementation of ConnectSandbox: E2B’s control plane with the host’s key, and envd inside the sandbox with the sandbox’s own token. Works on any host.

Effect.gen(function* () {
const devBox = yield* E2B.ConnectSandbox(DevBox);
// ...
}).pipe(Effect.provide(E2B.ConnectSandboxHttp))

Source: packages/e2b/src/CreateSandbox.ts

Create and manage E2B sandboxes from a Worker, Function or any other host.

E2B.CreateSandbox(base) creates sandboxes from a template or snapshot declared in the stack; E2B.CreateSandbox() takes the template per call. Both return the same client, whose calls take E2B’s own parameters.

Sandboxes created at run time are tagged with the stack, stage and binding (alchemy::stack, alchemy::stage, alchemy::id in their E2B metadata) and are not deleted when the stack is destroyed. List them by those tags to clean them up: sandboxes.list({ metadata: { "alchemy::stack": "my-app", "alchemy::stage": "prod" } }). Metadata you pass to create merges underneath; it never replaces the alchemy::* keys.

The host gets its own E2B API key, minted at deploy and revoked when the host is destroyed. E2B keys are not scoped, so the key reaches the whole team; it is never the deployer’s own.

One sandbox per request, killed when the request ends

const sandboxes = yield* E2B.CreateSandbox(Agent);
// in fetch:
const sandbox = yield* Effect.acquireRelease(
sandboxes.create({ timeout: 120 }),
(sandbox) => Effect.ignore(sandbox.kill()),
);
const result = yield* sandbox.exec("node --version");

A long-lived sandbox per task, found by metadata

const [existing] = yield* sandboxes.list({ metadata: { task }, state: ["running", "paused"] });
const sandbox = existing
? yield* sandboxes.connect(existing.sandboxID)
: yield* sandboxes.create({ metadata: { task }, autoPause: true, timeout: 1800 });

exec runs to completion and returns { exitCode, stdout, stderr }; a non-zero exit is returned, not failed. A string runs in a login shell, an array runs directly.

A shell line, an argument vector, stdin and a timeout

const test = yield* sandbox.exec("npm test", { cwd: "/home/user/app" });
const version = yield* sandbox.exec(["node", "--version"]);
const sorted = yield* sandbox.exec("sort", { stdin: "b\na\n" });
const slow = yield* sandbox.exec("make", { timeout: "2 minutes" }); // E2B.ExecTimedOut

Stream a process, then wait for it

const build = yield* sandbox.start("npm run build", { cwd: "/home/user/app" });
yield* build.output.pipe(
Stream.runForEach((chunk) => Effect.log(new TextDecoder().decode(chunk.data))),
);
const { exitCode } = yield* build.wait;

Closing the scope detaches: the process keeps running, and sandbox.processes.attach({ pid }) picks it up again.

A terminal

const pty = yield* sandbox.pty.open({ cols: 120, rows: 40 });
yield* pty.write("ls\n");
yield* pty.resize({ cols: 160, rows: 48 });
const github = yield* GitHubToken;
const sandbox = yield* sandboxes.create({
network: {
allowOut: ["github.com", "*.github.com"],
rules: { "api.github.com": [{ transform: { headers: { Authorization: `Bearer ${github.ref}` } } }] },
},
});

Source: packages/e2b/src/CreateSandboxHttp.ts Kind: Layer · Provides: E2B.CreateSandbox

HTTP implementation of CreateSandbox: E2B’s control plane with the team key, and envd inside each sandbox with that sandbox’s own token. Works on any host.

Effect.gen(function* () {
const sandboxes = yield* E2B.CreateSandbox(Agent);
// ...
}).pipe(Effect.provide(E2B.CreateSandboxHttp))

Source: packages/e2b/src/Sandbox.ts

An E2B sandbox the stack owns: one long-lived sandbox that pauses when it is idle and resumes when it is used, instead of one per request.

Use it for a workspace that outlives requests (an agent’s home, a dev box) or to serve a template program at a stable URL. The sandbox runs for timeout after it starts or resumes, then E2B pauses it with its memory and files. A request to one of its URLs resumes it (autoResume), and so does resume() on E2B.ConnectSandbox.

Alchemy stamps its stack, stage and logical ID into the sandbox’s metadata to recognise the sandboxes it owns.

import * as E2B from "@alchemy.run/e2b";
import * as Effect from "effect/Effect";
export const Node = E2B.Template("Node", { image: "node:22" });
export const DevBox = E2B.Sandbox("DevBox", {
template: Node,
timeout: "10 minutes",
});

A template program starts when its sandbox boots. Declare one sandbox for it: each new build of the program replaces the sandbox, so it always runs the current one.

import * as E2B from "@alchemy.run/e2b";
import * as Output from "alchemy/Output";
import * as Effect from "effect/Effect";
import Api from "./src/api.ts";
export const ApiSandbox = Effect.gen(function* () {
const sandbox = yield* E2B.Sandbox("Api", { template: Api });
return { url: Output.interpolate`https://3000-${sandbox.sandboxId}.${sandbox.domain}` };
});
const sandbox = yield* E2B.Sandbox("Private", {
template: Node,
network: { allowPublicTraffic: false },
});
// send sandbox.trafficAccessToken in the `e2b-traffic-access-token` header
const devBox = yield* E2B.ConnectSandbox(DevBox);
// in fetch:
yield* devBox.resume();
const result = yield* devBox.exec("git pull && npm test", { cwd: "/home/user/repo" });
export const Cache = E2B.Volume("Cache", {});
export const Builder = E2B.Sandbox("Builder", {
template: Node,
mounts: [{ volume: Cache, path: "/cache" }],
});

Sandbox: Referencing a sandbox the stack doesn’t manage

Section titled “Sandbox: Referencing a sandbox the stack doesn’t manage”
const shared = E2B.Sandbox.lookup({ sandboxId: "i1a2b3c4d5e6f7g8h9" });
const box = yield* E2B.ConnectSandbox(shared);