Skip to content
Docs

JS SDK Reference

Use the Edgenivo Sandbox JavaScript SDK to create isolated Linux microVMs on demand. Run untrusted code, test full Linux workflows, and manage files, ports, snapshots, drives, sessions, and network policy from Node.js applications.

Install the SDK:

Terminal
pnpm i @vercel/sandbox
Terminal
yarn add @vercel/sandbox
Terminal
npm i @vercel/sandbox
Terminal
bun add @vercel/sandbox

After installation, link your project and pull environment variables with vercel link and vercel env pull so the SDK can read a Edgenivo OpenID Connect (OIDC) token.

ClassWhat it doesExample
SandboxCreates and manages isolated microVM environmentsconst sandbox = await Sandbox.create()
SandboxUserRuns commands and file operations as a Linux userconst user = await sandbox.createUser('alice')
SessionRepresents a single running VM inside a sandboxconst session = sandbox.currentSession()
FileSystemProvides a node:fs/promises-compatible APIawait sandbox.fs.readFile('/tmp/a.txt')
CommandHandles running commands inside the sandboxconst cmd = await sandbox.runCommand('ls')
CommandFinishedContains the result after a command completesAccess cmd.exitCode and cmd.stdout()
NetworkPolicyDefines firewall rules for sandbox trafficSandbox.create({ networkPolicy: 'deny-all' })
SnapshotRepresents a saved sandbox state for fast restartsconst snapshot = await sandbox.snapshot()
DriveRepresents persistent storage mounted to sandboxesconst drive = await Drive.getOrCreate()
// 1. Create a sandbox (persistent by default)
const sandbox = await Sandbox.create({ name: 'my-sandbox' });
 
// 2. Run a command - it waits for completion and returns the result
const result = await sandbox.runCommand('node', ['--version']);
 
// 3. Check the result
console.log(result.exitCode); // 0
console.log(await result.stdout()); // v22.x.x
 
// 4. Later, retrieve the same sandbox by name
const same = await Sandbox.get({ name: 'my-sandbox' });

The Sandbox class gives you full control over isolated Linux microVMs. Use it to create new sandboxes, retrieve existing ones, stream command output, snapshot the filesystem, and shut sessions down once your workflow is complete.

Sandboxes are persistent by default: when a sandbox stops, the SDK snapshots its filesystem and restores it on the next resume. See Persistent sandboxes for the full lifecycle model.

The name accessor returns the sandbox's unique name within the project. Use this string with Sandbox.get() to retrieve the sandbox in a later process. If you didn't pass a name to Sandbox.create(), one is generated for you.

Returns: string.

console.log(sandbox.name);

Whether the sandbox automatically snapshots its filesystem on stop. Defaults to true for sandboxes created with Sandbox.create().

Returns: boolean.

console.log(sandbox.persistent);

The status accessor reads cached metadata for the current session. Repeated property access doesn't poll the API. Retrieve a fresh handle with Sandbox.get({ name, resume: false }) to observe external changes without resuming the sandbox. A status check doesn't coordinate concurrent lifecycle operations; finish stop() or snapshot() before submitting more commands.

Returns: "pending" | "running" | "stopping" | "stopped" | "failed" | "aborted" | "snapshotting".

const fresh = await Sandbox.get({ name: sandbox.name, resume: false });
console.log(fresh.status);

timeout is the sandbox's saved default session timeout, in milliseconds, when available. Change it with sandbox.update({ timeout }). Calling sandbox.extendTimeout() adds to the live session's deadline without changing this saved value. To read the deadline, use expiresAt.

Returns: number | undefined.

console.log(sandbox.timeout);

expiresAt returns the time when the current session will stop automatically. It works with sandbox.extendTimeout() and moves forward when you extend the timeout. For milliseconds remaining, subtract the current time.

Returns: Date | undefined.

const msRemaining = sandbox.expiresAt
  ? sandbox.expiresAt.getTime() - Date.now()
  : undefined;

Key-value tags associated with the sandbox. See Tags.

Returns: Record<string, string> | undefined.

console.log(sandbox.tags);

vcpus returns the number of allocated virtual CPUs. memory returns the allocated memory in MB (2048 MB per vCPU).

Returns: number | undefined.

console.log(sandbox.vcpus, sandbox.memory);

Deprecated: use image for image-based sandboxes.

The runtime image used by the sandbox (for example, "node24").

Returns: string | undefined.

console.log(sandbox.runtime);

The image used by the sandbox (for example, "vercel/sandbox/universal").

Returns: string | undefined.

console.log(sandbox.image);

The region where the sandbox runs. Set with the region parameter of Sandbox.create(). When no region was passed, this is the project's default sandbox region, or iad1 if the project doesn't set one.

Returns: string.

console.log(sandbox.region);

The additional regions this sandbox can fail over to. Returns an empty array when the sandbox has no failover regions.

Returns: string[].

console.log(sandbox.failoverRegions);

The ID of the Secure Compute network assigned to this sandbox. Returns undefined when no network is assigned. An updated ID reflects the saved configuration. Once updated, a running session uses the new network after it stops and resumes.

Returns: string | undefined.

console.log(sandbox.networkId);

Timestamps for when the sandbox was created and last updated.

Returns: Date.

console.log(sandbox.createdAt, sandbox.updatedAt);

The ID of the snapshot the sandbox would resume from. Updated automatically when a persistent sandbox stops.

Returns: string | undefined.

console.log(sandbox.currentSnapshotId);

Default expiration (in milliseconds) applied to snapshots automatically created for this sandbox. 0 means no expiration. If undefined, snapshots expire after 30 days (2,592,000,000 ms).

Returns: number | undefined.

console.log(sandbox.snapshotExpiration);

Retention policy that keeps only the N most recent snapshots of this sandbox. A new snapshot is created whenever a persistent session stops or when you call sandbox.snapshot().

When a new snapshot brings the total above count for a given named sandbox, the oldest snapshots are no longer kept.

The deleteEvicted field controls what happens to them: delete them immediately, or leave them to expire on their own.

Returns: { count: number; expiration?: number; deleteEvicted?: boolean } | undefined. Returns undefined when no retention policy is set.

The policy object has the following fields:

FieldTypeRequiredDetails
countnumberYesNumber of most recent snapshots to keep. Must be an integer from 1 to 10. When a new snapshot brings the total above count, the oldest snapshots are no longer kept.
expirationnumberNoExpiration in milliseconds applied to the kept snapshots. Use 0 for no expiration. Falls back to snapshotExpiration when omitted.
deleteEvictedbooleanNoWhat happens to a snapshot once it is no longer among the count most recent. When true (the default), the snapshot is deleted immediately. When false, it is not deleted and remains until its existing expiration.
console.log(sandbox.keepLastSnapshots);

The amount of CPU used by the current session, in milliseconds. Only populated after the VM stops. Use this to track billable CPU for the latest session.

Returns: number | undefined.

console.log(sandbox.activeCpuUsageMs);

The amount of network data used by the current session, in bytes. Only populated after the VM stops.

Returns: { ingress: number; egress: number } | undefined.

console.log(sandbox.networkTransfer);

Cumulative usage across every session this sandbox has run. Use these for long-running, persistent sandboxes where the most recent session does not represent total usage. Network ingress and egress are usage metrics, not billing totals. See Pricing and Limits to understand which traffic is billable.

Returns: number | undefined.

console.log(sandbox.totalDurationMs, sandbox.totalActiveCpuDurationMs);
console.log(sandbox.totalIngressBytes, sandbox.totalEgressBytes);

The mounts accessor returns the drives mounted on the sandbox, keyed by mount path.

Returns: SandboxMounts.

console.log(sandbox.mounts);

Use Sandbox.list() to enumerate sandboxes for a project, with optional filters and cursor-based pagination. The returned value is async-iterable: iterate it directly to auto-paginate through every page, iterate result.pages() to walk page by page, or call result.toArray() to collect everything.

Omit token, teamId, and projectId to use OIDC or local development authentication. To use an access token, pass all three fields together. projectId alone does not override the OIDC project.

SandboxMetaData items are plain API metadata records, not Sandbox instances. Records identify sandboxes by name and have no instance methods. Timestamps such as createdAt are numbers. Retrieve a handle with Sandbox.get({ name: record.name }).

Returns: Promise<Paginated<{ sandboxes: SandboxMetaData[]; pagination: Pagination; }>>.

ParameterTypeRequiredDetails
namePrefixstringNoFilter sandboxes whose name begins with this prefix. Requires sortBy: "name".
tagsRecord<string, string>NoFilter by a single tag.
sortBy"createdAt" | "name" | "statusUpdatedAt"NoSort field. Defaults to "createdAt".
sortOrder"asc" | "desc"NoSort direction. Defaults to "desc".
limitnumberNoMaximum number of sandboxes per page.
cursorstringNoPagination cursor returned by the previous page.
tokenstringNoAccess token. Requires teamId and projectId when supplied.
teamIdstringNoTeam ID. Requires token and projectId when supplied.
projectIdstringNoProject to list. Requires token and teamId when supplied.
signalAbortSignalNoCancel the request.
const result = await Sandbox.list({
  namePrefix: 'ci-',
  sortBy: 'name',
  tags: { env: 'staging' },
});
 
// Per-item iteration auto-paginates
for await (const sandbox of result) {
  console.log(sandbox.name);
}

