sandkiln
v0.12.0
Published
Client SDK for sandkiln: hardware-isolated Firecracker microVM sandboxes for running untrusted code.
Downloads
929
Maintainers
Readme
sandkiln
Client SDK for sandkiln — a compute primitive for safely running untrusted or AI-generated code in hardware-isolated Firecracker microVMs. Each sandbox is a real microVM: its own kernel, its own filesystem, its own network.
This package is the client. It talks to a sandkilnd daemon over HTTP —
you need one running somewhere reachable (see the main repo for how to
run one; there is no hosted service).
Install
npm install sandkilnUsage
import { Sandbox } from "sandkiln";
const sandbox = await Sandbox.create({
tags: { env: "ci" },
});
const result = await sandbox.runCommand("python3", ["analyze.py"]);
console.log(result.stdout, result.exitCode);
await sandbox.writeFile("/tmp/config.json", JSON.stringify({ ok: true }));
const bytes = await sandbox.readFile("/tmp/config.json");
const running = await Sandbox.list({ tags: { env: "ci" } });
await sandbox.stop();Configuration
- Daemon URL: pass
baseUrltoSandbox.create()/Sandbox.list(), or setSANDKILN_DAEMON_URL. Defaults tohttp://127.0.0.1:7777. - Auth: pass
authToken, or setSANDKILN_AUTH_TOKEN, if the daemon hasSANDKILN_AUTH_TOKENset. Omit entirely for an unauthenticated local daemon.
API
Sandbox.create(options?)— boots a sandbox.options.nameis a caller-given identity, unique among live sandboxes and held snapshots (409 if already taken) — seebyName/getOrCreatebelow to find it again later.options.tags,options.baseUrl,options.authToken,options.vcpuCount,options.memSizeMib(both override the daemon's configured defaults for this one sandbox, subject to the daemon's configured ceiling),options.imageId(boots from a registered image — seeImage.registerbelow — instead of the daemon's configured default rootfs).Sandbox.attach(id, options?)— wraps an already-existing sandbox id without a network round-trip.Sandbox.byName(name, options?)— resolves a name to a live sandbox and returns a handle to it. Rejects (409) if the name currently belongs to a stopped (snapshotted) sandbox instead — usegetOrCreateif you want that resumed automatically.Sandbox.getOrCreate(options)— resolvesoptions.nameto a sandbox in one race-safe call: a live sandbox with this name is returned as-is, a stopped one is resumed, otherwise a fresh one is created and given this name. Returns{ sandbox, created }.Sandbox.list(options?)— lists sandboxes.options.tagsfilters by exact match on every given key.sandbox.runCommand(command, args?)— runs a command, returns{ stdout, stderr, exitCode }.sandbox.readFile(path)— returns file contents asUint8Array.sandbox.writeFile(path, content)—contentis astringorUint8Array.sandbox.previewUrl(port, options?)— the URL a browser can open directly to reach a server listening onportinside this sandbox, proxied through the daemon.sandbox.stop(options?)— stops the sandbox. By default (options.keepomitted ortrue) this preserves its state as a resumable snapshot, same assnapshot(), and returns{ kept, snapshotId }. Pass{ keep: false }for the old "just destroy it" behavior — no snapshot, nothing left to resume.sandbox.snapshot()— saves the sandbox's full state to disk and stops it; returns a snapshot id. The daemon can also do this on its own, for an idle sandbox, if the operator hasSANDKILN_AUTO_SUSPEND_TIMEOUT_SECSconfigured — seeSandbox.listSnapshotsbelow for how to notice it and find the resulting snapshot.Sandbox.resume(snapshotId, options?)— boots a new sandbox from a snapshot, consuming it (the snapshot is gone afterward).Sandbox.fork(snapshotId, options?)— boots a new sandbox from a snapshot without consuming it, so it can be forked or resumed again later. Only one live fork of a given snapshot may run at a time — a second concurrentfork()rejects with a 409 until the first is stopped; seeROADMAP.md's "Persistence and snapshotting" section for why.Sandbox.listSnapshots(options?)— lists snapshots.options.sourceSandboxIdnarrows this to the (at most one) snapshot taken from that original sandbox id — the way to find out whether a sandbox id that dropped out ofSandbox.list()turned into a snapshot (via a manualsnapshot()or the daemon's auto-suspend) and what its new id is.Image.register(id, path, options?)— registers an already-built ext4 rootfs file atpathon the daemon's own host filesystem underid, forSandbox.create({ imageId })to boot from. Not a file upload — the daemon can't verify the guest agent is baked in without root access to loop-mount it (guestAgentVerifiedis alwaysfalse); runscripts/preflight-check.sh --root-checks --rootfs-image <path>out of band first.Image.list(options?)/Image.delete(id, options?)— list registered images, or delete one (refused with 409 while any live sandbox, in-flight boot, or held snapshot still references it).
Status
Published and real. This SDK matches the daemon's current HTTP API exactly — no more, no less. Remote storage mounts, snapshot history / time-travel restore, and per-sandbox egress policy are supported by the daemon and CLI but not yet exposed here; see the roadmap in the main repository for what's next. A Python equivalent mirrors this exactly.
License
MIT
