Docs

Overview

Run untrusted or AI-generated code in an isolated environment, created per session at runtime.

A Sandbox runs code you do not trust — generated by a model, submitted by a user, pulled from a pull request — in an environment isolated from your application, your customer's cloud, and every other sandbox. Your app creates one per agent turn if it wants, runs commands in it, moves files in and out, and throws it away.

Unlike most Alien resources, a Sandbox is not one thing that exists for the life of your stack. The declaration provisions a durable parent; sessions are created and destroyed at runtime through the binding.

Platform Mapping

PlatformRuntimeIsolationProvisioned by
AWSLambda MicroVMsFirecracker, VM-levelAlien
GCPCloud Run sandboxesNot published by GoogleAlien
AzureContainer Apps SandboxesHyper-V, VM-levelAlien
Kubernetes / On-PremPod under a sandboxed runtimeClassNamegVisor or KataAlien, on your cluster
LocalDocker containerShared kernelAlien local runtime

Azure and GCP are in public preview upstream. Kubernetes needs a sandboxed runtime class on the cluster, and refuses to provision without one. See Behavior & Limits.

When to Use

Use Sandbox to execute code your application did not write — an agent's generated script, a customer's build step, a submitted notebook cell — where a crash, a fork bomb, or an attempt to read the filesystem must not reach your service.

Use Container or Worker for code you control; both are simpler and cheaper. Don't reach for a Sandbox when you need a durable filesystem — sessions are disposable, so keep state outside. And Local shares a kernel with the host, so it is development-only for untrusted code unless gVisor or Kata is present.

Capabilities differ by platform

Check this before you build. Create, exec and terminate work everywhere; everything else varies, and calling an unsupported capability returns a typed error naming the platform and the capability — never a silent no-op.

CapabilityAWSAzureGCPKubernetesLocal
files — move files in and outyesnoyesyesyes
reconnect — reach a session againyesyesnoyesyes
preview — authenticated ingressyesnononoyes
suspendResumeyesnononono
snapshotnonononono
egressDenydeny is enforcedyesnoyesyesyes
domainEgressRules — hostname allowlistnonononono
enforcedLimits — cpu / memory / diskyesnonoyesyes
processLimitnonononoyes
sessionLifetime — platform deadlineyesnonoyesno
supervisorPidNamespacenonononono

preview and snapshot are Rust-only: the TypeScript binding exposes no method for either, so they never appear in the list capabilities() returns there.

File transfer is the one to check first, because it is the only floor operation a platform lacks: Alien's Azure binding does not implement it. Pass what the session needs on the command line there, or pick another platform.

A declaration a platform cannot honour is rejected when you deploy, not ignored at runtime. So maxProcesses is accepted on Local alone, and maxLifetimeSeconds on AWS and Kubernetes. Leave them out unless you are targeting a platform that applies them.

const caps = await box.capabilities()
if (caps.includes("reconnect")) {
  // multi-turn: reuse the session across turns
} else {
  // single-turn: finish inside one turn, or keep state outside the sandbox
}

Quick Start

Declare the sandbox in your stack:

alien.ts
import * as alien from "@alienplatform/core"

const agent = new alien.Sandbox("agent")
  .code({ type: "image", image: "ubuntu:24.04" })
  .limits({ cpu: "1", memory: "2Gi", disk: "20Gi" })
  .egress({ mode: "deny" })
  .session({})
  .build()

export default new alien.Stack("app")
  .add(agent, "live")
  .build()

That declaration targets AWS, Kubernetes and Local: .limits(...) needs enforcedLimits and deny needs egressDeny, and Azure has neither while GCP lacks the first. Drop both to deploy everywhere, and see Configuration for which field needs which capability.

Then drive sessions from your application:

import { sandbox } from "@alienplatform/sdk"

const box = sandbox("agent")
const session = await box.create({ sessionId: "turn-1" })

for await (const frame of box.runCommand(
  session.sessionId,
  ["python3", "-c", "print(2 + 2)"],
  { deadlineMs: 30_000 },
)) {
  if (frame.kind === "stdout") process.stdout.write(frame.data)
  if (frame.kind === "exit") console.log("exit", frame.exitCode)
}

await box.terminate(session.sessionId)

Every method is in the API Reference.

Configuration

MethodTypeRequiredDescription
.code(...){ type: "image", image }YesA prebuilt image reference. Building from source is not supported on any backend yet. Not applied on Azure — see Behavior & Limits.
.egress(...){ mode: "deny" | "allow" }YesOutbound network policy. allowDomains is refused on every platform.
.session(...){ idleSuspendSeconds?, maxLifetimeSeconds? }YesRequired, but both fields are optional — .session({}) deploys anywhere. idleSuspendSeconds needs suspendResume (AWS only); maxLifetimeSeconds needs sessionLifetime (AWS and Kubernetes).
.limits(...){ cpu, memory, disk, maxProcesses? }NoEnforced ceilings. Needs enforcedLimits; maxProcesses needs processLimit.
.previewPorts(...)number[]NoPorts eligible for a preview capability. A port not listed here can never be exposed, so an application cannot widen its own ingress.

Omitting .limits(...) takes the platform's defaults. Naming it on a platform that cannot enforce it is rejected at plan time rather than accepted and ignored.

Two things that bite if you skim

GCP sandboxes are single-turn. A session id is scoped to one Cloud Run instance, and session affinity was measured keeping 2 of 100 five-turn conversations. That is the absence of a reconnect guarantee, not a weak one. If your agent needs the same sandbox across turns, GCP is the wrong platform — see Behavior & Limits.

Limits are ceilings, not requests. The platform enforces them and your stack is validated against them when it is planned, which is why a GCP Sandbox declaring limits fails at plan time.

On this page