---
title: Daytona Sandbox Provider
description: Run a Smithers <Sandbox> child workflow inside a Daytona sandbox with createDaytonaSandboxProvider.
---

# Daytona Sandbox Provider

`@smithers-orchestrator/daytona` is a first-class Smithers `SandboxProvider`
backed by the [Daytona](https://www.daytona.io/) SDK. It runs a `<Sandbox>`
child workflow's request inside a Daytona sandbox: it uploads the request JSON,
runs the entry command, and reads the result JSON back. The shared provider-kit
owns the request/result protocol, egress, secret scrubbing, and cleanup, so this
package only maps the small `SandboxSession` seam onto Daytona.

The provider id is `daytona-sandbox` (exported as `DAYTONA_SANDBOX_PROVIDER_ID`).

```ts
import {
  createDaytonaSandboxProvider,
  registerDaytonaSandboxProvider,
  DAYTONA_SANDBOX_PROVIDER_ID,
} from "smithers-orchestrator/daytona";
```

## Credentials

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

```
npm install @daytonaio/sdk
```

Credentials come from factory options first, then the Daytona env chain. They
are used only to build the local SDK client. They are never written into the
request JSON and never forwarded into the remote sandbox env.

- `DAYTONA_API_KEY`: API key (required for a real client).
- `DAYTONA_API_URL`: API base url (optional).
- `DAYTONA_TARGET`: target region (optional).

The same values may be passed as `apiKey`, `apiUrl`, and `target` factory
options, or through `clientOptions`.

## Usage

Pass the provider straight to `<Sandbox>`:

```tsx
import { createSmithers, Sandbox } from "smithers-orchestrator";
import { createDaytonaSandboxProvider } from "smithers-orchestrator/daytona";

const provider = createDaytonaSandboxProvider({
  image: "ubuntu:22.04",
  autoStopInterval: 15, // minutes idle before Daytona auto-stops
  ephemeral: true,      // delete the sandbox on disconnect
});

export default parent.smithers((ctx) => (
  <parent.Workflow name="daytona-sandbox-run">
    <Sandbox
      id="remote-edit"
      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 { registerDaytonaSandboxProvider } from "smithers-orchestrator/daytona";

const unregister = registerDaytonaSandboxProvider({ image: "ubuntu:22.04" });

// <Sandbox provider="daytona-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? }`. The kit fills
`remoteRunId`/`workspaceId` from the Daytona sandbox id when the entry omits
them.

## Factory options

- `image` or `snapshot`: the base for `daytona.create` (pass one, not both).
- `autoStopInterval`: minutes idle before Daytona auto-stops (default `15`).
- `ephemeral`: delete the sandbox on disconnect (default `true`).
- `resources`, `labels`, `env`: forwarded to `daytona.create`.
- `command`: entry command (default `node /workspace/run-smithers-sandbox.js`).
- `workdir`: default `/workspace`.
- `cleanup`: `"destroy"` (default) or `"keep"`.
- `client` / `clientOptions`: inject an SDK client or its constructor options.

Per-run `request.config` may carry `image`, `snapshot`, `resources`, `labels`,
or a `workspace` block (`snapshotId`, `idleTimeoutSecs`,
`persistence: "ephemeral"`) which maps onto the create options.

## SDK subset used

`daytona.create({ image?|snapshot?, envVars, labels, ephemeral, autoStopInterval,
resources })`, `sandbox.fs.uploadFile(Buffer, path)`,
`sandbox.fs.downloadFile(path)`, `sandbox.process.executeCommand(command, cwd,
env, timeoutSecs)` (Daytona merges stderr into `result`), and
`daytona.delete(sandbox)`.

## Cleanup and cost

`cleanup: "destroy"` (default) deletes the sandbox after the run. `cleanup:
"keep"` leaves it running. `ephemeral` plus `autoStopInterval` bound idle cost
even when cleanup is skipped, so a crashed orchestrator does not leave a sandbox
billing forever. You pay for Daytona sandbox time from create to teardown.

`createMockDaytonaSandboxEnvironment(handler, faults?)` is an in-memory SDK
double for tests. It needs zero credentials, so unit tests run in CI without
touching Daytona.
