Docs

Overview

A Sandbox is an isolated machine your application starts on demand to run code it doesn't trust: code a model wrote, a script a user uploaded, a build from a pull request. Each sandbox has its own filesystem, processes, and network rules, and is isolated from your application and from other sandboxes.

You declare the sandbox once in alien.ts: the image it starts from, how much CPU and memory it gets, and whether it can reach the internet. At runtime your application creates sandboxes, runs commands in them, moves files in and out, and deletes them when it's done.

Platform Mapping

PlatformBacking ServiceProvisioned by
AWSAWS Lambda MicroVMsAlien
GCPGemini Enterprise Agent Platform sandboxesAlien
AzureAzure Container Apps SandboxesAlien
Kubernetes / On-PremPods isolated with gVisor or KataAlien Operator
LocalDocker containersAlien

Not every platform supports every feature. The capability table lists what each one supports. If alien.ts asks for something a platform can't do, Alien refuses the stack at plan time instead of ignoring the setting.

When to Use

Use Sandbox to run code you didn't write and can't trust: model-generated code, user scripts, CI steps, notebook cells. If that code crashes, fills the disk, or tries to read files it shouldn't, the damage stays in the sandbox.

Don't use Sandbox for your own services; use Worker or Container. Don't keep data you need in a sandbox either. Sandboxes are deleted when you're done with them, so save results to Storage.

On Local, sandboxes are Docker containers that share your machine's kernel. Use Local for development, not for running hostile code.

Stack Definition

Declare a Sandbox resource in your alien.ts:

alien.ts
const agent = new alien.Sandbox("agent")
  .code({ type: "image", image: "ubuntu:24.04" })
  .limits({ cpu: "1", memory: "2Gi", disk: "8Gi" })
  .egress({ mode: "deny" })
  .lifecycle({})
  .build()
MethodTypeDefaultDescription
id (constructor)stringrequiredResource identifier. Lowercase letters, digits, and hyphens; starts and ends with a letter or digit, with no --.
.code(code){ type: "image", image } or { type: "source", src, toolchain }requiredThe image every sandbox starts from. See Images.
.egress(policy){ mode: "deny" | "allow" | "allowDomains", domains? }requiredWhether sandboxes can reach the network. allowDomains is Azure only.
.lifecycle(policy){ maxLifetimeSeconds?, idlePauseSeconds? }requiredHow long a sandbox may live, and when an idle one pauses. Pass {} for neither.
.limits(limits){ cpu, memory, disk, maxProcesses? }cpu: "1", memory: "2Gi", disk: "8Gi"CPU, memory, and disk ceilings for each sandbox. On AWS see AWS Sizes.
.previewPorts(ports)number[][]Ports that can be exposed for a preview. AWS and Local only.
.privateBaseImage(image)stringnoneA private ECR image to build from. AWS only, and the sandbox must be "live".

Which values each platform accepts is in Behavior & Limits.

Images

What code.image points to depends on the platform:

Platformcode.image
AWSA sandbox bundle in S3 (s3://<bucket>/<key>). If you release through Alien's hosted platform, a regular container image works too; Alien turns it into a bundle when you release. ARM64 only.
GCPAn image that includes the Alien sandbox agent, in an Artifact Registry repository.
AzureA name from the Azure Container Apps Sandboxes image catalog, such as ubuntu, or a linux/amd64 image from a registry.
KubernetesAn image that includes the Alien sandbox agent.
LocalAny Docker image that has sh, setsid, od, tr, sleep, and kill.

On AWS you can also build the image from a Dockerfile with { type: "source", src, toolchain: { type: "docker" } }. alien build builds it and alien release pushes it. The sandbox must be "live".

Frozen or Live

A Sandbox resource is frozen by default: setup provisions it, and changing it means running setup again, though on Azure, and on GCP after a Terraform setup, a rollout can still replace its image. Add it as "live" to let Alien rebuild the image during a rollout; egress and network still change only through setup. Either way, the sandboxes themselves are created at runtime. Only AWS supports live sandboxes, and a sandbox built from source or from a private base image must be live. A Worker can link only a frozen sandbox. See Frozen & Live Resources.

Quick Start

Declare the sandbox, link it to the workload that uses it, and give that workload the sandbox permissions:

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: "8Gi" })
  .egress({ mode: "deny" })
  .lifecycle({})
  .build()

const api = new alien.Worker("api")
  .code({ type: "source", src: "./api", toolchain: { type: "typescript" } })
  .link(agent)
  .permissions("execution")
  .build()

export default new alien.Stack("code-runner")
  .add(agent, "frozen")
  .add(api, "live")
  .permissions({
    profiles: {
      execution: {
        agent: ["sandbox/management", "sandbox/execute"],
      },
    },
  })
  .build()

sandbox/management lets the workload create and delete sandboxes, and sandbox/execute lets it run commands and move files. See Permissions.

This example runs on Local, and on AWS when you release through Alien's hosted platform. On GCP, Azure, and Kubernetes, change code.image as shown in Images. On AWS, deny needs the deployment's network mode to be create or byo-vpc-aws (Network).

Then create a sandbox and run a command in it:

import { sandbox } from "@alienplatform/sdk"

const box = sandbox("agent")
const { sandboxId } = await box.create()

try {
  for await (const frame of box.runCommand(sandboxId, "sh", {
    args: ["-c", "echo hello from $(uname -m)"],
    timeoutMs: 30_000,
  })) {
    if (frame.kind === "stdout") process.stdout.write(frame.data)
    if (frame.kind === "exit") console.log("exit code", frame.exitCode)
  }
} finally {
  await box.terminate(sandboxId)
}

Core Operations

Run a Command

runCommand streams stdout and stderr, then one exit frame with the exit code. timeoutMs is required.

for await (const frame of box.runCommand(sandboxId, "python3", {
  args: ["-c", "print(sum(range(10)))"],
  timeoutMs: 60_000,
})) {
  if (frame.kind === "stdout") process.stdout.write(frame.data)
  if (frame.kind === "exit") console.log("exit code", frame.exitCode)
}

Move Files

await box.writeFiles(sandboxId, { "work/main.py": "print('hello')" })
const report = await box.readFile(sandboxId, "work/report.json")

Reconnect, Pause, Terminate

Save the sandboxId that create returns. Every later call uses it, including calls from another request (get returns the sandbox). pause and resume keep the sandbox's state on AWS and Azure; terminate deletes it.

For long commands, AWS, GCP, and Kubernetes also run jobs that you start once and poll from later calls. Every method is in the API Reference, and per-platform behavior is in Behavior & Limits.

Triggers

Sandbox has no triggers. Your application calls it through its binding.

Remote Access

To call a sandbox from your own backend instead of a workload in the deployment, add it with { remoteAccess: true } and use a Remote binding. It must declare egress: { mode: "allow" } and no previewPorts.

On this page