iii-sandbox
worker. Enable it in the managed Compose file before starting the engine:
worker-compose.yaml
This page is a quick tour of the sandbox worker. For the authoritative documentation, see the
iii-sandbox worker docs.
sandbox::* triggers, the same way you call any
function (see Triggering functions). Images
are catalog names such as python or node, not arbitrary OCI references. The examples below
capture the new sandbox’s id with jq and stop the sandbox when done so nothing keeps running.
One-shot run
sandbox::run boots a VM, runs a snippet, captures its output, and stops the VM in a single call,
so there is nothing to clean up.
lang accepts node, python, shell, or an interpreter path. Pass keep_sandbox=true to leave
the VM running afterwards (then stop it yourself with sandbox::stop).
Lifecycle
For multi-step work, create a sandbox, operate on it with its id, then stop it. Most triggers take flatkey=value arguments; only nested payloads need --json.
cmd is split into a command and its arguments. It is not a shell, so it
does not expand variables or chain commands; use sandbox::run with lang=shell for that.
The adapter caps the stdout and stderr that sandbox::exec and sandbox::run return. When it
cuts one, the reply sets stdout_truncated or stderr_truncated to true. A contract 1.0 adapter
sends neither key, and the router passes its reply through unchanged, so a missing key means
unknown. A client that parses replies with the iii-sandbox-contract types reads a missing key as
false, so it should trust a false only from an adapter whose contract minor is 1 or later. The
adapter’s contract version is info.contract in the sandbox::adapter::info
reply; the top-level contract there is the router’s own. For large output, write it to a file in
the sandbox and fetch it with sandbox::fs::read.
Concurrency and idle cleanup
A sandbox accepts up tomax_concurrent_exec_per_sandbox simultaneous sandbox::exec calls (four
by default). Concurrent execs share the guest filesystem and process table without mutual
exclusion, so coordinate commands that mutate shared state. sandbox::list reports
exec_in_flight and exec_slots_free; exec_in_progress only indicates whether at least one exec
is running and does not mean the next call will be rejected.
Idle cleanup treats an active exec or relayed TCP, UDP, or DNS payload as activity, so a sandbox
serving network traffic is not automatically stopped while it is handling requests. Bare
acknowledgements and keepalive probes do not count as activity. Each exec remains bounded by
max_exec_timeout_ms, and you should still call sandbox::stop when the sandbox is no longer needed.
Catalog
sandbox::catalog::list reports the images this engine can boot (presets plus any
operator-registered images). Call it when you do not already know what is available. It does not
boot a sandbox, so there is nothing to stop.
Filesystem
Thesandbox::fs::* triggers manipulate files inside a running sandbox. Each takes a sandbox_id
plus operation-specific fields.
sandbox::fs::write and sandbox::fs::read from the previous
section. Both operations stream through iii data channels, so they are also suitable for binary or
larger payloads when called through an SDK.
Error codes
A failedsandbox::* call returns the same error shape whichever sandbox adapter serves it: a stable
code (S-code), a type, and a retryable flag. The codes belong to the engine’s sandbox contract,
not to any one worker: they are part of the public sandbox::* API that every caller sees, the
router returns S301-S303, and every adapter returns the rest. The docs_url in the error payload
links to the matching entry below.
- S001 (validation): request payload failed validation (e.g. missing/malformed sandbox_id)
- S002 (validation): sandbox uuid not found in registry
- S003 (validation): sandbox is at its exec concurrency cap (max_concurrent_exec_per_sandbox, default 4); a slot frees when any in-flight exec finishes, but detach foreground processes rather than waiting on them
- S004 (validation): sandbox already stopped; create a new one
- S100 (config): image not in catalog or allowlist
- S101 (internal): rootfs missing on disk; enable auto_install or pre-provision cache
- S102 (transient): auto-install pull/extract failed; retry
- S200 (execution): exec exceeded timeout_ms
- S210 (filesystem): fs op rejected by supervisor as invalid
- S211 (filesystem): path does not exist
- S212 (filesystem): path is wrong type (file vs dir)
- S213 (filesystem): path already exists
- S214 (filesystem): directory is not empty
- S215 (filesystem): permission denied
- S216 (filesystem): generic fs I/O failure
- S217 (filesystem): invalid regex/pattern in grep or sed
- S218 (transient): fs streaming channel aborted mid-flight; retry
- S219 (filesystem): fs op unsupported by this supervisor; upgrade the sandbox adapter
- S300 (platform): VM boot or supervisor session failure
- S301 (transient): AdapterUnavailable: the sandbox adapter is not connected; retry once it is up
- S302 (config): ContractMismatch: the adapter implements a different contract major version than the engine
- S303 (config): NoAdapterConfigured: this engine has no sandbox adapter; set sandbox.adapter in its engine config
- S304 (platform): OperationUnsupported: the adapter does not implement this operation
- S400 (config): resource limit exceeded (cpu/memory/concurrency cap)
- S501 (validation): SourceRejected: bad manifest, or host_path outside the allowed roots
- S502 (validation): StateBusy: a VM already holds this state key; fix carries the live vm_id and instance
- S503 (validation): InstanceMismatch: the request names a different instance than the live VM; fix carries the live vm_id and instance
- S504 (validation): VmNotFound: no VM with this vm_id
- S505 (validation): Unauthorized: the request auth token does not match
- S506 (transient): PrepareFailed: worker::prepare failed; retry