To select a project with an access token, pass the complete credential set:

const result = await Sandbox.list({
  token: 'your_access_token_here',
  teamId: 'your_team_id_here',
  projectId: 'your_project_id_here',
});
 
for await (const record of result) {
  const sandbox = await Sandbox.get({
    name: record.name,
    token: 'your_access_token_here',
    teamId: 'your_team_id_here',
    projectId: 'your_project_id_here',
  });
  console.log(sandbox.name);
}

Sandbox.create() launches a new microVM with your chosen image, source, and resource settings. Defaults to an empty workspace when no source is provided. Pass source.depth when cloning large repositories to shorten setup time.

The promise resolves when the sandbox is ready to run commands and access files. Creation time includes startup and, when using a snapshot, the wait to restore it. Restoring a snapshot that isn't cached can take longer.

To compare end-to-end performance, measure from the start of creation through completion of the same first command or file operation.

Sandboxes are persistent by default: when the sandbox stops, the filesystem is automatically snapshotted and restored on the next resume. Pass persistent: false to opt out.

Returns: Promise<Sandbox>.

ParameterTypeRequiredDetails
namestringNoUnique sandbox name within the project. A random name is generated if omitted. Cannot be changed after creation.
sourcegitNoClone a Git repository.
url: string
username: string
password: string
depth?: number
revision?: string
sourcetarballNoMount a tarball.
url: string
sourcesnapshotNoCreate from a snapshot.
snapshotId: string
resources.vcpusnumberNoNumber of vCPUs (2048 MB RAM per vCPU). Defaults to 2.
runtimestringNoDeprecated: use image for image-based sandboxes. Runtime image such as "node26", "node24", "node22", or "python3.13".
imagestringNoVCR image reference, either a Edgenivo Managed Image or custom image. Defaults to "vercel/sandbox/universal".
portsnumber[]NoPorts to expose for sandbox.domain(). Up to 15.
timeoutnumberNoSession timeout in milliseconds. Defaults to 5 minutes.
regionstringNoRegion to create the sandbox in. Defaults to the project's default sandbox region, or iad1.
failoverRegionsstring[]NoAdditional regions the sandbox can fall back to when the main region is unavailable. Must not include the main region. Available on Pro and Enterprise plans, excluding Pro trials.
networkPolicyNetworkPolicyNoFirewall rules for sandbox egress traffic. Defaults to "allow-all".
networkIdstringNoSecure Compute network ID to use from the first session.
envRecord<string, string>NoDefault environment variables for commands run in this sandbox. Per-command runCommand({ env }) values override these defaults.
mountsSandboxMountsNoDrives to attach to the sandbox, keyed by absolute mount path. Pass a Drive for read-write access or drive.snapshot() for a point-in-time, read-only snapshot.
tagsRecord<string, string>NoUp to five key-value tags.
persistentbooleanNoAuto-snapshot the filesystem on stop and restore on resume. Defaults to true.
snapshotExpirationnumberNoDefault snapshot TTL in milliseconds. Defaults to 30 days (2,592,000,000 ms). Use 0 for no expiration.
keepLastSnapshotsobjectNoRetention policy that keeps only the N most recent snapshots. { count: 1-10, expiration?: number, deleteEvicted?: boolean }. See keepLastSnapshots for field details.
onResume(sandbox) => Promise<void>NoFires whenever a session resumes, including after auto-resume. Use to restart background services or rehydrate caches.
signalAbortSignalNoCancel sandbox creation.

For VCR repository setup and push commands, see Getting Started with Edgenivo Container Registry. For how custom images work in Sandbox, see Images.

const sandbox = await Sandbox.create({
  name: 'my-sandbox',
  image: 'vercel/sandbox/node:26',
  networkPolicy: 'deny-all',
  env: { NODE_ENV: 'production' },
  tags: { env: 'staging' },
  snapshotExpiration: 7 * 24 * 60 * 60 * 1000, // 7 days
});

Sandbox.get() retrieves an existing sandbox by name without resuming it by default (resume: false). A stopped persistent sandbox automatically resumes on the first SDK call that needs a running session, such as runCommand. Pass resume: true to resume it before get resolves.

Returns: Promise<Sandbox>.

ParameterTypeRequiredDetails
namestringYesSandbox name to retrieve.
resumebooleanNoWhether to resume the sandbox before get resolves. Defaults to false. A stopped sandbox still resumes automatically on the first SDK call that needs a running session.
onResume(sandbox) => Promise<void>NoFires whenever a session resumes (including after auto-resume).
signalAbortSignalNoCancel the request.
const sandbox = await Sandbox.get({ name: 'my-sandbox' });

Sandbox.getOrCreate() is the recommended pattern for long-lived sandboxes: it retrieves the sandbox if it exists, or creates it if it doesn't. By default (resume: false), an existing sandbox is retrieved without resuming it. A stopped persistent sandbox resumes on the first SDK call that needs a running session, or immediately when you pass resume: true. Use onCreate for initial setup and onResume for setup on later session starts. Creation calls onCreate only, so start services in both callbacks when they must run in every session.

Creation parameters (such as keepLastSnapshots or snapshotExpiration) apply only when getOrCreate creates the sandbox. If a sandbox with the same name already exists, getOrCreate returns it with its existing configuration and ignores the creation parameters you pass. To change the configuration of an existing sandbox, call sandbox.update().

Returns: Promise<Sandbox>.

ParameterTypeRequiredDetails
namestringNoSandbox name. If omitted, behaves like Sandbox.create (always creates, onCreate always fires).
resumebooleanNoWhether to resume the sandbox immediately when an existing one is retrieved. Defaults to false. When true, the sandbox is resumed before getOrCreate resolves, and onResume is awaited before it resolves.
onCreate(sandbox) => Promise<void>NoRuns once when the sandbox is freshly created. Awaited before getOrCreate resolves.
onResume(sandbox) => Promise<void>NoRuns every time the session resumes (including after auto-resume). By default, getOrCreate retrieves the sandbox but does not resume it, so onResume is not awaited before it resolves; the session resumes on the first SDK call (such as runCommand), and onResume runs at that point. Pass resume: true to resume immediately and have onResume awaited before getOrCreate resolves.
...all Sandbox.create paramsNoAny creation parameter is accepted and used only when the sandbox is created. Ignored when an existing sandbox is retrieved.

Behavior:

  • If a sandbox with that name exists, getOrCreate retrieves it without resuming it by default. The sandbox resumes on the first SDK call (such as runCommand), and onResume fires at that point, not before getOrCreate resolves. Pass resume: true to resume immediately and have onResume awaited before getOrCreate resolves.
  • If a sandbox with that name exists, its configuration is not updated. Creation parameters passed to getOrCreate are ignored; use sandbox.update() to change the configuration of an existing sandbox.
  • If no sandbox exists, a fresh sandbox is created with the parameters you pass, and onCreate is awaited before getOrCreate resolves.
  • If the sandbox exists but its snapshot expired, the stale sandbox is deleted, re-created with the same name, and onCreate fires.
async function startServer(sbx: Sandbox) {
  await sbx.runCommand({ cmd: 'npm', args: ['run', 'dev'], detached: true });
}
 
const sandbox = await Sandbox.getOrCreate({
  name: 'my-sandbox',
  onCreate: async (sbx) => {
    await sbx.runCommand('git', ['clone', repoUrl, '.']);
    await sbx.runCommand('npm', ['install']);
    await startServer(sbx);
  },
  onResume: startServer,
});

Both callbacks start the server in detached mode, while installation runs only on creation. Detached startup returns before the server is ready. Wait for your application's readiness check before forwarding requests.

Sandbox.fork() creates a new sandbox seeded from the current snapshot of an existing one. The new sandbox inherits the source's config, including its environment variables. Any field you pass in overrides the copied value. If the source has no current snapshot, the fork falls back to a fresh create with the source's image plus the copied config.

Pass env to override the copied environment variables. image is not accepted as an override. When the source has a snapshot, the image is inherited from it; otherwise it is copied from the source sandbox.

The fork runs in the source sandbox's region unless you pass region. The fork also inherits the source's failover regions; pass failoverRegions to replace them. If the source has a snapshot, that snapshot must be available in the target region.

The fork also inherits the source sandbox's Secure Compute network unless you pass networkId.

Returns: Promise<Sandbox>.

ParameterTypeRequiredDetails
sourceSandboxstringYesName of the sandbox to fork from.
...most Sandbox.create paramsNoAny field set here overrides the value copied from the source. source and image are not accepted.
// Inherit every supported field from the source
const fork = await Sandbox.fork({ sourceSandbox: 'prod-agent' });
 
// Override specific fields; the rest are copied from the source
const customized = await Sandbox.fork({
  sourceSandbox: 'prod-agent',
  name: 'forked-prod-agent',
  resources: { vcpus: 4 },
  env: { OPENAI_API_KEY: process.env.OPENAI_API_KEY! },
});

Call sandbox.getCommand() to retrieve a command by its ID from the current session, for example, after a detached execution. This parent method can resume the sandbox. Command IDs belong to their original session and do not transfer to a new VM. Use session.getCommand() to keep lookup on one session.

Returns: Promise<Command>.

ParameterTypeRequiredDetails
cmdIdstringYesIdentifier of the command to fetch.
opts.signalAbortSignalNoCancel the lookup if it takes too long.
const command = await sandbox.getCommand(cmdId);

