Concepts

Sandboxes

Isolated Docker containers managed by OpenSandbox — how they're created, configured, and closed.

A sandbox is a Docker container managed by OpenSandbox. client.sandbox() creates one and returns a live SandboxHandle object. The container persists until sb.close() is called.

Creating a sandbox

const sb = await client.sandbox({
  image: "ubuntu:22.04",
  resources: { cpu: "500m", memory: "512Mi" },
  name: "my-sandbox",
});

client.sandbox() does three things:

  1. Calls the OpenSandbox control API to create the container
  2. Waits until the container reaches Running state
  3. Returns the SandboxHandle object

SandboxOptions

OptionTypeDescription
imagestring | { uri, auth? }Container image. String form: "ubuntu:22.04". Object form for private registries: { uri: "ghcr.io/org/image", auth: { username, password } }
resources{ cpu: string; memory: string; gpu?: string }Resource limits. Required. cpu is a Kubernetes-style string like "500m" or "2". memory is "256Mi", "1Gi", etc.
envRecord<string, string>Environment variables set in the container at startup
metadataRecord<string, string>Arbitrary key-value labels attached to the sandbox (e.g. { runId: "ci-42" })
namestringUser-provided name for the run — used as the ledger key. Auto-generated if omitted.
timeoutnumberSandbox lifetime in seconds. Defaults to the OpenSandbox server's default.
hooksSandboxHooksLifecycle callbacks for observability. See Observability.
networkPolicy{ defaultAction, egress }Optional outbound network policy — which hosts the sandbox may reach. Required to use credentialProxy. See Network policy.
credentialProxybooleanOpt-in to credential injection via sb.credentials.*. See Credentials.

Sandbox lifecycle

Always use try/finally to ensure the container is deleted even if a step throws:

const sb = await client.sandbox({
  image: "ubuntu:22.04",
  resources: { cpu: "500m", memory: "256Mi" },
});

try {
  await sb.exec("npm ci");
  await sb.exec("npm test").pipe(process.stdout);
} finally {
  await sb.close(); // deletes the container, writes sandbox_closed to ledger
}

close() is idempotent — subsequent calls are no-ops.

SandboxHandle properties

sb.sandboxId; // OpenSandbox container ID — also the unique ledger key
sb.name; // User-provided name, or "sandbox-<short-id>" if omitted

Multiple sandboxes

Each client.sandbox() call creates an independent container. Hold them as separate variables:

const sbA = await client.sandbox({ image: "node:22", resources: { cpu: "500m", memory: "512Mi" } });
const sbB = await client.sandbox({
  image: "python:3.11",
  resources: { cpu: "500m", memory: "512Mi" },
});

try {
  await sbA.exec("node --version").pipe(process.stdout);
  await sbB.exec("python3 --version").pipe(process.stdout);
} finally {
  await sbA.close();
  await sbB.close();
}

Concurrency limits

SandboxClientOptions.maxConcurrency caps how many sandboxes may be active at once. When at capacity, client.sandbox() awaits until a slot is free (released by sb.close()):

const client = new Sandbox({
  baseUrl: "http://localhost:8080",
  adapter: new SQLiteAdapter("./alineo.db"),
  maxConcurrency: 5, // at most 5 active sandboxes
});