Skip to main content

Daytona Sandbox Provider

@smithers-orchestrator/daytona is a first-class Smithers SandboxProvider backed by the Daytona 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).
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>:
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:
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.