boxxkite-client 0.6.0

Rust client for a hosted boxxkite control-plane (sandboxes, exec, files, audit log, human takeover)
Documentation

boxxkite-client (Rust)

crates.io

A Rust client for a hosted boxxkite control-plane — create sandboxes, run commands, edit files, over HTTP/WebSocket. Not the boxxkite package itself (the self-hosted Python core that embeds SandboxManager against your own Kubernetes cluster) — use this to talk to someone else's running control-plane, hosted or self-hosted, over its API. Async-first (tokio), mirroring the same /v1/* REST API sdk-python/sdk-js wrap.

Install

[dependencies]
boxxkite-client = "0.1"
tokio = { version = "1", features = ["full"] }

Quickstart

use boxxkite_client::{Client, CreateSandboxOptions, ExecOptions};

#[tokio::main]
async fn main() -> Result<(), boxxkite_client::BoxxkiteError> {
    let client = Client::new("https://your-control-plane.example.com", "bxk_live_...")?;

    let sandbox = client
        .create_sandbox(CreateSandboxOptions::new().label("demo"))
        .await?;

    let result = client
        .exec(&sandbox.id, "python3 -c 'print(1 + 1)'", ExecOptions::new())
        .await?;
    println!("{}", result.stdout); // "2\n"

    client.file_create(&sandbox.id, "notes.txt", "hello from boxxkite-client\n", Default::default()).await?;
    println!("{}", client.view(&sandbox.id, "notes.txt", Default::default()).await?.content);

    client.destroy_sandbox(&sandbox.id).await?;
    Ok(())
}

Client::new(base_url, api_key) covers the common case; use Client::builder() for a custom timeout or a preconfigured reqwest::Client (e.g. pointed at a test server).

Also available: file/directory search (ls/glob/grep), an audit-log feed (get_log/watch, the latter a Stream over Server-Sent Events), interactive human takeover (takeover, a raw tokio-tungstenite WebSocket byte stream), and desktop (GUI) takeover over the same raw-WebSocket pattern (desktop_takeover) — plus CRUD for custom images, independent storage volumes, outbound-MCP connections, and webhook subscriptions. Full reference with examples for all of these: docs/API.md.

Error handling

Every fallible call returns Result<T, BoxxkiteError>. A non-2xx response becomes BoxxkiteError::Api { status, code, message }; a transport-level failure becomes BoxxkiteError::Connection.

use boxxkite_client::BoxxkiteError;

match client.exec(&sandbox.id, "echo hi", Default::default()).await {
    Ok(result) => println!("{}", result.stdout),
    Err(BoxxkiteError::Api { code, .. }) if code == "concurrent_sandbox_limit_reached" => {
        // back off, destroy an old session, etc.
    }
    Err(err) => return Err(err),
}

Scope

This crate wraps the same /v1/* API surface sdk-python/sdk-js/sdk-go wrap: sandbox lifecycle (create_sandbox/get_sandbox/list_sandboxes/ destroy_sandbox), exec/file operations, the http_request secrets-broker proxy, background process management, the audit log, human takeover, desktop takeover, network-ingress preview URLs (create_preview_url/ revoke_preview_url), agent-invokable LSP completions (lsp_start/lsp_open/ lsp_completion/lsp_stop), account/usage introspection (account/usage), dashboard auth-flow helpers (request_password_reset/confirm_password_reset/ verify_email/resend_verification/refresh_token/logout), the per-account command allowlist (get_allowed_commands/set_allowed_commands/ clear_allowed_commands), the SandboxSession/with_sandbox convenience helper, and CRUD for images/volumes/outbound-MCP connections/webhooks/secrets.

Automatic retry with exponential backoff + jitter (honoring Retry-After) is opt-in via Client::builder().max_retries(n) or .retry(RetryConfig {..}) — off by default. Only 429 (any method) and 5xx/transport errors on idempotent methods (GET/PUT/DELETE/…) are retried; a bare POST is never retried on a 5xx, since it may have already applied server-side.

Deliberately not included in this pass:

  • A LangChain-style tool-factory wrapper. sdk-python/sdk-js both ship one because LangChain/LangChain.js are the dominant integration point in their ecosystems. No comparably dominant Rust agent-framework tool-spec convention exists yet to mirror, so this crate stops at the plain HTTP/WS client — wire it into whatever tool-calling shape your own agent framework expects.
  • A synchronous/blocking client variant. Rust's async ecosystem is tokio-first by convention; this crate follows that rather than maintaining a redundant blocking wrapper. If you need one, wrap calls in tokio::runtime::Runtime::block_on yourself.
  • A route sdk-python/sdk-js themselves don't wrap yet, even though it exists on the control-plane: filesystem snapshots (/snapshots/*). This crate mirrors its two reference SDKs' actual method sets rather than inventing new surface area ahead of them.
  • Webhook's newer payload_format/hec_token fields (added to control_plane.schemas.WebhookCreateRequest for the Splunk HEC/audit-log export addendum, issue #125) — sdk-python/sdk-js haven't picked these up yet, so this crate's create_webhook sticks to url/event_types/ description, the same three fields those two SDKs send.

Development

cargo build
cargo test
cargo clippy --all-targets -- -D warnings
cargo fmt

Tests mock the control-plane with wiremock (a real local HTTP server, not an in-process shim) — no real deployment needed. takeover()'s test spins up a bare tokio TCP listener and does the WebSocket handshake by hand with tokio-tungstenite's server-side helpers, since wiremock is HTTP-only.

Related tools

Moving an in-progress local Claude Code/Codex CLI/opencode/Cursor session (full conversation history, not just a diff) into a fresh boxxkite sandbox is handled by boxxkite handoff <tool> (Python, part of the main boxxkite CLI, built on sdk-python, not this crate) — see ../docs/handoff-adapters.md.

See the root README for what boxxkite is and the full self-hosting story.

Questions, bug reports, or need a usage-limit bump? Join the Discord.