diff --git a/docs.json b/docs.json index 567115ad..b610d2a3 100644 --- a/docs.json +++ b/docs.json @@ -71,6 +71,7 @@ "docs/agents/pi", "docs/agents/deep-agents", "docs/agents/open-swe", + "docs/agents/eve", "docs/agents/google-adk", "docs/agents/mastra", "docs/agents/openai-agents-sdk", diff --git a/docs/agents/eve.mdx b/docs/agents/eve.mdx new file mode 100644 index 00000000..e7a59579 --- /dev/null +++ b/docs/agents/eve.mdx @@ -0,0 +1,228 @@ +--- +title: "Vercel Eve" +description: "Run Vercel Eve agent sandboxes on E2B with the @e2b/eve-sandbox backend." +icon: "/images/icons/vercel-eve.svg" +--- + +[Vercel Eve](https://vercel.com/docs/eve) is a filesystem-first framework for durable backend AI agents: you author an agent as files under `agent/`, and Eve compiles them into an app that runs on Vercel Functions. Every Eve agent has exactly one [sandbox](https://eve.dev/docs/sandbox) — the isolated bash environment rooted at `/workspace` that backs the built-in `bash`, `read_file`, `write_file`, `glob`, and `grep` tools. + +The [`@e2b/eve-sandbox`](https://github.com/e2b-dev/eve-sandbox) package is an E2B backend for that sandbox — the E2B counterpart to Eve's built-in `vercel()`, `docker()`, `microsandbox()`, and `justbash()` backends. It implements the public `SandboxBackend` interface from `eve/sandbox`, so Eve itself needs no changes and no fork. + + + `@e2b/eve-sandbox` is experimental, and Eve is in beta. Both APIs may change. + + +## Why E2B as the backend + +| | E2B backend | Eve's local backends | +| --- | --- | --- | +| Where it runs | E2B cloud microVMs, from local dev and production alike | Docker daemon, local VM, or a simulated shell on the host | +| Startup | ~150 ms from a snapshot or [custom template](/docs/template/quickstart) | Image pull / VM boot on the developer machine | +| Environment | Any [E2B template](/docs/template/quickstart) you build | Eve's `ghcr.io/vercel/eve` image, or a Docker image you supply | +| Persistence | [Auto-pause and resume](/docs/sandbox/persistence) — filesystem survives days of idle | Container/VM tied to the machine that started it | +| Egress control | E2B firewall: domain allow-lists and header injection | Domain policies on `vercel()`/`microsandbox()`; `docker()` is all-or-nothing | + +Use it when you want the same sandbox behavior in local dev, CI, and production, on infrastructure you control the image for. + +## Prerequisites + +- Node.js 22 or later +- An Eve project (`npx eve@latest init my-agent`) +- An [E2B API key](https://e2b.dev/dashboard?tab=keys) + +## Install + +```bash +npm i @e2b/eve-sandbox e2b +``` + +`eve` (>= 0.27) and `ai` (>= 7) are peer dependencies already present in an Eve project. + +Set your key — the backend reads `E2B_API_KEY` unless you pass `apiKey`: + +```bash +export E2B_API_KEY="e2b_***" +``` + +## Configure the backend + +Author `defineSandbox` and pass `e2b()` as the backend: + +```typescript title="agent/sandbox.ts" +import { defineSandbox } from 'eve/sandbox' +import { e2b } from '@e2b/eve-sandbox' + +export default defineSandbox({ + backend: () => e2b({ template: 'base' }), +}) +``` + +That is the whole integration. The agent's `bash` tool now runs commands in an E2B sandbox, its file tools read and write the sandbox filesystem, and nothing executes on your app runtime. + +Prefer the **factory form** (`backend: () => e2b({...})`) over `backend: e2b({...})`: it defers reading environment variables until first use and memoizes the backend, so its prewarmed-snapshot cache survives across calls. + +Use the folder layout (`agent/sandbox/sandbox.ts`) instead if you also seed files from `agent/sandbox/workspace/**`. + +## Run the agent + +```bash +pnpm dev # or: npx eve dev +``` + +Ask the agent to run a command and it executes in a real E2B sandbox. Sandboxes created this way appear in your [E2B dashboard](https://e2b.dev/dashboard). + +## Bootstrap and per-session setup + +Eve has two lifecycle hooks, and the E2B backend maps each onto a different E2B primitive: + +```typescript title="agent/sandbox/sandbox.ts" +import { defineSandbox } from 'eve/sandbox' +import { e2b } from '@e2b/eve-sandbox' + +export default defineSandbox({ + backend: () => + e2b({ + template: 'base', + timeoutMs: 30 * 60 * 1000, + envs: { NODE_ENV: 'production' }, + }), + + // Build time, once per template — baked into a reusable E2B snapshot. + async bootstrap({ use }) { + const sandbox = await use() + await sandbox.run({ command: 'sudo apt-get install -y jq' }) + }, + + // Once per durable session — tighten egress for the turn. + async onSession({ use }) { + await use({ networkPolicy: { allow: ['*.npmjs.org', 'github.com'] } }) + }, +}) +``` + +- **`bootstrap`** runs during Eve's build-time `prewarm()`. The backend creates a sandbox, runs your bootstrap, writes your `workspace/` seed files, then captures an **E2B snapshot**. Later sessions fork that snapshot, so installs are paid once, not per session. +- **`onSession`** runs once per durable session against a live sandbox, and is the right place for network policy, per-user credentials, and one-time markers. + +Snapshots are named from Eve's `templateKey` (which tracks your authored sandbox source, seed contents, and `revalidationKey`) plus a hash of the snapshot-affecting backend options — base template, baked `envs`, and network policy. Change any of those and the next build captures a fresh snapshot instead of reusing a stale one. + +## Session persistence + +E2B sandboxes are created with `lifecycle: { onTimeout: 'pause', autoResume: true }`, and the default timeout is **30 minutes** (E2B's own 5-minute default would expire mid-turn). On timeout the sandbox [pauses rather than dies](/docs/sandbox/persistence): the filesystem is preserved and the next message resumes it. + +On the next turn the backend reattaches in this order: + +1. The `sandboxId` persisted by Eve for that session. +2. A still-running or paused sandbox whose E2B metadata matches `eveBackend: "e2b"` and the session's `eveSessionKey`. +3. Otherwise a fresh sandbox, forked from the prewarmed snapshot. + +Two consequences worth knowing: + +- **The backend's `shutdown()` is intentionally a no-op.** Killing the sandbox on server shutdown would drop background work and force Eve to recreate it without rerunning `onSession`. Sandboxes are left paused and expire on their own — kill them from the [dashboard](https://e2b.dev/dashboard) or the [SDK](/docs/sandbox) if you want them gone sooner. Opt out with `autoPause: false` to kill on timeout instead. +- **Reconnects do not re-apply the configured `networkPolicy`.** A resumed sandbox keeps its live policy, including any tightening `onSession` applied. Re-stamping the create-time default could silently loosen a locked-down session. + +## Network policy + +Egress rules go on the factory (applied to each fresh session, before authored `bootstrap` runs) or in `onSession`'s `use()`. The backend translates Eve's `SandboxNetworkPolicy` into an [E2B firewall](/docs/network/internet-access) update: + +| Eve policy | E2B update | +| --- | --- | +| `"allow-all"` (default) | `allowInternetAccess: true` | +| `"deny-all"` | `allowInternetAccess: false` | +| `{ allow: ['github.com', '*.npmjs.org'] }` | `allowOut: [...]` + `denyOut: ['0.0.0.0/0']` | +| `{ allow: ['*'], subnets: { deny: ['10.0.0.0/8'] } }` | `denyOut: ['10.0.0.0/8']` | +| `{ allow: [] }` | `allowInternetAccess: false` | + +Restricting egress to an allow-list requires the catch-all in `denyOut` — E2B gives allow rules absolute precedence over deny rules, so the listed hosts pass and everything else is blocked. The backend adds that for you. + + + Because allow beats deny in E2B, some Eve policies cannot be expressed faithfully. The backend **throws instead of silently weakening them**: + + - `subnets.deny` combined with an allow-list — the "denied" hosts would stay reachable. Use `{ allow: ['*'], subnets: { deny: [...] } }` for deny-lists, or drop `subnets.deny` (anything outside an allow-list is already blocked). + - A per-rule `match` condition — E2B applies header transforms to every request to a host. + - `forwardURL` request proxying — no E2B equivalent. + - A header `transform` alongside a catch-all `"*"` allow — a transform host must be allow-listed, which an allow-all cannot also be. + + +### Credential brokering + +E2B can inject a header at the firewall so a secret authenticates egress without ever entering the sandbox. Eve expresses this as a per-domain `transform`: + +```typescript +async onSession({ use }) { + await use({ + networkPolicy: { + allow: { + 'github.com': [{ transform: [{ headers: { authorization: 'Basic your_base64_credentials_here' } }] }], + 'api.example.com': [], + }, + }, + }) +} +``` + +List every host explicitly. Eve's documented `"*": []` catch-all pattern is rejected by this backend for the reason above — pair transforms with an explicit allow-list instead. + +To change policy mid-turn, call `sandbox.setNetworkPolicy(...)` on the live handle from any authored tool. + +## Custom templates + +`template` accepts any E2B template ID or alias. Build a [custom template](/docs/template/quickstart) with your runtimes, system packages, and toolchain pre-installed, and every session starts from it: + +```typescript +export default defineSandbox({ + backend: () => e2b({ template: 'your-template-id-or-name' }), +}) +``` + +Templates and snapshots compose: the template is the base image, and Eve's `bootstrap` layers your agent-specific setup into a snapshot on top of it. Put slow, stable work (compilers, system packages) in the template; put agent-specific work (cloning a baseline repo, installing project dependencies) in `bootstrap`. + +## Backend options + +```typescript +e2b({ + template: 'base', // E2B template ID or alias + timeoutMs: 30 * 60 * 1000, // auto-pause timeout; default 30 minutes + envs: { NODE_ENV: 'production' }, // env vars baked into every sandbox + metadata: { team: 'growth' }, // E2B metadata, create-time only + tags: { tier: 'beta' }, // diagnostic tags, merged into metadata + apiKey: process.env.E2B_API_KEY, // defaults to E2B_API_KEY + domain: 'e2b.dev', // self-hosted or region endpoint + networkPolicy: 'allow-all', // initial policy for each fresh session + autoPause: true, // false → kill on timeout instead of pause + createOptions: {}, // raw E2B SandboxOpts escape hatch +}) +``` + +`createOptions` is merged into every `Sandbox.create()` call, so anything the [E2B JavaScript SDK](/docs/sdk-reference/js-sdk) supports is reachable even when this backend has no dedicated option for it. Explicit options win over `createOptions`. + +## How the integration works + +| Component | Responsibility | +| --- | --- | +| Eve runtime | Runs turns, owns durable session state, calls the backend's `prewarm` and `create` | +| `defineSandbox` | Declares the backend plus the `bootstrap` and `onSession` hooks | +| `e2b()` backend | Creates, reconnects, and configures E2B sandboxes; maps network policy | +| E2B snapshot | Captures bootstrap + seed files once, forked per session | +| E2B template | Base OS, runtimes, and pre-installed dependencies | + +Paths line up because the backend anchors relative paths to `/workspace`, Eve's cross-backend namespace — E2B's own default working directory is `/home/user`, and the backend creates `/workspace` during base setup. Every E2B API call this backend makes is tagged with an `eve-sandbox/` integration identifier. + +## Learn more + +- [`@e2b/eve-sandbox` on GitHub](https://github.com/e2b-dev/eve-sandbox) — source, unit tests, and a runnable Next.js example +- [Eve sandbox documentation](https://eve.dev/docs/sandbox) — session handle API, lifecycle hooks, and backends +- [Eve on Vercel](https://vercel.com/docs/eve) — how Eve maps onto Vercel Functions, Workflows, and AI Gateway + +## Related guides + + + + Build custom sandbox templates with pre-installed dependencies + + + Auto-pause, resume, and manage sandbox lifecycle + + + Restrict sandbox egress with domain allow-lists + + diff --git a/images/icons/vercel-eve.svg b/images/icons/vercel-eve.svg new file mode 100644 index 00000000..811ddfce --- /dev/null +++ b/images/icons/vercel-eve.svg @@ -0,0 +1,9 @@ + + + +