Skip to main content

Python SDK for the Sail sandbox platform

Project description

sail-sdk

Python SDK for Sail sailboxes.

Install

pip install sail-sdk
uv add sail-sdk

Configure

The SDK reads configuration from environment variables:

export SAIL_API_KEY=sk_...

By default the SDK talks to production.

For dev or staging, set SAIL_MODE:

export SAIL_MODE=staging  # or dev

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,
)

print(sb.sailbox_id)
print(sb.status)
print(sb.worker_address)

Sailbox.create returns after the VM is running. Supported create arguments are:

  • app: a sail.App from App.find
  • image: a sail.Image value or a built custom image
  • name: sailbox name
  • cpu: vCPU count, default 1
  • memory_mib: memory in MiB, default 1024
  • disk_gib: writable disk size in GiB, default 1
  • ingress_ports: optional list of guest ports to expose

AMD64 image values still exist in the SDK for compatibility, but new sailboxes currently must use arm64 images. The scheduler rejects AMD64 sailbox create and image build requests.

Daemon Sailboxes

Use Sailbox.create_daemon when the sailbox should start and manage one long-running process. Daemon sailboxes support lifecycle operations, listeners, and outbound requests, but public exec() is disabled after the daemon is registered.

import sail

app = sail.App.find(name="daemon-demo", mint_if_missing=True)

sb = sail.Sailbox.create_daemon(
    app=app,
    image=sail.Image.debian_arm64,
    name="daemon-1",
    command="python3 -m http.server 3000",
    checkpoint_poll_frequency_s=5,
    min_checkpoint_cooldown_s=30,
    ingress_ports=[3000],
)

print(sb.sailbox_id)
print(sb.daemon_pgid)

Exec

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 let the command run without an SDK-provided runtime limit. Exec requests use an idempotency key so transient worker-proxy connection closures can be retried safely. The SDK generates one automatically; pass idempotency_key when you need a stable key across process restarts.

Background exec starts a detached process and waits only for the launcher shell:

sb.exec("python3 -m http.server 3000", background=True).wait()

Only one exec request may run at a time for a sailbox. If another exec is already active, the SDK raises sail.SailboxExecAlreadyRunningError.

Networking

Expose guest ports when creating the sailbox:

sb = sail.Sailbox.create(
    app=app,
    image=sail.Image.debian_arm64,
    name="sandbox-net",
    ingress_ports=[3000],
)

sb.exec("python3 -m http.server 3000", background=True).wait()

listener = sb.listener(3000)
print(listener.url)
print(listener.route_status)
listener.wait(timeout=60)

Listeners can front plain HTTP services, server-sent events, and WebSocket-style upgrade traffic running inside the sailbox.

Use listeners() to list every exposed port:

for listener in sb.listeners():
    print(listener.port, listener.url, listener.route_status)

Use request() to ask the worker proxy to make an outbound HTTP request on behalf of the sailbox:

req = sb.request(
    "POST",
    "https://example.com/api",
    json={"hello": "world"},
    idempotency_key="example-1",
)

completed = req.wait()
print(completed.status)
if completed.response:
    print(completed.response.status_code)
    print(completed.response.text)

idempotency_key is required for request() and ensures only a single request is sent from the client side. data and json are mutually exclusive.

You can refresh, wait for, or cancel a tracked request:

req.refresh()
req.wait(timeout=30)
req.cancel()

Lifecycle

sb.checkpoint()  # durably checkpoint while keeping the sailbox running
sb.stop()        # checkpoint and stop
sb.start()       # resume a stopped sailbox
sb.terminate()   # permanently terminate

After stop(), exec() raises SailboxExecutionError until start() succeeds.

Custom Images

Start from the arm64 Debian base image, add build steps, then call build():

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"})
)

built_image = image.build(timeout=1800)

sb = sail.Sailbox.create(
    app=app,
    image=built_image,
    name="custom-image-demo",
)

Supported image build helpers:

  • apt_install(*packages)
  • pip_install(*packages)
  • run_commands(*commands)
  • env(dict[str, str])
  • build(timeout=1800)

Images

Current image properties:

arm64_image = sail.Image.debian_arm64
arm_image = sail.Image.debian_arm
amd64_image = sail.Image.debian_amd64
amd_image = sail.Image.debian_amd

Common SDK exceptions:

  • sail.SailError
  • sail.SailboxError
  • sail.SailboxCreationError
  • sail.SailboxExecutionError
  • sail.SailboxExecAlreadyRunningError
  • sail.SailboxExecRequestNotFoundError
  • sail.SailboxTerminatedError
  • sail.SailboxWorkerLostError
  • sail.ImageBuildError

Examples

  • examples/sailbox_smoke.py: start an arm64 Debian sailbox and run exec commands.
  • examples/sailbox_custom_image.py: build an arm64 custom image with apt_install, pip_install, run_commands, and env, then launch a sailbox from it.

The repo-level daemon examples under testing/examples include force-rebuild knobs for validating rootfs or guest-agent changes without reusing an existing custom image:

OPENCODE_FORCE_IMAGE_REBUILD=1 uv run --project testing python testing/examples/opencode_server.py
OPENCLAW_FORCE_IMAGE_REBUILD=1 uv run --project testing python testing/examples/openclaw_server.py

Publishing

Build a distributable package locally from the repo root:

just python-sdk-build

Publish from a developer machine with a PyPI token:

export UV_PUBLISH_TOKEN=pypi-...
just python-sdk-publish

The repository also includes a GitHub Actions release workflow at .github/workflows/python-sdk-publish.yml. It publishes when you push a tag like python-sdk-v0.1.0, after verifying that the tag version matches sail.__version__.

Recommended setup:

  1. Create the sail-sdk project on PyPI.
  2. Configure PyPI Trusted Publishing for this GitHub repository and the python-sdk-publish.yml workflow.
  3. Bump sdk/python/src/sail/__about__.py.
  4. Push a matching tag: git tag python-sdk-v0.1.0 && git push origin python-sdk-v0.1.0

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

sail_sdk-0.1.4.tar.gz (34.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

sail_sdk-0.1.4-py3-none-any.whl (32.3 kB view details)

Uploaded Python 3

File details

Details for the file sail_sdk-0.1.4.tar.gz.

File metadata

  • Download URL: sail_sdk-0.1.4.tar.gz
  • Upload date:
  • Size: 34.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","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

Hashes for sail_sdk-0.1.4.tar.gz
Algorithm Hash digest
SHA256 b54a050f03944852e7f28c05a7e80c86d54215591585edce0a21945ded3687e9
MD5 d5a3ff5cc92c073c084e9a47b516e2b5
BLAKE2b-256 e232111f289d4b63bb38967b8405d871e2515518a05344ee35d686b2a19333c6

See more details on using hashes here.

File details

Details for the file sail_sdk-0.1.4-py3-none-any.whl.

File metadata

  • Download URL: sail_sdk-0.1.4-py3-none-any.whl
  • Upload date:
  • Size: 32.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","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

Hashes for sail_sdk-0.1.4-py3-none-any.whl
Algorithm Hash digest
SHA256 0b222bd2cd3580b4dd1b89cd8fb0cf586aa6a92f25149080b861516d545b9e65
MD5 92f707a3bebc3f5fe5d24d8b0aa0c1aa
BLAKE2b-256 aa4f85d84e26f5cd4059c217119b3a55dcce0494dc53a0f8a042a9745f96e675

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page