Releases: The-Ixway/ex_daytona
Release list
v0.4.0 — Testing, snapshots & warm pools, lazy streams, Webhooks.Plug, telemetry
Developer-experience release: no breaking changes, no new required runtime dependencies.
ExDaytona.Testing— in-package test doubles: real clients answered by process-owned expectations/stubs (expect/3+verify!/0,stub/3), HTTP-free sandbox structs, and scripted streaming/websocket transports. Test apps built on the SDK with no Daytona account,async: truesafe.ExDaytona.Snapshot+ExDaytona.WarmPool— build an image into a snapshot once (Snapshot.build/4, with live build-log streaming), thenSandbox.create(snapshot: name)skips the build; warm pools keep sandboxes pre-provisioned for millisecond creates.- Lazy Enumerable streams —
FS.stream!/3(backpressured chunk enumeration; halting cancels the request) andLogStream.events!/2.ExDaytona.Erroris now an exception, so the bang APIs raise it. ExDaytona.Webhooks.Plug— drop-in Svix-verified webhook endpoint that handles the raw-body footgun; compiled only when the optional:plugdependency is present.- Domain telemetry —
[:ex_daytona, ...]spans for sandbox/session/fs/snapshot operations with redaction-safe metadata (outcome, error codes, ids, byte counts). Event table inExDaytona.Telemetry.
Full details in the CHANGELOG.
v0.3.0 — Quota & metering primitives
Quota, usage, and metering primitives for applications that build their
own per-end-user limit or billing logic on top of Daytona's org-level
enforcement. Read primitives only — the SDK ships no tenant policy.
Added
ExDaytona.Quota— normalized org quota truth:overview/2
(snapshot/volume gauges + per region × sandbox-class used-vs-total for
cpu/memory/disk/gpu with per-sandbox maxima),headroom/3(remaining
capacity per dimension, with known-combination hints on miss), and
limits/2(org per-sandbox maxima, secret quota, and
create/lifecycle/API rate limits).- Metering primitives on
ExDaytona.Platform(analytics API, connection
derived automatically):usage_aggregated/4,usage_per_sandbox/4
(the per-sandbox CPU-seconds/RAM-GB-seconds/disk/price rows that
application-defined scopes roll up), andusage_chart/5. ExDaytona.Sandbox.list/2acceptslabels:as a map (JSON-encoded
into the server-side filter) — the attribution primitive for scoping
sandboxes to application-defined identities via labels set at create
time. Pre-encoded strings still pass through.
Live-verified contracts
- The analytics metering endpoints accept plain API keys — verified
against production. Quota.limits/2reads the organization record, which is JWT-gated
(401 with API keys, documented); API-key callers get the per-sandbox
maxima fromQuota.overview/2instead.
All gates green: 356 tests, dialyzer/credo/docs clean, 96.6% coverage, CI incl. Elixir 1.18/OTP 27 matrix, and the full live suite (8/8) against app.daytona.io.
v0.2.0 — Production hardening
Production-hardening release: constant-memory transfer, structured
bounded log streaming, response/rate-limit metadata, retry correctness,
credential redaction, a completed security facade, and injectable
streaming transports. No breaking changes to 0.1.0 call sites.
Added
- Constant-memory file transfer:
ExDaytona.FS.upload_stream/4,
upload_file/4,download_stream/4, anddownload_file/4— lazy
multipart uploads from Enumerables/IO devices/files, consumer-driven
downloads,max_bytes/idle/overall limits, caller cancellation,
incremental SHA-256 with optional verification, and atomic
temp-file-then-rename downloads that never clobber an existing
destination. Existing small-file helpers unchanged (buffering now
documented). - Structured log streaming:
ExDaytona.LogStream+
ExDaytona.Session.open_log_stream/3— pull-based (next/2,
collect/2), owner-monitored, bounded (buffer/frame caps with
explicit overflow errors), with idle and overall timeouts and no
silent reconnect. Decodes the provider's stdout/stderr channel-marker
protocol into separate:stdout/:stderrevents; daemons that stream
unlabeled output (the production daemon at release time — verified
live) yield merged{:output, bytes}events instead of dropping data.Session.stream_logs/4
remains as the documented merged-output HTTP path and gains:halt
cancellation and a:deadline. - Response metadata:
response: :fullon every generated operation
returns%ExDaytona.Response{}(status, normalized headers, request
id, rate-limit state, parsed Retry-After, transport retry count).
ExDaytona.Errorcarries the same fields on every facade error, plus
anoutcome(:definite|:unknown) that refuses to claim a
definite result for non-idempotent requests lost in transport. - Security facade:
Sandbox.create/2now exposes every
CreateSandbox field (domain_allow_list, vaultsecretsbindings,
auto_pause_interval,gpu_type,spot,linked_sandbox,
outbound_proxy_url,otel_endpoint_override);
Sandbox.update_network_settings/2for runtime policy;
ExDaytona.Secrets(manage, bind, resolve — resolved values redacted
under inspect);ExDaytona.Platform(regions, sandbox classes,
snapshots, usage/quota). - Injectable streaming transports:
ExDaytona.Transportbehaviours
for streaming HTTP and websockets, selected via
Client.new(transports: [...])and carried into every derived stream —
built for deterministic failure simulation in tests. - Dedicated Finch pool (
ExDaytona.Finch.Stream, tunable via
:stream_pool_size/:stream_pool_count) isolates long-lived streams
and bulk transfers from lifecycle/control requests.
Changed
- Credential redaction everywhere: every generated model, the
client, errors, and the new SSH/preview/storage result structs render
credential-shaped fields as"[REDACTED]"underinspect/1; error
details/headers are deep-sanitized; URLs in messages have signed query
parameters scrubbed; telemetry no longer carries the Tesla client (and
its embedded bearer token). Note: field-name matching intentionally
over-redacts (e.g. paginationnextTokencursors render redacted). - Retries now forward
jitter_factorand honorRetry-Afteron safe
(idempotent) retries by default (use_retry_after_header: true,
capped bymax_delay); POSTs remain never auto-retried. Sandbox.ssh_access/2,preview_url/2,signed_preview_url/3, and
ObjectStorage.push_access/1return dedicated structs with redacted
inspection — pattern-matching on the previous map shapes continues to
work.
Migration notes
- No call-site changes required. If you pattern-matched telemetry
metadata'senv.__client__, it is nownil(credential hygiene). - If your handlers relied on retries NOT honoring
Retry-After, pass
retry: [use_retry_after_header: false].
Every gate validated: 347 unit/mock tests, dialyzer clean, credo strict clean, 96.7% coverage, docs zero-warning, and the full live suite (8/8) against app.daytona.io — including 4MB checksummed streaming transfer, structured log streaming over the live websocket, create-time secret bindings, tier-aware network policy, and owner-death stream cleanup.
v0.1.0 — Initial release
Added
- Initial SDK generated from Daytona's OpenAPI specifications (main
platform, toolbox, and analytics APIs merged byscripts/fetch-spec.sh) ExDaytona.Toolbox.connection/2— builds a connection to a sandbox's
toolbox API from itstoolboxProxyUrlExDaytona.Analytics.connection/1— builds a connection to the analytics
API (base URL configurable viaconfig :ex_daytona, :analytics_base_url)- High-level SDK facade (hand-written, survives regeneration):
ExDaytona.Client(API-key auth,DAYTONA_API_KEYfallback),
ExDaytona.Error(all generated-client failure shapes normalized to
{:error, %ExDaytona.Error{}}), andExDaytona.Sandbox
(create/get/list/start/stop/delete with state waiting,exec/3,
write_file/3,read_file/2,list_files/2) ExDaytona.Session— persistent shell sessions with shared state:
synchronousrun/2, asynchronousrun_async/2+await/3, raw
logs/2, and real-timestream_logs/4(incremental chunked-HTTP
streaming)ExDaytona.Git— clone/status/branches/create_branch/checkout/add/
commit/push/pull/history inside a sandbox, with credential options for
authenticated remotesExDaytona.Sandbox.build_logs/1andstream_build_logs/3— fetch or
follow a building sandbox's build logsExDaytona.Pty— interactive terminals: create/resize/list/delete plus
a real websocket connection (connect/2,send_input/2) built on the
newExDaytona.WebSocketclient (Mint.WebSocket)- SSH access on the sandbox facade:
ssh_access/2,revoke_ssh_access/1,
validate_ssh_access/2 - Preview URLs on the sandbox facade:
preview_url/2,
signed_preview_url/3,expire_signed_preview_url/3; plus
ExDaytona.PreviewProxyverification helpers for running a custom
preview proxy ExDaytona.Webhooks— organization webhook setup (initialize, status,
Svix app portal access, endpoint refresh) andverify/4for receiving:
Svix/Standard-Webhooks HMAC signature verification with timestamp
toleranceExDaytona.Image— declarative image DSL (from/run/env/workdir/user/ label/expose/entrypoint/cmd+ raw Dockerfiles) wired into
ExDaytona.Sandbox.create(client, image: ...)for building sandboxes
from DockerfilesExDaytona.FS— the full file-system facade:write_file/read_file/
write_files, localupload/download,mkdir,stat,delete
(recursive),move,chmod, globsearch, contentgrep, and text
replacewith per-file results (theSandboxfile helpers delegate
here)ExDaytona.Sandbox.run_code/3— stateless code snippets (Python/
JavaScript/TypeScript) with argv/env/timeout and chart artifactsExDaytona.CodeInterpreter— stateful Python execution over the
interpreter websocket: state persists betweenrun/3calls, isolated
contexts (create_context/list_contexts/delete_context), streaming
on_stdout/on_stderr/on_errorcallbacks, and structured execution
errorsExDaytona.Session.send_input/3(with a retry for the daemon's
stdin-pipe startup race and asuppress_input_echooption on
run_async/3), plusentrypoint/1andentrypoint_logs/1- Local build contexts:
ExDaytona.Image.add_local_file/3and
add_local_dir/3copy local files into declaratively built images —
contexts are content-hashed, tarred, and uploaded to Daytona's object
storage automatically on create (ExDaytona.ObjectStorage), with a
dependency-free AWS SigV4 signer pinned by AWS's published test
vectors
Fixed
- Request bodies no longer send explicit JSON
nulls for unset optional
model fields — the Daytona API rejects them (500) where an empty object
succeeds; models now omit nil fields when encoding
Changed
- Cleaned generated function names via a spec patch: analytics endpoints
that ship without operationIds get descriptive ones
(get_organization_usage_aggregated/5instead of
organization_organization_id_usage_aggregated_get/5), and leaked NestJS
controller prefixes are stripped (Health.check/2instead of
Health.health_controller_check/2)