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.
| Plugin | Priority | Isolation Type | Runs on |
|---|---|---|---|
| Vercel | 100 | Firecracker microVM | Vercel infrastructure |
| E2B | 90 | Firecracker microVM (managed) | E2B cloud |
| Daytona | 85 | Managed cloud sandbox | Daytona cloud |
| nsjail | 75 | Linux namespace sandbox | Same host (Linux only) |
| Plugin default | 60 | SANDBOX_DEFAULT_PRIORITY | -- |
| Sidecar | 50 | HTTP-isolated container | Separate container |
| just-bash (built-in) | 0 | OverlayFs (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 e2bConfiguration
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
apiKey | string | Yes | -- | E2B API key |
template | string | No | E2B default | Sandbox template ID for custom VM images |
timeoutSec | number | No | 30 | Per-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
| Property | Value |
|---|---|
| Network isolation | Yes |
| Filesystem isolation | Yes (ephemeral VM filesystem) |
| Unprivileged execution | Yes |
| Priority | 90 |
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
e2bpackage 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/sdkConfiguration
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
apiKey | string | Yes | -- | Daytona API key |
apiUrl | string | No | Daytona cloud endpoint | Custom API URL for self-hosted Daytona |
timeoutSec | number | No | 30 | Per-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
| Property | Value |
|---|---|
| Network isolation | Yes |
| Filesystem isolation | Yes (ephemeral per sandbox) |
| Unprivileged execution | Yes |
| Priority | 85 |
Daytona managed sandbox. Cloud-hosted ephemeral environment with network isolation, filesystem isolation, and unprivileged execution. Sandbox is deleted after use.
Notes
- The
@daytonaio/sdkpackage is an optional peer dependency, lazy-loaded at runtime. - Daytona combines stdout and stderr into a single
resultfield, 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 initto 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/ DockerfileNo npm dependencies are required beyond @useatlas/plugin-sdk.
Configuration
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
nsjailPath | string | No | Auto-detected on PATH | Explicit path to the nsjail binary |
timeLimitSec | number | No | 10 | Per-command time limit in seconds |
memoryLimitMb | number | No | 256 | Per-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
| Property | Value |
|---|---|
| Network isolation | Yes (disabled by nsjail) |
| Filesystem isolation | Yes (read-only bind-mount) |
| Unprivileged execution | Yes (runs as nobody:65534) |
| Priority | 75 |
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:
| Variable | Value |
|---|---|
PATH | /bin:/usr/bin |
HOME | /tmp |
LANG | C.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
PATHifnsjailPathis 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 APIConfiguration
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
url | string | Yes | -- | Sidecar service URL (e.g. http://sandbox-sidecar:8080) |
authToken | string | No | -- | Shared secret for sidecar authentication |
timeoutMs | number | No | 10000 | Per-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
| Property | Value |
|---|---|
| Network isolation | Yes (container boundary) |
| Filesystem isolation | Yes (container filesystem) |
| Unprivileged execution | Yes |
| Priority | 50 |
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
semanticRootpath is not used -- thecreate()method ignores it. - HTTP requests include a 5-second overhead on top of the configured
timeoutMsto 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 /healthon 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
accessTokenandteamIdfor non-Vercel environments
Installation
bun add @vercel/sandboxConfiguration
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
accessToken | string | No | Auto-detected OIDC on Vercel | Access token for non-Vercel environments |
teamId | string | When 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
| Property | Value |
|---|---|
| Network isolation | Yes (deny-all network policy) |
| Filesystem isolation | Yes (ephemeral VM filesystem) |
| Unprivileged execution | No |
| Priority | 100 |
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/sandboxpackage is an optional peer dependency, lazy-loaded at runtime. - This is the only sandbox plugin where
unprivilegedExecutionisfalse-- 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:
- Deploying on Vercel? Use
vercel-sandbox-- it is auto-selected as the highest-priority backend and requires no configuration. - Want managed cloud isolation with no infrastructure? Use
e2b-sandboxordaytona-sandbox. Both provide ephemeral VMs/sandboxes via a managed API. Choose based on which service you already use or prefer. - Self-hosting on Linux with root access? Use
nsjail-sandbox-- strong namespace isolation with no external dependencies beyond the nsjail binary. - Self-hosting on Docker/Railway without root? Use
sidecar-sandbox-- deploy the sidecar container alongside the API. No special kernel capabilities needed. - Local development? The built-in
just-bashfallback (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",
}),
],
});