sandbox.runCommand() executes commands inside the microVM, either blocking until completion or returning immediately in detached mode. Use detached: true for long-running servers, stream output to local log handlers, and call command.wait() later for results.

Returns: Promise<CommandFinished> when detached is false; Promise<Command> when detached is true.

ParameterTypeRequiredDetails
commandstringYesCommand to execute (string overload).
argsstring[]NoArguments for the string overload.
opts.signalAbortSignalNoCancel the command (string overload).
opts.timeoutMsnumberNoMaximum command runtime in milliseconds (string overload).
params.cmdstringYesCommand to execute when using the object overload.
params.argsstring[]NoArguments for the object overload.
params.cwdstringNoWorking directory for execution.
params.envRecord<string, string>NoAdditional environment variables.
params.sudobooleanNoRun the command with sudo.
params.detachedbooleanNoReturn immediately with a live Command object.
params.stdoutWritableNoStream standard output to a writable.
params.stderrWritableNoStream standard error to a writable.
params.stdinboolean | ReadableNoKeep stdin open, or pipe a readable to it.
params.signalAbortSignalNoCancel the command when using the object overload.
params.timeoutMsnumberNoMaximum command runtime in milliseconds, including detached commands.
params.onStart(command: Command) => void | Promise<void>NoAwaited start callback. Requires detached to be omitted or false.

timeoutMs is enforced by the sandbox when execution starts. Expiry sends SIGKILL, even with detached: true. onStart receives the command as soon as it starts and is awaited before runCommand() resolves; use it only for non-detached commands:

const command = await sandbox.runCommand({
  cmd: 'sleep',
  args: ['60'],
  detached: true,
  timeoutMs: 5_000,
});
await command.wait();

The detached command is killed after five seconds unless it exits earlier. To observe a non-detached command starting, pass onStart: (command) => { console.log(command.cmdId); } instead of detached: true. SandboxUser handles accept these same controls.

Set stdin: true with detached: true to write to the command with writeStdin(), or pass a Readable such as process.stdin to pipe it to the command, with or without detached. Without stdin, the command reads from an empty stdin.

const result = await sandbox.runCommand('node', ['--version']);

sandbox.openInteractive() opens interactive access, resuming the sandbox if needed. It returns a WebSocket URL and token for connecting your WebSocket client to a controller-hosted PTY:

const { url, token } = await sandbox.openInteractive();
ParameterTypeRequiredDetails
opts.signalAbortSignalNoCancel opening interactive access.

Returns: Promise<{ url: string; token: string }>.

Treat the token as a credential and close your WebSocket connection when finished. Use session.openInteractive() when access must stay on one exact VM.

sandbox.mkDir() creates a directory in the sandbox filesystem before you write files or clone repositories. Paths are relative to sandbox.cwd unless you provide an absolute path. Call this before writeFiles() when your target directory does not exist yet.

await sandbox.mkDir('assets');
ParameterTypeRequiredDetails
pathstringYesDirectory to create.
opts.signalAbortSignalNoCancel the operation.

Returns: Promise<void>.

Use sandbox.readFile() to pull file contents from the sandbox to a Node.js readable stream. The promise resolves to null when the file does not exist. You can use sandbox.readFileToBuffer() directly if you prefer receiving a Buffer.

const stream = await sandbox.readFile({ path: 'package.json' });
if (stream) {
  stream.pipe(process.stdout);
}
ParameterTypeRequiredDetails
file.pathstringYesPath to the file inside the sandbox.
file.cwdstringNoBase directory for resolving file.path.
opts.signalAbortSignalNoCancel the read operation.

Returns: Promise<NodeJS.ReadableStream | null>.

Use sandbox.readFileToBuffer() to pull entire file contents from the sandbox to an in-memory buffer. The promise resolves to null when the file does not exist.

const buffer = await sandbox.readFileToBuffer({ path: 'package.json' });
ParameterTypeRequiredDetails
file.pathstringYesPath to the file inside the sandbox.
file.cwdstringNoBase directory for resolving file.path.
opts.signalAbortSignalNoCancel the read operation.

Returns: Promise<null | Buffer>.

Use sandbox.downloadFile() to pull file contents from the sandbox to a local destination. The promise resolves to the absolute destination path or null when the source file does not exist.

const dstPath = await sandbox.downloadFile(
  { path: 'package.json', cwd: sandbox.cwd },
  { path: 'local-package.json', cwd: '/tmp' }
);
ParameterTypeRequiredDetails
src.pathstringYesPath to the file inside the sandbox.
src.cwdstringNoBase directory for resolving src.path.
dst.pathstringYesPath to local destination.
dst.cwdstringNoBase directory for resolving dst.path.
opts.signalAbortSignalNoCancel the download operation.
opts.mkdirRecursivebooleanNoCreate destination directories recursively if they do not exist.

Returns: Promise<null | string>.

sandbox.writeFiles() uploads one or more files into the sandbox filesystem. Relative paths resolve from sandbox.cwd; use absolute paths for custom locations and bundle related files into a single call to reduce round trips.

await sandbox.writeFiles([{ path: 'hello.txt', content: Buffer.from('hi') }]);

You can set Unix file permissions using the optional mode property. For example, use 0o755 for executable scripts:

await sandbox.writeFiles([
  { path: 'run.sh', content: Buffer.from('#!/bin/bash\necho "Hello"'), mode: 0o755 },
]);
ParameterTypeRequiredDetails
files{ path: string; content: string | Uint8Array; mode?: number; }[]YesFile descriptors to write.
files.pathstringYesPath to the file inside the sandbox.
files.contentstring | Uint8ArrayYesFile contents as a string or byte array, including Buffer.
files.modenumberNoUnix file permissions in octal (for example, 0o644 for read/write, 0o755 for executable).
opts.signalAbortSignalNoCancel the write operation.

Returns: Promise<void>.

sandbox.domain() resolves a publicly accessible URL for a port you exposed during creation. It throws if the port is not registered to a route, so include the port in the ports array when creating the sandbox and cache the returned URL so you can share it quickly with collaborators.

const previewUrl = sandbox.domain(3000);
ParameterTypeRequiredDetails
pnumberYesPort number declared in ports.

Returns: string.

Returns the current Session for the sandbox. A session represents a single running VM instance. The SDK creates and resumes sessions for you; usually you don't need to interact with sessions directly.

const session = sandbox.currentSession();
console.log(session.sessionId, session.status);

Returns: Session.

sandbox.stop() resolves once the VM is fully stopped, and returns the final session state. For persistent sandboxes, the resolved value also includes metadata for the snapshot captured during shutdown. It's safe to call multiple times.

const result = await sandbox.stop();
console.log(result.snapshot?.id);
console.log(result.activeCpuDurationMs);
console.log(result.networkTransfer); // { ingress, egress }
ParameterTypeRequiredDetails
opts.signalAbortSignalNoCancel the stop operation.

Returns: Promise<{ snapshot?: { id: string; status: "created" | "deleted" | "failed"; sizeBytes: number; createdAt: number; expiresAt?: number; parentId?: string }; activeCpuDurationMs: number; networkTransfer: { ingress: number; egress: number } }>.

sandbox.update() changes saved sandbox configuration. Resource, mount, region, and Secure Compute network changes apply to later sessions; they don't resize or move the running VM. ports replaces the full exposed port list and updates the current session's routes. networkPolicy applies to the active session and future sessions.

timeout sets the default for future sessions. If it exceeds the running session's timeout, the SDK extends the live deadline by the difference. Lowering the saved timeout doesn't shorten the live deadline. Use sandbox.extendTimeout() to add time without changing the saved default.

Updates aren't atomic. The SDK first attempts a positive live timeout extension, then applies a live network policy, then submits configuration and tags. The session-policy request can also persist the policy for future sessions when it targets the sandbox's current session. A later failure doesn't roll back earlier live changes or saved-policy changes. If the call rejects, retrieve fresh metadata and inspect the active session before retrying; don't assume nothing changed.

await sandbox.update({
  resources: { vcpus: 4 },
  timeout: 30 * 60 * 1000,
  networkPolicy: 'deny-all',
  ports: [3000, 8000],
  tags: { env: 'prod' },
  persistent: false,
  snapshotExpiration: 14 * 24 * 60 * 60 * 1000,
  keepLastSnapshots: { count: 1 },
  currentSnapshotId: 'snap_xyz', // Roll back to a previous snapshot
  mounts: {
    '/data': { drive: 'workspace-cache', mode: 'read-write' },
  }, // Replaces the mounts; pass {} to remove them
});
ParameterTypeRequiredDetails
resources.vcpusnumberNoNew vCPU count for future sessions (memory auto-scales to 2048 MB per vCPU).
timeoutnumberNoSaved session timeout in milliseconds. A higher value can extend the running deadline; a lower value applies only to future sessions.
networkPolicyNetworkPolicyNoReplace the firewall policy on the current and future sessions.
networkIdstring | nullNoAssign a Secure Compute network by ID, or pass null to remove it. The running session keeps its current network until it stops and resumes.
portsnumber[]NoReplace the exposed ports. Ports not present in the array are deregistered.
tagsRecord<string, string>NoReplace the tag set. Pass {} to clear.
persistentbooleanNoEnable or disable auto-snapshotting on stop.
snapshotExpirationnumberNoNew default snapshot TTL in milliseconds. Use 0 for no expiration.
keepLastSnapshotsobject | nullNoRetention policy that keeps only the N most recent snapshots. Pass null to clear. See keepLastSnapshots for field details.
currentSnapshotIdstringNoPoint the sandbox at a different snapshot. New sessions resume from it.
regionstringNoRegion for the next session. The running session keeps its current region.
failoverRegionsstring[]NoReplace the failover regions. Must not include the sandbox's main region. Pass [] to remove them. Applies to the next session; the running session keeps the region it started in. Available on Pro and Enterprise plans, excluding Pro trials.
mountsSandboxMountsNoReplace the drives mounted in the next session. Pass a Drive for read-write access or drive.snapshot() for a point-in-time, read-only snapshot. Pass {} to remove all mounts.
opts.signalAbortSignalNoCancel the operation.

