---
title: Sandbox Providers
description: The SandboxProvider contract, the shared provider-kit, provider selection and precedence, egress and secret redaction, and the built-in cloud providers.
---

# Sandbox Providers

A [`<Sandbox>`](/components/sandbox) boundary runs a child workflow outside the
parent task process. Where it runs is decided by a provider. A provider is a
plain object that takes a request and returns a result. Smithers ships
first-class providers for five cloud backends and a shared kit so a new provider
is a thin adapter.

The built-in providers are Daytona, Vercel, AWS, GCP, and Cloudflare. Each is a
separate optional package with its own SDK as an optional dependency, so you
install only what you use.

## The SandboxProvider contract

A provider implements `run()` and an optional `cleanup()`:

```ts
type SandboxProvider = {
  id: string;
  run(request: SandboxProviderRequest): Promise<SandboxProviderResult> | SandboxProviderResult;
  cleanup?(request: SandboxProviderRequest): Promise<void> | void;
};
```

`run()` receives the child workflow, the `<Sandbox input>` value, the request and
result bundle paths, the network and output-size limits, an abort signal, and a
heartbeat callback. It returns either a `bundlePath` to a bundle it wrote, or a
structured `{ status, output, remoteRunId?, workspaceId?, diffBundle? }`. Smithers
validates the result, enforces diff-review policy, and returns the outputs to the
parent task. See [`<Sandbox>`](/components/sandbox) for the full request and
result types.

## The provider-kit

Cloud providers do not implement `run()` from scratch. They call
`createCommandSandboxProvider` from `smithers-orchestrator/sandbox`, which owns
the request/result-file protocol once so every provider behaves the same:

1. Create a vendor session and emit a `session-created` heartbeat.
2. Write the request JSON, carrying only the safe fields (`runId`, `sandboxId`,
   `input`, `config`, `allowNetwork`, `maxOutputBytes`, redacted egress).
3. Merge the command env: `options.env`, the egress env, and the two path vars
   below. It never copies arbitrary `process.env`.
4. Run the entry command with the request's `toolTimeoutMs` and abort signal.
5. Parse the result from stdout if it starts with `{`, otherwise read the result
   file. A nonzero exit with no valid result JSON throws
   `SANDBOX_EXECUTION_FAILED` with stdout and stderr truncated to
   `maxOutputBytes` and redacted.
6. On `cleanup`, resolve the cached session and destroy it per policy.

A provider author implements a small `SandboxSession` seam, not the protocol:

```ts
type SandboxSession = {
  readonly remoteId: string;
  readonly writeFile: (path: string, content: string) => Promise<void>;
  readonly readFile: (path: string) => Promise<string>;
  readonly exec: (command: string, opts: SandboxExecOptions) => Promise<SandboxExecResult>;
  readonly destroy?: () => Promise<void>;
};
```

The kit 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`. Providers with no shared filesystem (AWS, GCP)
transport those files through S3 or GCS and inject extra vars pointing the entry
at the object keys.

## Selecting a provider

There are three ways to pick a provider, in precedence order:

1. **Provider object (primary).** Pass the factory result straight to the prop:
   `<Sandbox provider={createDaytonaSandboxProvider({...})} workflow={child} />`.
2. **Registered id.** Register once, then reference by id:
   `registerSandboxProvider(createVercelSandboxProvider({...}))` then
   `<Sandbox provider="vercel-sandbox" />`. The provider packages also export
   `register<Provider>SandboxProvider(...)` convenience wrappers.
3. **Env default (planned, not yet shipped).** A future `SMITHERS_SANDBOX_PROVIDER`
   env var will select a registered id when a `<Sandbox>` sets no explicit
   `provider`, resolving a registered id only (never auto-creating a cloud
   client). It is not implemented yet; setting it today has no effect. The
   intended precedence, once shipped, mirrors the DB-backend chain: an explicit
   `provider` prop wins, then workflow config, then `SMITHERS_SANDBOX_PROVIDER`,
   then the local default. Until then, select a provider with method 1 or 2.

Credentials always come from factory options first and the vendor env chain
second. They are never required in `request.config`.

## Egress and secret redaction

Egress is configured on the `<Sandbox egress>` prop and reaches the provider as
the normalized `request.egress`. The kit projects it into the command env
through `sandboxEgressEnv()`, and when `caCertPem` is set it uploads the CA to
the workspace so `NODE_EXTRA_CA_CERTS` resolves inside the sandbox. See
[`<Sandbox>` egress controls](/components/sandbox#egress-controls).

Providers with a real remote filesystem (Daytona, Vercel, Cloudflare) resolve
that CA path directly. The object-store providers (AWS, GCP) have no shared
filesystem, so they upload the CA to their bundle store and pass its location to
the container: AWS injects `SMITHERS_SANDBOX_CA_S3_KEY` and GCP injects
`SMITHERS_SANDBOX_CA_GCS_OBJECT`. The container entry command must download that
object and write it to the path named by `NODE_EXTRA_CA_CERTS` before it makes
outbound calls.

Credentials are used only to build the local SDK client. They are never placed
in the request JSON and never forwarded into the remote env unless you list them
explicitly in `options.env`. `redactSandboxProviderValue()` redacts keys
containing `token`, `secret`, `key`, `password`, `credential`, or
`authorization` from every thrown message, heartbeat payload, and the persisted
`configJson` audit row.

## Provider comparison

| Provider | Package | Provider id | Compute | Bundle transport | Auth | Exit code |
|---|---|---|---|---|---|---|
| Daytona | `smithers-orchestrator/daytona` | `daytona-sandbox` | Daytona sandbox | Sandbox filesystem | `DAYTONA_API_KEY` | numeric |
| Vercel | `smithers-orchestrator/vercel` | `vercel-sandbox` | Vercel Sandbox | Sandbox filesystem | OIDC or token trio | numeric |
| AWS | `smithers-orchestrator/aws` | `aws-sandbox` | Fargate or CodeBuild | S3 bucket (BYO) | AWS credential chain | Fargate numeric, CodeBuild status |
| GCP | `smithers-orchestrator/gcp` | `gcp-sandbox` | Cloud Run Jobs | GCS bucket (BYO) | Application Default Credentials | task status (0/1) |
| Cloudflare | `smithers-orchestrator/cloudflare` | `cloudflare-sandbox` | Sandbox SDK container | Sandbox filesystem | Durable Object binding | numeric |

Per-provider setup, options, and cost live on each provider page:

- [Daytona Sandbox Provider](/integrations/daytona-sandbox-provider)
- [Vercel Sandbox Provider](/integrations/vercel-sandbox-provider)
- [AWS Sandbox Provider](/integrations/aws-sandbox-provider)
- [GCP Sandbox Provider](/integrations/gcp-sandbox-provider)
- [Cloudflare](/integrations/cloudflare)
