@amends-dev/sandbox-e2b
@amends-dev/sandbox-e2b implements the sandbox contract
with E2B’s TypeScript SDK. Hosts install this adapter; shared packages depend only on the contract.
import { E2bSandboxProvider } from "@amends-dev/sandbox-e2b"import { BunCrypto } from "@effect/platform-bun"import { Config, Effect, Layer } from "effect"
const sandboxes = Layer.unwrap( Effect.map(Config.Redacted("E2B_API_KEY"), (apiKey) => E2bSandboxProvider.layer({ apiKey }))).pipe(Layer.provide(BunCrypto.layer))make constructs the provider and needs Effect’s Crypto service. layer delegates to it.
Options include apiKey, apiUrl, domain, template, timeoutMs, and requestTimeoutMs.
providerOptions.e2b can override template and timeoutMs for a create. The SDK version is
pinned in the root catalog. Credentials are supplied explicitly; the adapter does not read them
from SDK environment variables.
Identity and lifecycle
Section titled “Identity and lifecycle”Each logical sandbox ID is recorded in E2B metadata beside amends-provider=e2b. Resume queries
both running and paused sandboxes, including every page, so a fresh host can find the same machine.
Unknown IDs fail without allocation. A duplicate logical ID fails without modifying the existing
machine. Sandboxes outlive the provider; createScoped destroys its sandbox on scope exit.
E2B does not provide atomic reservation of a custom sandbox name. Creates are serialized within
one provider and check metadata before and after allocation. A detected concurrent create deletes
only its own allocation. Two hosts can still race while each sees no other allocation. If duplicates
exist, resume fails with UnknownError instead of selecting a machine arbitrarily. Applications
that create the same explicit ID on different hosts must serialize those creates externally. IDs
generated by the adapter are UUIDs. E2B volumes are not used for naming because they require access
to a separate private beta.
stop uses pause({ mode: "filesystem" }), which preserves files and ends processes. Resume
cold-boots the machine. Repeated stop and destroy succeed. E2B requires a maximum running lifetime:
the default here is one hour, after which E2B pauses with filesystem-only persistence. Set
timeoutMs within the account’s permitted limit. The host must resume the sandbox and restart its
services after that pause; the adapter does not renew timeouts in the background. Auto-resume on
port traffic is disabled because it cannot restart services after a filesystem-only pause.
A contract template prepares each new machine, then its network policy applies. Preparation failure
or interruption destroys that allocation before returning. Template snapshots are not cached;
template can select an E2B template or previously prepared snapshot supplied by the operator.
Files, processes, and ports
Section titled “Files, processes, and ports”The image must provide Linux /proc, POSIX shells, Python 3 for the conformance server, and GNU
core utilities including setsid, stat, find, base64, and mktemp. E2B’s base image supplies
the tools used by the adapter. File reads and writes transfer whole files. File handles, watch,
glob, and append flags are unsupported and fail with PlatformError. Exclusive-write checks and
mode application use multiple API calls and are not atomic against other writers.
Each command runs in a process group with stdout and stderr written to private files. Polling reads them as bytes, preserving non-UTF-8 data and final output without a newline. stdin uses E2B’s byte API and closes when its sink ends. Environment scripts avoid placing environment values in recorded command lines. Commands support cwd, environment replacement or extension, pipelines, shell mode, ignored streams, and sinks. Extra file descriptors and host-inherited streams fail.
Scope exit kills a referenced command’s process group and removes its scratch directory.
unref leaves the process running after its scope closes. kill uses SIGKILL regardless of the
requested signal. Handles capture the Linux boot ID and refuse to signal an old PID after resume.
Unreferenced commands keep their output files until removed by sandbox code or sandbox destruction.
Output polling costs API requests and holds at most one MiB per read, while output files can grow
until their command’s scope closes. Output is unavailable while the sandbox is paused.
Port markers in /var/tmp/amends/ports survive pause and make ports exposed through one handle
visible to another. Independent markers avoid lost updates when hosts expose different ports.
Endpoints use HTTPS or WSS and E2B’s e2b-traffic-access-token header; other headers, including
Authorization, remain available to the application. E2B’s private traffic mode applies to the
machine, while port markers describe which endpoints this adapter returns; they are not per-port
firewall rules.
Network policies
Section titled “Network policies”Allow-all, deny-all, and allow lists of hosts and CIDRs without denied exceptions are supported.
An allow list with deniedCidrs fails with UnsupportedError before allocation: E2B’s allow rules
override deny rules, while the contract requires the opposite. An empty allow list blocks egress.
Host filtering follows E2B’s HTTP Host/TLS SNI filtering on ports 80/443 and its DNS exception;
other protocols require IP/CIDR allowances. See E2B’s network documentation.
Request transformations fail with UnsupportedError. E2B’s per-host transforms lack the contract’s
path-prefix and matching-header conditions; the adapter never broadens credential injection.
Verification
Section titled “Verification”bun run --cwd adapters/sandbox-e2b typecheckbun run test -- adapters/sandbox-e2b/test/provider.test.tsAMENDS_TEST_E2B=1 E2B_API_KEY=... bun run test -- adapters/sandbox-e2b/testThe default tests exercise the real SDK against a local fake control plane, including error mapping, network preflight, duplicate detection, cleanup ownership, pagination, and lifecycle payloads. Live tests run the shared conformance suites and E2B-specific reboot, binary stdin/EOF, and network regressions. They allocate paid cloud machines and are skipped unless both variables are set. Live verification requires an E2B region that supports filesystem-only persistence; a rejected pause remains an error and is never silently substituted with memory persistence.