Sandbox backends
Sandbox support is intentionally incremental. A backend is not presented as production-ready merely because it can execute a command: its isolation model, lifecycle guarantees, operational dependencies, and known limits must also be clear.
Local and Docker execution do not add another HTTP service. Remote adapters call an independently deployed sandbox service through the same in-process Go boundary:
Temporal Activity -> SessionManager -> sandbox.Provider -> sandbox.Sandbox
SessionManager gives each session one logical sandbox. PostgreSQL persists the
provider name and opaque external ID; a restarted worker calls Attach instead
of creating an empty replacement. Sandbox exposes command execution, confined
file access, a workspace root, and teardown. The execution worker selects one
compiled adapter through an internal registry. MANAGED_AGENT_SANDBOX accepts
local (the default), docker, e2b, cube, opensandbox, or daytona; an
unknown name fails startup instead of falling back to host execution. Provider
selection does not add fields to the Managed Agents Environment or Session
APIs.
Support levels
- Available: implemented, documented, and exercised by repository tests.
- Preview: implemented with offline and opt-in live conformance, but still awaiting promotion based on repeatable service-level validation.
- Planned: selected for a dedicated adapter, but not implemented.
- Evaluating: useful integration shape, without a committed adapter.
These labels describe project support, not a security certification.
Backend matrix
| Backend | Status | Isolation model | Session state | Intended use |
|---|---|---|---|---|
| Local process | Available; default | Host process plus confined workspace; not an isolation boundary | Reattaches by durable workspace path on the same host | Offline tests and trusted local development only |
| Docker | Available; opt-in | Container filesystem, namespaces/cgroups, configurable limits, network disabled by default | Reattaches by container ID on the same Docker daemon | Controlled single-host self-hosting |
| E2B | Preview | Managed microVM service | E2B ID plus auto-pause filesystem persistence | Managed production |
| Tencent CubeSandbox | Preview | E2B-compatible microVM service | Provider-owned durable sandbox ID | Self-hosted production on Linux/KVM |
| OpenSandbox | Preview; Docker runtime manually live-verified | Docker or Kubernetes-backed sandbox service | Provider-owned durable sandbox ID | Self-hosted production |
| Daytona | Preview | Managed or self-hosted sandbox service | Deterministic name, durable ID, and auto-pause | Managed production |
| Modal | Planned | Managed sandbox service | Provider-owned durable sandbox ID | Managed production |
| Runloop | Planned | Managed devbox service | Suspend, resume, and snapshot lifecycle | Managed production |
| Kubernetes SIG Agent Sandbox | Planned | Kubernetes CRD, controller, and routing layer | Stateful sandbox resource | Kubernetes deployments |
| Anthropic Sandbox Runtime, Vercel Sandbox, and Cloudflare Sandbox | Evaluating | Backend-specific | Backend-specific | Later adapters |
The Docker provider has not been audited for hostile multi-tenant workloads. The local provider is not a security boundary. No backend currently carries a production security claim.
Compatibility contract
A backend implements the core lifecycle contract when it can:
- expose a stable provider name;
- idempotently create one resource for a session key;
- attach to a persisted opaque reference after restart;
- execute a command with cancellation and bounded output;
- read and write paths relative to the workspace;
- destroy the resource idempotently.
These requirements are executable in internal/sandbox/sandboxtest. Local,
Docker, and every remote provider's opt-in live test run the same suite,
including cross-client Create/Attach, workspace preservation, ownership
rejection, cancellation, and post-delete missing-reference behavior. Offline
adapter tests cover the same contract without credentials. Provider-specific
tests cover protocol translation, isolation, and resource controls separately.
The built-in toolset currently assumes a POSIX-like environment with
/bin/sh, find, and grep. A backend that does not provide those commands is
not compatible with all executing built-ins yet.
Lifecycle today
- The first tool-using run idempotently creates the provider resource and
persists
{provider, external_id, spec_hash}in PostgreSQL. - Remote services receive a fixed-length hash of the session key as their ownership label; credentials and raw session identifiers are not persisted in the provider reference.
- Later turns reuse the cached client; a restarted worker attaches through the persisted reference.
- Different sessions never share a logical sandbox.
- Becoming idle retains it.
- Deleting the session fences admission, stops its Session Workflow, durably retries provider teardown on the worker, removes the binding, and only then deletes the session row.
- A persisted reference that no longer exists fails explicitly; Mango does not silently replace lost workspace state with an empty sandbox.
Required production lifecycle
Remote adapters must use the same lifecycle tests. Production deployments still need orphan reconciliation for the create-before-binding crash window, provider health reporting, and provider-aware task routing when heterogeneous workers share a control plane. Pause, snapshot, fork, quotas, and eviction remain optional capabilities rather than requirements of the core interface.
Remote provider configuration
Credentials stay in worker configuration. They are never written to PostgreSQL or returned by the Managed Agents API.
| Provider | Required | Common optional values |
|---|---|---|
e2b | E2B_API_KEY | E2B_API_URL, E2B_TEMPLATE_ID, E2B_DOMAIN, E2B_IDLE_TIMEOUT |
cube | CUBE_API_URL, CUBE_TEMPLATE_ID | CUBE_API_KEY, CUBE_SANDBOX_DOMAIN, CUBE_PROXY_*, CUBE_IDLE_TIMEOUT |
opensandbox | OPEN_SANDBOX_DOMAIN | OPEN_SANDBOX_API_KEY, OPEN_SANDBOX_IMAGE, OPEN_SANDBOX_USE_SERVER_PROXY |
daytona | DAYTONA_API_KEY | DAYTONA_API_URL, DAYTONA_TARGET, DAYTONA_SNAPSHOT, DAYTONA_IMAGE, DAYTONA_AUTO_PAUSE_MINUTES |
For local development, make dev-env-init creates
~/.config/mango/dev.env from config/dev.env.example with mode 0600.
scripts/with-dev-env <command> loads it explicitly and works across
worktrees.
Live conformance is opt-in:
scripts/with-dev-env env MANGO_LIVE_E2B=1 \
go test ./internal/sandbox -run '^TestE2BLiveConformance$' -count=1
scripts/with-dev-env env MANGO_LIVE_OPENSANDBOX=1 \
go test ./internal/sandbox -run '^TestOpenSandboxLiveConformance$' -count=1
The equivalent gates are MANGO_LIVE_CUBE and MANGO_LIVE_DAYTONA.
Ordinary tests never contact a service or create billable resources.
The upstream behavior informing the session/environment distinction is
documented in Claude's
cloud environment setup
and
self-hosted sandbox
guides. managed-agent-go remains an independent implementation and does not
claim Anthropic's hosted isolation properties.
Adding a backend
A backend contribution should:
- keep its external dependency optional and fail fast when explicitly selected but unavailable;
- register a lazy factory under a stable lowercase provider name and pass the
shared
sandboxtestlifecycle suite; - preserve the session-scoped ownership contract;
- document its trust boundary, network defaults, resource controls, host requirements, and unsupported lifecycle features;
- keep default tests offline and make daemon/network-dependent tests opt-in;
- avoid changing the AgentRuntime or public HTTP API for provider-specific mechanics;
- avoid a production or multi-tenant safety claim without evidence and an explicit security review.
Open an issue before a substantial backend integration so the intended use case and lifecycle implications can be reviewed independently of the adapter code.