High-level sandbox facade: lifecycle, command execution, and files.
Wraps the generated ExDaytona.Api.Sandbox (platform) and toolbox APIs
(Api.Process, Api.FileSystem) with idiomatic Elixir — snake_case
options, {:error, %ExDaytona.Error{}} failures, and a sandbox struct
that carries its client so toolbox calls need no extra wiring:
{:ok, client} = ExDaytona.Client.new()
{:ok, sandbox} = ExDaytona.Sandbox.create(client, snapshot: "...")
{:ok, %{exit_code: 0, output: out}} = ExDaytona.Sandbox.exec(sandbox, "echo hello")
:ok = ExDaytona.Sandbox.write_file(sandbox, "/workspace/hello.txt", "hi")
{:ok, "hi"} = ExDaytona.Sandbox.read_file(sandbox, "/workspace/hello.txt")
:ok = ExDaytona.Sandbox.delete(sandbox)For sandbox operations the facade doesn't cover, drop down to the
generated modules with ExDaytona.Client.conn/1 (platform) or
toolbox_conn/1 (toolbox).
Summary
Functions
Poll until the sandbox reaches state (a string or list of strings).
The sandbox's build logs so far, as a binary. Only sandboxes built from build info have build logs; snapshot-based sandboxes return an error.
Create a sandbox and (by default) wait for it to reach the started
state.
Delete a sandbox (by struct or id). Returns :ok.
Run a shell command inside the sandbox.
Expire a signed preview URL before its natural expiry. Returns :ok.
Fetch a sandbox by id or name.
The sandbox id.
List sandboxes. Accepts the generated list_sandboxes filters
(:limit, :name, :labels, :states, ...) and returns
{:ok, %{items: [...], next_cursor: cursor}} where items are
ExDaytona.Model.SandboxListItem structs.
List files at path inside the sandbox (default: the working
directory). Delegates to ExDaytona.FS.list_files/2.
The preview URL for a port the sandbox is listening on. Returns
{:ok, %{url, token}} — for private sandboxes, send the token as the
x-daytona-preview-token header (browsers hitting the URL directly get
Daytona's auth flow).
Read the file at path inside the sandbox. Delegates to
ExDaytona.FS.read_file/2.
Re-fetch the sandbox's current state from the API.
Revoke the sandbox's SSH access. Returns :ok.
Run a code snippet with a fresh interpreter each time (stateless).
Python, JavaScript, and TypeScript are supported; for persistent state
across runs use ExDaytona.CodeInterpreter.
A signed (self-authenticating, expiring) preview URL for a port —
shareable without exposing an auth token header. Returns
{:ok, %{url, token}}; expire it early with
expire_signed_preview_url/3.
Create SSH access to the sandbox. Returns
{:ok, %{token, ssh_command, expires_at}} — run ssh_command in a
terminal, or use token as the SSH username against Daytona's SSH
gateway.
Start a stopped sandbox. Waits for started unless wait: false.
The sandbox state as a string ("started", "stopped", ...).
Stop a running sandbox. Waits for stopped unless wait: false.
Follow the sandbox's build logs in real time: fun is invoked with
each chunk as it is produced, and the call returns :ok when the build
finishes and the stream closes.
The Tesla client for this sandbox's toolbox API, for generated toolbox
operations the facade doesn't cover (Api.Git, Api.Lsp,
Api.ComputerUse, ...). Fails if the sandbox has no toolboxProxyUrl
yet (still starting).
Update the sandbox's network policy at runtime. At least one option is required
Validate an SSH access token (for building SSH gateways/tooling).
Returns {:ok, %{valid: boolean, sandbox_id: id | nil}}.
Write content to path inside the sandbox. Delegates to
ExDaytona.FS.write_file/3 — see ExDaytona.FS for the full
file-system surface.
Types
@type t() :: %ExDaytona.Sandbox{ client: ExDaytona.Client.t(), info: ExDaytona.Model.Sandbox.t() }
A sandbox bound to the client that created or fetched it.
info is the raw ExDaytona.Model.Sandbox (camelCase fields, as sent by
the API); id/1 and state/1 are the ergonomic accessors.
Functions
@spec await_state(t(), String.t() | [String.t()], keyword()) :: {:ok, t()} | {:error, ExDaytona.Error.t()}
Poll until the sandbox reaches state (a string or list of strings).
Fails fast with {:error, %Error{}} if the sandbox enters an error state
(["error", "build_failed", "destroyed", "destroying"]), and with a timeout error after :timeout
milliseconds (default 120_000; poll interval :poll_interval, default
1_000).
@spec build_logs(t()) :: {:ok, binary()} | {:error, ExDaytona.Error.t()}
The sandbox's build logs so far, as a binary. Only sandboxes built from build info have build logs; snapshot-based sandboxes return an error.
@spec create( ExDaytona.Client.t(), keyword() ) :: {:ok, t()} | {:error, ExDaytona.Error.t()}
Create a sandbox and (by default) wait for it to reach the started
state.
Options
:wait— wait until the sandbox is running (defaulttrue):timeout— max milliseconds to wait (default120_000):poll_interval— milliseconds between state polls (default1_000):image— build the sandbox declaratively instead of from a snapshot: anExDaytona.Imageor a raw Dockerfile string. Building takes longer than starting from a snapshot — raise:timeoutaccordingly and watch progress withstream_build_logs/3.- sandbox settings, all optional:
:snapshot,:name,:user,:cpu,:gpu,:gpu_type,:memory,:disk,:env,:labels,:public,:target,:volumes,:spot,:linked_sandbox,:outbound_proxy_url,:otel_endpoint_override,:ttl_minutes,:auto_stop_interval,:auto_pause_interval(at most one of stop/pause may be non-zero),:auto_archive_interval,:auto_delete_interval - network policy:
:network_block_all,:network_allow_list(CIDRs),:domain_allow_list(comma-separated domains) — changeable later withupdate_network_settings/2 :secrets— vault-backed secret bindings, a list of single-entry maps%{"ENV_VAR" => "vault-secret-name"}(seeExDaytona.Secrets)
With no settings the API uses the organization's default snapshot.
env values are not secrets
Ordinary :env values are stored and transmitted in plain text —
they are visible to sandbox processes, in sandbox metadata, and to
the provider API. Put credentials in vault secrets and mount them
with :secrets instead.
@spec delete(t() | String.t(), ExDaytona.Client.t() | nil) :: :ok | {:error, ExDaytona.Error.t()}
Delete a sandbox (by struct or id). Returns :ok.
@spec exec(t(), String.t(), keyword()) :: {:ok, %{exit_code: integer() | nil, output: String.t()}} | {:error, ExDaytona.Error.t()}
Run a shell command inside the sandbox.
Options
:cwd— working directory:env— extra environment variables (map):timeout— command timeout in seconds (API-side)
Returns {:ok, %{exit_code: integer, output: binary}}.
@spec expire_signed_preview_url(t(), pos_integer(), String.t()) :: :ok | {:error, ExDaytona.Error.t()}
Expire a signed preview URL before its natural expiry. Returns :ok.
@spec get(ExDaytona.Client.t(), String.t()) :: {:ok, t()} | {:error, ExDaytona.Error.t()}
Fetch a sandbox by id or name.
The sandbox id.
@spec list( ExDaytona.Client.t(), keyword() ) :: {:ok, %{ items: [ExDaytona.Model.SandboxListItem.t()], next_cursor: String.t() | nil }} | {:error, ExDaytona.Error.t()}
List sandboxes. Accepts the generated list_sandboxes filters
(:limit, :name, :labels, :states, ...) and returns
{:ok, %{items: [...], next_cursor: cursor}} where items are
ExDaytona.Model.SandboxListItem structs.
:labels may be a map — it is JSON-encoded into the server-side label
filter, the primitive for attributing sandboxes to your application's
own scopes (e.g. per end-user of a SaaS product):
Sandbox.list(client, labels: %{"my-app/tenant" => "user-123"})A pre-encoded JSON string is also accepted.
@spec list_files(t(), String.t() | nil) :: {:ok, [ExDaytona.Model.FileInfo.t()]} | {:error, ExDaytona.Error.t()}
List files at path inside the sandbox (default: the working
directory). Delegates to ExDaytona.FS.list_files/2.
@spec preview_url(t(), pos_integer()) :: {:ok, ExDaytona.Sandbox.PreviewUrl.t()} | {:error, ExDaytona.Error.t()}
The preview URL for a port the sandbox is listening on. Returns
{:ok, %{url, token}} — for private sandboxes, send the token as the
x-daytona-preview-token header (browsers hitting the URL directly get
Daytona's auth flow).
@spec read_file(t(), String.t()) :: {:ok, binary()} | {:error, ExDaytona.Error.t()}
Read the file at path inside the sandbox. Delegates to
ExDaytona.FS.read_file/2.
@spec refresh(t()) :: {:ok, t()} | {:error, ExDaytona.Error.t()}
Re-fetch the sandbox's current state from the API.
@spec revoke_ssh_access(t()) :: :ok | {:error, ExDaytona.Error.t()}
Revoke the sandbox's SSH access. Returns :ok.
@spec run_code(t(), String.t(), keyword()) :: {:ok, %{ exit_code: integer() | nil, result: String.t() | nil, artifacts: ExDaytona.Model.CodeRunArtifacts.t() | nil }} | {:error, ExDaytona.Error.t()}
Run a code snippet with a fresh interpreter each time (stateless).
Python, JavaScript, and TypeScript are supported; for persistent state
across runs use ExDaytona.CodeInterpreter.
Options
:language—"python"|"javascript"|"typescript"(server default when omitted):argv— command-line arguments (list):env— environment variables (map):timeout— execution timeout in seconds (API-side)
Returns {:ok, %{exit_code, result, artifacts}} where result is the
combined output and artifacts carries chart captures when present.
@spec signed_preview_url(t(), pos_integer(), keyword()) :: {:ok, ExDaytona.Sandbox.PreviewUrl.t()} | {:error, ExDaytona.Error.t()}
A signed (self-authenticating, expiring) preview URL for a port —
shareable without exposing an auth token header. Returns
{:ok, %{url, token}}; expire it early with
expire_signed_preview_url/3.
Options: :expires_in_seconds — link lifetime (server default when
omitted).
@spec ssh_access( t(), keyword() ) :: {:ok, ExDaytona.Sandbox.SshAccess.t()} | {:error, ExDaytona.Error.t()}
Create SSH access to the sandbox. Returns
{:ok, %{token, ssh_command, expires_at}} — run ssh_command in a
terminal, or use token as the SSH username against Daytona's SSH
gateway.
Options: :expires_in_minutes — token lifetime (server default when
omitted).
@spec start( t(), keyword() ) :: {:ok, t()} | {:error, ExDaytona.Error.t()}
Start a stopped sandbox. Waits for started unless wait: false.
The sandbox state as a string ("started", "stopped", ...).
@spec stop( t(), keyword() ) :: {:ok, t()} | {:error, ExDaytona.Error.t()}
Stop a running sandbox. Waits for stopped unless wait: false.
@spec stream_build_logs(t(), (binary() -> any()), keyword()) :: :ok | {:error, ExDaytona.Error.t()}
Follow the sandbox's build logs in real time: fun is invoked with
each chunk as it is produced, and the call returns :ok when the build
finishes and the stream closes.
Options: :timeout — max milliseconds to wait between chunks
(default :infinity).
@spec toolbox_conn(t()) :: {:ok, Tesla.Env.client()} | {:error, ExDaytona.Error.t()}
The Tesla client for this sandbox's toolbox API, for generated toolbox
operations the facade doesn't cover (Api.Git, Api.Lsp,
Api.ComputerUse, ...). Fails if the sandbox has no toolboxProxyUrl
yet (still starting).
@spec update_network_settings( t(), keyword() ) :: {:ok, t()} | {:error, ExDaytona.Error.t()}
Update the sandbox's network policy at runtime. At least one option is required:
:domain_allow_list— comma-separated allowed domains:network_allow_list— comma-separated allowed CIDRs:network_block_all— block all network access
Returns the updated sandbox. Organizations on tiers with enforced network restrictions reject sandbox-level overrides with a 400 ("Network access is restricted…", verified live) — see Daytona's tier-based network restriction docs.
@spec validate_ssh_access(ExDaytona.Client.t(), String.t()) :: {:ok, %{valid: boolean() | nil, sandbox_id: String.t() | nil}} | {:error, ExDaytona.Error.t()}
Validate an SSH access token (for building SSH gateways/tooling).
Returns {:ok, %{valid: boolean, sandbox_id: id | nil}}.
Authentication
This endpoint authenticates gateway infrastructure — with a regular
user API key it returns 403 "Invalid authentication context".
@spec write_file(t(), String.t(), iodata()) :: :ok | {:error, ExDaytona.Error.t()}
Write content to path inside the sandbox. Delegates to
ExDaytona.FS.write_file/3 — see ExDaytona.FS for the full
file-system surface.