---
title: Vercel Sandbox Provider
description: Run a Smithers <Sandbox> child workflow on Vercel Sandbox compute with createVercelSandboxProvider.
---

# Vercel Sandbox Provider

`@smithers-orchestrator/vercel` is a first-class Smithers `SandboxProvider`
backed by the [Vercel Sandbox](https://vercel.com/docs/vercel-sandbox) SDK. It
runs a `<Sandbox>` child workflow's request on Vercel Sandbox compute: it ships
the request JSON into the sandbox workdir, runs the entry command, and reads the
result JSON back. The shared provider-kit owns request shipping, egress, result
parsing, secret scrubbing, and cleanup, so this package only supplies the Vercel
`createSession` seam.

The provider id is `vercel-sandbox` (exported as `VERCEL_SANDBOX_PROVIDER_ID`).
The default workdir is `/vercel/sandbox`.

```ts
import {
  createVercelSandboxProvider,
  registerVercelSandboxProvider,
  VERCEL_SANDBOX_PROVIDER_ID,
} from "smithers-orchestrator/vercel";
```

## Credentials

`@vercel/sandbox` is an optional dependency, imported lazily inside the session.
Install it to use the real provider:

```bash
npm install @vercel/sandbox
```

OIDC is preferred; the access-token trio is the fallback. Auth is resolved and
validated before the SDK is touched, so a misconfigured provider fails fast with
`INVALID_INPUT`. These values are used only to create the sandbox. They are
never written into the request JSON and never forwarded into the remote command
env.

- `VERCEL_OIDC_TOKEN` (preferred), or
- `VERCEL_TOKEN` + `VERCEL_TEAM_ID` + `VERCEL_PROJECT_ID`.

Each can also be passed as a factory option (`oidcToken`, `token`, `teamId`,
`projectId`).

## Usage

```tsx
import { createSmithers, Sandbox } from "smithers-orchestrator";
import { createVercelSandboxProvider } from "smithers-orchestrator/vercel";

const provider = createVercelSandboxProvider({
  runtime: "node24",
  vcpus: 2,
  ports: [3000],
  maxDurationMs: 45 * 60_000,
});

export default parent.smithers((ctx) => (
  <parent.Workflow name="vercel-sandbox-run">
    <Sandbox
      id="remote-build"
      provider={provider}
      workflow={childWorkflow}
      input={{ prompt: ctx.input.prompt }}
      output={parent.outputs.result}
    />
  </parent.Workflow>
));
```

Or register it once and reference it by id:

```ts
import { registerVercelSandboxProvider } from "smithers-orchestrator/vercel";

const unregister = registerVercelSandboxProvider({ runtime: "node24" });

// <Sandbox provider="vercel-sandbox" workflow={child} output={outputs.result} />
```

## Request/result contract

The kit writes the request JSON to `.smithers/sandbox-request.json` in the
workdir and hands the entry command two env vars:

- `SMITHERS_SANDBOX_REQUEST_PATH`: where to read the request JSON.
- `SMITHERS_SANDBOX_RESULT_PATH`: where to write the result JSON.

The entry command either prints the result JSON to stdout or writes it to
`SMITHERS_SANDBOX_RESULT_PATH`. The result is `{ bundlePath }` or a structured
`{ status, output|outputs, patches?, diffBundle?, runId? }`. When ports are
declared, the reachable `sandbox.domain(port)` is surfaced through a heartbeat.

## Factory options

- `runtime`: Vercel Sandbox runtime (default `node24`).
- `vcpus`: vCPU count passed to `resources`.
- `ports`: ports whose domains are surfaced.
- `timeoutMs` / `maxDurationMs`: see duration cap below.
- `persist`: `stop()` (pause) instead of `delete()` on teardown.
- `command`: entry command.
- `workdir`: default `/vercel/sandbox`.
- `cleanup`: `"destroy"` (default) or `"keep"`.
- `env`: merged into the command env.
- `client` / `createOptions`: inject the SDK class or extra `Sandbox.create`
  options.

## Duration and plan cap

The default session timeout is 5 minutes. `options.timeoutMs` (or
`request.toolTimeoutMs`) maps to the create `timeout`. Durations above the
5-minute default warn through a heartbeat and call `sandbox.extendTimeout()`, up
to `options.maxDurationMs` (default 45 minutes). A request above that cap throws
`INVALID_INPUT` rather than provisioning a sandbox that would overrun the plan.
Raise `maxDurationMs` for Pro (up to 5 hours).

## Cleanup and cost

Ephemeral sandboxes are deleted permanently on teardown (`cleanup: "destroy"`,
the default). Set `persist: true` to `stop()` (pause) the sandbox instead so it
can be resumed. `cleanup: "keep"` skips teardown entirely. You pay for Vercel
Sandbox wall-clock from create to teardown.

`createMockVercelSandboxEnvironment(handler, config?)` is an in-memory SDK
double for tests. Pass it as `options.client`; it needs zero credentials, so
unit tests run in CI without touching Vercel.
