Skip to content
Sandbox SDK
Esc
↑↓navigate↵open⌘Jpreview
On this page

Superserve

Run Firecracker microVMs on Superserve. Pause and resume, snapshots, egress allow lists, secret bindings, and HTTPS previews.

superserve() runs each sandbox as a Firecracker microVM on Superserve. It calls Superserve’s HTTP API directly, so there is no second package to install.

Install

npm install @opencoredev/sandbox-sdk
pnpm add @opencoredev/sandbox-sdk
yarn add @opencoredev/sandbox-sdk
bun add @opencoredev/sandbox-sdk

Set SUPERSERVE_API_KEY on your server, and keep it out of browser bundles. Commands that take stdin use the runtime’s global WebSocket, which Node 22 and Bun have.

Quickstart

import { createSandbox } from "@opencoredev/sandbox-sdk";
import { superserve } from "@opencoredev/sandbox-sdk/superserve";

await using box = await createSandbox(superserve({ installPython: true }));
const result = await box.run("node --version");
console.log(result.stdout);

The default template, superserve/node-22, has no Python, which the adapter needs. installPython: true installs it on first use. await using deletes the VM when the block ends.

What it supports

Feature Support Notes
Commands and streaming Partial Output arrives when the command finishes
Files Yes mode uses a chmod after the write
Background processes Yes
Preview URLs Yes Private by default; send the returned header
Pause and resume Yes VM state survives
Snapshots Yes Kept until deleted; no expiresInMs
Machine size No Set by the template
Network policy Yes "none" and allow lists of hosts, wildcards, or IPv4 CIDRs
Hard lifetime (timeoutMs) No Use autoPauseMs and autoDeleteMs
Idle timeout (idleTimeoutMs) No
Keys Best-effort Found by metadata lookup
Terminal No

The capability matrix compares these with every other provider.

Run commands

Await box.run for the result. Iterating works too, but output is read from guest disk after the command exits, so chunks arrive together at the end.

import { createSandbox } from "@opencoredev/sandbox-sdk";
import { superserve } from "@opencoredev/sandbox-sdk/superserve";

await using box = await createSandbox(superserve({ installPython: true }));
const result = await box.run("ls -la", { cwd: "/workspace", timeoutMs: 30_000 });
console.log(result.exitCode, result.stdout);

for await (const chunk of box.run("echo one; echo two")) {
  process.stdout.write(chunk.text);
}

Files

Relative paths resolve against /workspace.

import { createSandbox } from "@opencoredev/sandbox-sdk";
import { superserve } from "@opencoredev/sandbox-sdk/superserve";

await using box = await createSandbox(superserve({ installPython: true }));
await box.files.write("notes/todo.txt", "ship the docs\n");
await box.files.write(".env", "TOKEN=abc\n", { mode: 0o600 });
console.log(await box.files.read("notes/todo.txt"));
console.log(await box.files.list("notes"));

Superserve’s file API has no mode, so the SDK runs chmod right after a write that passes mode.

Background processes and previews

No ports to declare. box.preview(port) publishes the port as private and returns the auth header in preview.headers. Send it with every request. With previewAccess: "public", ports are published publicly and preview.headers is absent. Ports must be between 1024 and 65535, except 49983.

import { createSandbox } from "@opencoredev/sandbox-sdk";
import { superserve } from "@opencoredev/sandbox-sdk/superserve";

await using box = await createSandbox(superserve({ installPython: true }));
await box.start("python3 -m http.server 3000", { name: "web", ready: { port: 3000 } });
const preview = await box.preview(3000, { waitMs: 10_000 });
const response = await fetch(preview.url, { headers: preview.headers });
console.log(response.status);

Pause, resume, and snapshots

Pause and resume keep the VM state. snapshot() returns an ID that from accepts.

import { createSandbox } from "@opencoredev/sandbox-sdk";
import { superserve } from "@opencoredev/sandbox-sdk/superserve";

await using box = await createSandbox(superserve({ installPython: true }), { key: "user:123" });
await box.pause();
await box.resume();

const { id } = await box.snapshot();
await using copy = await createSandbox(superserve(), { from: id });

Snapshots outlive their source and have no expiry. Delete one through the API with provider.api.request("DELETE", "/snapshots/{snapshotId}", { params: { snapshotId: id } }). snapshot({ expiresInMs }) with a value above 0 fails with unsupported.

Size, network, and lifetime

Resources come from the template. size, idleTimeoutMs, and timeoutMs fail with unsupported before a VM is allocated.

import { createSandbox, SandboxError } from "@opencoredev/sandbox-sdk";
import { superserve } from "@opencoredev/sandbox-sdk/superserve";

try {
  await createSandbox(superserve(), { size: "large" });
} catch (error) {
  if (error instanceof SandboxError && error.code === "unsupported") {
    console.log("Pick a template with the machine you need.");
  }
}

