The official JavaScript/TypeScript SDK for Sandbox0, providing typed models and ergonomic high-level APIs for managing secure code execution sandboxes.
npm install sandbox0
# or
yarn add sandbox0
# or
pnpm add sandbox0- Node.js 18.0.0 or later
Streaming APIs prefer a runtime-native globalThis.WebSocket when a Node-compatible host exposes one, and fall back to the ws package on older Node runtimes. This keeps the main SDK entry compatible with SandFunc-style runtimes that expose a standard outbound WebSocket client without requiring raw socket access in user code.
| Environment Variable | Required | Default | Description |
|---|---|---|---|
SANDBOX0_TOKEN |
Yes | - | API authentication token |
SANDBOX0_BASE_URL |
No | https://api.sandbox0.ai |
API base URL |
import { Client } from "sandbox0";
const client = new Client({
token: process.env.SANDBOX0_TOKEN!,
});
async function main() {
// Claim a sandbox
const sandbox = await client.sandboxes.claim("default");
try {
// Execute Python code (REPL - stateful)
const result = await sandbox.run("python", "print('Hello, Sandbox0!')");
process.stdout.write(result.outputRaw);
} finally {
// Cleanup
await client.sandboxes.delete(sandbox.id);
}
}
main().catch(console.error);const stream = await sandbox.cmdStream("sh -c 'echo hello && echo warn >&2'", {
command: ["sh", "-c", "echo hello && echo warn >&2"],
});
for await (const output of stream.outputs()) {
process.stdout.write(output.data);
}
const done = await stream.wait();
console.log(`exit=${done.exitCode} state=${done.state}`);const volume = await client.volumes.create({});
const sandbox = await client.sandboxes.claim("default", {
mounts: [{ sandboxvolumeId: volume.id, mountPoint: "/workspace/data" }],
});
for (const mount of sandbox.bootstrapMounts) {
console.log(mount.sandboxvolumeId, mount.state);
}Pause is accepted before its rootfs checkpoint is fully committed. Use the waiting helpers when the next operation depends on committed lifecycle state:
const paused = await client.sandboxes.pauseAndWait(sandbox.id, {
timeoutMs: 120_000,
signal: abortController.signal,
});
const resumed = await client.sandboxes.resumeAndWait(paused.id);
console.log(resumed.status, resumed.runtimeGeneration);waitForLifecycle() is available for other committed-state conditions. Aborting
a wait stops polling locally; it does not undo a pause or resume already accepted
by Sandbox0.
Capture the current root filesystem of an existing sandbox into a new template:
const template = await client.templates.createFromSandbox(
{
templateId: "python-ready",
sandboxId: sandbox.id,
specOverrides: {
displayName: "Python Ready",
tags: ["python"],
},
},
{
idempotencyKey: "python-ready-v1",
wait: true,
timeoutMs: 10 * 60_000,
},
);
console.log(template.templateId, template.status?.creation?.state);Without wait: true, creation returns as soon as Sandbox0 accepts the request.
The rootfs capture point is status.creation.capturedAt, not request
acceptance, so keep the source sandbox available and avoid rootfs writes while
the stage is capturing. waitUntilReady() can wait for the same template
later. Aborting a wait only stops local polling; it does not cancel image
creation on the server.
getMetrics() returns bounded, chart-ready sandbox metrics. Each series is split
into segments at runtime restarts and collector resets, so chart segments should
not be connected across those boundaries.
import { SandboxRuntimeMetricName } from "sandbox0";
const metrics = await sandbox.getMetrics({
startTime: new Date(Date.now() - 60 * 60 * 1000),
metrics: [
SandboxRuntimeMetricName.SandboxCpuUtilization,
SandboxRuntimeMetricName.SandboxMemoryUtilization,
],
maxPoints: 240,
});
for (const series of metrics.series) {
for (const segment of series.segments) {
console.log(series.metric, segment.points);
}
}Use sandbox.getMetricsCatalog() to discover metric kinds, units, and
dimensions supported by the API.
client.usage.listWindows() incrementally reads immutable, closed usage
windows for the team derived from the SDK token. The API never accepts a team
id from the caller. Retain nextCursor only after durably processing the
returned page.
let cursor: string | undefined;
do {
const page = await client.usage.listWindows({
cursor,
limit: 1000,
windowType: "sandbox.runtime_mib_milliseconds",
});
for (const window of page.windows) {
console.log(
window.sandboxId,
window.windowStart,
window.windowEnd,
window.value,
window.unit,
);
}
cursor = page.nextCursor || undefined;
} while (cursor);The token needs usage:read, and the Sandbox0 region must have metering and
its ClickHouse query projection enabled. GET /api/v1/quotas reports current
operational quota status; it is not a cumulative usage API.
Apache-2.0