Documentation
Snapshots
Sandboxes are persistent by default: stopping one saves its filesystem, and it resumes exactly where it left off.
What it is
When a persistent sandbox stops, Syva captures a snapshot of its filesystem as a compressed block delta against the base image and stores it durably. Resuming boots a fresh session from that snapshot — installed packages, cloned repos, and written files are all there.
Snapshots capture the filesystem, not memory. Running processes do not survive a stop; the sandbox start_command runs again on resume.
Stop and resume
Name a sandbox to make it a durable, reconnectable workspace. get_or_createresumes it when it exists and creates it when it does not.
// Persistent sandboxes (the default) snapshot their filesystem on stop.
const sandbox = await Sandbox.getOrCreate({
name: "agent-workspace",
image: "node-22:base",
});
await sandbox.runCommand("npm", ["install"]);
const stopped = await sandbox.stop();
console.log(stopped.lastSnapshot?.id, stopped.lastSnapshot?.sizeBytes);
// Later — even from another process — resume by name.
const resumed = await Sandbox.getByName("agent-workspace").then((s) => s.start());A stopped sandbox bills no CPU or memory — only snapshot storage. Sandbox TTLs stop persistent sandboxes with a snapshot instead of destroying them, so an expired workspace is never lost. Deleting a sandbox removes its snapshots and frees its name.
Fork
Forking boots a new sandbox from another sandbox's latest snapshot. Prepare an environment once — dependencies installed, repo cloned, caches warm — then fan out identical copies in seconds.
// Fork: boot a new sandbox from another sandbox's latest snapshot.
const base = await Sandbox.getByName("agent-workspace");
const fork = await base.fork({ name: "experiment-1" });
// Or fork many rollouts from one prepared state:
const rollouts = await Promise.all(
[1, 2, 3].map((n) => Sandbox.create({ sourceSandbox: "agent-workspace", name: `rollout-${n}` })),
);Retention and billing
Snapshot storage is billed at $0.08 per GiB-month on the compressed delta — the bytes your sandbox actually changed, not the full disk. Every snapshot is kept until it expires: 30 days after its last use by default, with each resume or fork resetting the timer. Add a keep_last_snapshots policy to bound how many snapshots a sandbox keeps, and tune everything after creation with sandbox.update() — including rolling back to an older snapshot via current_snapshot_id.
// Default: every snapshot is kept until it expires (30 days after last use).
const sandbox = await Sandbox.create({
image: "node-22:base",
name: "tuned-retention",
snapshotExpirationSeconds: 7 * 24 * 3600, // expire 7 days after last use
keepLastSnapshots: { count: 3 }, // bound storage to the 3 newest
});
// Tune later, or roll back to an older snapshot:
await sandbox.update({ keepLastSnapshots: { count: 1 } });
await sandbox.update({ currentSnapshotId: "snap_..." });
// Manual checkpoint with its own expiration (stops the sandbox):
const snapshot = await sandbox.snapshot({ expirationSeconds: 3600 });
// Ephemeral sandboxes skip snapshots entirely:
const scratch = await Sandbox.create({ image: "node-22:base", persistent: false });keep_last_snapshotskeeps the N newest snapshots (1–10). The object form addsexpiration_secondsfor the kept snapshots anddelete_evicted=Falseto let evicted snapshots age out instead of deleting them immediately.snapshot_expiration_secondssets the TTL; 0 keeps snapshots until deleted.sandbox.update(...)changes any of these later, and rolls back viacurrent_snapshot_id.persistent=Falseopts out entirely for one-off workloads.