E2B

loam speaks E2B's API as well as its own, so code written for E2B's official SDKs (Python e2b and JavaScript e2b, unmodified) creates, uses, pauses, resumes and kills sandboxes on loam. It's checked against both SDKs at 2.52.0.

Setup

Two environment variables, with your loam project key as the E2B key:

export E2B_DOMAIN=e2b.loam.example.com
export E2B_API_KEY=$LOAM_TOKEN     # loam_...
Control plane https://api.e2b.loam.example.com
A sandbox's port P https://<P>-<sandbox id>.e2b.loam.example.com
Key X-API-Key: loam_... (the SDKs send it)
import { Sandbox } from 'e2b'

const sbx = await Sandbox.create({ timeoutMs: 5 * 60_000, metadata: { owner: 'docs' } })
console.log('sandbox', sbx.sandboxId)

const r = await sbx.commands.run('echo hello from $(hostname); id -un')
console.log(r.exitCode, r.stdout.trim())

await sbx.files.write('/home/user/hello.txt', 'hi\n')
console.log('read:', (await sbx.files.read('/home/user/hello.txt')).trim())

await sbx.commands.run('python3 -m http.server 3000', { background: true })
await new Promise((r) => setTimeout(r, 1500))
const host = sbx.getHost(3000)
console.log('host', host, (await fetch(`https://${host}/`)).status)

await sbx.pause()
const again = await Sandbox.connect(sbx.sandboxId)
console.log('after resume:', (await again.commands.run('cat /home/user/hello.txt')).stdout.trim())

try {
  await Sandbox.create('some-other-template')
} catch (e) {
  console.log('refused:', e.constructor.name, e.message)
}

await again.kill()
$ node e2b.mjs
sandbox sb-nlucxvii5em3
0 hello from rustic-meadow-7517
user
read: hi
host 3000-sb-nlucxvii5em3.e2b.loam.example.com 200
after resume: hi
refused: SandboxError 404: template 'some-other-template' not found

Python is the same: Sandbox.create(), sbx.commands.run(...), sbx.files.write(...), and so on, with the same two variables.

An E2B sandbox is a loam sandbox

The adapter makes ordinary loam sandboxes in your key's project, through loam's own rules, so loam ls and the native API see them and can act on them:

E2B loam
sandbox ID the loam sandbox ID, sb-…
template base (the only one) the loam template e2b
metadata k: v the label e2b/k: v
envVars the sandbox's env
timeout the sandbox's deadline, action delete (with autoPause, pause); idle is none, so an E2B sandbox runs until its timeout
pause() / connect() loam's pause (or stop, with autoPauseMemory: false) and wake
allow_internet_access, network.allowOut / denyOut the sandbox's egress rules

A sandbox the adapter didn't make isn't an E2B sandbox: E2B's API answers 404 for it and doesn't list it.

What works

Create (Sandbox.create, with timeout, metadata, envVars, autoPause, autoPauseMemory, autoResume, secure, allow_internet_access, network), connect and resume, pause, kill, setTimeout, getInfo, list (with metadata and state filters, paged), getMetrics (one current sample), and everything the SDK does in the sandbox through envd: commands (streaming, background, stdin, PTY, signals), files (read, write, list, watch, signed upload and download URLs), and getHost(port) for a sandbox's ports. Processes survive a pause and are listed after the resume.

What's refused

Answered 404 Not found: this E2B-compatible server does not implement …: templates and their build API (/v3/templates, /templates/…), snapshots and fork, volumes, sandbox logs and events, GET /sandboxes/metrics, teams and API key management.

Refused at create with a 400 that names the field: any template but base, mcp, iam, volume mounts, egress proxies and transform rules (network.rules, network.egressProxy), IP addresses and CIDRs in allowOut/denyOut (except 0.0.0.0/0, "all traffic"), allowPublicTraffic: false, secure: false, and a denyOut name that an allowOut wildcard covers.

$ curl -sS -H "X-API-Key: $LOAM_TOKEN" https://api.e2b.loam.example.com/v3/templates
{"code":404,"message":"Not found: this E2B-compatible server does not implement GET /v3/templates"}
$ curl -sS -H "X-API-Key: $LOAM_TOKEN" -H 'Content-Type: application/json' \
    https://api.e2b.loam.example.com/v2/sandboxes -d '{"templateID":"base","mcp":{}}'
{"code":400,"message":"mcp: not supported by this server"}
$ curl -sS -H "X-API-Key: nope" https://api.e2b.loam.example.com/v2/sandboxes
{"code":401,"message":"Invalid API key: use a loam project key (loam_...) as E2B_API_KEY"}

loam's own refusals come through in E2B's terms: no node with room is a 429 with Retry-After (the SDKs retry it), something loam finds invalid (a label, an env name) a 400 with loam's message, a sandbox that can't be woken a 503.

Egress

E2B's options become loam egress rules, which are names, decided by the most specific:

Sandbox.updateNetwork (PUT /sandboxes/{id}/network) replaces the rules and applies them at once. The SDK's own traffic to envd and to your ports comes in through loam, not the network, so it works whatever the rules.

Differences from hosted E2B

The commands run as user (uid 1000, home /home/user, passwordless sudo), as on E2B. Using the same sandbox through loam's own API, exec runs as loam (uid 1001) unless you give user.