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:
- Calls the OpenSandbox control API to create the container
- Waits until the container reaches
Runningstate - Returns the
SandboxHandleobject
SandboxOptions
| Option | Type | Description |
|---|---|---|
image | string | { 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. |
env | Record<string, string> | Environment variables set in the container at startup |
metadata | Record<string, string> | Arbitrary key-value labels attached to the sandbox (e.g. { runId: "ci-42" }) |
name | string | User-provided name for the run — used as the ledger key. Auto-generated if omitted. |
timeout | number | Sandbox lifetime in seconds. Defaults to the OpenSandbox server's default. |
hooks | SandboxHooks | Lifecycle 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. |
credentialProxy | boolean | Opt-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 omittedMultiple 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
});