DEFAULT_LIST_LIMIT
Default page size forSailbox.list and Sailbox.list_page, sourced
from the shared core.
IngressPort
protocol selects how the port is published:
"http"(the default) exposes the port as an HTTP service with a stable HTTPS URL."tcp"exposes the port as a byte-transparent raw-TCP service reachable at a stable host and port by any TCP client (for example a database client such aspsql -h <host> -p <port>).
Sailbox.create(ingress_ports=...) also accepts a bare int as
shorthand for IngressPort(port) (i.e. an HTTP port).
allowlist restricts which sources may connect to this port. Entries
that parse as CIDR prefixes (e.g. ["203.0.113.0/24"]) match source IPs;
other entries are treated as Sail app names whose sailboxes may connect.
App-name entries are supported on "http" listeners only. Raw-TCP
connections carry no source app identity, so "tcp" allowlists must
be CIDR prefixes. Each port carries its own allowlist. An empty/omitted
list means any source may connect.
Exposing a well-known unauthenticated service port (e.g. a database) as raw
TCP without an explicit allowlist is rejected. Use source restrictions,
or pass ["0.0.0.0/0", "::/0"] to explicitly allow every source.
guest_port
The in-guest port to expose (1-65535).protocol
"http" (the default) or "tcp".
allowlist
Source addresses allowed to reach the port; empty allows all.ingress_auth_headers
allowlist contains the caller’s app name. The helper is only
available inside a sailbox.
HttpEndpoint
"http" listener.
url
The routable HTTPS URL.TcpEndpoint
"tcp" listener.
host
Hostname to dial.port
Port to dial.Listener
guest_port is the guest port you exposed. endpoint is how you reach it: an
HttpEndpoint for "http" listeners or a TcpEndpoint for "tcp"
listeners. It is None until the listener is routable.
guest_port
The in-guest port traffic is forwarded to.protocol
Wire protocol exposed ("http" or "tcp").
route_status
Status of the listener’s ingress route.endpoint
How to reach the listener;None until it is routable.
SailboxPage
Sailbox.list_page results plus the server’s pagination envelope.
items
The sailboxes on this page.limit
The page size that was applied.offset
The offset that was applied.total
Total matching sailboxes across all pages.has_more
Whether more results exist past this page.UpgradeResult
applied
True when applied immediately (running box); False when deferred to the next wake.status
Lifecycle status of the sailbox after the upgrade call.SailboxCheckpoint
checkpoint_id
The checkpoint id.sailbox_id
The sailbox the checkpoint was taken from.checkpoint_generation
Checkpoint generation captured by this checkpoint.expires_at
RFC3339 UTC time at which the handle expires and is eligible for garbage collection, orNone for a handle to an existing checkpoint that
carries no fresh retention bound (a paused or sleeping source).
status
The source sailbox’s status after checkpointing.Sailbox
sailbox_id is the stable identifier and the durable external handle.
The remaining fields are the read-only snapshot as of get/list
(status, resource usage, image, timestamps); a handle born from
create carries only what the create response returns. Fetch a fresh
snapshot with Sailbox.get(sailbox_id). Every operation resolves the
live endpoint on demand, waking a sleeping box only when the operation
needs it.
sailbox_id
The sailbox id: the stable, durable external handle.name
The sailbox name.status
Lifecycle status (for example"running").
app_id
Identifier of the owning app.app_name
Name of the owning app.image_id
Identifier of the image the sailbox was created from.memory_mib
Configured memory, in MiB.vcpu_count
Configured number of vCPUs.state_disk_size_gib
Configured state-disk size, in GiB.cpu_requested_vcpu
Requested CPU, in vCPUs.cpu_used_vcpu
Current CPU usage, in vCPUs.memory_requested_bytes
Requested memory, in bytes.memory_used_bytes
Current memory usage, in bytes.disk_requested_bytes
Requested disk, in bytes.disk_used_bytes
Current disk usage, in bytes.architecture
CPU architecture (for example"arm64").
guest_schema_version
In-guest agent version the sailbox last booted (or was created) with.None for handles born from create or for boxes created before
agent versions were recorded.
error_message
Human-readable error detail when the box is in an error state.checkpoint_generation
Monotonic checkpoint generation counter.started_at
When the box last started, if it ever has.last_checkpointed_at
When the most recent checkpoint was taken, if any.created_at
When the sailbox was created.updated_at
When the sailbox was last updated.create
timeout (seconds) bounds each create attempt, since creating a
sailbox can block for many minutes while it queues for
capacity and boots the VM. The create is retried (reattaching to the same
box), so it returns as soon as the box is ready and gives up after roughly
three attempts. If the budget is exhausted it raises rather than hanging;
the box may still come up server-side, and because the request was aborted
before returning an id you find or terminate it by name. Pass 0 to
leave each attempt unbounded.
size picks the vCPU count and the default memory/disk ceilings;
memory_gib and disk_gib tune those ceilings in whole GiB
within the size’s range.
ingress_ports exposes guest ports for ingress. Each
entry is either a bare int (shorthand for an HTTP port) or an
IngressPort carrying an explicit protocol, e.g.
ingress_ports=[80, 443, IngressPort(22, "tcp")]. Call
listener / listeners on the returned sailbox for the
public address of each exposed port: an HTTP listener’s endpoint is
an HttpEndpoint with a routable url and a TCP listener’s is a
TcpEndpoint with a host/port any TCP client can dial (for
example psql -h <host> -p <port>).
Pass ssh=True to get an SSH-ready box in one call. It calls
enable_ssh on the new box, which trusts your org’s SSH
certificate authority, starts sshd, and exposes guest port 22 as
tcp. A port-22 entry in ingress_ports is then not exposed at
create; only its allowlist is kept, applied when enable_ssh
exposes the port. To connect, wire up your machine with sail box ssh alias <id> (or sail box ssh enable <id>) and then ssh <name>.sail; a plain ssh to the raw port will not present your
certificate.
volumes mounts shared persistent NFS storage into the guest. Pass a
mapping from absolute guest mount path to a sail.Volume returned
by sail.Volume.find(), e.g. volumes={"/mnt/shared": volume}.
size selects the resource size: "s" or "m" (the default).
Each size sets the vCPU count plus default memory and disk.
Billing is based on observed usage, so a bigger size costs no more on
its own. Choose "s" when you want the fastest cold starts, forks,
and resumes. Its lower ceilings also cap what a runaway workload can
consume, so you don’t accidentally use more than you need.
autosleep_timeout_minutes optionally lets Sail sleep the box after that
many minutes of continuous idleness. When set, Sail will sleep the box
after this many minutes with no active execs, no recent inbound network
ingress, and CPU usage less than 0.05. Leave it as None to
disable autosleep. The value must be between 1 and 1440 minutes.
await Sailbox.create.aio(...) is the async form, building the image
and provisioning the VM without blocking the event loop.
list
app_id filters by the owning app id (resolve
an app name through App.find first if needed).
max_guest_schema_version keeps only sailboxes whose in-guest agent
version is at or below the given version (sailboxes created before
agent versions were recorded always match), useful for finding
sailboxes that need upgrade().
Returns just the sailboxes. Use list_page for the same call with the
pagination envelope (limit/offset/total/has_more).
list_page
get
LookupError (the server returns 404 for both to avoid leaking
ownership across orgs). Nothing wakes here; operations resume a paused
or sleeping box on demand. Call get again for a fresh snapshot.
from_checkpoint
Sailbox.exec() sessions are reaped in the
child: a command still running when the checkpoint was taken does not
resume here. Its on-disk effects up to the checkpoint are preserved, but
the command itself is not continued. Start fresh execs on the new box.
terminate
pause
sleep
checkpoint
name sets a display name for the handle. ttl_seconds, when set,
must be positive and overrides the server’s default retention window;
use it to keep a checkpoint you intend to reuse as a template alive
longer than the default. The returned handle’s expires_at reports
when it will be garbage collected.
upgrade
UpgradeResult: applied is True when the
upgrade happened immediately (the sailbox was running) and False
when it will apply at the next wake; already-up-to-date sailboxes
report True without rebooting.
resume
listener
listeners
wait_for_listener
guest_port is reachable end to end.
For an HTTP listener this probes the url, so a successful return
means your guest HTTP server answered. For a TCP listener it opens a
connection through the ingress edge and treats it as ready once the
guest sends bytes (e.g. an SSH banner) or holds the connection open.
This is a connectivity check, not an application-level health check.
Raises TimeoutError if the listener does not become reachable
within timeout seconds; float("inf") waits indefinitely.
expose
protocol is "http" (a routable URL, the default) or "tcp"
(a public host/port for raw TCP: ssh, Postgres, etc.).
allowlist restricts which source IP CIDRs (e.g. ["203.0.113.0/24"])
or Sail app names may connect (app names on "http" listeners only;
"tcp" allowlists must be CIDR prefixes). Re-exposing a port under the same protocol updates its
allowlist to the value you pass (otherwise keeping the same address:
a raw-TCP port reclaims its reserved one). Changing an exposed port’s
protocol is rejected: unexpose an HTTP port and re-expose it, or use
a different guest port for a raw-TCP one (its address stays reserved).
Returns the Listener (its route_status is
"unspecified": the expose response does not report reachability).
This works on a paused or sleeping sailbox without waking it; a later
resume serves the new listener.
Probing reachability with wait_for_listener needs a running,
connected box, so wait only once the box is running.
unexpose
"tcp" port stops counting against your org’s raw-TCP quota once
removed, but its public host/port stays reserved to this sailbox.
Re-expose-ing the same guest port reclaims the exact address, and no
other sailbox ever takes it (a sailbox that ran on an address may have an
ssh host key pinned to it, so the address is never reused). An "http"
port carries no such reservation and is removed outright. Removing a port
that is not exposed raises a LookupError.
ingress_auth_headers
allowlist contains
this sailbox’s app name, useful for host-side orchestrators and tests
that drive sailboxes from outside. Requires an organization-scoped API
key and a live (non-terminated) sailbox.
Inside a sailbox guest, prefer the module-level
sail.ingress_auth_headers, which reads the same values from
the guest environment without an API call.
enable_ssh
sshd, and exposes guest port 22 as tcp ingress once the CA-only
daemon verifiably owns it (a failed enable never leaves port 22 newly
exposed). Works on any running sailbox; create(ssh=True) is sugar
for calling this right after create. Safe to re-run: the box’s host key
is generated once and never rotated, so a caller’s known_hosts
stays valid; re-run it to bring sshd back up if the (unsupervised)
daemon stops. sshd survives sleep and checkpoint→resume.
allowlist restricts which source CIDRs may connect to port 22,
replacing any existing restriction. Left empty, a first enable opens
the port to any source, and a re-enable leaves an existing restriction
unchanged. Disabling SSH (sail box ssh disable) unexposes port 22
together with its restriction, so a later enable is a first enable.
Anyone in the org connects with a short-lived certificate signed for
their key, rather than installed keys. The sail box ssh CLI fetches
that certificate and writes the local SSH config; this method only
prepares the box. By default it blocks until the port-22 listener is
reachable and returns its TcpEndpoint; pass wait=False to
skip the readiness probe and return None.
read
read_stream, which yields chunks without buffering.
read_stream
write_stream
FileWriter: push chunks with write and
confirm with finish (only finish commits the write). Best used
as a context manager, which finishes on a clean exit and aborts on an
exception::
with sb.write_stream(“/logs/run.log”) as writer:
for chunk in produce_chunks():
writer.write(chunk)
write
exec
ExecProcess immediately
after the backend accepts the command: iterate proc.stdout /
proc.stderr for live output and call proc.wait() for the
result. open_stdin=True opens the command’s stdin for proc.stdin
writes; by default stdin is /dev/null so stdin-reading commands
see immediate EOF instead of blocking.
pty=True runs the command under a pseudo-terminal: isatty() is
true, control bytes written to proc.stdin become signals (Ctrl-C is
b"\x03"), and proc.resize(cols, rows) adjusts the window.
stdout and stderr merge onto proc.output (proc.stderr stays
empty). pty implies open_stdin. For a full interactive shell that
drives the local terminal, use shell instead.
background=True launches the command through a detached shell that
returns immediately. Its output is discarded, so proc.stdout /
proc.stderr stay empty and proc.wait() only confirms the
launcher started it.
retry_timeout budgets the transient-RPC retries while the backend
admits the command, covering the delay of waking or migrating a
sailbox.
await sb.exec.aio(...) is the async form: a shell command resolves
to an AsyncExecProcess, a function to its return value.
shell
command, runs an interactive login shell. Pass command
to run that under a pty instead (e.g. a REPL or vim). Either way the
session is bridged to the local terminal: raw-mode keystrokes (including
Ctrl-C, Ctrl-Z, and Ctrl-D) reach the remote process, its output renders
locally, and terminal resizes propagate. Blocks until the remote process
exits and returns its exit code. Requires an interactive local terminal
(stdin and stdout must be TTYs).
This is the streaming-exec equivalent of ssh-ing into the box,
without a separate SSH server: it rides the same channel as
exec(pty=True). shell overrides the login shell (default
$SHELL or /bin/bash); it is ignored when command is given.
FileWriter
write and confirm the write with finish;
only finish commits it. An unfinished writer aborts on __exit__
(or explicit abort), canceling the RPC so the server does not
treat the stream end as a completed write; the guest file state after an
abort is unspecified. Usable as a context manager (sync and async): a
clean exit finishes, an exception aborts and propagates.
write
write_aio
write.
finish
finish_aio
finish.
abort
finish.
FileStream
read_stream cheap to call, and the async
path runs it off the event loop so other tasks keep running. Iterate to
completion, or call close (or use it as a context manager) to
release the stream early.
close
App
id
Stable server-assigned app identifier.name
Human-readable app name unique within the owning org.created_at
App creation time.find
list
ImageDefinition
apt_install
apt-get install step for packages.
pip_install
pip install step for packages.
run_commands
add_local_file
remote_path must be an absolute POSIX path. If it ends with a
slash, the basename of local_path is appended.
mode is the POSIX permission bits (low 9 bits, max 0o777). When
omitted (None) or 0 the default 0o644 applies; an explicit
mode=0 is treated the same as omitting the argument.
add_local_dir
local_path is hashed and uploaded;
per-file modes come from the local stat(). Symlinks are skipped.
ignore accepts a sequence of gitignore patterns or a Path to a
file containing them (e.g. .dockerignore); pass a list to use
patterns directly. remote_path must be an absolute POSIX path.
env
build
TimeoutError if the build does not finish within
timeout seconds.
ImageNamespace
sail.Image singleton, for example
sail.Image.debian_arm64 or sail.Image.devbox_arm64. See the
Images guide for how to
choose between the Debian and devbox bases and the CPU architectures.
builtin_debian
debian_amd64
debian_amd
debian_amd64.
builtin_debian_amd64
debian_arm64
debian_arm
debian_arm64.
builtin_debian_arm64
devbox_amd64
devbox_arm64
builtin_devbox_amd64
devbox_amd64.
builtin_devbox_arm64
devbox_arm64.
Volume
volume_id
The volume id.name
The volume name.backend
Storage backend serving the volume.status
Lifecycle status.mount_path
Mount path inside the sailbox, if reported.created_at
Creation time, if reported.updated_at
Last-update time, if reported.find
list
delete
sail.Volume.delete("name") as a
convenience for deleting a named volume without first keeping the
returned handle.
from_mount
ExecResult
stdout
Buffered stdout: a capped, drop-oldest tail of the output.stderr
Buffered stderr: a capped, drop-oldest tail of the output.exit_code
The command’s exit code.timed_out
Whether the command was killed for exceeding its timeout.stdout_truncated
The server output ring overflowed and dropped the oldest bytes of the buffered copy returned here.stderr_truncated
Likestdout_truncated, for stderr.
stdout_complete
The live stream delivered the output through to the command’s exit, so a consumer that streamed it already holds the complete output even when the buffered copy here is a truncated tail.stderr_complete
Likestdout_complete, for stderr.
ExecProcess
Sailbox.exec. stdout/stderr iterate live output as
it arrives, streamed into bounded buffers and resuming if the stream
breaks; wait() returns the authoritative final result even when the
stream cannot be resumed. The command runs inside the sailbox independent
of this handle and the connection that launched it, so closing the handle
or losing the network leaves it running and its result retrievable through
wait().
exec_request_id
stdout
stderr
stdout. Empty for a pty
exec, which merges stderr onto stdout.
output
stdout).
stdin
open_stdin=True exec; raises ValueError
otherwise.
exit_code
wait() is authoritative. An exec whose worker was
lost before it produced a real exit code raises SailboxWorkerLostError
here, exactly as wait() does.
poll
exit_code; never blocks.
cancel
retry_timeout > 0 keeps retrying within that budget, covering the
brief window right after the command starts when the guest cannot
accept signals for it yet.
resize
pty=True exec.
Advisory and best-effort: an unknown, finished, or not-yet-placed exec is
a server no-op, and transient transport errors are swallowed since the
next resize resends. A no-op for a non-pty exec.
close
wait
result.stdout/result.stderr are always the full capped tail,
independent of how much was consumed via live iteration.
Ctrl-C sends SIGINT and resumes waiting (the guest’s natural
128+SIGINT=130 exit code flows back); a second Ctrl-C escalates to
SIGKILL and re-raises so a wedged guest can’t trap the caller.
If stop is given and fires before the stream ends, wait() returns
None and leaves the exec running, so a caller that no longer needs the
result can return promptly. Without stop the result is non-None.
If this exec opened a Voyages auto-span, wait() closes it so the span
records this run’s outcome.
AsyncExecProcess
await Sailbox.exec.aio(...). stdout and stderr are
async iterators over live output (async for line in proc.stdout), and
await proc.wait() returns the buffered result. Like the sync handle, the
command runs detached inside the sailbox, so dropping this handle leaves it
running and its result retrievable through wait().
Cancelling the task awaiting wait() stops waiting but leaves the command
running; call await proc.cancel() to signal the command itself.
exec_request_id
stdout
stderr
stdout. Empty for a
pty exec, which merges stderr onto stdout.
output
stdout).
stdin
open_stdin=True exec; raises ValueError
otherwise.
exit_code
poll
exit_code; never blocks.
cancel
resize
pty=True exec; a no-op otherwise.
close
wait
result.stdout/result.stderr are the full capped tail, independent
of how much was consumed via live iteration. A repeat wait() returns
the cached result. If this exec opened a Voyages auto-span, wait()
closes it with the run’s outcome.
SailFunction
function
Sailbox.exec.
SailError
SailboxError
SailboxCreationError
ImageBuildError
SailboxExecutionError
SailboxTerminatedError
SailboxExecRequestNotFoundError
SailboxWorkerLostError
Sailbox.exec again starts it over from the
beginning, so any side effects the partial run applied will happen again.
The sailbox itself recovers automatically, so you do not need to resume it.
SailboxFunctionError
SailboxFunctionSerializationError
Config
~/.sail.
Sail resolves the API key and service endpoints, with environment
variables taking precedence over the stored ~/.sail credentials. Set
SAIL_API_KEY (or run sail auth login) to authenticate.
SAIL_API_URL, SAILBOX_API_URL, SAIL_IMAGEBUILDER_URL, and
SAILBOX_INGRESS_URL override individual endpoints, for custom or
self-hosted stacks. Configuration is resolved once per process; in a
long-lived process, call sail.reset_transports() after changing these
variables. ingress_base and ingress_scheme describe how a
listener’s public URL is built from the sailbox id and port when the
server does not return one: "path" addresses
<base>/_sailbox/{id}/{port}, "subdomain" addresses
<sailbox>-<port>.<base host>.
from_env
ValueError when no API key is configured.
from_env_optional_api_key
from_env() but does not raise when no key is configured, so
endpoints still resolve for paths that do not need to authenticate (such
as building a listener’s public URL).
RetryPolicy
max_attempts counts the initial request, so max_attempts=3 means at
most two retries. base_delay and max_delay are seconds; the schedule
is exponential with full jitter, capped at max_delay, and a server-sent
Retry-After wins (also capped at max_delay). Retried mutations
carry an Idempotency-Key header, so a retry cannot double-apply.
max_attempts
Attempts including the initial request.base_delay
First backoff delay, in seconds.max_delay
Backoff cap, in seconds; a serverRetry-After is capped here too.