Returns: Promise<void>.

Permanently delete the sandbox and all of its sessions. By default, snapshots stay available until they expire or you delete them with snapshot.delete(). Pass deleteOrphanSnapshots: true to also request asynchronous deletion of this sandbox's snapshots that no other sandbox uses. Snapshots referenced by another sandbox remain available.

Discard the handle after deletion.

await sandbox.delete({ deleteOrphanSnapshots: true });
ParameterTypeRequiredDetails
opts.deleteOrphanSnapshotsbooleanNoAsynchronously delete snapshots no other sandbox uses. Defaults to false.
opts.signalAbortSignalNoCancel the operation.

Returns: Promise<void>.

List the VM sessions that have been created for this sandbox. Returns an async-iterable that auto-paginates.

const sessions = await sandbox.listSessions();
for await (const session of sessions) {
  console.log(session.id, session.status);
}
ParameterTypeRequiredDetails
limitnumberNoMaximum number of sessions per page.
cursorstringNoPagination cursor.
sortOrder"asc" | "desc"NoSort direction. Defaults to "desc".
signalAbortSignalNoCancel the request.

SessionMetaData items are plain API metadata records, not Session instances. Their identifier is id, and timestamps such as createdAt are numbers. Use sandbox.currentSession() for the current session handle.

Returns: Promise<Paginated<{ sessions: SessionMetaData[]; pagination: Pagination; }>>.

List the snapshots that belong to this sandbox. Returns an async-iterable that auto-paginates.

const snapshots = await sandbox.listSnapshots();
for await (const snapshot of snapshots) {
  console.log(snapshot.id, snapshot.status);
}
ParameterTypeRequiredDetails
limitnumberNoMaximum number of snapshots per page.
cursorstringNoPagination cursor.
sortOrder"asc" | "desc"NoSort direction. Defaults to "desc".
signalAbortSignalNoCancel the request.

SnapshotMetadata items are plain API metadata records, not Snapshot instances. Their identifier is id, and timestamps such as createdAt are numbers. Retrieve a handle with Snapshot.get({ snapshotId: record.id }).

Returns: Promise<Paginated<{ snapshots: SnapshotMetadata[]; pagination: Pagination; }>>.

sandbox.updateNetworkPolicy() is deprecated. Use sandbox.update({ networkPolicy }) instead.

This deprecated method targets the current session's policy endpoint rather than the sandbox configuration endpoint and can auto-resume the sandbox. The API also saves the policy for future sessions when the target remains the named sandbox's current session. The sandbox handle's cached policy can remain stale. Use sandbox.update({ networkPolicy }) to update the live policy and saved configuration, or session.update({ networkPolicy }) to target a pinned session. See live updates.

await sandbox.updateNetworkPolicy('allow-all'); // Allow all egress from the sandbox
 
await sandbox.updateNetworkPolicy('deny-all'); // Block all egress from the sandbox
 
await sandbox.updateNetworkPolicy({allow: ["google.com", "ai-gateway.vercel.sh"]}); // Allow traffic to specific websites only
 
// Allow traffic to specific websites and private network
await sandbox.updateNetworkPolicy({
  allow: ["google.com", "ai-gateway.vercel.sh"],
  subnets: {
    allow: ["10.0.0.0/8"],
  },
});
 
// Allow traffic to the Internet while blocking the specified private range
await sandbox.updateNetworkPolicy({
  allow: ["*"],
  subnets: {
    deny: ["10.0.0.0/8"],
  },
});
 
// Allow traffic to a specific website with credential brokering
await sandbox.updateNetworkPolicy({
  allow: {
    "ai-gateway.vercel.sh": [{
      transform: [{
        headers: {
          "x-api-key": "secret-key"
        }
      }]
    }]
  }
});
ParameterTypeRequiredDetails
networkPolicyNetworkPolicyYesReplace the current session's entire firewall policy.
opts.signalAbortSignalNoCancel the operation.

Returns: Promise<NetworkPolicy>, containing the updated session's incomplete policy readback.

Use sandbox.extendTimeout() to add the specified duration to the live session's deadline, up to the maximum execution timeout for your plan. Check sandbox.expiresAt to estimate the remaining time. The saved sandbox.timeout default stays unchanged.

This parent-handle method can automatically resume a stopped sandbox before extending its new session. To extend only an already-running session, use sandbox.currentSession().extendTimeout() or sandbox extend.

await sandbox.extendTimeout(60000); // Extend by 60 seconds
ParameterTypeRequiredDetails
durationnumberYesDuration in milliseconds to extend the timeout by.
opts.signalAbortSignalNoCancel the operation.

Returns: Promise<void>.

Call sandbox.snapshot() to capture the current state of the sandbox, including the filesystem and installed packages. Use snapshots to skip lengthy setup steps when creating new sandboxes. To learn more, see Snapshots.

sandbox.snapshot() resumes the sandbox if necessary, then snapshots and stops that session. The stopped VM's processes terminate, and its Session and command handles remain tied to that session. You don't need to call stop() afterwards.

The parent Sandbox handle remains usable. A later operation such as sandbox.runCommand() can automatically resume a new session, which can incur additional usage charges. Resuming doesn't restart old processes or reactivate handles from the previous session.

Snapshots expire 30 days after their last use by default. Set expiration to 0 to disable expiration, or choose a custom duration in milliseconds (e.g., ms('14d')) to fit your workflow.

index.ts
const snapshot = await sandbox.snapshot({ expiration: ms('14d') });
console.log(snapshot.snapshotId);
 
// Later, create a new sandbox from the snapshot
const newSandbox = await Sandbox.create({
  source: { type: 'snapshot', snapshotId: snapshot.snapshotId },
});
ParameterTypeRequiredDetails
opts.expirationnumberNoOptional expiration time in milliseconds, measured from the snapshot's last use. Use 0 for no expiration at all.
opts.signalAbortSignalNoCancel the operation.

Returns: Promise<Snapshot>.

sandbox.createUser() adds a Linux user with an isolated home directory at /home/<username>. The user gets /bin/bash as their login shell, and their home directory is private to other users but readable by the SDK. Use this to give each agent in a multi-agent workflow its own workspace.

const alice = await sandbox.createUser('alice');
console.log(alice.username, alice.homeDir); // "alice" "/home/alice"
ParameterTypeRequiredDetails
usernamestringYesLinux username. Must match /^[a-z_][a-z0-9_-]*$/ and be at most 32 characters.
opts.signalAbortSignalNoCancel the operation.

Returns: Promise<SandboxUser>.

sandbox.asUser() returns a SandboxUser handle for a user that already exists, without creating it. Use it for users restored from a snapshot or created outside the SDK, such as root.

const bob = sandbox.asUser('bob');
ParameterTypeRequiredDetails
usernamestringYesLinux username. Must match /^[a-z_][a-z0-9_-]*$/ and be at most 32 characters.

Returns: SandboxUser.

sandbox.createGroup() creates a Linux group with a shared directory at /shared/<groupname>. The directory uses the setgid bit, so files created inside it inherit the group, and every member can read one another's files and add their own. Add members with sandbox.addUserToGroup().

const devs = await sandbox.createGroup('devs');
console.log(devs.sharedDir); // "/shared/devs"
ParameterTypeRequiredDetails
groupnamestringYesGroup name. Must match /^[a-z_][a-z0-9_-]*$/ and be at most 32 characters.
opts.signalAbortSignalNoCancel the operation.

Returns: Promise<{ groupname: string; sharedDir: string }>.

sandbox.addUserToGroup() adds a user to a group. Once added, the user can access the group's shared directory at /shared/<groupname>, where every member can read one another's files and add their own.

await sandbox.addUserToGroup('alice', 'devs');
ParameterTypeRequiredDetails
usernamestringYesThe user to add.
groupnamestringYesThe group to add the user to.
opts.signalAbortSignalNoCancel the operation.

Returns: Promise<void>.

sandbox.removeUserFromGroup() removes a user from a group, revoking their access to the group's shared directory. Revocation applies to new commands immediately. Processes already running as that user keep the group until they exit.

await sandbox.removeUserFromGroup('alice', 'devs');
ParameterTypeRequiredDetails
usernamestringYesThe user to remove.
groupnamestringYesThe group to remove the user from.
opts.signalAbortSignalNoCancel the operation.

Returns: Promise<void>.

A SandboxUser runs commands and file operations as a specific Linux user. Commands execute as that user, and file methods resolve relative paths against the user's home directory. Get an instance from sandbox.createUser() or sandbox.asUser(). See Multi-agent sandboxes for a task-oriented guide.

