Atlas

Sandbox Plugins

Isolate code execution with E2B, Daytona, nsjail, sidecar, and Vercel sandbox plugins.

Sandbox plugins provide isolation backends for the explore tool. They run user-initiated commands (file listing, grep, cat) in restricted environments to prevent unauthorized filesystem or network access. Each plugin implements the AtlasSandboxPlugin interface from @useatlas/plugin-sdk.

All five plugins follow the same pattern: validate config via a Zod schema, create an isolated environment, upload semantic layer files, execute commands, and clean up. The differences are in the isolation technology and where the sandbox runs.

Priority Order

When multiple sandbox backends are available, Atlas selects the highest-priority one. Higher priority means stronger isolation guarantees.

PluginPriorityIsolation TypeRuns on
Vercel100Firecracker microVMVercel infrastructure
E2B90Firecracker microVM (managed)E2B cloud
Daytona85Managed cloud sandboxDaytona cloud
nsjail75Linux namespace sandboxSame host (Linux only)
Plugin default60SANDBOX_DEFAULT_PRIORITY--
Sidecar50HTTP-isolated containerSeparate container
just-bash (built-in)0OverlayFs (dev fallback)Same host

The built-in just-bash backend (priority 0) is a development-only fallback. It uses OverlayFs for read-only filesystem protection but provides no network isolation. Never use it in production.

E2B

Managed Firecracker microVM isolation via the E2B API. Each agent session gets an ephemeral VM -- semantic layer files are uploaded at creation time and the VM is destroyed when the session ends. No infrastructure to manage; E2B handles provisioning, scaling, and cleanup.

Installation

bun add e2b

Configuration

FieldTypeRequiredDefaultDescription
apiKeystringYes--E2B API key
templatestringNoE2B defaultSandbox template ID for custom VM images
timeoutSecnumberNo30Per-command timeout in seconds

Usage

// atlas.config.ts
import { defineConfig } from "@atlas/api/lib/config";
import { e2bSandboxPlugin } from "@atlas/plugin-e2b-sandbox";

export default defineConfig({
  plugins: [
    e2bSandboxPlugin({
      apiKey: process.env.E2B_API_KEY!,
      template: "my-custom-template", // optional
      timeoutSec: 60,                 // optional
    }),
  ],
});

Security

PropertyValue
Network isolationYes
Filesystem isolationYes (ephemeral VM filesystem)
Unprivileged executionYes
Priority90

E2B Firecracker microVM (managed). Ephemeral VM with isolated network and filesystem. Semantic files uploaded at sandbox creation. No host access -- the VM is destroyed after use.

Notes

  • The e2b package is an optional peer dependency -- it is lazy-loaded at runtime. If the package is missing, the plugin throws a clear error with install instructions.
  • Health checks create and immediately destroy a sandbox instance, so they do incur a small API cost.
  • Symlinks in the semantic layer that escape the semantic root are skipped during file upload for safety.

Daytona

Managed cloud sandbox isolation via the Daytona SDK. Similar to E2B -- ephemeral, cloud-hosted environments with no local infrastructure required. Semantic layer files are uploaded into the sandbox and commands are executed remotely.

Installation

bun add @daytonaio/sdk

Configuration

FieldTypeRequiredDefaultDescription
apiKeystringYes--Daytona API key
apiUrlstringNoDaytona cloud endpointCustom API URL for self-hosted Daytona
timeoutSecnumberNo30Per-command timeout in seconds

Usage

// atlas.config.ts
import { defineConfig } from "@atlas/api/lib/config";
import { daytonaSandboxPlugin } from "@atlas/plugin-daytona-sandbox";

export default defineConfig({
  plugins: [
    daytonaSandboxPlugin({
      apiKey: process.env.DAYTONA_API_KEY!,
      apiUrl: "https://my-daytona.example.com", // optional
    }),
  ],
});

Security

PropertyValue
Network isolationYes
Filesystem isolationYes (ephemeral per sandbox)
Unprivileged executionYes
Priority85

Daytona managed sandbox. Cloud-hosted ephemeral environment with network isolation, filesystem isolation, and unprivileged execution. Sandbox is deleted after use.

