Python SDK for the Sail sandbox platform
Project description
sail-sdk
Python SDK for Sail sailboxes.
Sailboxes are persistent Linux sandboxes managed by Sail. Use the sail-sdk
Python package to create a VM, run shell commands, expose ports, make tracked
network requests, checkpoint state, pause, resume, and terminate it.
Sailboxes are currently in beta. APIs and operational behavior may change as we stabilize the product.
Install
pip install sail-sdk
uv add sail-sdk
Set your API key before using the SDK:
export SAIL_API_KEY=sk_...
By default, the SDK connects to production. For internal dev or staging
environments, set SAIL_MODE=dev or SAIL_MODE=staging. For an isolated
local tunnel created by just redeploy-sailbox-dev, set SAIL_MODE=local.
Create a sailbox
import sail
app = sail.App.find(name="example-app", mint_if_missing=True)
sb = sail.Sailbox.create(
app=app,
image=sail.Image.debian_arm64,
name="sandbox-1",
cpu=1,
memory_mib=1024,
disk_gib=8,
)
print(sb.sailbox_id)
print(sb.status)
Sailbox.create() returns after the VM is running. The main create arguments
are:
| Argument | Default | Description |
|---|---|---|
app |
Required | A sail.App, usually from sail.App.find() |
image |
Required | A sail.Image value or custom image definition |
name |
Required | Human-readable sailbox name |
image_build_timeout |
1800 |
Seconds to wait for custom image builds before creating the VM |
cpu |
1 |
vCPU count; must be greater than 0 |
memory_mib |
1024 |
Memory in MiB; must be between 1024 and 65536 |
disk_gib |
8 |
Writable state disk in GiB; must be between 1 and 64 |
ingress_ports |
None |
Guest ports to expose publicly |
Sailboxes should use sail.Image.debian_arm64 or sail.Image.debian_arm. We
have plans to support AMD64 images soon - please contact us if you would like
us to prioritise this.
Run commands
exec() starts a shell command and returns a SailboxExecRequest. Call wait()
to retrieve stdout, stderr, and the return code.
result = sb.exec("echo hi", timeout=5).wait()
print(result.stdout)
print(result.stderr)
print(result.returncode)
timeout is the command runtime budget in seconds. Omit it to run without an
SDK-provided runtime limit. Only one exec request can be active on a sailbox at
a time; if another command is running, the SDK raises
sail.SailboxExecAlreadyRunningError.
Pass cwd to run the command from a specific working directory. With
background=True, Sail launches the process through a detached shell and
wait() only waits for that launcher to succeed.
Expose ports
Pass ingress_ports when creating the sailbox, then start a service inside the
VM and fetch its listener URL.
sb = sail.Sailbox.create(
app=app,
image=sail.Image.debian_arm64,
name="web-demo",
ingress_ports=[3000],
)
sb.exec("python3 -m http.server 3000", background=True, cwd="/srv/app").wait()
listener = sb.listener(3000)
listener.wait(timeout=60)
print(listener.url)
print(listener.route_status)
Use listeners() to list every exposed port:
for listener in sb.listeners():
print(listener.port, listener.url, listener.route_status)
Ports must be unique and between 1 and 65535. Ports 22 and 10000 are
reserved by the platform.
Tracked network requests
Use request() to ask the worker proxy to make an HTTP request and track its
result. idempotency_key is highly recommended so that retries do not send
duplicate outbound requests.
req = sb.request(
"POST",
"https://example.com/api",
headers={"X-Source": "sailbox"},
json={"hello": "world"},
idempotency_key="example-request-1",
)
completed = req.wait(timeout=30)
print(completed.status)
if completed.response:
print(completed.response.status_code)
print(completed.response.headers)
print(completed.response.text)
print(completed.response.json())
params, headers, data, json, timeout, and durability are supported.
data and json are mutually exclusive. A SailboxRequest can be refreshed,
waited on, or cancelled:
req.refresh()
req.wait(timeout=30)
req.cancel()
refresh() re-fetches the current status of the tracked request from the worker
proxy and updates the same SailboxRequest object in place. It does not wait
for completion, does not retry the outbound HTTP request, and does not send a
new request to the destination URL. Use it when you want to poll manually or
inspect whether a request has completed before deciding what to do next. Use
wait() when you want to block until the request reaches a terminal state.
Long-running services
Create a normal sailbox and start long-running processes with background exec.
There is no separate daemon creation API. Existing guest-to-egress connections can
remain open while background processes run. Sailboxes are checkpointed only when
you call checkpoint(), pause(), or sleep(), or when Sail receives an AWS
preemption signal for the host.
sb = sail.Sailbox.create(
app=app,
image=sail.Image.debian_arm64,
name="web-daemon",
ingress_ports=[3000],
)
sb.exec("python3 -m http.server 3000", background=True, cwd="/srv/app").wait()
sb.checkpoint()
Lifecycle
sb.checkpoint() # Snapshot while keeping the sailbox running
sb.pause() # Checkpoint and pause until explicit resume
sb.sleep() # Checkpoint and sleep until network ingress, exec, or resume
sb.resume() # Resume a paused or sleeping sailbox
sb.terminate() # Permanently destroy the sailbox
After pause(), exec() raises sail.SailboxExecutionError until resume()
succeeds. Network traffic does not wake a paused sailbox; only resume() does.
After sleep(), network ingress, exec(), or resume() wakes the sailbox from
its latest checkpoint.
terminate() is permanent.
We highly recommend calling sb.checkpoint() after any non-deterministic
command. Upon a node failure, Sailboxes will replay commands from the latest
checkpoint onwards. Checkpointing immediately after a non-deterministic command
allows you to avoid diverging behaviour on replays.
Custom images
Start from the arm64 Debian base image, add build steps, and pass the image
definition to Sailbox.create():
image = (
sail.Image.debian_arm64
.apt_install("git", "curl")
.pip_install("requests")
.run_commands("python3 -m pip show requests >/tmp/requests.txt")
.env({"APP_ENV": "demo"})
)
sb = sail.Sailbox.create(
app=app,
image=image,
name="custom-image-demo",
image_build_timeout=1800,
)
Image definitions are immutable; each helper returns a new definition. Supported
helpers are apt_install(*packages), pip_install(*packages),
run_commands(*commands), env(dict[str, str]), and build(timeout=1800). The
SDK retries short-lived imagebuilder socket-close transport races during
BuildImage and status polling, but retry sleeps are capped by the same
timeout or image_build_timeout budget.
Calling build() eagerly builds the image before you create a sailbox. If the
same image was already built, the cached image is returned. This step is
optional because Sailbox.create() will build custom image definitions before
creating the VM.
API surface
Common exported SDK types:
| Type | Description |
|---|---|
sail.App |
Sail application namespace; use App.find(name=..., mint_if_missing=True) |
sail.Image |
Image namespace with debian_arm64, debian_arm, debian_amd64, and debian_amd |
sail.Sailbox |
Standard sailbox handle |
sail.SailboxExecRequest |
Durable exec request returned by exec() |
sail.SailboxExecResult |
stdout, stderr, and returncode from wait() |
sail.SailboxListener |
Public listener metadata for an exposed guest port |
sail.SailboxRequest |
Tracked outbound HTTP request |
sail.SailboxResponse |
HTTP response with status_code, headers, content, text, and json() |
Common exceptions:
| Exception | Raised when |
|---|---|
sail.SailError |
Base SDK error |
sail.SailboxError |
Base sailbox-specific error |
sail.SailboxCreationError |
Creation, checkpoint, stop, start, or scheduler lifecycle operation fails |
sail.SailboxExecutionError |
Exec, listener, or network request operation fails |
sail.SailboxExecAlreadyRunningError |
Another exec request is already active |
sail.SailboxExecRequestNotFoundError |
A durable exec request cannot be found |
sail.SailboxTerminatedError |
The sailbox no longer exists on the worker |
sail.SailboxWorkerLostError |
The assigned worker was lost during exec wait |
sail.ImageBuildError |
Custom image build fails |
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file sail_sdk-0.1.11.tar.gz.
File metadata
- Download URL: sail_sdk-0.1.11.tar.gz
- Upload date:
- Size: 39.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.10 {"installer":{"name":"uv","version":"0.11.10","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
60043ecdc95a3a0db04a17db4cf690f999c2ec8537749cc9b7777532fd0cb5d1
|
|
| MD5 |
f484b2d7c4955deaec5726b54f2f9ede
|
|
| BLAKE2b-256 |
c2d417ffe2ac0f81de29295c0c257303b03da235206bf68d9457ccf22bbbe7e2
|
File details
Details for the file sail_sdk-0.1.11-py3-none-any.whl.
File metadata
- Download URL: sail_sdk-0.1.11-py3-none-any.whl
- Upload date:
- Size: 40.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.10 {"installer":{"name":"uv","version":"0.11.10","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c0a67d75ad5a7c4d6fe51209552befec3f80e46c790a8868a95ad7fd8c4c838a
|
|
| MD5 |
aafcf3774ff7cfdf757b7b4cd0074671
|
|
| BLAKE2b-256 |
1344cb9ce685aec6148a57ce4ec153be7f2c21f615071024f297dc351c997d00
|