The sandbox image must include /bin/bash for user execution.

The Linux username this instance runs as.

Returns: string.

The user's home directory. This is /home/<username> for users created with createUser(), and /root for the root user.

Returns: string.

const alice = await sandbox.createUser('alice');
console.log(alice.homeDir); // "/home/alice"

user.runCommand() runs a command as this user. It accepts the same arguments as sandbox.runCommand(), with two differences: the working directory defaults to the user's home directory, and passing sudo: true escalates that single command to root.

Returns: Promise<CommandFinished> when detached is false; Promise<Command> when detached is true.

ParameterTypeRequiredDetails
commandstringYesCommand to execute (string overload).
argsstring[]NoArguments for the string overload.
opts.signalAbortSignalNoCancel the command (string overload).
opts.timeoutMsnumberNoMaximum command runtime in milliseconds (string overload).
params.cmdstringYesCommand to execute when using the object overload.
params.argsstring[]NoArguments for the object overload.
params.cwdstringNoWorking directory. Defaults to the user's home directory.
params.envRecord<string, string>NoAdditional environment variables.
params.sudobooleanNoRun the command as root instead of this user.
params.detachedbooleanNoReturn immediately with a live Command object.
params.stdoutWritableNoStream standard output to a writable.
params.stderrWritableNoStream standard error to a writable.
params.stdinboolean | ReadableNoKeep stdin open, or pipe a readable to it.
params.signalAbortSignalNoCancel the command when using the object overload.
params.timeoutMsnumberNoMaximum command runtime in milliseconds, including detached commands.
params.onStart(command: Command) => void | Promise<void>NoAwaited start callback. Requires detached to be omitted or false.
const whoami = await alice.runCommand('whoami');
console.log(await whoami.stdout()); // "alice\n"
 
// Object form with environment variables and a custom working directory
await alice.runCommand({
  cmd: 'node',
  args: ['index.js'],
  env: { API_KEY: 'your_api_key_here' },
  cwd: '/tmp',
});

user.writeFiles() writes files as this user. Relative paths resolve to the user's home directory, and the written files are owned by the user.

await alice.writeFiles([
  { path: 'app.js', content: Buffer.from('console.log("hi")') },
]);
ParameterTypeRequiredDetails
files{ path: string; content: string | Uint8Array; mode?: number; }[]YesFile descriptors to write. Relative paths resolve to the home directory.
opts.signalAbortSignalNoCancel the operation.

Returns: Promise<void>.

user.readFile() reads a file as this user and returns a stream, or null if the file does not exist. Relative paths resolve to the user's home directory.

const stream = await alice.readFile({ path: 'app.js' });
// stream is a Node.js ReadableStream, or null if the file does not exist.
// For a Buffer instead of a stream, use readFileToBuffer().
ParameterTypeRequiredDetails
file{ path: string; cwd?: string }YesFile to read. A relative path resolves to the home directory.
opts.signalAbortSignalNoCancel the operation.

Returns: Promise<NodeJS.ReadableStream | null>.

user.readFileToBuffer() reads a file as this user and returns a Buffer, or null if the file does not exist. Relative paths resolve to the user's home directory.

const buf = await alice.readFileToBuffer({ path: 'app.js' });
console.log(buf?.toString());
ParameterTypeRequiredDetails
file{ path: string; cwd?: string }YesFile to read. A relative path resolves to the home directory.
opts.signalAbortSignalNoCancel the operation.

Returns: Promise<Buffer | null>.

user.downloadFile() reads a file as this user and writes it to the local filesystem, returning the absolute local path, or null if the source file does not exist.

await alice.downloadFile({ path: 'app.js' }, { path: './app.js' });
ParameterTypeRequiredDetails
src{ path: string; cwd?: string }YesSource file in the sandbox. A relative path resolves to the home directory.
dst{ path: string; cwd?: string }YesDestination on the local machine.
opts.mkdirRecursivebooleanNoCreate parent directories on the local machine if they are missing.
opts.signalAbortSignalNoCancel the operation.

Returns: Promise<string | null>.

user.mkDir() creates a directory owned by this user. Relative paths resolve to the user's home directory.

await alice.mkDir('projects/my-app');
ParameterTypeRequiredDetails
pathstringYesDirectory to create. A relative path resolves to the home directory.
opts.signalAbortSignalNoCancel the operation.

Returns: Promise<void>.

user.addToGroup() adds this user to a group. It is a convenience wrapper around sandbox.addUserToGroup().

await alice.addToGroup('devs');
ParameterTypeRequiredDetails
groupnamestringYesThe group to join.
opts.signalAbortSignalNoCancel the operation.

Returns: Promise<void>.

user.removeFromGroup() removes this user from a group. It is a convenience wrapper around sandbox.removeUserFromGroup().

await alice.removeFromGroup('devs');
ParameterTypeRequiredDetails
groupnamestringYesThe group to leave.
opts.signalAbortSignalNoCancel the operation.

Returns: Promise<void>.

A Session represents a single running VM instance inside a sandbox. Sessions are created and resumed for you whenever the sandbox transitions from stopped to running. Use sandbox.currentSession() to inspect the active one, or sandbox.listSessions() to enumerate every session the sandbox has run.

A Session stays pinned to one VM. Its methods don't automatically resume a stopped sandbox or switch to a newer session. Use the parent Sandbox methods when automatic resume is intended. Historical handles can expose metadata, but stopping the VM ends its running processes and interactive connections.

MethodParameters and behaviorReturns
getCommand(cmdId, opts?)Look up a command in this session. IDs from another session don't transfer.Promise<Command>
runCommand(...)Same overloads and controls as sandbox.runCommand(), on this VM.Promise<CommandFinished> or Promise<Command>
mkDir(path, opts?)Same arguments as sandbox.mkDir().Promise<void>
readFile(file, opts?)Same arguments as sandbox.readFile().Promise<NodeJS.ReadableStream | null>
readFileToBuffer(file, opts?)Same arguments as sandbox.readFileToBuffer().Promise<Buffer | null>
downloadFile(src, dst, opts?)Same arguments as sandbox.downloadFile().Promise<string | null>
writeFiles(files, opts?)Same arguments as sandbox.writeFiles().Promise<void>
domain(port)Resolve this session's route for a port.string
openInteractive(opts?)Same connection details as sandbox.openInteractive(), on this VM.Promise<{ url: string; token: string }>
stop(opts?)Stop this session.Promise<{ session: SandboxSnapshot; sandbox?: SandboxMetaData; snapshot?: SnapshotMetadata }>
update({ networkPolicy }, opts?)Update this session's network policy. Accepts no parent sandbox settings such as timeout.Promise<void>
extendTimeout(duration, opts?)Extend this running session by a duration in milliseconds.Promise<void>
snapshot(opts?)Snapshot this session. Accepts expiration in milliseconds (0 for no expiration) and signal. Stops this VM.Promise<Snapshot>

opts.signal cancels operations that accept an options object. domain() is synchronous and takes only a port. snapshot() takes its options directly.

Keep a session handle when command lookup must target the original VM:

const session = sandbox.currentSession();
const command = await session.runCommand({ cmd: 'sleep', args: ['30'], detached: true });
const sameCommand = await session.getCommand(command.cmdId);
await sameCommand.wait();

A later parent resume creates a different session. It doesn't reactivate old process handles or move their command IDs to the new VM.

Read-only accessors on a Session instance returned by sandbox.currentSession(). Iterating sandbox.listSessions() yields plain API metadata records, not Session instances. Records use id instead of sessionId and numeric timestamps instead of Date accessors.

AccessorReturnsDescription
sessionIdstringUnique identifier of the session.
status"pending" | "running" | "stopping" | "stopped" | "failed" | "aborted" | "snapshotting"Cached lifecycle state. Repeated reads don't poll the API.
createdAtDateWhen the session started.
activeCpuUsageMsnumber | undefinedCPU used during the session, in milliseconds. Available once the session is stopped.
networkTransfer{ ingress: number; egress: number } | undefinedNetwork traffic during the session. Available once the session is stopped.
routesSandboxRouteData[]Routes associated with this session.
timeoutnumberThis session's timeout in milliseconds, separate from the parent sandbox's saved default.
interactivePortnumber | undefinedInteractive controller port when available.
networkPolicyNetworkPolicy | undefinedSession network policy.
sourceSnapshotIdstring | undefinedSnapshot used to start this VM.
memory, vcpusnumberMemory in MB and vCPU count, respectively.
regionstringVM region.
cwdstringSession working directory.
requestedAt, updatedAtDateRequest and metadata update times.
startedAt, requestedStopAt, stoppedAt, abortedAt, snapshottedAtDate | undefinedLifecycle timestamps when available.
durationnumber | undefinedWall-clock duration in milliseconds.
runtimestring | undefinedDeprecated runtime identifier. Use parent sandbox image metadata.
const session = sandbox.currentSession();
console.log(session.sessionId, session.status);

To observe external changes, retrieve a fresh parent handle with Sandbox.get({ name: sandbox.name, resume: false }) and inspect its current session. The old Session handle continues to describe its original VM; a later resume creates a different session. Status reads don't synchronize concurrent stop, snapshot, or command operations.

FileSystem gives you a node:fs/promises-compatible surface for sandbox files and directories. Use it through sandbox.fs to keep code portable when you already have utilities that expect familiar Node.js filesystem methods.

