Skip to main content

Runbox Python SDK

Python client for Runbox, the lightweight sandbox for agent commands and files. Standard library only — no requests, no build step.

pip install runbox-sdk

Quick start

import os
from runbox import Client, Identity

client = Client(
    "http://localhost:8099",
    api_key=os.environ["RUNBOX_API_KEY"],
    identity=Identity(namespace="team-a", principal="user-42"),
)

result = client.exec('python3 -c "print(6 * 7)"', timeout=30)
print(result["exit_code"], result["stdout"])      # 0 42

The client is safe to reuse across threads: bind an identity once, or pass identity= on any call.

Identity

Exactly one identity form per request:

Form Constructor Headers sent
Opaque token Identity(token="user-42") X-Runbox-Identity
Tenant + user Identity(namespace="team-a", principal="user-42") X-Runbox-Namespace + X-Runbox-Principal

Supplying both forms, or half of the scoped form, returns 400 from the runner.

Run a command

result = client.exec(
    "python3 analyze.py files/input.csv",
    timeout=60,
    files=[FileUpload(name="input.csv", path="input.csv")],   # streamed from disk
)

with open("input.csv", "rb") as handle:
    result = client.exec(
        "cat files/input.csv",
        files=[FileUpload(name="input.csv", stream=handle)],  # streamed, never closed
    )

Attachments land in files/<name> inside the workspace; the command refers to them by that path. exec returns the parsed JSON response, including exit_code, stdout, stderr, files and duration_ms.

Files

uploaded = client.upload_file("report.xlsx")       # -> {"path": "files/report.xlsx", ...}
client.upload("notes.txt", b"hello")
client.upload_batch([FileUpload(name="a.csv", path="a.csv"), FileUpload(name="b.csv", path="b.csv")])
entries = client.list("out")
client.delete_workspace()                          # keeps the identity and its UID

Downloads can go to a path or to any file-like object:

client.download_path("out/report.csv", "report.csv")
client.download_path_to("out/report.csv", sink)    # sink: anything with .write()
client.download_archive(["out", "tmp"], "artifacts.tar")
client.download_archive_to(["out"], sink)

Streaming and forwarding

open_path returns the raw http.client.HTTPResponse, so nothing is buffered. iter_path yields chunks, which is what a framework response wants:

from fastapi import FastAPI
from fastapi.responses import StreamingResponse

app = FastAPI()

@app.get("/files")
def download(path: str):
    return StreamingResponse(client.iter_path(path), media_type="application/octet-stream")

Forwarding a byte range (video scrubbing, resumable downloads) works the same way; the runner answers with 206 and Content-Range, and the SDK passes the body straight through:

@app.get("/files")
def download(path: str, range: str | None = None):
    return StreamingResponse(client.iter_path(path, range_header=range))

A whole directory as one tar stream:

@app.get("/archive")
def archive():
    return StreamingResponse(
        client.iter_archive(["out", "tmp/step2"]),
        media_type="application/x-tar",
        headers={"Content-Disposition": 'attachment; filename="artifacts.tar"'},
    )

If you need the response itself — headers, status, manual chunking — use open_path / open_archive and close what you get back:

response = client.open_path("out/report.pdf", range_header="bytes=0-1023")
try:
    print(response.status, response.getheader("Content-Length"))
    for chunk in iter(lambda: response.read(65536), b""):
        handle(chunk)
finally:
    response.close()

Streaming stops when the client disconnects: the generator's finally closes the response, and the runner notices the closed connection.

Health and limits

client.health()
client.ready()
client.capabilities()                  # limits and detected tooling
client.wait_ready(timeout=30)
client.metrics()

Errors

Every non-2xx response raises RunboxError with status, code and message:

from runbox import RunboxError

try:
    client.exec("rm -rf out && echo gone")
except RunboxError as error:
    if error.code in ("busy", "quota_exceeded"):
        ...                            # retry later, or clean the workspace
    elif error.code == "timeout":
        ...                            # raise the timeout
    else:
        raise

Common codes: 400 bad identity or path, 401 bad bearer token, 404 path missing or owned by another identity, 409 busy / quota_exceeded, 413 request too large.

Metadata

Release files for runbox-sdk 0.2.5

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for runbox-sdk 0.2.5
File Size Uploaded Provenance Actions
runbox_sdk-0.2.5.tar.gz 14.2 kB Without provenance Details for runbox_sdk-0.2.5.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for runbox-sdk 0.2.5
File Interpreter ABI Platform Actions
runbox_sdk-0.2.5-py3-none-any.whl Python 3 none any Details for runbox_sdk-0.2.5-py3-none-any.whl

Total release size: 24.9 kB

Release files / runbox_sdk-0.2.5.tar.gz

Download URL runbox_sdk-0.2.5.tar.gz
Size 14.2 kB
Tags Source
SHA-256 checksum
How to use checksums
e0f622abc361d0a54bcb5b2a4ebb07c7dbfd22704ec04a554021b90a0787561e
BLAKE2b-256 checksum
How to use checksums
11f014e51bf768966b9d1c49748bd061076cd003bf680146fd8a23e6edbf9c82
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.16

Release files / runbox_sdk-0.2.5-py3-none-any.whl

Download URL runbox_sdk-0.2.5-py3-none-any.whl
Size 10.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
ac0d3baa028b0104d1904af8e677660a87e4a991dca8416014f60ec1b7807fdf
BLAKE2b-256 checksum
How to use checksums
006d175b29671e5b7cff43bed338f427d811c175b62c55a7277a3a1a37512567
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.16

Security for runbox-sdk 0.2.5

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page