E2B sandboxes → the Naive compute primitive
Sandbox.create(), then run shell commands, execute model-generated code, and read/write files —
all in a container that is firewalled from your own infrastructure. It does that job well, the
spin-up is fast, and the SDK is mature. But the sandbox is also a separate vendor account:
- Every sandbox lives behind an
E2B_API_KEY, its own dashboard, and its own billing — disconnected from wherever the agent’s email, cards, secrets, and KYC live. - The key is all-or-nothing: anything holding it can spawn a box and
commands.run()arbitrary shell. There is no per-action gate — no “this agent may run code but a human must approve a shell session.” - “Who let this agent execute that, and what else can it touch?” is answered in E2B for sandboxes, and in unrelated systems for everything else. The box has no shared accountability with the rest of the agent’s footprint.
compute primitive gives the agent the same capability —
an isolated cloud container it can exec into and run code in — but the workload is rooted in one
governed identity:
- The tenant user that owns the workload is the same user that owns its email inboxes, its cards, its vault secrets, and its KYC.
- The agent never holds a cloud key. Naive owns the underlying cloud credentials and scopes every task to your tenant — the same model as Apps.
- Whether an agent may spin up a workload at all, and whether opening a shell (
exec/ssh) freezes for human approval, is decided by that user’s Account Kit at execution time — and every shell session is transcript-logged.
E2B is a trademark of its owner, used here for identification only. No endorsement or affiliation is implied.
Tested against: the E2B JavaScript SDK
e2b v2.30.x
(Sandbox.create, sandbox.commands.run, sandbox.files.*, sandbox.getHost, sandbox.kill;
base https://api.e2b.dev) and the code-interpreter SDK
@e2b/code-interpreter (sandbox.runCode)
— docs snapshot June 2026 — and the Naive Node SDK @usenaive-sdk/server against
the Naive API (base https://api.usenaive.ai/v1, docs snapshot June 2026).Version notes:- E2B ships two packages: base
e2b(Sandbox+commands+files) and@e2b/code-interpreter(addsrunCode(), a stateful Jupyter-style kernel with rich outputs). Naive maps thecommands.run/ exec-a-process model; therunCodekernel does not map yet — see what doesn’t map yet. - E2B’s unit is an ephemeral sandbox with a default 5-minute timeout (max 1h Hobby / 24h
Pro), spun up from a template (the
baseimage, or a custom one built with the E2B CLI). Naive’s unit is a workload built from any Docker image you bring — a long-runningservice, a run-to-completionjob, or a cronschedule— with no template build step. - E2B identifies a box by its
sandboxIdand can pause/resume to preserve full memory+filesystem state. Naive identifies a workload by its id and offers scale-to-zero (stop/start), which is not a memory snapshot — see gaps. - On Naive, creating a workload and
exec/sshare sensitive — depending on the user’s Account Kit they may return{ status: "pending_approval", approval_id }. E2B has no equivalent execution-time gate.
Concept map
Before / after: the core path
The path that matters for almost every code-running agent is get an isolated box, run a command, read the output back, tear it down. Here it is on both platforms.- You bring the image; you hold no key. E2B spins up from a template under your
E2B_API_KEY. Naive runs any Docker image you pass, on cloud credentials it owns — the agent never holds a cloud key. - The box is long-running, not auto-killed. An E2B sandbox auto-kills at its
timeoutMs. A Naiveserviceruns until youstop(scale to zero) ordestroyit. Use ajobfor run-to-completion work. execis a governed action. Acommands.runin E2B is ungated. On Naive,exec/sshis sensitive: depending on the Account Kit it may returnpending_approvaland is transcript-logged.- The id is your identity, not a separate account. In E2B the key scopes E2B. In Naive the
same
forUser(id)handle also owns the agent’s email, cards, vault, and KYC.
The workflow that actually changes: running model-generated code
This is where the two models diverge most. E2B’s headline is the code interpreter:runCode() executes a snippet in a stateful kernel and hands back rich outputs (stdout, results,
charts). Naive has no code-interpreter kernel — you run code by exec-ing a process inside an
image that already has the runtime. The capability (run untrusted, model-generated code in an
isolated box) maps; the ergonomics differ.
- No stateful kernel. Each
execis a fresh process — variables do not persist between calls the way they do acrossrunCodeinvocations. Hold state in the image’s filesystem or an external store. - No rich result objects.
runCodereturns structured results (charts, dataframes);execreturns a managed session — read process output from the stream. If you rely on inline charts/images, that is a gap. - The win is custody, not ergonomics. The runtime, its secrets, and its shell now live under one identity with execution-time approval and a unified log — not behind a standalone key.
Minimal viable migration
The smallest swap that keeps a working code-running agent alive is get a box → run a command → read output → tear down.1
Install the SDK and set your key
NAIVE_API_KEY (a server-side key from the dashboard).
You can drop E2B_API_KEY for this path.2
Pick an image instead of a template
Replace the E2B template with a Docker image that has your runtime
(
python:3.12-slim, node:22-slim, or your own). Push custom images to a registry and pass
them as image. There is no template build step to manage.3
Swap sandbox creation
Replace
Sandbox.create({ timeoutMs }) with
client.compute.create({ name, type: "service", image, command: ["sleep", "infinity"] }).
Keep the returned workload id. (Use type: "job" with a command for run-to-completion work.)4
Swap command execution
Map
sandbox.commands.run(cmd) → client.compute.exec(id, cmd). Handle a possible
pending_approval result if the Account Kit gates exec, then connect to the returned exec
session for output. For an interactive session use
client.compute.shell(id) (or naive compute ssh <id>).5
Swap teardown
Replace
sandbox.kill() with client.compute.destroy(id). For a box you want to reuse cheaply,
client.compute.stop(id) scales it to zero (billing stops) and start(id) wakes it.6
Ship it
At this point you are off E2B for the core get-box → run → read → teardown path. Everything below
is upside, not a requirement.
Consolidate further once you’re on Naive
This is where the migration pays for itself. In E2B, the API key isolates sandboxes and nothing else, and any code the agent runs executes behind a key with no per-action gate. On Naive, the unit of isolation is a tenant user, and compute is one of many primitives that identity owns.Gain #1 — one identity across primitives
- With E2B, the agent’s sandboxes are an island behind an API key, and any secret you inject is one
you store, rotate, and protect yourself. With Naive,
naive.forUser(acme.id)is a single handle to compute and email and cards and vault and KYC. - The container’s secrets are not loose env vars — they are encrypted under the identity that owns everything else. Tear the customer down from one place; sibling tenants can never read each other’s workloads, secrets, cards, or logs.
Gain #2 — execution-time permission enforcement
- Whether an agent may run compute at all, and whether opening a shell freezes for human review, is policy on the identity — not a key scope you manage in a second console, and not a check you hand-write.
- The agent’s code is identical for every tier —
client.compute.exec(...),client.compute.create(...). Whether the call runs is decided at execution time:- Creating a workload and
exec/sshare sensitive. With approval on, the call returns{ status: "pending_approval", approval_id }and runs only after a human approves it. - The agent holds no cloud key. Naive owns the underlying cloud credentials; the workload is scoped to your tenant, so a leaked agent key can’t spawn boxes on your raw cloud account.
- Shell access is transcript-logged over a managed exec channel (no port 22, no SSH keys).
- Creating a workload and
Gain #3 — unified accountability
- Every workload, run, exec, and shell session for a customer lands in one per-user activity log — alongside their email, card, vault, and KYC events, not in a separate E2B dashboard:
- That is the question that is hard to answer when code execution lives in E2B, secrets live in your own manager, cards live in Stripe, and email lives somewhere else. Under Naive it is a single query.
What does not map yet
A migration guide that hides gaps is worse than none. The core path (create → exec → read → teardown), secrets, jobs, schedules, and a public-port service map cleanly. But the following E2B capabilities have no direct equivalent on Naive’s compute primitive today — and a couple are central to how many teams use E2B. Check this list against your app before you commit.Where to go next
computeprimitive — service / job / schedule, secrets, logs, shellcomputeSDK sub-client — typed method signaturescomputeCLI — create / run / logs / ssh / exec- Queue — pair a
servicewith a queue for a cheap autoscaling worker - Apps — the same “agent never holds a cloud key” model for managed infra
- Account Kits and Approvals — the policy model behind execution-time governance and exec approval
- Tenant users — the identity that owns the compute, email, cards, and vault
Related reading (blog)
- Why consolidate agent infra on one governed identity — migration thesis behind this guide
- Building AI Agents Into Your SaaS — tenant user anchor for every migration
- How To Build An Agentic Data Pipeline Platform — compute + queue tutorial