Sandbox
The main client — sandbox(), resume(), restoreSnapshot(), connect(), and sandboxes management.
import { Sandbox } from "@alineo-labs/sandbox";Constructor
new Sandbox(options: SandboxClientOptions)SandboxClientOptions
| Option | Type | Required | Description |
|---|---|---|---|
baseUrl | string | Yes | OpenSandbox server URL, e.g. "http://localhost:8080" |
apiKey | string | No | OpenSandbox API key. Empty string for local dev with no auth. |
adapter | IStorageAdapter | Yes | Storage adapter for the run ledger |
maxConcurrency | number | No | Max simultaneous active sandboxes. Omit for no limit. |
useServerProxy | boolean | No | Route execd and proxy traffic through the server. Required when the server runs in Docker via alineo init. Defaults to false. |
credentialBroker | CredentialBroker | No | Backend for sb.credentials.*. Defaults to OpenSandboxCredentialBroker (@alineo-labs/vault) — see Credentials. |
const client = new Sandbox({
baseUrl: process.env.OPEN_SANDBOX_URL ?? "http://localhost:8080",
apiKey: process.env.OPEN_SANDBOX_API_KEY ?? "",
adapter: new SQLiteAdapter("./ledger.db"),
maxConcurrency: 10,
});Methods
sandbox()
await client.sandbox(opts: SandboxOptions): Promise<SandboxHandle>Creates a container, waits until it reaches Running state, and returns a live SandboxHandle object. Writes a sandbox.created event to the ledger.
If maxConcurrency is set, awaits a slot before creating the container. The slot is released when sb.close() is called.
See Sandboxes for SandboxOptions reference.
const sb = await client.sandbox({
image: "ubuntu:22.04",
resources: { cpu: "500m", memory: "512Mi" },
name: "my-run",
});
try {
await sb.exec("echo hello").pipe(process.stdout);
} finally {
await sb.close();
}resume()
await client.resume(
sandboxId: string,
opts?: { tag?: string; resolveCredential?: CredentialResolver },
): Promise<SandboxHandle>Restores a sandbox session from its last checkpoint (or the checkpoint matching opts.tag, if given):
- Reads the ledger to find the last
sandbox.checkpoint_createdevent (or the last one matchingopts.tag) - Creates a new container from the snapshot
- Populates a replay cache from execs that completed before the checkpoint
- Re-registers any credentials the sandbox had bound (see Credentials) —
opts.resolveCredentialsupplies values that can't be re-derived automatically - Returns a
SandboxHandle— pre-checkpoint execs return cached results; later execs run live
Throws SandboxClientError (404) if the session is not found or has no matching checkpoint. Throws SandboxError if a bound credential can't be resolved.
const sbResume = await client.resume(originalSandboxId);
try {
await sbResume.exec("pip install -q requests").pipe(process.stdout); // replayed
await sbResume.exec("python3 script.py").pipe(process.stdout); // live
} finally {
await sbResume.close();
}
// Resume from a specific named checkpoint instead of the most recent one:
const sbTagged = await client.resume(originalSandboxId, { tag: "after-apt" });restoreSnapshot()
await client.restoreSnapshot(
snapshotId: string,
name: string,
resources: { cpu: string; memory: string; gpu?: string },
): Promise<SandboxHandle>Creates a fresh sandbox from a snapshot ID without exec replay — unlike resume(), the new sandbox starts with a clean exec history; nothing from the ledger is replayed. Use this when you want to restore a checkpointed environment and run new commands from scratch, rather than continue exactly where the checkpoint left off.
This is also what sb.fork() uses under the hood — see Forking sandboxes for the relationship between checkpoint(), fork(), resume(), and restoreSnapshot().
const snapshotId = await sb.checkpoint();
await sb.close();
// Later — restore and run fresh commands:
const sb2 = await client.restoreSnapshot(snapshotId, "my-sandbox", {
cpu: "500m",
memory: "256Mi",
});
await sb2.exec("npm test");
await sb2.close();connect()
await client.connect(
sandboxId: string,
name: string,
opts?: { resources?: { cpu: string; memory: string; gpu?: string } },
): Promise<SandboxHandle>Attaches to an already-Running sandbox by ID, no snapshot or ledger replay involved — the container must already be running (throws SandboxClientError (409) otherwise). Use this to reconnect to a live sandbox created outside the current process, e.g. from a saved sandboxId.
opts.resources is only needed if you intend to call .fork() on the returned SandboxHandle afterwards — the control API doesn't echo back a running sandbox's own resource limits, so there's no way to discover them here; without it, .fork() is unavailable on the returned object.
// In a new process, reconnect to a sandbox started earlier:
const sb = await client.connect(savedSandboxId, "my-sandbox");
const { stdout } = await sb.exec("cat /results.txt");
await sb.close();sandboxes
client.sandboxes: {
list(opts?: ListSandboxOptions): Promise<SandboxDetails[]>
listByName(name: string, opts?: ListSandboxOptions): Promise<SandboxDetails[]>
get(name: string, sandboxId: string): Promise<SandboxDetails | null>
delete(name: string, sandboxId: string): Promise<void>
}Access to the ledger's session history.
// List all sessions, newest first
const all = await client.sandboxes.list();
// Filter
const running = await client.sandboxes.list({ status: SandboxStatus.Running, limit: 20 });
// By name
const named = await client.sandboxes.listByName("my-ci-job");
// Single session
const session = await client.sandboxes.get("my-job", sandboxId);
// Delete
await client.sandboxes.delete("my-job", sandboxId);ListSandboxOptions
| Option | Type | Description |
|---|---|---|
status | SandboxStatus | Filter by status: "running" or "completed" |
limit | number | Max results to return |
before | number | Return only sessions started before this Unix timestamp (ms) |
SandboxHandle class
See SandboxHandle below for the object returned by sandbox() and resume().
SandboxHandle methods
| Method | Returns | Description |
|---|---|---|
sb.exec(cmd, opts?) | ExecHandle | Run a shell command |
sb.execCode(code, opts?) | ExecHandle | Run code via the interpreter |
sb.proxy(port) | Promise<{ url, headers }> | Get a proxied URL for an in-sandbox port |
sb.metrics() | Promise<{ cpu, memory, timestamp }> | Current CPU and memory usage |
sb.writeFile(path, content) | Promise<void> | Write a UTF-8 file into the container |
sb.readFile(path) | Promise<string> | Read a file from the container as a string |
sb.moveFile(from, to) | Promise<void> | Move or rename a file |
sb.deleteFile(path) | Promise<void> | Delete a file |
sb.createDirectory(path) | Promise<void> | Create a directory (and parents) |
sb.deleteDirectory(path) | Promise<void> | Delete a directory |
sb.getFileInfo(path) | Promise<FileInfo> | File metadata: size, type, mode, timestamps |
sb.replaceInFiles(replacements) | Promise<void> | In-place substring replacement across files |
sb.transfer(path, target) | Promise<void> | Copy a file to another SandboxHandle instance |
sb.searchFiles(pattern, path?) | Promise<string[]> | Search for files matching a glob |
sb.listDirectory(path, opts?) | Promise<FileInfo[]> | List directory entries with metadata |
sb.checkpoint(name?) | Promise<string> | Snapshot the container state, returns the snapshot ID |
sb.listCheckpoints() | Promise<CheckpointInfo[]> | All checkpoints for this sandbox |
sb.fork(tag?, runId?, opts?) | Promise<SandboxHandle> | Snapshot and return an independent copy |
sb.close() | Promise<void> | Delete the container and release resources |
sb.credentials
sb.credentials: {
set(name: string, value: string, binding: CredentialBinding, source?: CredentialSource): Promise<void>
patch(name: string, changes: Partial<{ value: string; binding: CredentialBinding }>): Promise<void>
remove(name: string): Promise<void>
listBindings(): Promise<Array<{ name: string; binding: CredentialBinding }>>
}Register, update, remove, and list credentials injected into this sandbox's outbound requests — requires the sandbox to have been created with credentialProxy: true. See Credentials.
SandboxHandle properties
| Property | Type | Description |
|---|---|---|
sb.sandboxId | string | OpenSandbox container ID |
sb.name | string | User-provided name or auto-generated |