sail on PyPI) supports Python 3.9+. It shares one
engine with the TypeScript and
Rust SDKs, so behavior matches across languages.
Install
sail CLI on your PATH. To install
the CLI on its own, see Install the CLI.
The Sail API warns when your SDK version is nearing the end of its support
window. The SDK emits it as a SailDeprecationWarning through Python’s
warnings module, once per process. A version past the end of its support
window is rejected with an upgrade error before any operation runs. Upgrade
with pip install -U sail.
Configure
SetSAIL_API_KEY in the environment; the SDK also reads the credential
sail auth login stores under ~/.sail. See
Configuration.
Quickstart
Sync and async
Every method that does I/O has an async twin under.aio (the interactive
shell is sync-only), so the same code works from scripts and from asyncio.
You choose sync or async once, at the call. A handle returned by an .aio call
is already async (await proc.wait(), async for chunk in proc.stdout), with
no further .aio:
Python-only features
@sail.function: run a local Python function inside a Sailbox.- Voyages and Inference: record agent runs and attribute model calls to them.
Errors
Product and transport failures derive fromsail.SailError, and the error
classes that match a Python builtin also inherit it (sail.NotFoundError is a
LookupError), so both except sail.SailError and idiomatic builtin handlers
work. A few argument mistakes raise plain ValueError/TypeError. See
Errors.
Reference
The docs below are auto-generated.Sailbox
A sandbox instance on the Sail platform: the operable handle plus the monitoring snapshot from the call that produced it.sailbox_id is the stable identifier and the durable external handle.
Equality and hashing follow it: two handles for the same Sailbox compare
equal, regardless of when their snapshots were taken. 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 Sailbox only when the operation
needs it.Attributes:create
Create a new Sailbox.timeout (seconds) bounds each attempt of the call, since creating
a Sailbox can block for many minutes while it queues for capacity and
boots the VM. A call that times out raises, and the Sailbox may still
come up in the background; it then shows up in list. Pass
0 to wait without a bound.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 Sailbox in one call. It calls
enable_ssh on the new Sailbox, 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.By default a Sailbox is org-wide: any credential in your org can exec,
copy files, SSH, or run lifecycle operations on it. Pass
private=True to restrict all of that to you. An org admin can
override that with a recorded reason for exec, files, setting a wake
time, and the pause, sleep, resume, terminate, and upgrade operations.
SSH, exposing or removing listeners, checkpoint, and restore stay
creator-only. Requires an API key minted by your user (not a service
key).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}.
Volumes are currently in Alpha. To pilot them, reach out in the Sail
Slack:
https://join.slack.com/t/sailresearchcrew/shared_invite/zt-41pdcym9j-UU0Ey~A~r6n2H0DQVQsQHQ.size selects the resource size: "s", "m" (the default),
or "l".
Each size sets the vCPU count plus default memory and disk.
Ongoing billing is based on observed usage, so a bigger size does not
reserve CPU, memory, or disk. Each size has a separate one-time
creation charge. Choose "s" when you want the fastest cold starts
and resumes. Its lower ceilings also cap what a runaway workload
can consume, so you don’t accidentally use more than you need.
memory_limit_gib and disk_limit_gib tune that size’s default memory and
disk ceilings in whole GiB, within its range. Omit them to keep the
size’s own ceilings.image is the image to boot; omit it for the prebuilt Debian base
(no image build). A custom image is built at create if it is not
already cached; image_build_timeout (seconds) bounds that build.Sail may sleep a fully idle Sailbox; it wakes transparently on traffic
or the next operation. auto_sleep turns that off or makes Sail
wait longer first; see AutoSleep.await Sailbox.create.aio(...) is the async form, building the image
and provisioning the VM without blocking the event loop.list
List the Sailboxes for the current org that match the filters, fetching pages until every match (orlimit of them) is collected.
Use list_page to page through results manually instead.app_id filters by the owning app: a
sail.App value or an app id string (resolve an app name through
App.find first if needed).
order sorts the results: "newest_active" returns the most
recently active first (the default the server applies),
"newest_created" the newest-created first. limit caps the
total returned, bounding the fetch for large orgs; None returns
every match.list_page
List one page of Sailboxes alongside the pagination envelope (limit/offset/total/has_more).list, plus limit and offset to
select the page. order sorts the results: "newest_active"
returns the most recently active first (the default the server
applies), "newest_created" the newest-created first.get
Fetch a Sailbox by id: the operable handle plus a fresh snapshot.LookupError (the server returns 404 for both to avoid leaking
ownership across orgs). Nothing wakes here; operations resume a paused
or sleeping Sailbox on demand. Call get again for a fresh snapshot.from_id
Bind a handle to an existing Sailbox id without a network call.name and
status are empty), just the operable surface. The id is not
verified to exist: operations on an unknown or inaccessible id fail
with NotFoundError. Use get to validate the id and fetch
a fresh snapshot instead.from_checkpoint
Create a new running Sailbox from a durable checkpoint handle.exec stop here, though their writes up to
the checkpoint are kept, and one started with background=True keeps
running. Start the other execs the new Sailbox needs. Sometimes it
comes up cold instead, with the disk intact and nothing running, and
a Sailbox that mounts a volume always does. Volumes are mounted on it
at the same paths as on the original, and they are the same volumes, so
both Sailboxes read and write the same files.name sets the new Sailbox’s display name. timeout (seconds)
bounds the call, since a restore can block for many minutes while the
new Sailbox queues for capacity. A call that times out raises, and
the restore may still finish in the background; the new Sailbox then
shows up in list. timeout must be positive when given;
omit it to wait without a bound.terminate
Permanently terminate this Sailbox.pause
Checkpoint and pause this Sailbox until it is explicitly resumed.sleep
Checkpoint and sleep this Sailbox until traffic or a wake restores it.wake_at, when given, schedules a wall-clock wake before the sleep
starts and returns the effective wake time: the sooner of this
request and any wake already scheduled. If the Sailbox is sleeping
when that moment arrives, Sail restores it. The wake can fire a
little after the time you set, so treat it as approximate. A naive
wake_at is interpreted as local time. Calling sleep on an
already-sleeping Sailbox succeeds and just updates the scheduled
wake.set_auto_sleep
Replace when Sail may sleep this Sailbox on its own.AutoSleep.never
clears any minimum wait set earlier, and switching back does not
restore it.checkpoint
Create a durable checkpoint handle for this Sailbox.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 the checkpoint expires; starting a Sailbox from it after that
fails.upgrade
Upgrade this Sailbox’s runtime to the latest version.UpgradeResult: applied is True when nothing
is left to apply, either because the Sailbox took the upgrade just now
or because it was already current, and False when the upgrade is
recorded for the next wake.resume
Resume this paused or sleeping Sailbox, returning it to running.listener
Fetch one listener by guest port without waking the Sailbox.listeners
List this Sailbox’s listeners without waking it.wait_for_listener
Block until the listener onguest_port is reachable end to end.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
Expose an additional ingress port on this Sailbox at runtime.protocol is "http" (a routable URL, the default) or "tcp"
(a public host/port for raw TCP: ssh, Postgres, etc.).
allowlist restricts which sources may connect: an entry that reads
as an address or a range (e.g. ["203.0.113.0/24"]) matches source
IPs, and every other entry is a Sail app name (app names on "http"
listeners only; "tcp" allowlists must be addresses or ranges). An
address must not carry an IPv6 zone, such as fe80::1%eth0, which
names an interface on one machine rather than a source.
Re-exposing a port under the same protocol sets its allowlist to
what you pass, so pass the whole list every time; passing none clears
the restriction and reopens the port. A raw-TCP port reclaims its
previous address while your org still holds it idle; if another of
your org’s Sailboxes took the address over, a new one is allocated, so
read the endpoint from the returned Listener.
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.
Returns the Listener (its route_status is "unknown":
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 Sailbox, so wait only once the Sailbox is running.unexpose
Stop serving an exposed ingress port on this Sailbox."tcp" port stops counting against your org’s raw-TCP quota once
removed, but its public host/port stays owned by your org:
another of your org’s Sailboxes may reuse the idle address, and it is
never given to a different org. Re-expose-ing the same guest port
reclaims the exact address while your org still holds it idle; after a
reuse you get a new one. An "http" port carries no such reservation
and is removed outright. Removing a port that is not exposed raises a
LookupError.ingress_auth_headers
Fetch the ingress-identity headers for this Sailbox via the API.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
Make this Sailbox reachable over SSH, returning its endpoint.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 Sailbox’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 addresses or ranges 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 (a private Sailbox is the exception,
accepting only its creator’s certificates). The sail box ssh CLI
fetches that certificate and writes the local SSH config; this method only
prepares the Sailbox. 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.fs
Filesystem operations on this Sailbox’s guest: read and write files (buffered or streaming), and directory helpers.run
Run a command to completion and return its buffered result.exec followed by wait(). A
str runs via /bin/sh -lc; a sequence is exec’d directly. env
adds environment variables for the command; cwd sets the working
directory (string commands only, like exec); user picks the
guest user the command runs as (see exec). The
result’s stdout/stderr are the buffered output (capped, drop-oldest);
for unbounded output, stream it live via exec instead.check=True raises sail.CommandFailedError (carrying the
completed result as result) when the command exits nonzero or
times out.When timeout (seconds) elapses, the command is killed and run
returns an ExecResult with timed_out=True, raising only when
check=True.idempotency_key deduplicates retries: calling run again with
the same key returns the original command’s result instead of
launching it a second time.exec
Run a shell command or decorated Python function in the Sailbox.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.env adds environment variables for the command. Entries override
the guest defaults (including LANG and the IS_SANDBOX=1 sandbox
marker) and the image environment. A few
reserved variables that identify the Sailbox (such as SAILBOX_ID)
cannot be overridden. For pty execs the local terminal environment
(COLORTERM, LANG, LC_*, TERM_PROGRAM) is forwarded
automatically for keys not set here.cwd runs the command in the directory you name (string commands
only). Without it, commands start in the image’s working directory, or
/ when the image does not set one.user runs the command as that guest user: a user name, a numeric
uid, or either with a group appended after a colon ("alice",
1000, "alice:staff", "1000:100", the Docker USER
syntax). A named user must exist in the Sailbox’s /etc/passwd; a
numeric uid need not. HOME (and USER/LOGNAME when a name
resolves) default to the resolved account, with env entries still
winning. When user is not given, commands run as the image’s
USER if the image sets one, root otherwise; pass user="0:0"
to force root ("root" is a user name like any other, resolved
through the Sailbox’s /etc/passwd). Sailboxes created before user
support shipped must call upgrade once first; until then such
execs fail rather than run as root. The exact spelling user="0:0"
needs no upgrade.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.await sb.exec.aio(...) is the async form: a shell command resolves
to an AsyncExecProcess, a function to its return value.shell
Open an interactive pty session on the Sailbox, driving the local terminal.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) on a Unix machine.This is the equivalent of ssh-ing into the Sailbox, without a separate
SSH server. shell overrides the login shell (default $SHELL or
/bin/bash); it is ignored when command is given.The session runs as the image’s USER when the image sets one, root
otherwise: the same identity exec uses. user runs it as
someone else instead (a user name or numeric uid, optionally with a
group after a colon, like "alice", 1000, "alice:staff");
user="0:0" is always root. A user other than "0:0"
requires a Sailbox whose guest honors requested users; on older
Sailboxes the session fails until upgrade is called.While attached, several local conveniences are forwarded: the Sailbox’s
browser opens and localhost servers reach your machine, files dragged
onto the terminal upload into the Sailbox and paste as guest paths, and
Ctrl+V forwards your clipboard. On devbox images the clipboard is
two-way: pasted images and text land on the Sailbox’s clipboard, and text
copied inside the Sailbox comes back to yours. Other images upload a pasted
image as a file and paste its path instead. Pass no_forward=True to
turn all of it off (for example for an untrusted or automated session),
or no_forward_browser=True to keep everything forwarded except
browser opens.App
A Sail application.Attributes:find
Find an app by name, optionally creating it if it doesn’t exist.list
Return every app the current org owns, newest first.ImageDefinition
apt_install
Add anapt-get install step for packages.pip_install
Add apip install step for packages.run_commands
Add shell commands to the build, each as its own step.add_local_file
Bake the contents of one local file into the image at remote_path.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
Bake a local directory into the image at remote_path.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
Bake environment variables into the image.build
Build the image now and return the built definition.TimeoutError if the build does not finish within
timeout seconds. Any automatic retries are included in that
timeout.Creating Sailboxes from the returned definition needs no further
build. For an image imported with Image.from_registry through
a tag, it is also pinned to the exact version the build resolved
the tag to, even if the tag later moves upstream.By default, Sail may reuse an existing ready build for this
definition. Pass force_build=True to build it again: new
Sailboxes use the fresh image once it is ready, Sailboxes that
already exist keep the filesystem they were created with, and a
forced build that fails changes nothing. For an image imported
through a registry tag, a forced build also asks the registry what
the tag points at now and builds that version. The tag then means
that version for your whole organization, while definitions built
earlier keep their pinned version. A forced build of an image
built with Image.from_dockerfile looks up the tags its
FROM and COPY --from instructions name and moves those
pins for your whole organization, while definitions built
earlier keep the versions their build used. If
forced builds overlap, the last-requested one
that succeeds decides which image new Sailboxes use and, for a
tag, what the tag means.ImageNamespace
Base images a Sailbox can build on.Access these through the module-levelsail.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.debian
Debian base for the given CPU architecture (default amd64).python3 matching your local Python
version, which is what lets @sail.function run local Python
functions inside a Sailbox. Pass install_python=False to keep
the base’s stock python3 instead; a base with no Python
install and no other build steps is prebuilt, so creating a Sailbox
from it needs no build.Image.debian_amd64 and Image.debian_arm64 are shorthand for
debian("amd64") and debian("arm64").debian_amd64
Debian base for x86-64; shorthand fordebian("amd64").debian_arm64
Debian base for arm64; shorthand fordebian("arm64").devbox_amd64
Devbox base for x86-64: Debian plus a baked development toolchain.devbox_arm64
Devbox base for arm64: Debian plus a baked development toolchain.from_registry
Your own image as the Sailbox root filesystem.Sailbox.create all work the same. The image keeps its own
python3, which pip_install and @sail.function use. Unlike
debian, an imported image never gets a Python matching your local
interpreter: installing one would shadow the Python the image was
built around. An image without Python still gets one from the
packages Sail installs. If you use @sail.function, an optional
feature of the Python SDK that runs local Python functions inside a
Sailbox, the image’s Python must match your local Python’s
major.minor version.Reference an image on a supported public registry (docker.io,
ghcr.io, public.ecr.aws, or quay.io), fully qualified,
for example docker.io/library/python:3.13. You can pass a tag, a
@sha256:... digest, or just the name, which means the latest
tag. The image must be Debian- or Ubuntu-based.Your Sailbox runs on the CPU architecture the image was built for. An
image published for both amd64 and arm64 runs on amd64. Pass
architecture to require one instead, and building fails if the
image was not built for it.A tag is pinned for your organization once an image has been built
from it: later builds keep using that image even after the tag
moves upstream. Call build(force_build=True) to look the tag up
again and build the version it points at now for your whole
organization; see build for how the switch propagates. A digest
names exactly one image, so it never moves.The image’s environment variables, working directory, and USER
become the defaults for commands you run with Sailbox.exec or
Sailbox.run; per-call env, cwd, and user override them
(pass user="0:0" to run as root on an image that sets USER).
The image’s ENTRYPOINT and CMD are not run: a Sailbox manages
its own processes, and your commands say what to execute. Build steps
you chain onto the image (such as apt_install) and SSH sessions
still run as root.from_dockerfile
Build a Dockerfile into a Sailbox image.contents=.context_dir is the build context that COPY and ADD read
from. A .dockerignore file in the context is honored, and
ignore patterns are applied on top of it. A file named after
your Dockerfile, like Dockerfile.dockerignore, sitting next to
it is used instead of the context’s .dockerignore, as it is
with Docker. The files the ignore rules keep are hashed and
uploaded when from_dockerfile is called, and their modes,
empty directories, and symbolic links are carried into the build.
Edits made after the call do not reach the build; call
from_dockerfile again to pick them up. Without
context_dir the build runs with an empty context.Every image a FROM or COPY --from instruction names must
live on a supported public registry (docker.io, ghcr.io,
public.ecr.aws, or quay.io). A short name like
python:3.12 means docker.io/library/python:3.12. The
Dockerfile must produce a Debian- or Ubuntu-based filesystem.
Sail layers everything a Sailbox needs on top, so build steps,
env, and Sailbox.create all work the same as any other
image.A # syntax= line can declare docker/dockerfile:1 or a
release from 1.4 through 1.22.0. A file that declares anything
else is rejected. The declared release does not change how the
file is built.The image builds for amd64 unless architecture says otherwise.
build_args provides values for the Dockerfile’s ARG
instructions. Names may not start with the reserved BUILDKIT_
prefix, and Docker’s proxy names (HTTP_PROXY, HTTPS_PROXY,
FTP_PROXY, NO_PROXY, ALL_PROXY, in any letter case) are
rejected; a step that needs a proxy can set one inside its RUN
command.Multi-stage Dockerfiles work. A RUN --mount of type cache,
secret, or ssh is rejected; tmpfs mounts work, and
bind mounts work when they read from the build context or
another build stage. Mount options must be literal text, and
ONBUILD is not supported, in the Dockerfile or in an image a
FROM names.Each image a FROM or COPY --from names is pinned to the
version its tag pointed at the first time your organization used
it, so rebuilding the same Dockerfile keeps using those versions
even after a tag moves. To look every tag up again and build with
the versions they point at now, pass force_build=True to
build.The image’s environment variables, working directory, and USER
become the defaults for commands you run with Sailbox.exec or
Sailbox.run; per-call env, cwd, and user override
them (pass user="0:0" to run as root on an image that sets
USER), and values set with .env() win over the image’s. The
image’s ENTRYPOINT and CMD are not run: a Sailbox manages
its own processes, and your commands say what to execute. Build
steps you chain onto the image (such as apt_install) and SSH
sessions still run as root. The image keeps its own python3; if
you use @sail.function, that Python must match your local
Python’s major.minor version.ExecProcess
A command running in a Sailbox.Returned bySailbox.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
Stable server-assigned identifier of this exec.stdout
Live stdout iterator yieldingstr chunks (incrementally decoded
UTF-8; use stdout_bytes for the raw byte stream). Ends when the
stream ends; a reader that falls more than the buffer cap behind skips
the dropped head. Each access returns a fresh iterator that replays the
retained output from the beginning before following live.stderr
Live stderr iterator; same semantics asstdout. Empty for a pty
exec, which merges stderr onto stdout.output
Live merged terminal output for a pty exec (alias ofstdout).stdout_bytes
Live stdout iterator yielding rawbytes chunks, exactly as the
command wrote them (escape sequences and binary payloads included).stderr_bytes
Rawbytes twin of stderr.output_bytes
Rawbytes twin of output (alias of stdout_bytes).stdin
Stdin writer for anopen_stdin=True exec; raises
sail.InvalidArgumentError otherwise.exit_code
Exit code if the live stream delivered it, else None.wait() is authoritative. An exec whose host was
lost before it produced a real exit code raises SailboxHostLostError
here, exactly as wait() does.poll
Alias ofexit_code; never blocks.cancel
Signal the guest command: SIGINT by default, SIGKILL if force=True.resize
Set the pty window (cols x rows) for apty=True exec.resync
Ask apty=True exec to repaint its current screen.close
Release the live stream without touching the remote command.wait
Wait for the exec to complete and return its buffered result.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
A command running in a Sailbox, with an async interface.Returned byawait Sailbox.exec.aio(...). stdout and stderr are
async iterators over live output chunks (async for chunk in proc.stdout; chunks are arbitrary slices of the stream, not lines), 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
Stable server-assigned identifier of this exec.stdout
Live stdout async iterator yieldingstr chunks (incrementally
decoded UTF-8; use stdout_bytes for raw bytes). Each access returns
a fresh iterator that replays the retained output from the start, then
follows live.stderr
Live stderr async iterator; same semantics asstdout. Empty for a
pty exec, which merges stderr onto stdout.output
Live merged terminal output for a pty exec (alias ofstdout).stdout_bytes
Live stdout async iterator yielding rawbytes chunks, exactly as
the command wrote them.stderr_bytes
Rawbytes twin of stderr.output_bytes
Rawbytes twin of output (alias of stdout_bytes).stdin
Stdin writer for anopen_stdin=True exec; raises
sail.InvalidArgumentError otherwise.exit_code
Exit code if the live stream delivered it, else None (see the sync handle’s note).poll
Alias ofexit_code; never blocks.cancel
Signal the guest command: SIGINT by default, SIGKILL if force=True.resize
Set the pty window for apty=True exec; a no-op otherwise.resync
Ask apty=True exec to repaint its current screen; a no-op
otherwise. See the sync ExecProcess.resync.close
Release the live stream without touching the remote command.wait
Wait for the exec to complete and return its buffered result.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.StdinWriter
File-like write side of an exec’s stdin, reached viaproc.stdin.Writes block (with backoff) while the guest buffer is full, like a real pipe;
a completed or stdin-closed exec surfaces as BrokenPipeError.close
Send EOF; the guest closes the pipe once the backlog drains.AsyncStdinWriter
Async write side of an exec’s stdin, reached viaproc.stdin.await stdin.write(data) applies backpressure (it resolves once the guest
accepts the bytes); await stdin.close() sends EOF.close
Send EOF; the guest closes the pipe once the backlog drains.function
Decorate a Python function so it can run throughSailbox.exec.SailFunction
A Python function that can be executed inside a Sailbox.SailboxFs
Filesystem operations on a Sailbox’s guest, reached viaSailbox.fs.File I/O streams bytes to/from the guest; the directory helpers create,
remove, test, and transfer paths. Paths are remote POSIX paths in the
guest, accepted as str or PurePosixPath.Writes give what they create to the image’s USER by default (root
when the image sets none), the same identity commands run as, so an
uploaded file is usable by the code in the Sailbox. Reads and the
directory helpers act as root by default, so they work on any path.Every operation except the reads and the directory download takes an
optional user in Docker’s USER syntax (name, uid,
name:group, or uid:gid; "0:0" is always root). The
directory helpers other than the transfers run their command as that
user, with its permissions enforced. Writes and the directory upload
keep running as root but give that user what they create, like
COPY --chown. Reads and the download take no user: a user
only decides which paths an operation may touch and who owns what it
creates. A read creates nothing in the Sailbox, and a download reads
any path as root, the way the reads do. A user other than
"0:0" requires a Sailbox whose guest honors requested users; on
older Sailboxes these calls fail until Sailbox.upgrade is
called.read
Read a regular file from the Sailbox as bytes.read_stream, which yields chunks without buffering.read_stream
Stream a regular file’s contents from the Sailbox as chunks.write_stream
Open a streaming write to a regular file in the Sailbox.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:AsyncFileWriter whose write /
finish / abort are awaited:user names the owner for the written file and any parent
directories the write creates, defaulting to the image’s USER,
else root; the write itself always runs as root.write
Write data to a regular file in the Sailbox.user names
the owner for the written file and any parent directories the write
creates, defaulting to the image’s USER, else root; the write
itself always runs as root.mkdir
Create a directory and any missing parents (likemkdir -p); a
no-op if it already exists. A user runs the mkdir as that user, so
created directories are owned by it.remove
Remove a file or directory tree (likerm -rf); a no-op if it is
already absent. A user runs the removal as that user, limiting it
to what that user may delete.exists
Whetherpath exists in the guest. Follows symlinks (like
test -e), so a dangling symlink reports False even though
ls lists it. A user reports existence as observable by
that user: a path the user lacks permission to reach also reports
False.ls
List a directory’s immediate entries asDirEntry records (no
recursion). Runs GNU find in the guest, which the default Debian
image ships. A missing path raises, as does a path that is not a
directory and a listing too large for the exec output cap. An entry
whose name is not valid UTF-8 fails the listing, since the path API
cannot address it. A user runs the listing as that user, so a
directory it may not read raises a permission error.upload_dir
Upload a local directory’s contents into a directory on the Sailbox.local_dir’s entries land inside guest_dir, which is created
if needed. Entries the upload does not name are left in place; a
same-named file is replaced. Uploaded files belong to the image’s
USER, the same identity commands run as, so the code in the
Sailbox can use them. When the image sets no USER, or that user
cannot be resolved in the Sailbox, they belong to root.
guest_dir and any missing parents the upload creates get the
same owner. Files keep their permission bits, except that the
setuid, setgid, and sticky bits are cleared. A user (the same
syntax the other operations take) gives the entries to that user
instead, like COPY --chown; it must exist in the Sailbox, and
like the other operations’ user it requires a Sailbox whose
guest honors requested users. The Sailbox’s image must provide
tar and gzip, which the transfer uses to ship the directory
as one compressed archive; the default images do.download_dir
Download a directory’s contents from the Sailbox into a local directory.guest_dir’s entries land inside local_dir, which is created
if needed. Entries the download does not name are left in place; a
same-named file is replaced. The transfer reads every file in the
tree, so download directories of ordinary files: system trees like
/proc or /sys hold files that cannot be read as plain data,
and downloading them fails. A file that is being written while the
download runs is captured as it is at that moment, the way copying
a live file would; download after writers finish for a consistent
copy. On Windows, a directory that contains symbolic links cannot
be downloaded, since Windows restricts creating them. The
Sailbox’s image must provide tar and gzip, which the
transfer uses to ship the directory as one compressed archive; the
default images do.FileWriter
A streaming write to a guest file.Push chunks withwrite and confirm the write with finish;
only finish commits it. An unfinished writer aborts on __exit__
(or explicit abort), so a stream that ends without finish is
never committed as a completed write; the guest file state after an abort
is unspecified. Usable as a context manager: a clean exit finishes, an
exception aborts and propagates. AsyncFileWriter, returned by
write_stream.aio, is the async form.write
Write bytes (or UTF-8 text); writes are chunked at the transport size.finish
Confirm the write, creating an empty file when nothing was written.abort
Cancel the write so it is never committed. Idempotent; a no-op afterfinish.AsyncFileWriter
A streaming write to a guest file, with an async interface.Returned bywrite_stream.aio; same commit semantics as
FileWriter with await-able write, finish, and
abort. Usable as an async context manager: a clean exit finishes,
an exception aborts and propagates.write
Write bytes (or UTF-8 text); writes are chunked at the transport size.finish
Confirm the write, creating an empty file when nothing was written.abort
Cancel the write so it is never committed. Idempotent; a no-op afterfinish.FileStream
An iterable stream of file chunks that opens on first use.Opening resolves the Sailbox’s live endpoint (waking it on demand). Deferring that to first iteration keepsread_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
Stop the stream and release its resources; safe to call twice.aclose
Async twin ofclose, run off the event loop.Volume
A managed NFS volume that can be mounted into one or more Sailboxes.Volumes are currently in Alpha. To pilot them, reach out in the Sail Slack: https://join.slack.com/t/sailresearchcrew/shared_invite/zt-41pdcym9j-UU0Ey~A~r6n2H0DQVQsQHQ.Attributes:find
Get an org-scoped NFS volume by name, optionally creating it.list
List active NFS volumes for the current organization, newest first.delete
Delete this NFS volume, returning the deleted handle.allow_missing=True an already-deleted volume returns None
instead of raising. To delete by name without a handle, use
delete_by_name.delete_by_name
Delete the NFS volume with the given name, returning the deleted handle.allow_missing=True a name that does not resolve to a volume
returns None instead of raising.from_mount
Load the volume handle for a path mounted into a Sailbox.ingress_auth_headers
Headers that authenticate this Sailbox as an ingress allowlist source.allowlist contains the caller’s app name. The helper is only
available inside a Sailbox.Voyages
A Voyage records what an agent run did. The module-level calls below act on the Voyage attached to the current process, so most code never holds aVoyage object itself.create
Create a new Voyage and make it the current Voyage.asyncio.run returns) resolves the process’s
most recently created Voyage.Always creates, even when SAIL_VOYAGE_ID is set in the environment;
a child process joining its parent’s Voyage uses attach instead.
Without a Sail API key (SAIL_API_KEY or a sail auth login
credential) this degrades to a NoopVoyage so
instrumented code keeps running with telemetry disabled. Arguments are
validated before that gate: a malformed call raises even when telemetry
is off.attach
Attach to an existing Voyage and make it the current Voyage (context-scoped, likecreate).voyage_id defaults to SAIL_VOYAGE_ID, the handoff a parent
process sets so its children join the parent’s Voyage. Raises
ValueError when neither is provided and telemetry is enabled.
Without a Sail API key (SAIL_API_KEY or a sail auth login
credential) this degrades to a NoopVoyage so
instrumented code keeps running with telemetry disabled. This includes a
keyless child whose parent exported no handoff env. An explicitly malformed
argument always raises; an absent ambient config in a telemetry-off
environment is the no-op state.run
Run one Voyage around a block. This is the recommended entry point.with sail.voyage.run("code-review") as voyage: creates the Voyage on
enter (same arguments and semantics as create: always creates,
never reads SAIL_VOYAGE_ID), emits voyage.completed on a clean exit,
and on an exception emits voyage.failed with the exception’s type and
message, then re-raises. Use async with sail.voyage.run(...) for the
async form, which drives the same lifecycle without blocking the event loop.
Terminal delivery is a bounded best-effort flush; call voyage.flush()
inside the block for strict delivery confirmation. Without a Sail API key
the block runs with telemetry disabled, exactly like create.
Controllers that create and complete the Voyage in different places keep
using create / attach directly.disable
Disable Voyage telemetry (context-scoped, likecreate).create, this also resets the process-wide fallback, so contexts
that never started their own Voyage (a raw worker thread, code after
asyncio.run) resolve the disabled state too.Installs and returns a NoopVoyage as the current Voyage. This is
the public form of the telemetry-off state create()/attach() enter
when no Sail API key is available. For controllers that catch a startup
telemetry failure and choose to continue unobserved:
except VoyageError: voyage = sail.voyage.disable().child_env
Env vars a child process needs toattach() to the current Voyage.{} when there is no current Voyage or telemetry is disabled,
so the handoff pattern is safe to use without an API key. See
Voyage.child_env.voyage_id
The current Voyage’s id, orNone when no Voyage is current.headers
Headers attributing a Sail API request to the current Voyage and to the span/agent context active at call time. Compute per request, never once at client construction.wrap_openai
Attribute an OpenAI-style client’s Sail calls to the live Voyage context.responses.create,
responses.retrieve, and chat.completions.create, whichever
exist) so every call computes the attribution headers AT CALL TIME
(voyage id plus the span/agent active at that moment) and injects them
via extra_headers. Snapshotting stale headers at client construction
(default_headers=sail.voyage.headers()) becomes impossible: there is
nothing to snapshot. Like the sail.inference wrappers, un-spanned
create calls get a synthesized auto-span so the model call
lands scoped; retrieve polls carry headers but never synthesize.voyage= pins attribution to one Voyage handle; the default follows
the current Voyage per call. Async clients
(AsyncOpenAI) are supported: coroutine-function methods get an async
wrapper whose auto-span covers the awaited request, not just coroutine
creation. Wrapping mutates the client in place (every holder of the
object sees attribution), is idempotent, returns the client, and raises
TypeError for an object exposing none of the known request methods.event
Record one timestamped event on the current Voyage.payload is keyword-only so a stale positional caller
fails loudly rather than being silently reinterpreted.Agent attribution comes from the enclosing agent() context (or the
SAIL_AGENT_* env defaults); there is no per-event override.span
Open a named span on the current Voyage; context manager or decorator.span() is evaluated. A
module-level @sail.span(...) declared before create() attributes
correctly. span_name may be omitted only in the decorator form,
where it defaults to the function’s __qualname__.agent
Declare the named agent on the current Voyage; context manager or decorator.@sail.agent(...) declared before create()
attributes correctly. Arguments are validated eagerly: a bad name or
slug raises at the declaration site regardless of voyage state.complete
Mark the current Voyage completed. A no-op when no Voyage is active.fail
Mark the current Voyage failed. A no-op when no Voyage is active.cancel
Mark the current Voyage canceled. A no-op when no Voyage is active.flush
Flush the current Voyage’s buffered events. A no-op when none is active.Voyage
A Sail Voyage attached to the current process.headers
Headers attributing a Sail API request to this Voyage and to the span/agent context active at call time.existing are replaced. Agent ids
are slug-derived and therefore header-safe by construction; a
non-header-safe caller-supplied span id is omitted rather than sent.child_env
Env vars a child process needs toattach() to this Voyage.subprocess.run(cmd, env={**os.environ, **voyage.child_env()}).
With agent=True (default) the active agent() context rides
along as the child’s SAIL_AGENT_* defaults, so the child’s
events attribute to the same agent without re-declaring it.
NoopVoyage.child_env() returns {}. The handoff is
safe to call without an API key.event
Record one timestamped event.payload is keyword-only so a stale positional
caller fails loudly rather than having an argument silently
reinterpreted as span_id.Agent attribution carries no per-event override: it comes
from the enclosing agent() context, or from the SAIL_AGENT_*
env defaults when no context is active. A one-shot attributed event
is with voyage.agent(...): voyage.event(...).span
Open a named span of work; nests under the active span automatically.span_name may be
omitted only in the decorator form (@voyage.span()), where it
defaults to the decorated function’s __qualname__; the with
form requires a name.A span carries no agent identity of its own. Events emitted inside it
(including the span’s own lifecycle events) are attributed to the
enclosing agent() context.agent
Declare the named agent as the active participant.name is the display identity shown in the dashboard; the stable
attribution key (agent_id) is derived from it: lowercased,
ASCII-folded, hyphenated. Pass slug= to pin the attribution key
across display renames or multi-process attach; role= is an
optional freeform taxonomy used for dashboard filtering.cancel
Mark this Voyage cancelled without emitting a customer event.NoopVoyage.cancel() and unattached Voyage instances are
no-ops when no API key is configured.Unlike complete()/fail() (which never raise: there is a
buffered terminal flush behind them), cancel() is a single
synchronous request to Sail and raises VoyageHTTPError on
a failed response. Wrap it if you call it from a finally/cleanup
path where an exception would mask the original error.NoopVoyage
No-op Voyage used when no Sail API key is available.Attribute-compatible withVoyage so fail-open code that reads voyage
fields does not crash when telemetry is disabled.SailTokenCompleter
Tinker TokenCompleter backed by Sail’s raw-token Responses path.ExtendsTokenCompleter.TinkerSandbox
Run a tinker-cookbook sandbox on a Sailbox.Implements the cookbook’s sandbox interface, so recipes that take a sandbox (or a sandbox factory, viatinker_sandbox_factory) can
execute their rollout commands in an isolated Sailbox instead of on the
training machine. Each instance owns one Sailbox for its whole life,
and cleanup terminates it; give each sandbox its own Sailbox, since
two sandboxes sharing one would share its filesystem and processes and
the first cleanup would terminate it for both. The factory creates a
fresh Sailbox per sandbox; constructing directly is for supplying your
own, such as one restored from a warmed checkpoint.timeout_seconds is the sandbox’s lifetime budget: once it has
elapsed, the next operation terminates the Sailbox and raises the
cookbook’s SandboxTerminatedError. None means no budget.sandbox_id
The backing Sailbox’s id.run_command
Run a shell command in the Sailbox and return itsSandboxResult.max_output_bytes keeps only the first bytes of each output
stream; without it the full output is returned, up to a large
safety ceiling that keeps a runaway stream from exhausting the
training process’s memory. A
command that outlives timeout
(seconds) is killed and reported with metrics["timed_out"] set.
Errors from the Sailbox surface as a result with exit code -1,
except a terminated or lost Sailbox, which raises the cookbook’s
SandboxTerminatedError.read_file
Read a file from the Sailbox into aSandboxResult’s stdout.max_bytes keeps the file’s first bytes (without it the whole
file, up to a large safety ceiling), and timeout (seconds)
bounds the whole read. A missing or unreadable file is reported as a
result with exit code 1 rather than raised, matching the
cookbook’s contract, and so is a read that runs out of time.write_file
Write a file into the Sailbox, marked executable when asked.timeout (seconds) bounds the write; one that runs out of time is
reported as a result with exit code 1.send_heartbeat
Check the sandbox’s lifetime budget.timeout_seconds, terminating the
Sailbox and raising the cookbook’s SandboxTerminatedError once
the budget has elapsed. timeout is part of the cookbook’s
heartbeat signature and is unused here, since there is no request
for it to bound.cleanup
Terminate the backing Sailbox. Safe to call more than once (termination is idempotent for an already-gone Sailbox); a cancellation arriving mid-cleanup still lets the termination finish, within a bounded grace, before propagating, so the Sailbox does not stay running and billable.tinker_sandbox_factory
Create aTinkerSandbox for a tinker-cookbook environment.functools.partial of it, to preset the
keyword arguments) wherever the cookbook accepts a sandbox factory; being
a module-level function, it pickles by reference, so it survives the
cookbook’s process boundaries.The Sailbox’s image comes from the [environment].docker_image
reference in the task.toml next to env_dir, or from image_ref
when given. Docker-style short references are accepted
(python:3.11). The Sailbox is created in the app app (default
$SAIL_APP or "tinker", created on first use) and
timeout_seconds becomes the sandbox’s lifetime budget.SailboxEnvironment
A Harbor environment whose commands run in a Sailbox.ExtendsComposeServiceOpsMixin, BaseEnvironment.start creates the Sailbox from the task’s image: a declared
docker_image is used as a registry reference (Docker-style short
references are accepted), and a task that ships an
environment/Dockerfile instead has it built into a Sailbox image.
Task and persistent environment variables apply to every command, and
the task’s environment/ directory is uploaded into the working
directory for prebuilt-image tasks, the same way Harbor’s other cloud
providers do.A task that ships an environment/docker-compose.yaml runs as a
Docker Compose project inside the Sailbox instead: the services’
images are pulled or built there, commands run in the task’s main
service, and per-service operations (exec, download, stop) reach the
other services.stop puts the Sailbox to sleep, so a kept environment stops billing
and resumes with its filesystem intact on the next start (in
compose mode the whole project sleeps and wakes with the Sailbox);
deletion terminates it. Capabilities Sailboxes lack (GPUs, TPUs,
Windows, and enforcement of restrictive network policies: the
no-network mode and egress allowlists) are declared unsupported, so
Harbor rejects tasks needing them up front rather than running them
degraded; a task
with default public networking is unaffected. Host mount specs are
accepted and unused outside compose mode, which Harbor permits for
cloud providers that do not bind-mount; in compose mode they are bound
into the main service from the Sailbox’s own filesystem.start
Create the environment’s Sailbox, building its image if needed.exec
Run a shell command in the environment and return its result.main service;
otherwise it runs in the Sailbox itself.upload_file
Copy a local file in, keeping its permission bits.download_file
Copy a file out to the local filesystem.upload_dir
Copy a local directory’s contents in.download_dir
Copy a directory’s contents out to a local directory.stop
Stop the environment: terminate its Sailbox, or put it to sleep.Config
SDK configuration resolved from the environment and~/.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, 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
Load SDK config, raisingValueError when no API key is configured.from_env_optional_api_key
Load SDK config without requiring an 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
Knobs governing the auto-retry path.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.Attributes:Types
Plain data types accepted by and returned from the calls above.IngressPort
A guest port to expose for ingress.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. An entry
that reads as an address or a range (e.g. ["203.0.113.0/24"]) matches
source IPs; every other entry is a Sail app name whose Sailboxes may
connect. An app name cannot read as an address or a range, and cannot
contain a /. An address must not carry an IPv6 zone, such as
fe80::1%eth0, which names an interface on one machine rather than a
source.
App-name entries are supported on "http" listeners only. Raw-TCP
connections carry no source app identity, so "tcp" allowlists must
be addresses or ranges. 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.Attributes:HttpEndpoint
The routable HTTPS address of an"http" listener.Attributes:TcpEndpoint
The host and port to connect to for a"tcp" listener.Attributes:Listener
An exposed guest port.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.Attributes:SailboxPage
One page ofSailbox.list_page results plus the server’s pagination envelope.Attributes:SailboxCheckpoint
A durable checkpoint handle that can be used to start new Sailboxes.Attributes:UpgradeResult
The outcome of a Sailbox runtime upgrade.Attributes:SailboxDeprecation
Actionable notice that a Sailbox’s runtime should be upgraded.Attributes:SailboxVolumeMount
One volume attached to a Sailbox and where it is mounted.Attributes:AutoSleep
When Sail may put a Sailbox to sleep on its own.Sail sleeps Sailboxes that are doing nothing, freeing their memory and waking them the moment anything needs them again. Waking takes a couple of seconds: free for a batch job, unwelcome if someone is waiting at a terminal.Build one withdefault, not_before, or never.
Every choice can only make Sail sleep the Sailbox less often. None of them
causes a sleep, and Sail still sleeps a Sailbox only when it sits fully
idle: no busy process, no imminent timer, nothing a sleep would interrupt.Calling Sailbox.sleep yourself is unaffected, and so are
Sailbox.pause, Sailbox.resume, and scheduled wakes.Attributes:default
Let Sail decide, on its own timing. The default.not_before
Let Sail decide, but not before this much idle time.never instead.A wait of zero is the default, and is stored and read back as the
default rather than as a minimum of nothing.never
Stop Sail sleeping a Sailbox on its own.DirEntry
One entry in a directory listing fromSailboxFs.ls.Attributes:ExecResult
The completed result of a Sailbox command: its buffered output, exit code, whether it was killed on timeout, and flags describing how complete the buffered output is.Attributes:GuestPath
A path inside the guest: a str or aPurePosixPath. Guest paths are
remote POSIX paths, independent of the local platform.DEFAULT_LIST_LIMIT
Default page size forSailbox.list and Sailbox.list_page, sourced
from the shared core.Errors
Exceptions raised by this SDK surface. Every one of them extendsSailError, so except sail.SailError catches them all. SailDeprecationWarning is the one entry below that is not an error: it is a warning the SDK emits through Python’s warnings module.SailError
Base class for Sail SDK errors.Every operation failure the SDK raises derives from this class, and the classes that match a Python builtin also inherit it (for exampleNotFoundError is a LookupError), so except sail.SailError and
builtin-based handlers both work. A few argument-type mistakes raise the
plain builtin TypeError.Attributes:NotFoundError
Raised when a resource (Sailbox, app, volume, or checkpoint) is not found.ExtendsSailError, LookupError.PermissionDeniedError
Raised for a missing or invalid API key, or insufficient scope.ExtendsSailError, PermissionError.InvalidArgumentError
Raised when an argument or the SDK configuration is rejected as invalid.ExtendsSailError, ValueError.InternalError
Raised for an unexpected internal SDK failure.ExtendsSailError, RuntimeError.FileNotFoundError
Raised when a guest file operation references a path that does not exist.ExtendsSailError, builtins.FileNotFoundError.BrokenPipeError
Raised when a stdin write hits a command that already finished.ExtendsSailError, builtins.BrokenPipeError.TimeoutError
Raised when a transport attempt exceeds its deadline.ExtendsSailError, builtins.TimeoutError.TransportError
Raised when the transport cannot establish or maintain a connection.ExtendsSailError, ConnectionError.ApiError
Raised for any other non-2xx API response.ExtendsSailError, RuntimeError.RuntimeError inheritance keeps generic retry-on-RuntimeError loops
working; prefer branching on retryable.SailDeprecationWarning
Warning that a Sail client or Sailbox runtime should be upgraded.ExtendsUserWarning.SailboxError
Base class for Sailbox-specific SDK errors.ExtendsSailError.SailboxCreationError
Raised when Sailbox creation fails.ExtendsSailboxError.ImageBuildError
Raised when a custom image build fails.ExtendsSailboxError.SailboxExecutionError
Base class for Sailbox exec-related SDK errors.ExtendsSailboxError.SailboxTerminatedError
Raised when the Sailbox no longer exists.ExtendsSailboxExecutionError.SailboxExecRequestNotFoundError
Raised when a wait references an unknown exec request.ExtendsSailboxExecutionError.SailboxHostLostError
Raised when the machine hosting your Sailbox failed before the command finished.ExtendsSailboxExecutionError.The command may have run only partially, and its output is gone. The run
cannot be resumed. Calling 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
Raised when a Python function fails while running in a Sailbox.ExtendsSailboxExecutionError.SailboxFunctionSerializationError
Raised when a Python function payload or result cannot be serialized.ExtendsSailboxExecutionError.CommandFailedError
Raised byrun(check=True) when the command exits nonzero or times out.Extends SailboxExecutionError.Carries the completed sail.ExecResult as result.VoyageError
Base class for Voyage SDK errors.ExtendsSailError.VoyageHTTPError
Raised when the Voyage API returns an HTTP error.ExtendsVoyageError.VoyageNotFoundError
Raised when a Voyage cannot be found for the current API key.ExtendsVoyageHTTPError.InferenceError
Base class for Sail inference wrapper errors.ExtendsSailError.InferenceHTTPError
Raised when a Sail inference endpoint returns an HTTP error.ExtendsInferenceError.