Notes

  • The @daytonaio/sdk package is an optional peer dependency, lazy-loaded at runtime.
  • Daytona combines stdout and stderr into a single result field, so the plugin returns stderr as an empty string.
  • Parent directories are explicitly created before file upload since Daytona may not auto-create them for nested paths (e.g. semantic/entities/users.yml).
  • Health checks create a real sandbox instance. Avoid calling at high frequency to minimize API costs.
  • The plugin errors if no semantic layer files are found. Run atlas init to generate one before using this plugin.

nsjail

Linux namespace isolation using nsjail. Commands run in a jailed process on the same host with no network access, a read-only bind-mount of the semantic/ directory, and strict resource limits. This is the reference sandbox implementation -- no external API or cloud service required, just the nsjail binary on the host.

Installation

nsjail is a system-level binary, not an npm package. Install it via your OS package manager or build from source:

# Debian/Ubuntu
apt-get install nsjail

# Or build from source
git clone https://github.com/google/nsjail && cd nsjail && make

# Docker: included in the examples/docker/ Dockerfile

No npm dependencies are required beyond @useatlas/plugin-sdk.

Configuration

FieldTypeRequiredDefaultDescription
nsjailPathstringNoAuto-detected on PATHExplicit path to the nsjail binary
timeLimitSecnumberNo10Per-command time limit in seconds
memoryLimitMbnumberNo256Per-command memory limit in MB

Usage

// atlas.config.ts
import { defineConfig } from "@atlas/api/lib/config";
import { nsjailSandboxPlugin } from "@atlas/plugin-nsjail-sandbox";

export default defineConfig({
  plugins: [
    nsjailSandboxPlugin({
      timeLimitSec: 15,
      memoryLimitMb: 512,
    }),
  ],
});

Security

PropertyValue
Network isolationYes (disabled by nsjail)
Filesystem isolationYes (read-only bind-mount)
Unprivileged executionYes (runs as nobody:65534)
Priority75

Linux namespace isolation via nsjail. No network access, read-only semantic/ bind-mount, writable tmpfs for scratch, runs as nobody:65534. Additional resource limits: max 5 processes, 64 open file descriptors, 10 MB max file size.

Jail environment

The jailed process gets a minimal environment with no host secrets:

VariableValue
PATH/bin:/usr/bin
HOME/tmp
LANGC.UTF-8

Read-only bind-mounts: /bin, /usr/bin, /lib, /lib64, /usr/lib, /dev/null, /dev/zero, /dev/urandom, and the semantic/ directory at /semantic.

Notes

  • Requires Linux with namespace support. Will not work on macOS or Windows (use E2B, Daytona, or the sidecar instead).
  • The nsjail binary is auto-detected on PATH if nsjailPath is not set. If the binary is not found, the plugin logs a warning at initialization and fails on first use.
  • Stdout and stderr are each capped at 1 MB to prevent memory exhaustion from runaway commands.
  • The create() method is synchronous -- no async setup needed since the jail is created per-command.

Sidecar

HTTP-isolated container running alongside the main Atlas API. The sidecar is a separate Docker container (packages/sandbox-sidecar/) containing only bash, coreutils, and the semantic layer files. Commands are sent over HTTP and executed inside the container. No host filesystem or secrets are accessible.

Installation

No npm dependencies required. The sidecar is deployed as a separate container using the packages/sandbox-sidecar/Dockerfile.

# Build the sidecar container
docker build -f packages/sandbox-sidecar/Dockerfile -t atlas-sidecar .

# Or use docker-compose / Railway to deploy alongside the API

Configuration

