Getting started
Run a command in a sandbox from your own TypeScript application. You’ll need a provider account and Bun to run this example. The SDK also supports server-side Node.js.
1. Install
Section titled “1. Install”In your project, install the SDK and the adapter for your provider:
npm install sandbar-sdk @sandbar-sdk/daytonanpm install sandbar-sdk @sandbar-sdk/e2bThe Modal adapter is not published to npm. Run it from a source checkout with Bun 1.3.14:
git clone https://github.com/pandemicsyn/sandbar.gitcd sandbarbun install --frozen-lockfilebun run build:packagespnpm add and bun add work too. boxd and Vercel install the same way; see boxd setup and Vercel setup. Compare provider support before choosing.
To run OpenCode rather than a shell command, use the runnable agent examples.
2. Add your credentials
Section titled “2. Add your credentials”Create an untracked .env.local file in your project root (the repository root for Modal):
DAYTONA_API_KEY="<your-daytona-api-key>"The example uses the daytona-small Linux snapshot in us. Select an active snapshot available in your region. daytona-default permits Daytona’s essential services; it does not mean all egress is blocked.
E2B_API_KEY="<your-e2b-api-key>"The example uses E2B’s public base template. No team ID or custom template is required.
MODAL_TOKEN_ID=your-modal-token-idMODAL_TOKEN_SECRET=your-modal-token-secretMODAL_APP_NAME=your-existing-appMODAL_IMAGE_ID=im-your-existing-image-idUse an existing Modal App in the main environment and an existing prepared image containing /bin/sh and standard shell utilities. The example does not create an App or build an image.
Add .env.local to your .gitignore. Keep credentials on the server and out of browser code. Running the example creates a sandbox in your provider account and may incur usage charges.
3. Run a command
Section titled “3. Run a command”Choose a provider and save the example as quickstart.ts:
import { Image, Sandbar } from "sandbar-sdk";import { daytona } from "@sandbar-sdk/daytona";
const apiKey = process.env.DAYTONA_API_KEY;
if (!apiKey) throw new Error("Set DAYTONA_API_KEY before running this example");
const sandbar = await Sandbar.connect( daytona({ apiKey, target: "us", environment: Image.prepared("daytona-small"), ttlMinutes: 15, network: "daytona-default", }),);
try { await sandbar.sandboxes.with({}, async (box) => { const output = await box.exec(["printf", "Hello from Sandbar!\n"]); console.log(output.stdoutText()); });} finally { await sandbar.close();}import { Sandbar } from "sandbar-sdk";import { e2b } from "@sandbar-sdk/e2b";
const apiKey = process.env.E2B_API_KEY;
if (!apiKey) throw new Error("Set E2B_API_KEY before running this example");
const sandbar = await Sandbar.connect(e2b({ apiKey }));
try { await sandbar.sandboxes.with({}, async (box) => { const output = await box.exec(["printf", "Hello from Sandbar!\n"]); console.log(output.stdoutText()); });} finally { await sandbar.close();}import { Image, Sandbar } from "sandbar-sdk";import { modalAdapter } from "@sandbar-sdk/modal";
const tokenId = process.env.MODAL_TOKEN_ID;
const tokenSecret = process.env.MODAL_TOKEN_SECRET;
const appName = process.env.MODAL_APP_NAME;
const imageId = process.env.MODAL_IMAGE_ID;
if (!tokenId || !tokenSecret || !appName || !imageId) throw new Error("Set MODAL_TOKEN_ID, MODAL_TOKEN_SECRET, MODAL_APP_NAME, and MODAL_IMAGE_ID");
const sandbar = await Sandbar.connect({ adapter: modalAdapter, config: { appName, environment: "main", timeoutSeconds: 300 }, credentials: { tokenId, tokenSecret },});
try { const box = await sandbar.sandboxes.create({ environment: Image.prepared(imageId), network: "blocked", });
try { const output = await box.exec(["/bin/sh", "-c", "printf 'Hello from Sandbar!\n'"]); console.log(output.stdoutText()); } finally { await box.destroy(); }} finally { await sandbar.close();}Run it with Bun:
bun --env-file=.env.local run quickstart.tsbun --env-file=.env.local run quickstart.tsFrom the repository root:
bun --env-file=.env.local run apps/docs/examples/getting-started-modal.tsYou should see Hello from Sandbar!. The Daytona and E2B examples use sandboxes.with() to clean up the sandbox after the callback succeeds or throws. An outer finally closes the client. The experimental Modal example shows manual sandbox cleanup. close() alone does not destroy compute. If creation or cleanup has an uncertain outcome, follow Errors and recovery before retrying.
For temporary work, prefer scoped sandbox cleanup. The scope retains snapshots and volumes, and cleanup failures preserve the callback result or error plus recovery information. Use manual create() when the sandbox should outlive the callback.
Next steps
Section titled “Next steps”-
Run OpenCode inside a sandbox to capture a response or edit a remote file.
-
Give your agent a Sandbar prompt to start building an application.
-
Check tested provider support for live-tested workflows and limits.
Bound connection waits
Section titled “Bound connection waits”Sandbar.connect(adapter, { signal, timeoutMs: 30_000 }) accepts ConnectOptions.
The advanced definition/config/credentials form accepts the same controls in its options object.
await client.close({ signal: cleanupSignal, timeoutMs: 5_000 }) accepts CloseOptions.
Only positive finite safe-integer budgets are accepted; longer budgets are allowed.
Compatibility change: setup now defaults to a 30-second local wait and each close call defaults to a 5-second local wait. These budgets do not change sandbox lifetime, command deadlines or remote compute. Setup cancellation reports WAIT_ABORTED; expiry reports TIMEOUT, with effect none, cause and a phase-specific message. Aborting the setup signal after the client returns has no effect on the usable client.
Close immediately prevents new work and starts local cleanup once. Cancelling or timing out one close wait leaves that attempt running; a later close can wait for it. Cleanup runs in reverse registration order. A hanging hook can leave later hooks pending; failed releases are sent to onDiagnostic and remaining cleanup continues. Successful close means registered cleanup was attempted, not that every failed release succeeded. Late resources from abandoned setup are released locally. Neither setup failure nor close deletes sandboxes, volumes or snapshots; use explicit resource operations for those actions.