const sandbox = await Sandbox.create();
 
await sandbox.fs.writeFile('/tmp/hello.txt', 'hello from sandbox');
const text = await sandbox.fs.readFile('/tmp/hello.txt', 'utf8');
console.log(text);

FileSystem currently implements a focused subset of node:fs/promises for sandbox workflows. APIs such as file handles and watchers are not currently included.

All methods support AbortSignal through an options object. Methods throw Node.js-style filesystem errors with fields such as code, syscall, and path.

MethodSignature summaryReturns
sandbox.fs.readFile()path, optional encoding ('utf8' or { encoding, signal })Promise<Buffer | string>
sandbox.fs.writeFile()path, string | Buffer | Uint8Array, optional { encoding, signal }Promise<void>
sandbox.fs.appendFile()path, string | Buffer | Uint8Array, optional { encoding, signal }Promise<void>
await sandbox.fs.writeFile('/tmp/config.json', JSON.stringify({ mode: 'test' }));
const config = await sandbox.fs.readFile('/tmp/config.json', 'utf8');
await sandbox.fs.appendFile('/tmp/config.json', '\n');
MethodSignature summaryReturns
sandbox.fs.mkdir()path, optional { recursive, signal }Promise<string | undefined>
sandbox.fs.readdir()path, optional { withFileTypes, signal }Promise<string[] | Dirent[]>
sandbox.fs.stat()path, optional { signal }Promise<Stats>
sandbox.fs.lstat()path, optional { signal }Promise<Stats>
sandbox.fs.realpath()path, optional { signal }Promise<string>
sandbox.fs.mkdtemp()prefix, optional { signal }Promise<string>
await sandbox.fs.mkdir('/tmp/results', { recursive: true });
 
const entries = await sandbox.fs.readdir('/tmp', { withFileTypes: true });
for (const entry of entries) {
  if (entry.isDirectory()) console.log(`dir: ${entry.name}`);
}
 
const stats = await sandbox.fs.stat('/tmp/results');
console.log(stats.isDirectory()); // true
MethodSignature summaryReturns
sandbox.fs.unlink()path, optional { signal }Promise<void>
sandbox.fs.rm()path, optional { recursive, force, signal }Promise<void>
sandbox.fs.rmdir()path, optional { signal }Promise<void>
sandbox.fs.rename()oldPath, newPath, optional { signal }Promise<void>
sandbox.fs.copyFile()src, dest, optional { signal }Promise<void>
sandbox.fs.truncate()path, optional len, optional { signal }Promise<void>
await sandbox.fs.copyFile('/tmp/input.txt', '/tmp/output.txt');
await sandbox.fs.rename('/tmp/output.txt', '/tmp/final.txt');
await sandbox.fs.truncate('/tmp/final.txt', 1024);
await sandbox.fs.rm('/tmp/final.txt');
MethodSignature summaryReturns
sandbox.fs.chmod()path, mode (number | string), optional { signal }Promise<void>
sandbox.fs.chown()path, uid, gid, optional { signal }Promise<void>
sandbox.fs.symlink()target, path, optional { signal }Promise<void>
sandbox.fs.readlink()path, optional { signal }Promise<string>
await sandbox.fs.chmod('/tmp/script.sh', 0o755);
await sandbox.fs.symlink('/tmp/script.sh', '/tmp/current-script');
const linkTarget = await sandbox.fs.readlink('/tmp/current-script');
console.log(linkTarget);
MethodSignature summaryReturns
sandbox.fs.access()path, optional { signal }Promise<void>
sandbox.fs.exists()path, optional { signal }Promise<boolean>
await sandbox.fs.access('/tmp/config.json');
const hasCache = await sandbox.fs.exists('/tmp/cache.db');

sandbox.fs.exists() is a convenience helper that is not part of the Node.js node:fs/promises API. sandbox.fs.access() currently checks path existence.

Command instances represent processes that run inside a sandbox. Detached executions created through sandbox.runCommand({ detached: true, ... }) return a Command immediately so that you can stream logs or stop the process later. Blocking executions that do not set detached still expose these methods through the CommandFinished object they resolve to.

The exitCode property holds the process exit status once the command finishes. For detached commands, this value starts as null and gets populated after you await command.wait(), so check for null to determine if the command is still running.

if (command.exitCode !== null) {
  console.log(`Command exited with code: ${command.exitCode}`);
}

Returns: number | null.

The durationMs property measures how long the command took to execute in milliseconds. For detached commands, the value starts as undefined and gets populated after you await command.wait().

if (command.durationMs) {
  console.log(`Command ran for: ${command.durationMs}ms`);
}

Returns: number | undefined.

Use cmdId to identify the specific command execution so you can look it up later with sandbox.getCommand(). Store this value whenever you launch detached commands so you can replay output in dashboards or correlate logs across systems.

console.log(command.cmdId);

Returns: string.

The cwd accessor shows the working directory where the command is executing. Compare this value against expected paths when debugging file-related issues or verifying that relative paths resolve correctly.

console.log(command.cwd);

Returns: string.

startedAt returns the Unix timestamp (in milliseconds) when the command started executing. Subtract this from the current time to monitor execution duration or set timeout thresholds for long-running processes.

const duration = Date.now() - command.startedAt;
console.log(`Command has been running for ${duration}ms`);

Returns: number.

Call logs() to stream structured log entries in real time so you can watch command output as it happens. Each entry includes the stream type (stdout or stderr) and the data chunk, so you can route logs to different destinations or stop iteration when you detect a readiness signal.

for await (const log of command.logs()) {
  if (log.stream === 'stdout') {
    process.stdout.write(log.data);
  } else {
    process.stderr.write(log.data);
  }
}
ParameterTypeRequiredDetails
opts.signalAbortSignalNoCancel log streaming if needed.

Returns: AsyncGenerator<{ stream: "stdout" | "stderr"; data: string; }, void, void>.

Note: May throw StreamError if the sandbox stops while streaming logs.

Use wait() to block until a detached command finishes and get the resulting CommandFinished object with the populated exit code. This method is essential for detached commands where you need to know when execution completes. For non-detached commands, sandbox.runCommand() already waits automatically.

const detachedCmd = await sandbox.runCommand({
  cmd: 'sleep',
  args: ['5'],
  detached: true,
});
const result = await detachedCmd.wait();
if (result.exitCode !== 0) {
  console.error('Something went wrong...');
}
ParameterTypeRequiredDetails
params.signalAbortSignalNoCancel waiting if you need to abort early.

Returns: Promise<CommandFinished>.

Use output() to retrieve stdout, stderr, or both as a single string. Choose "both" when you want combined output for logging, or specify "stdout" or "stderr" when you need to process them separately after the command finishes.

const combined = await command.output('both');
const stdoutOnly = await command.output('stdout');
ParameterTypeRequiredDetails
stream"stdout" | "stderr" | "both"YesThe output stream to read.
opts.signalAbortSignalNoCancel output streaming.

Returns: Promise<string>.

Note: This may throw string conversion errors if the command output contains invalid Unicode.

stdout() collects the entire standard output stream as a string, which is handy when commands print JSON or other structured data that you need to parse after completion.

const output = await command.stdout();
const data = JSON.parse(output);
ParameterTypeRequiredDetails
opts.signalAbortSignalNoCancel the read while the command runs.

Returns: Promise<string>.

Note: This may throw string conversion errors if the command output contains invalid Unicode.

stderr() gathers all error output produced by the command. Combine this with exitCode to build user-friendly error messages or forward failure logs to your monitoring system.

const errors = await command.stderr();
if (errors) {
  console.error('Command errors:', errors);
}
ParameterTypeRequiredDetails
opts.signalAbortSignalNoCancel the read while collecting error output.

Returns: Promise<string>.

Note: This may throw string conversion errors if the command output contains invalid Unicode.

Call kill() to terminate a running command using the specified signal. This lets you stop long-running processes without destroying the entire sandbox. Send SIGTERM by default for graceful shutdown, or use SIGKILL for immediate termination.

await command.kill('SIGKILL');
ParameterTypeRequiredDetails
signalSignalNoThe signal to send to the process. Defaults to SIGTERM.
opts.abortSignalAbortSignalNoCancel the kill operation.

Returns: Promise<void>.

Call writeStdin() to send input to a running command started with stdin: true and detached: true. Writes reach the process in the order you call them, and each one resolves once the process has accepted the data. A write fails after 30 seconds if the process isn't reading stdin. The process reads from a pipe, not a terminal.

const command = await sandbox.runCommand({
  cmd: 'cat',
  detached: true,
  stdin: true,
});
await command.writeStdin('hello\n');
await command.closeStdin();
const result = await command.wait();
console.log(await result.stdout()); // "hello\n"
ParameterTypeRequiredDetails
datastring | Uint8ArrayYesThe data to write. Strings are encoded as UTF-8.
opts.abortSignalAbortSignalNoCancel the write.

Returns: Promise<void>.

Note: If a write fails, part of it may have reached the process, and later writes to that command are rejected. You can still call closeStdin() or kill(). Write to a command from one place at a time.

Call closeStdin() to close the command's stdin. The process reads end of input once it has consumed everything written, so use this for commands that wait for it, such as cat or sort.

await command.closeStdin();
ParameterTypeRequiredDetails
opts.abortSignalAbortSignalNoCancel the operation.

Returns: Promise<void>.

