Current section
Files
Jump to
Current section
Files
docs/client.md
# Low-level client
This API performs one verified worker operation at a time. It does not persist
request identities, reserve capacity, reconcile a crash or authorize deletion.
Use it when your application owns those responsibilities. For a complete managed
execution, begin with [Getting started](getting-started.md).
Install a [supported worker](supported-platforms.md), prepare an approved image
for its architecture and start a private `smolvm serve` endpoint. For bounded
small-file workloads, set `SMOLVM_FILE_TRANSFER_MAX_BYTES=1048576` before starting
the server. Check [host prerequisites](supported-platforms.md#host-prerequisites),
including `resize2fs`, before requesting disks below the worker's template sizes.
For an existing deployment, follow [Upgrading SmolBox](upgrading.md).
```elixir
{:ok, worker} = SmolBox.Worker.new("worker-1", "https://worker.internal.example",
token: System.fetch_env!("WORKER_PROXY_TOKEN"),
ca_cert_file: "/etc/my-app/worker-ca.pem")
{:ok, client} = SmolBox.Client.new(worker)
```
Remote workers require an authenticated TLS proxy. The operator supplies it;
SmolBox supplies no public worker service. TLS peer and hostname verification
cannot be disabled. Loopback development requires `allow_insecure_loopback: true`.
A local Unix socket can instead use `unix_socket: "/private/run/smolvm.sock"`
with `http://localhost`. Client construction makes no network request.
`Client.health(client)` reads the server-reported version, optional inventory
counts and uptime as a `SmolBox.Health` value. Missing counts remain `nil`; they
are not treated as zero. `Client.readiness(client)` requires the separate
blocking-pool probe to return HTTP 200 with an empty body. It preserves normal
authentication, TLS, deadline and byte-limit checks. JSON/SSE responses still
require their own media types. These endpoints do not attest artifact digests,
host quotas or isolation.
Creation replies must match the requested name, CPU count, guest memory, both
disk allocations, network policy and canonical port mappings. A mismatch returns a protocol error with uncertain
creation evidence. Managed execution does not start that guest or adopt it for
automatic cleanup; the original reservation remains available for operator
investigation. An erroneous worker reply must not silently change execution policy.
By default an owned disposable machine starts from an approved artifact with
guest networking disabled. Explicit outbound policies are described in
[Controlled network access](network-access.md). Fixed TCP mappings require a
supported worker from 1.17.0 onward; see [Port mappings](port-mappings.md).
This example uses offline networking without mounts, published ports, GPU or
automatic workload restart:
```elixir
{:ok, name} = SmolBox.Identity.machine_name("myapp")
# Persist name and intent in your host before creating the machine.
{:ok, spec} = SmolBox.MachineSpec.new(name, "/approved/python.smolmachine",
storage_gb: 20, overlay_gb: 10)
{:ok, created} = SmolBox.Client.create(client, spec)
# Persist creation evidence before continuing.
{:ok, running} = SmolBox.Client.start(client, name)
true = SmolBox.Machine.same_incarnation?(created, running)
source = "print('hello')\n"
:ok = SmolBox.Client.upload(client, name, "/workspace/main.py",
source, SmolBox.Files.sha256(source))
{:ok, command} = SmolBox.Command.new(["python", "/workspace/main.py"], timeout_secs: 10)
{:ok, result} = SmolBox.Client.exec(client, name, command, max_output_bytes: 65_536)
```
The example shows the successful path. Production callers must handle each typed
error. A machine name or matching `createdAt` alone is insufficient ownership
proof after a conflict or manual replacement. Use an exclusive managed namespace,
save creation evidence, and refuse cleanup when identity differs. To preserve a
machine's disks, stop it and verify the same incarnation is no longer running.
For authorized disposal after collection and any required retention, delete the
verified owned machine and then inspect it to confirm absence. A failed stop
alone does not authorize disposal. No cleanup primitive should be placed in an
unconditional `after` block without checking identity and preserving uncertain
execution evidence first. The managed runtime's rules are described in
[preservation and disposal](recovery.md#preservation-and-disposal).
`exec/4` returns byte-exact stdout/stderr from upstream base64 fields. Nonzero exit
codes are observed results, not transport errors. `exec_stream/4` uses the actual
SSE protocol, captures bounded **lossy UTF-8**, and accepts `on_event: callback`.
Callbacks are synchronous, provide backpressure, and are bounded by the overall
operation timeout. A crashing optional callback is detached. Notifications are
advisory; they are not a durable event log. SmolBox rejects streaming stdin.
Use buffered execution or staged binary files instead.
Worker configuration separately bounds connection, pool checkout, receive idle,
overall operation, request bytes, and response bytes. The response cap includes
SSE framing and ignored events; exec also has an aggregate decoded stdout/stderr
cap. Req automatic retries, redirects, decompression, and body decoding are
disabled. Compressed responses are rejected. Limits on controller capture do not
replace upstream or worker-host limits.
A timeout, lost connection, malformed result, or output overflow after possible
dispatch returns uncertainty. **Never replay exec automatically.** Disconnecting
the stream does not cancel the guest. Stop an owned VM separately and verify its
state; stopping a VM does not recover an unknown command exit code.
For read-only CPU, memory, disk and network snapshots, use
`Client.machine_measurements/2`. `Client.capacity/1` returns typed worker usage;
`Client.metrics/1` returns bounded Prometheus text. See
[measurement units, availability and admission boundaries](worker-measurements.md).
## Checkpoint sources
An operator-approved idle checkpoint can be created with
`MachineSpec.new(name, path, source: :checkpoint, ...)`. The explicit allocations
must match the capture. This requires a supported worker from 1.16.1 onward and
an offline source; creation must return a created branchable machine before it
may be started. Captured
processes resume on start, so a workload entrypoint override cannot neutralize
an arbitrary checkpoint. See [Executing from a checkpoint](checkpoints.md) for
approval, managed execution, examples and schema v3 upgrade requirements.
## Execution and transport budgets
Foreground timeouts accept 1–86,400 seconds. Commands exceeding 300 seconds require
a supported worker from 1.17.0 onward and an explicitly extended client operation
budget. Managed commands also require an approved profile whose `execution_ms`
covers the guest timeout.
Allow observation headroom for startup, transport and the final timeout result.
Preparation, collection and cleanup keep separate budgets. Caller `await` expiry
never cancels work. Extended exec uses its remaining operation budget for receive
idle time, allowing quiet commands without changing global client defaults.
`background: true` uses buffered `Client.exec/4` on a non-checkpoint image machine
and returns `%SmolBox.LaunchResult{pid: pid}`. It rejects stdin, non-nil guest timeout,
and streaming. A PID confirms launch, not readiness, liveness or final exit status.
See [Long-running commands and background launch](long-running-exec.md) for exact
budgets, persistence, cancellation and examples.
Historical 1.16.0 qualification observed client operations over five minutes due
to startup plus a guest command under 300 seconds. Those measurements do not
establish longer guest execution; they remain in the historical qualification report.
## File operations
File manifests use exact approved guest paths and opaque host artifact references.
Defaults remain `/workspace` and 1 MiB. `GuestPaths` and coordinated profile, client,
transport and artifact-store budgets allow broader directories and files up to
16 MiB on image machines with a supported worker from 1.17.0 onward. Uploads
verify their source digest before I/O and validate the worker acknowledgment;
downloads require an explicit
byte limit. See [guest paths and larger files](guest-files.md) for setup, buffered
memory costs, worker download caps and the v9 persistence upgrade.
The pinned agent has an atomic file-install path, but the HTTP API offers no
caller-controlled rename transaction or permissions. Symlink handling also depends
on the agent's active guest namespace; lexical validation alone does not certify
race-free containment. No archives or recursive patterns are interpreted.
Both file endpoints can **auto-start a stopped machine**. Never download as a
harmless recovery probe or collect after confirmed termination. Keep stop/delete
as the final lifecycle operations for cancelled work. Host artifact credentials
must stay outside the guest, and host storage must protect command and environment
contents at rest.
## Preparing the reference runtimes
For a first Python execution, follow [Getting started](getting-started.md). It
includes image preparation, a separate demo worker and a downloadable Livebook.
The recipes below also cover Node and production artifact approval.
This is an operator step using upstream smolvm, outside the library's execution
API. It prepares a base language runtime; it does not build or publish user
functions. Perform it on an isolated preparation host with the matching native
architecture, sufficient disk/memory and the pinned installation. Preparation
may fetch images with networking; offline execution later must not.
From a new private directory with enough space for layers, templates and output,
the supported CLI provides these preparation commands:
```sh
smolvm pack create --image python:3.12-alpine --entrypoint /bin/true \
--cpus 1 --mem 256 --staging-dir ./staging --output ./python
smolvm pack create --image node:22-alpine --entrypoint /bin/true \
--cpus 1 --mem 256 --staging-dir ./staging --output ./node
```
The output names are executable stubs; the corresponding payloads are
`python.smolmachine` and `node.smolmachine`. Do not pass a `.smolmachine` extension
as `--output`, use `--single-file`, or reuse existing output names. SmolBox uses
the sidecar payload and does not invoke the packed executable. The CLI's default
pack memory is 8192 MiB, so the explicit resource options matter. Check local
`smolvm pack create -h` against the pinned version before changing the recipe.
These tags identify the initial test recipe, not immutable production approvals.
Select and record an approved OCI digest for repeatable preparation, then record
the resulting payload's SHA-256, host OS/architecture, upstream binary checksum
and template geometry. Use `shasum -a 256` on macOS or `sha256sum` on Linux to
hash each payload. A rebuilt artifact gets a new approved revision and new live
evidence even when its source image tag is unchanged.
Copy only the verified runtime payload into an operator-owned catalog on the
worker and verify the digest there. Inspect neutrality and keep secrets,
host mounts, ports and automatic workload restart out of the artifact/configuration.
Managed creation additionally forces `/bin/true`, empty command arguments and
restart `never`. Qualify offline Python/JS execution, binary staging/collection
and stop/start without replay before admitting the artifact. The real-runtime
tests exercise those behaviors; a pack operation alone does not qualify an image.
Choose the command's guest identity explicitly with `Command.new(argv, user:
"65534:65534")` when it matters, and verify the resulting UID and file access
with your artifact. SmolBox passes this choice to execution; it does not enforce
an application-wide guest user policy. Changes to upstream CLI `USER` handling
do not by themselves establish the same behavior through the HTTP API.
The released templates measured 20/10 GiB. Validate them and any larger artifact
templates before setting `allocation_floor`; do not infer physical disk capacity
from a smaller create request or initially sparse files. Keep preparation caches
under a separate host budget. See [resource qualification](resource-qualification.md).
Start a separately provisioned private worker with the tested file cap:
```sh
SMOLVM_FILE_TRANSFER_MAX_BYTES=1048576 \
smolvm serve start --listen 127.0.0.1:19470
```
This starts a local service, not an authenticated public endpoint. Configure
worker account isolation, hard host limits and the remote proxy independently
as described in [deployment boundaries](security.md). On Linux, an explicitly
configured `SMOLVM_DATA_DIR` can separate worker state. The pinned macOS build
uses its normal account state directory; that environment variable does not
isolate it. Use a dedicated account/host for a new macOS worker and never clear
shared caches or inventories to simulate a fresh installation.
## Interactive terminals
`Client.open_terminal/3` opens a byte-preserving WebSocket PTY and returns a
consumer-bound `SmolBox.Terminal.Handle`. Use `Terminal.input/2`, `next/2`,
`resize/3` and `close/1`. The supported program is one executable, with initial
columns and rows; ordinary argv, environment, stdin, user and workdir options
are rejected. The PTY merges stdout and stderr. A successful handshake does not
prove the guest program is ready.
The low-level caller supplies ownership checks and lifecycle coordination. The
upstream upgrade can start the machine, so use `Terminal.open/3` on a managed
machine for persisted intent and the shared command slot. HTTP(S) and Unix sockets
use the configured worker token and TLS verification. Custom HTTP transport
adapters are rejected rather than bypassed. See [Interactive terminals](interactive-terminals.md)
for deadlines, bounded streaming, cancellation and recovery.
## Startup workloads and console diagnostics
`SmolBox.MachineSpec.new/3` accepts optional `workload: %SmolBox.Workload{}`
on image machines with a supported worker from 1.17.0 onward. `Client.logs/3`
returns a console `LogResult` or follows bounded SSE events. These diagnostics
exclude application stdout/stderr;
automatic restart policies are rejected. See [workloads](workloads.md) for exact
argument inheritance, stream bounds and cancellation behavior.
## Local volumes and mounts
On smolvm 1.20.2 or 1.22.0, `Client.provision_volume/3` and `Client.delete_volume/2` expose
local provisioning and explicit deletion. `MachineSpec` accepts controlled
`SmolBox.Mount` values; source, guest target and read-only state are decoded in
observations. Low-level callers own host path approval and attachment cleanup.
Use `SmolBox.Volumes` for durable ownership and reservations. See
[local volumes](local-volumes.md) for the Linux policy, 204 deletion acknowledgment,
permission limitations and the PostgreSQL walkthrough.