FieldTypeRequiredDefaultDescription
urlstringYes--Sidecar service URL (e.g. http://sandbox-sidecar:8080)
authTokenstringNo--Shared secret for sidecar authentication
timeoutMsnumberNo10000Per-command timeout in milliseconds

Usage

// atlas.config.ts
import { defineConfig } from "@atlas/api/lib/config";
import { sidecarSandboxPlugin } from "@atlas/plugin-sidecar-sandbox";

export default defineConfig({
  plugins: [
    sidecarSandboxPlugin({
      url: process.env.ATLAS_SANDBOX_URL ?? "http://sandbox-sidecar:8080",
      authToken: process.env.SIDECAR_AUTH_TOKEN,
    }),
  ],
});

Security

PropertyValue
Network isolationYes (container boundary)
Filesystem isolationYes (container filesystem)
Unprivileged executionYes
Priority50

HTTP-isolated container with no secrets or DB drivers. Communication occurs only via HTTP to the sidecar service. The container has only bash/coreutils and the semantic layer files baked in at build time.

Notes

  • The sidecar container has its own copy of the semantic layer files, baked into the Docker image at build time. The host's semanticRoot path is not used -- the create() method ignores it.
  • HTTP requests include a 5-second overhead on top of the configured timeoutMs to account for network latency.
  • Connection errors (ECONNREFUSED) produce a specific error message suggesting the sidecar service may be down.
  • The close() method is a no-op -- the sidecar is stateless and shared across requests.
  • Health checks hit GET /health on the sidecar with a 5-second timeout.

Vercel

Firecracker microVM isolation via @vercel/sandbox. This is the highest-priority sandbox backend (priority 100) and the default on Vercel deployments. VMs run with a deny-all network policy and an ephemeral filesystem. Semantic layer files are copied into the VM at creation time.

Supports two authentication modes:

  • Auto-detected OIDC (default on Vercel) -- no configuration needed when deployed to Vercel
  • Access token -- pass accessToken and teamId for non-Vercel environments

Installation

bun add @vercel/sandbox

Configuration

FieldTypeRequiredDefaultDescription
accessTokenstringNoAuto-detected OIDC on VercelAccess token for non-Vercel environments
teamIdstringWhen using accessToken--Vercel team ID (required with access token)

When accessToken is provided, teamId is required. The config schema enforces this at validation time.

Usage

// atlas.config.ts
import { defineConfig } from "@atlas/api/lib/config";
import { vercelSandboxPlugin } from "@atlas/plugin-vercel-sandbox";

// On Vercel (auto-detected OIDC -- no config needed):
export default defineConfig({
  plugins: [
    vercelSandboxPlugin({}),
  ],
});

// Off Vercel (access token):
export default defineConfig({
  plugins: [
    vercelSandboxPlugin({
      accessToken: process.env.VERCEL_ACCESS_TOKEN!,
      teamId: process.env.VERCEL_TEAM_ID!,
    }),
  ],
});

Security

PropertyValue
Network isolationYes (deny-all network policy)
Filesystem isolationYes (ephemeral VM filesystem)
Unprivileged executionNo
Priority100

Firecracker microVM with deny-all network policy. Ephemeral filesystem -- writes do not affect the host. Semantic layer files copied in at creation time. Uses Node.js 24 runtime inside the VM.

Notes

  • The @vercel/sandbox package is an optional peer dependency, lazy-loaded at runtime.
  • This is the only sandbox plugin where unprivilegedExecution is false -- the VM provides strong isolation at the hypervisor level, so unprivileged execution inside the VM is not required for security.
  • Sensitive patterns in error messages (passwords, connection strings, auth failures) are automatically scrubbed before surfacing to users.
  • Directories are created before file upload since the sandbox SDK requires parent directories to exist.
  • Health checks create a real sandbox VM, so they incur Vercel sandbox usage.

Choosing a Sandbox

Use this decision tree to pick the right sandbox for your deployment:

  1. Deploying on Vercel? Use vercel-sandbox -- it is auto-selected as the highest-priority backend and requires no configuration.
  2. Want managed cloud isolation with no infrastructure? Use e2b-sandbox or daytona-sandbox. Both provide ephemeral VMs/sandboxes via a managed API. Choose based on which service you already use or prefer.
  3. Self-hosting on Linux with root access? Use nsjail-sandbox -- strong namespace isolation with no external dependencies beyond the nsjail binary.
  4. Self-hosting on Docker/Railway without root? Use sidecar-sandbox -- deploy the sidecar container alongside the API. No special kernel capabilities needed.
  5. Local development? The built-in just-bash fallback (priority 0) works for development. It provides OverlayFs read-only protection but no network isolation.

Combining multiple sandboxes

You can register multiple sandbox plugins. Atlas picks the highest-priority one that is available at runtime:

// atlas.config.ts
import { defineConfig } from "@atlas/api/lib/config";
import { nsjailSandboxPlugin } from "@atlas/plugin-nsjail-sandbox";
import { sidecarSandboxPlugin } from "@atlas/plugin-sidecar-sandbox";

export default defineConfig({
  plugins: [
    // nsjail (priority 75) is preferred when available
    nsjailSandboxPlugin({ timeLimitSec: 15 }),
    // Sidecar (priority 50) is the fallback
    sidecarSandboxPlugin({
      url: process.env.ATLAS_SANDBOX_URL ?? "http://sandbox-sidecar:8080",
    }),
  ],
});

On this page