network maps to Superserve egress rules. "none" denies 0.0.0.0/0. An allow list denies everything else and accepts hostnames, *.example.com wildcards (subdomains only, not the apex), IPv4 addresses, and IPv4 CIDRs. When the list holds a hostname, the adapter also allows Superserve’s DNS resolvers, 1.1.1.1/32 and 8.8.8.8/32, because names can’t resolve otherwise. The adapter reads the rules back after creation and fails with unsupported if they differ.

import { createSandbox } from "@opencoredev/sandbox-sdk";
import { superserve } from "@opencoredev/sandbox-sdk/superserve";

await using box = await createSandbox(superserve({ installPython: true }), {
  network: { allow: ["registry.npmjs.org", "*.github.com"] },
});

Superserve has no hard lifetime or idle timeout. It has an auto-pause window and an auto-delete window, set on the provider:

import { createSandbox } from "@opencoredev/sandbox-sdk";
import { superserve } from "@opencoredev/sandbox-sdk/superserve";

const provider = superserve({
  installPython: true,
  autoPauseMs: 30 * 60_000, // pause after 30 minutes of active time per session
  autoDeleteMs: 24 * 60 * 60_000, // delete after a day spent paused
});
await using box = await createSandbox(provider);

autoPauseMs counts active time per session and re-arms on resume. autoDeleteMs counts from the pause, and 0 deletes the VM as soon as it pauses. Both round up to whole seconds.

Configuration

Option Type Default Env var
apiKey string SUPERSERVE_API_KEY
baseUrl string https://api.superserve.ai SUPERSERVE_BASE_URL
template string "superserve/node-22"
installPython boolean false
requestTimeoutMs number 90_000
autoPauseMs number Never
autoDeleteMs number Never
secretBindings Record<string, string> None
previewAccess "private" | "public" "private"

secretBindings maps env var names to Superserve secrets you created: { OPENAI_API_KEY: "openai-prod" }. The sandbox sees a proxy token in the env var, and Superserve attaches the real credential to outbound requests for the secret’s hosts. These are secret names, not values, unlike createSandbox’s secrets option.

A template needs Git, tar, and nohup, plus Python 3 unless installPython is on. Installing Python needs root and package-repository access and slows the first operation. For fast starts, build a template with Python 3 included; the repo’s benchmark has one in bench/templates/superserve/build.ts.

Call the API directly

box.native calls any Superserve endpoint with the adapter’s credentials, retries, and error mapping. {sandboxId} in a path fills in from the sandbox. Superserve has two planes:

  • "api", the default, is the control plane at https://api.superserve.ai, sent with your API key.
  • "data" is exec and files inside the sandbox. The adapter sends the sandbox’s access token and routing hint, and activates the sandbox again when they expire or the data plane refuses them.

request returns the parsed JSON body. fetch returns the raw Response for bytes and streams. A non-2xx answer rejects with SandboxError from both.

import { createSandbox } from "@opencoredev/sandbox-sdk";
import { superserve } from "@opencoredev/sandbox-sdk/superserve";

await using box = await createSandbox(superserve({ installPython: true }));
const ports = await box.native.request<{ ports: { port: number }[] }>(
  "GET",
  "/sandboxes/{sandboxId}/preview-ports",
);
console.log(ports.ports);

await box.files.write("report.txt", "done\n");
const file = await box.native.fetch("GET", "/files", {
  plane: "data",
  query: { path: "/workspace/report.txt" },
});
console.log(await file.text());

provider.api is the same API with no sandbox bound, for account-level calls on the control plane. The data plane needs a sandbox, so it works only on box.native.

import { superserve } from "@opencoredev/sandbox-sdk/superserve";

const provider = superserve();
const sandboxes = await provider.api.request<{ id: string; status: string }[]>("GET", "/sandboxes");
console.log(sandboxes.length);

To use the official SDK, install @superserve/sdk yourself and pass its import to sdk. On a sandbox you get the SDK’s Sandbox from Sandbox.connect, which resumes the sandbox if it is paused. On provider.api you get the Sandbox class.

import { createSandbox } from "@opencoredev/sandbox-sdk";
import { superserve } from "@opencoredev/sandbox-sdk/superserve";

await using box = await createSandbox(superserve({ installPython: true }));
const sandbox = await box.native.sdk(() => import("@superserve/sdk"));
console.log(await sandbox.getInfo());

If @superserve/sdk isn’t installed, sdk rejects with unavailable.

Limitations

  • Keys are best-effort. Pass a locator for exact get-or-create.
  • No sizes, idle timeouts, hard lifetimes, or terminals.
  • Egress rules cover IPv4 only.
  • Command output isn’t live.
  • Connecting to a paused VM resumes it.

How it works

  • Commands run under a guest supervisor in their own process group. A timeout or abort triggers a separate cancellation check, and an unconfirmed kill returns outcome_unknown.
  • A restore inherits the snapshot’s auto-pause window. The adapter replaces it with autoPauseMs, or clears it when autoPauseMs is unset.
  • Listing uses native metadata and doesn’t wake VMs.
  • A failed snapshot reports outcome_unknown with the snapshot name. List the source’s snapshots before retrying.

Next steps

Was this page helpful?