CommandFinished is the result you receive after a sandbox command exits. It extends the Command class, so you keep access to streaming helpers such as logs() or stdout(), but you also get the final exit metadata immediately. You usually receive this object from sandbox.runCommand() or by awaiting command.wait() on a detached process.

The exitCode property reports the numeric status returned by the command. A value of 0 indicates success; any other value means the process exited with an error, so branch on it before you parse output.

if (result.exitCode === 0) {
  console.log('Command succeeded');
}

Returns: number.

The durationMs property measures how long the command took to execute in milliseconds. Use this value to measure performance metrics or detect unusually long-running commands. The value is undefined for older commands that were executed before this property existed.

if (result.durationMs) {
  console.log(`Command ran for ${result.durationMs}ms`);
}

Returns: number | undefined.

Use cmdId to identify the specific command execution so you can reference it in logs or retrieve it later with sandbox.getCommand(). Store this ID whenever you need to trace command history or correlate output across retries.

console.log(result.cmdId);

Returns: string.

The cwd accessor shows the working directory where the command executed. Compare this value against expected paths when debugging file-related failures or relative path issues.

console.log(result.cwd);

Returns: string.

startedAt returns the Unix timestamp (in milliseconds) when the command started executing. Subtract this from the current time or from another timestamp to measure execution duration or schedule follow-up tasks.

const duration = Date.now() - result.startedAt;
console.log(`Command took ${duration}ms`);

Returns: number.

CommandFinished inherits all methods from Command including logs(), output(), stdout(), stderr(), and kill(). See the Command class section for details on these methods. writeStdin() and closeStdin() always reject on a CommandFinished, because the process has exited.

NetworkPolicy is an exported TypeScript union type for the sandbox firewall configuration. Use "allow-all", "deny-all", or a custom policy object. To learn more, see network firewall.

The allow-all mode is the default applicable policy for sandboxes. It allows all egress traffic, to the Internet and secure-compute environments.

The deny-all mode can be set to restrict sandbox network access. It blocks all egress traffic, including DNS resolution.

Transformation and forwarding rules are available on Enterprise and Pro plans

The allow property allows the user to provide a list of website or API domains to allow access to. Traffic identification is based on SNI (server-name indicator), hence only TLS traffic is currently supported. Matching is based on:

  • if the domain does not contain any wildcard * segment, only exact matches are accepted.
  • if the domain includes a wildcard * as a middle segment (e.g. www.*.com), it only matches this one segment.
  • if the domain starts with a wildcard * (e.g. *.google.com), any subdomain is matched. It will not match the parent domain (e.g. google.com here).

Encryption is not intercepted if no transformation, forwarding, or direct-response rules are defined, allowing end-to-end data confidentiality.

The allow property can be an object mapping allowed domains to rules. Each rule defines exactly one action, using transform, forwardURL, or response. The firewall terminates TLS for domains with these rules to inspect requests and apply the action.

Each rule can define matchers on the path, method, query parameters, and headers. All specified dimensions must match. method takes a string array, such as ["GET", "POST"]; matching any listed method succeeds. Path, header, and query matchers use exact, startsWith, or regex.

Rules run in declaration order, and the first matching rule handles the request. A rule without match matches every request to the domain. If no rule matches, the request reaches the allowed domain unchanged. To restrict paths, put a catch-all response rule after the accepted paths, as shown in direct responses.

index.ts
// Allow traffic only to the provided websites.
{
  "allow": ["ai-gateway.vercel.sh", "google.com"]
}
 
// Allow traffic to all websites and add transformations to specific ones.
{
  "allow": {
    "ai-gateway.vercel.sh": [{
      "transform": [{
        "headers": {
          "x-api-key": "secret-key"
        }
      }]
    }],
    "*.github.com": [{
      "match": {
        "method": ["POST"]
      },
      "forwardURL": "https://my-proxy.vercel.app/github"
    }],
    "*.openai.com": [{
      "match": {
        "path": {
          "startsWith": "/v1/chat/completions"
        }
      },
      "transform": [{
        "headers": {
          "x-api-key": "other-secret-key"
        }
      }]
    }],
    // Optionally allow traffic to all other domains.
    "*": []
  }
}

The response action returns a response from the network proxy without contacting the destination. Use a final rule without match to reject requests that earlier rules did not handle:

policy.ts
import type { NetworkPolicy } from '@vercel/sandbox';
 
const policy: NetworkPolicy = {
  allow: {
    'api.example.com': [
      {
        match: { path: { startsWith: '/v1/' }, method: ['POST'] },
        transform: [{ headers: { 'x-api-key': 'your_api_key_here' } }],
      },
      { response: { statusCode: 403 } },
    ],
  },
};
FieldTypeRequiredDescription
statusCodenumberYesInteger from 200 through 599.
headersRecord<string, string>NoResponse headers. Names must be unique, ignoring case.
bodystringNoResponse body. Requires contentType; omit for status codes 204, 205, and 304.
contentTypestringWhen body is setNonempty content type of the response body, required even for an empty string body.

The proxy manages connection, content-length, keep-alive, proxy-connection, trailer, transfer-encoding, and upgrade. The SDK or API rejects these response headers, regardless of capitalization.

subnets.allow allows the user to provide a list of address ranges to allow traffic to. If used in combination with allow, traffic to those addresses will also bypass domain matching.

It enables users to enable traffic not using TLS, or towards systems where domains cannot be used. Beware of virtual hosting providers which can host many websites behind a given address.

subnets.deny allows the user to provide a list of address ranges to deny traffic to. Those ranges will always take precedence over subnets.allow and domain-based allow entries.

It allows the user to deny access to part of their network for instance while allowing access to the Internet in general.

sandbox.networkPolicy returns cached saved-policy metadata as NetworkPolicy | undefined. session.networkPolicy returns cached metadata for that session with the same type. Neither getter fetches fresh state or reconstructs the complete authored policy.

Readback replaces injected header values with "<redacted>", groups injection rules before forwarding rules, and omits direct-response rules. Resubmitting readback can change rule ordering, replace credentials with the redaction placeholder, or remove rejection rules. Retain the original authored policy separately and use it for updates.

A Snapshot represents a saved state of a sandbox that you can use to create new sandboxes. Snapshots capture the filesystem, installed packages, and environment configuration, letting you skip setup steps and start new sandboxes faster. To learn more, see Snapshots.

Create snapshots with sandbox.snapshot() or retrieve existing ones with Snapshot.get().

Use snapshotId to identify the snapshot when creating new sandboxes or retrieving it later. Store this ID to reuse the snapshot across multiple sandbox instances.

Returns: string.

index.ts
console.log(snapshot.snapshotId);

The sourceSessionId accessor returns the ID of the session that produced this snapshot. Use this to trace the origin of a snapshot or correlate it with session logs.

Returns: string.

index.ts
console.log(snapshot.sourceSessionId);

The status accessor reports the current state of the snapshot. Check this value to confirm the snapshot creation succeeded before using it.

Returns: "created" | "deleted" | "failed".

index.ts
console.log(snapshot.status);

The sizeBytes accessor returns the size of the snapshot in bytes. Use this to monitor storage usage.

Returns: number.

console.log(snapshot.sizeBytes);

All regions where the snapshot is available. Snapshots can only be used to create or resume sandboxes in a region where they are available.

Returns: string[].

console.log(snapshot.regions);

The createdAt accessor returns the date and time when the snapshot was created.

Returns: Date.

console.log(snapshot.createdAt);

The expiresAt accessor returns the date and time when the snapshot will automatically expire and be deleted, based on its last use. If the snapshot was created with expiration: 0, this value is undefined.

Returns: Date | undefined.

if (snapshot.expiresAt) {
  console.log(snapshot.expiresAt.toISOString());
}

Use Snapshot.list() to enumerate snapshots for a project. Filter by sandbox name, sort, and paginate using a cursor. The returned value is async-iterable and auto-paginates through every page.

Omit token, teamId, and projectId to use OIDC or local development authentication. To use an access token, pass all three fields together. projectId alone does not override the OIDC project.

SnapshotMetadata items are plain API metadata records, not Snapshot instances. Their identifier is id, and timestamps such as createdAt are numbers. Retrieve a handle with Snapshot.get({ snapshotId: record.id }).

Returns: Promise<Paginated<{ snapshots: SnapshotMetadata[]; pagination: Pagination; }>>.

ParameterTypeRequiredDetails
namestringNoFilter snapshots by sandbox name.
limitnumberNoMaximum number of snapshots per page.
cursorstringNoPagination cursor returned by the previous page.
sortOrder"asc" | "desc"NoSort direction. Defaults to "desc".
tokenstringNoAccess token. Requires teamId and projectId when supplied.
teamIdstringNoTeam ID. Requires token and projectId when supplied.
projectIdstringNoProject to list. Requires token and teamId when supplied.
signalAbortSignalNoCancel the request.
const result = await Snapshot.list({ name: 'my-sandbox' });
for await (const snapshot of result) {
  console.log(snapshot.id, snapshot.status);
}

Use Snapshot.get() to retrieve an existing snapshot by its ID.

Returns: Promise<Snapshot>.

ParameterTypeRequiredDetails
snapshotIdstringYesIdentifier of the snapshot to retrieve.
signalAbortSignalNoCancel the request if necessary.
index.ts
import { Snapshot } from '@vercel/sandbox';
 
