# sail-rs

The official Rust SDK for [Sail](https://sailresearch.com). Create and drive
**sailboxes**, sandboxed cloud VMs, from Rust: lifecycle, streaming exec, file
transfer, and ingress.

This crate is the canonical core that the Sail Python SDK, the TypeScript SDK,
and the `sail` CLI are all built on, so the Rust API is the source of truth for
behavior.

```toml
[dependencies]
sail-rs = "0.2"
# tokio must be a direct dependency to write #[tokio::main]:
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
```

The crate is published as `sail-rs` and imported as `sail`:

```rust
use sail::Client;
```

Adding the crate does not install the `sail` CLI (a library crate cannot ship a
companion binary). Install the CLI separately:

```bash
curl -fsSL https://cli.sailresearch.com/install.sh | sh
```

## Quickstart

The client reads its API key from `SAIL_API_KEY` (falling back to the credential
stored under `~/.sail`), the same configuration the CLI uses.

```rust,no_run
use sail::exec::ExecOptions;
use sail::Client;

#[tokio::main]
async fn main() -> Result<(), sail::error::SailError> {
    let client = Client::from_env()?;

    // List the sailboxes in your org.
    let page = client.list_sailboxes(&Default::default()).await?;
    println!("{} sailboxes", page.items.len());

    // Bind a sailbox by id and run a command in it.
    let sb = client.sailbox("sb_...");
    let proc = sb.exec_shell("echo hello from the guest", ExecOptions::default()).await?;
    let result = proc.wait().await?;
    print!("{}", result.stdout);

    Ok(())
}
```

## Runtime

Every method is `async`. The client is meant to be driven on a Tokio runtime:

- **Async hosts** await the methods directly on their own runtime. Streaming
  operations (`exec`, `read_stream`, `write_stream`) spawn their background
  pump on the calling task's runtime, so call them from within one.
- **Synchronous code** can drive any method with [`sail::block_on`], which runs
  it to completion on a shared internal runtime. Do not call `block_on` from
  inside an existing runtime; await the method instead.

[`Client`] is cheap to clone (it shares its connection pools and config behind an
`Arc`), so clone it to share across tasks. Use one client per runtime.

## Sail in other languages

- **Python**: `pip install sail` (this crate compiled as a native
  extension).
- **TypeScript**: `npm install @sailresearch/sdk` (this crate compiled as a napi-rs
  native addon for Node and Bun).
- **CLI**: the `sail` command, built on this crate.

## Documentation

- [API reference on docs.rs](https://docs.rs/sail-rs).
- [Sailbox Rust guide](https://docs.sailresearch.com/rust-sdk).
- [Sail documentation](https://docs.sailresearch.com): quickstarts, guides, and examples.

## Stability

The public API is everything documented here. Items hidden from this
documentation (including the `internal` module) back the first-party bindings and
are not part of the public API.

## License

Licensed under the [Apache License, Version 2.0](LICENSE).
