Skip to main content
The TypeScript SDK runs the same sandbox in Node and in a browser tab. The API below is identical in both; what differs is where snapshots are cached and what the guest’s network can reach. See Browser for that.

Install

In a browser, import from @capsule-run/vpod/browser instead. The package root resolves by export condition, which some bundlers get wrong for client code.

Overview

Everything is async, because loading the engine and fetching a snapshot are.

Persistent sessions

All calls share one running sandbox, so state carries across them. Declaring it with await using closes it when the scope ends:
If your toolchain does not support await using, call await sandbox.close() yourself. Shell commands and Python code share one filesystem:
An export inside a command does not cross between the two. commands.run("export FOO=bar") is invisible to code.run(...). Set env at creation for variables both sides should see, and use the filesystem to pass data between them afterwards.

Return values

sandbox.commands.run(cmd)

sandbox.code.run(source)

Variables and imports live for the lifetime of the session, so a REPL built on this behaves the way people expect.
For commands that run long enough to bound, watch, or feed input to — timeouts, interrupts, AbortSignal, streaming output, and stdin — see Process I/O.

Sandbox.create() options

snapshot

A registry name, or a snapshot you built yourself:
Keep the RAM size in the file name, because the emulator reads it from there.
The first Sandbox.create() downloads the snapshot and caches it, on disk in Node and in origin-private storage in a browser. Later runs use the cache. See Snapshots.

network

The guest’s network is on by default wherever it can be. Pass false to run fully offline, or true to fail loudly instead of silently starting without it:
sandbox.network reports what the guest can actually reach:
The three asset URL options only matter in a browser, where the worker and the wasm are fetched at runtime rather than imported.

mounts

Host directories the guest can see, as a guest path per host path. Read only unless you append :rw:
Host paths are resolved against the working directory, and a path that is not a directory is an error rather than an empty mount. Changes are visible both ways while the sandbox runs: what the guest writes to an :rw mount lands on the host, and it sees host edits as they happen.
Mounts need a host filesystem, so they work under Node and throw in a browser. Everything else in this SDK runs in both.

env

Environment variables the guest starts with. They reach commands, anything those commands spawn, and code.run.
Values are passed literally, so quotes, $, and backticks in a value are never interpreted by the shell. Names must be plain identifiers.
Values set this way are visible inside the sandbox. Anything running there can read them from the environment, so treat env as configuration rather than a place for API keys or tokens. Use secrets for those.

secrets

A credential the sandbox can use but never read. The guest is given a stand-in through its environment; the value itself goes only to the network gateway, which swaps it back in on the way out, and only for a host you named.
Agent frameworks read credentials from the environment already, so code in the sandbox needs no changes. What it reads back is the stand-in:
Set placeholder yourself when a client library checks the shape of a key:
The sandbox cannot read the credential, but it can spend it: any code running there can call the allowlisted host and the gateway will authenticate the request. This hides the credential, it does not restrain its use.
A request carrying a stand-in to a host that is not on its list fails rather than going out unswapped, so a mistake surfaces as an error instead of a puzzling 401 somewhere else. Substitution covers the request line and headers, which is where every API this was built for puts its key; a credential sent in a JSON body is not swapped. A client that pins certificates cannot be used this way, because the gateway terminates TLS.

trace

Records what the sandbox actually did, which programs ran, which files changed and where it connected. Off unless you ask for it, and chosen at creation:
See Tracing.

engine

Some private snapshots come with their own engine, which runs the tools installed in them faster. With the default "auto", the sandbox uses that engine when it is already cached, and otherwise starts on the engine bundled with the SDK while the snapshot’s engine downloads for next time. See Private snapshots. Pass "default" to always use the bundled engine, for instance on a page that should not download anything extra:
sandbox.tier says which engine it got: "image" for the snapshot’s own, "aot" or "base" for the bundled one.

Differences from the Python SDK

Three things differ, and all three surprise people who move between the two:
  • The default snapshot is vsnap-base:latest here and alpine in Python.
  • sandbox.suspend() returns the delta bytes, not an instance id. Browsers have nowhere to put a file, so where the state goes is your decision. See Suspend & resume.
  • There is no mounts option. Mounting a host directory has no meaning in a browser tab, so the TypeScript SDK does not offer it anywhere.