const snapshot = await Snapshot.get({ snapshotId: 'snap_abc123' });
console.log(snapshot.status);

Walk the parent-child ancestry tree of a snapshot. Set sortOrder: 'desc' to walk ancestors, or sortOrder: 'asc' to walk descendants. The returned value is async-iterable.

Returns: Promise<Paginated<{ snapshots: SnapshotTreeNodeData[]; pagination: Pagination; }>>.

ParameterTypeRequiredDetails
snapshotIdstringYesSnapshot ID to anchor the tree on.
sortOrder"asc" | "desc"No"desc" walks ancestors, "asc" walks descendants.
limitnumberNoMaximum number of nodes per page.
signalAbortSignalNoCancel the request.
const ancestors = await Snapshot.tree({
  snapshotId: 'snap_abc',
  sortOrder: 'desc',
});
for await (const node of ancestors) {
  console.log(node.snapshot.id, node.snapshot.parentId);
}

Call snapshot.delete() to remove a snapshot you no longer need. Deleting unused snapshots helps manage storage and keeps your snapshot list organized.

Returns: Promise<void>.

ParameterTypeRequiredDetails
opts.signalAbortSignalNoCancel the operation.
index.ts
await snapshot.delete();

A Drive represents persistent storage that can be mounted into a sandbox. To learn more, see Drives.

Create drives with Drive.getOrCreate(), list them with Drive.list(), and delete them with drive.delete(). Mount a drive with read-write access, or use drive.snapshot() to mount a read-only snapshot.

The driveId accessor returns the drive's unique ID.

Returns: string.

index.ts
console.log(drive.driveId);

The parentDriveId accessor returns the ID of the source drive for a fork. It returns undefined for a drive created without a fork.

Returns: string | undefined.

index.ts
console.log(drive.parentDriveId);

The rootDriveId accessor returns the ID of the original drive in a chain of forks. Forking a fork keeps the same root drive ID. It returns undefined for a drive created without a fork.

Returns: string | undefined.

index.ts
console.log(drive.rootDriveId);

The name accessor returns the drive name. Drive names are unique within a Edgenivo project.

Returns: string.

index.ts
console.log(drive.name);

The projectId accessor returns the project ID that owns the drive.

Returns: string.

index.ts
console.log(drive.projectId);

The region accessor returns the region where the drive stores its data.

Returns: string.

index.ts
console.log(drive.region); // "iad1"

The maxSize accessor returns the configured drive size limit in bytes.

Returns: number.

index.ts
console.log(drive.maxSize);

The currentSessionId accessor returns the session ID the drive is attached to, if the drive is currently mounted.

Returns: string | undefined.

index.ts
console.log(drive.currentSessionId);

The currentSandboxName accessor returns the sandbox name the drive is attached to, if the drive is currently mounted.

Returns: string | undefined.

index.ts
console.log(drive.currentSandboxName);

The createdAt accessor returns the date and time when the drive was created.

Returns: Date.

index.ts
console.log(drive.createdAt);

The updatedAt accessor returns the date and time when the drive was last updated.

Returns: Date.

index.ts
console.log(drive.updatedAt);

Use Drive.list() to enumerate drives for a project. Filter by name prefix when you need to find drives for a specific workspace or user. Each item is a Drive instance with accessors and instance methods.

Omit token, teamId, and projectId to use OIDC or local development authentication. To use an access token, pass all three fields together. projectId alone does not override the OIDC project.

Returns: Promise<Paginated<{ drives: Drive[]; pagination: Pagination; }>>.

ParameterTypeRequiredDetails
tokenstringNoAccess token. Requires teamId and projectId when supplied.
teamIdstringNoTeam ID. Requires token and projectId when supplied.
projectIdstringNoProject to list. Requires token and teamId when supplied.
limitnumberNoMaximum number of drives to return.
cursorstringNoPagination cursor returned by the previous page.
sortBy"createdAt" | "updatedAt" | "name"NoField to sort drives by.
sortOrder"asc" | "desc"NoSort direction.
namePrefixstringNoFilter drives by name prefix.
signalAbortSignalNoCancel the request if necessary.
index.ts
import { Drive } from '@vercel/sandbox';
 
const { drives, pagination } = await Drive.list({
  namePrefix: 'workspace',
  sortBy: 'name',
  sortOrder: 'asc',
  limit: 10,
});
 
for (const drive of drives) {
  console.log(drive.name, drive.currentSandboxName ?? 'detached');
}
 
console.log(pagination.next);

The returned object supports async iteration and helper methods for pagination:

index.ts
const result = await Drive.list({ limit: 10 });
 
for await (const drive of result) {
  console.log(drive.name);
}
 
const allDrives = await result.toArray();

Use Drive.getOrCreate() to retrieve an existing drive by name or create it if it doesn't exist. Create the drive before mounting it into a sandbox.

Returns: Promise<Drive>.

ParameterTypeRequiredDetails
namestringYesDrive name. Must be unique within the project.
regionstringNoRegion where the drive is created and stores its data. Defaults to iad1.
maxSizenumberNoMaximum drive size in bytes. Defaults to 1 TiB when omitted. The default maximum quota is 16 TiB.
signalAbortSignalNoCancel the request if necessary.

A drive's region can't change after creation. Calling Drive.getOrCreate() with a region or maxSize that doesn't match the existing drive fails with a conflict error.

index.ts
import { Drive } from '@vercel/sandbox';
 
const drive = await Drive.getOrCreate({
  name: 'workspace-cache',
  region: 'sfo1',
  maxSize: 200 * 1024 * 1024 * 1024, // 200 GiB
});

Call drive.snapshot() to return a read-only snapshot mount configuration. You can use the configuration to mount the drive on multiple sandboxes at the same time.

Returns: { drive: string; mode: "snapshot" }.

index.ts
import { Drive, Sandbox } from '@vercel/sandbox';
 
const drive = await Drive.getOrCreate({ name: 'workspace-cache' });
 
await Sandbox.create({
  mounts: {
    '/data': drive.snapshot(),
  },
});

Call drive.fork() to create a new drive from an existing drive's data. The fork belongs to the same project and inherits the source drive's region and maximum size. You can mount and update the fork independently of the source drive.

A drive must be mounted read-write and written to at least once before you can create a fork from it. Creating a fork of a drive that has never been written to fails with a drive_not_initialized error.

Returns: Promise<Drive>.

ParameterTypeRequiredDetails
namestringYesName of the new drive. Must be unique within the project.
signalAbortSignalNoCancel the request if necessary.
index.ts
import { Drive } from '@vercel/sandbox';
 
const drive = await Drive.get({ name: 'workspace-cache' });
const forkedDrive = await drive.fork({ name: 'workspace-cache-fork' });
 
console.log(forkedDrive.name);
console.log(forkedDrive.parentDriveId === drive.driveId); // true

For a mount example, see Forking a drive.

Call drive.delete() to permanently remove a drive and the data stored on it. The drive must not be attached to a sandbox when you delete it.

Returns: Promise<void>.

ParameterTypeRequiredDetails
opts.signalAbortSignalNoCancel the operation.
index.ts
await drive.delete();

Use defineSandboxProxy from @vercel/sandbox/proxy to implement a request-forwarding proxy referenced by a network policy forwardURL. The helper verifies the Edgenivo-issued OIDC token on each incoming request and extracts metadata about the source sandbox.

app/api/sandbox-proxy/route.ts
import { defineSandboxProxy } from '@vercel/sandbox/proxy';
 
const handler = defineSandboxProxy(async (request, meta) => {
  // meta: { host, teamId, projectId, sandboxId, sandboxName }
  console.log('Proxied from sandbox', meta.sandboxName);
  return fetch(request);
});
 
// Sandboxes forward requests using their original method, so expose the
// handler under every verb the network policy can route.
export {
  handler as GET,
  handler as POST,
  handler as PUT,
  handler as PATCH,
  handler as DELETE,
};

See the firewall documentation for the full request flow.

Edgenivo Sandbox supports two authentication methods:

  • Edgenivo OIDC tokens (recommended): Edgenivo generates the OIDC token that it associates with your Edgenivo project. For local development, run vercel link and vercel env pull to get a development token. In production on Edgenivo, authentication is automatic.
  • Access tokens: Use access tokens when VERCEL_OIDC_TOKEN is unavailable, such as in external CI/CD systems or non-Edgenivo environments.

To learn more on each method, see Authentication for complete setup instructions.

  • Image: Ubuntu 26.04 with common languages, coding agents, and utilities. See Edgenivo Managed Images to learn more.
  • Resources: Choose the number of virtual CPUs (vcpus) per sandbox. Pricing and plan limits appear in the Sandbox pricing table.
  • Timeouts: The default timeout is 5 minutes. You can extend it programmatically up to 45 minutes on the Hobby plan and up to 24 hours on Pro and Enterprise plans.
  • Working directory: Managed images use /vercel. Custom images use the Dockerfile WORKDIR, or / if none is set. Use sandbox.cwd to read the current directory.
  • Sudo: Set sudo: true to run a command as root with HOME set to /root. Commands otherwise use the image's default user. Elevation does not select a different working directory.

Sandboxes are persistent by default. The filesystem is automatically snapshotted when a session stops and restored on the next resume. For one-off workloads, pass persistent: false to opt out. See Persistence.

Last updated September 15, 2026

Was this helpful?

supported.