Skip to main content

EU-hosted sandbox VMs for AI agents - official Python SDK

Project description

orkestr Python SDK

EU-hosted sandbox VMs for AI agents. Type-safe Python client for the api.orkestr.eu/v1/sandboxes REST API.

Status: 0.1.0 - first stable release. The client surface may still have breaking changes within the 0.x line; 1.0.0 will signal API stability.

Install

pip install orkestr

Requires Python 3.10+.

Authenticate

Mint an API token in the orkestr console with the sandboxes:write scope.

export ORKESTR_API_KEY="ork_..."

The SDK picks the key up from ORKESTR_API_KEY or you pass it explicitly.

Quick start

One-shot execution inside a fresh sandbox. The with block auto-terminates the sandbox on exit, preventing runaway costs if the caller crashes.

from orkestr import Sandbox

with Sandbox.create(template="python-3.12") as sbx:
    sbx.files.write("/workspace/main.py", "print(sum(range(1_000_000)))")
    result = sbx.exec("python /workspace/main.py")
    print(result.stdout)        # 499999500000
    print(result.duration_ms)   # ~120

API

Create a sandbox

sbx = Sandbox.create(
    template="python-3.12",          # one of the templates listed below
    size="small",                    # "small" | "medium" | "large" (plan-capped)
    network="off",                   # "off" | "restricted" | "open"
    timeout_seconds=600,             # auto-terminate after this many seconds
    env={"OPENAI_API_KEY": "sk-..."},
    metadata={"agent_run": "r_123"},
    region="fsn1",                   # "fsn1" (DE) | "hel1" (FI) | None for auto
    api_key=None,                    # falls back to ORKESTR_API_KEY
)
print(sbx.id)             # "sbx_01HXYZ..."
print(sbx.status)         # "running"

Templates

Template Description
python-3.12 CPython 3.12 with pip and common libs
python-3.12-bare CPython 3.12 only, faster start
node-22 Node 22 with npm
ubuntu-24.04 Minimal Ubuntu shell environment

Sizes

size picks from a fixed menu. Allowed sizes are plan-capped.

Size vCPU RAM Plans
small 0.5 512 MB free, pro, team
medium 2 4 GB pro, team
large 4 8 GB team

Check your plan limits

Sandbox.limits() reports the sizes and caps available to your API key's plan — pick a size up front instead of discovering the limit from a PlanLimitError. Handy when the same code runs under keys on different plans (e.g. a reseller provisioning per customer).

limits = Sandbox.limits()
limits.plan                        # "free"
limits.allowed_sizes               # ["small"]
"medium" in limits.allowed_sizes   # False
limits.max_concurrent              # 1
limits.usage_gb_hours_used         # 2.5  (memory consumed this month)
limits.usage_gb_hours_included     # 10.0 (memory budget for the month)
limits.usage_cpu_hours_used        # 0.42 (CPU consumed this month)
limits.usage_cpu_hours_included    # 2.0  (CPU budget for the month)
limits.usage_resets_at             # datetime - 1st of next month UTC
for s in limits.sizes:             # full menu, each with .allowed
    print(s.size, s.cpu, s.memory_mb, s.allowed)

When usage_gb_hours_used >= usage_gb_hours_included, Sandbox.create() raises PlanLimitError with code="usage_exhausted" until the counter resets. The metering rolls up mem_mb_seconds from the host's per-second compute samples, so an idle sandbox still accrues toward the budget until it is terminated.

Run a command

result = sbx.exec("python /workspace/main.py", timeout_seconds=60)
result.stdout       # str
result.stderr       # str
result.exit_code    # int
result.duration_ms  # int

Stream a long command

for chunk in sbx.exec_stream("python long_task.py"):
    if chunk.stream == "stdout":
        print(chunk.data, end="", flush=True)
    else:
        print(chunk.data, end="", flush=True, file=sys.stderr)

Files

sbx.files.write("/workspace/data.json", '{"x": 1}')
sbx.files.write_bytes("/workspace/blob.bin", b"\x00\x01\x02")

content = sbx.files.read("/workspace/out.txt")       # returns str
raw = sbx.files.read_bytes("/workspace/blob.bin")     # returns bytes

for entry in sbx.files.list("/workspace"):
    print(entry.name, entry.is_dir, entry.size)

sbx.files.delete("/workspace/out.txt")

Pause + resume

Pausing snapshots the sandbox memory + disk and stops the compute meter. Resume restores it on the same or a different host. pause() returns the sandbox id; persist it across processes and pass to Sandbox.resume.

sbx = Sandbox.create(template="node-22", network="restricted")
sandbox_id = sbx.pause()
# ... minutes or hours later, from any worker:
sbx = Sandbox.resume(sandbox_id)

Snapshot retention is plan-capped (free: 1, pro: 3, team: 10). Calling pause() over the cap raises SnapshotCapReached.

Terminate

Context-manager exit calls terminate(). Outside with:

sbx.terminate()

After terminate() the sandbox row stays in your account history but the VM and any in-memory state are gone.

List your sandboxes

for sbx in Sandbox.list(status="running"):
    print(sbx.id, sbx.template, sbx.created_at)

Errors

All SDK errors inherit from orkestr.OrkestrError.

Exception When
AuthError Missing / invalid / expired API key, or scope mismatch
RateLimitError Plan rate limit hit
PlanLimitError Sandbox limit, concurrent limit, snapshot cap
SandboxNotFound sandbox_id doesn't exist or isn't yours
SandboxNotReady Operation called on a paused / terminated sandbox
ExecTimeout exec() exceeded timeout_seconds
NetworkPolicyError Command tried to reach a host the policy blocks
OrkestrError Any other API-level error

Async support

The MVP SDK is sync only. Async (AsyncSandbox) lands in v0.2.0 if a design partner blocks on it; the wire format is identical.

Versioning

Follows Semantic Versioning. The SDK targets the /v1/sandboxes API; bumps to v1 of the API are non-breaking for SDK callers. v2 of the API will require an SDK major.

Links

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

orkestr-0.1.2.tar.gz (23.2 kB view details)

Uploaded Source

Built Distribution

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

orkestr-0.1.2-py3-none-any.whl (17.6 kB view details)

Uploaded Python 3

File details

Details for the file orkestr-0.1.2.tar.gz.

File metadata

  • Download URL: orkestr-0.1.2.tar.gz
  • Upload date:
  • Size: 23.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.13

File hashes

Hashes for orkestr-0.1.2.tar.gz
Algorithm Hash digest
SHA256 f2659ab118299abb745dfa12b798a1c46b5cfae7dccee73f94a3408c4fe9273d
MD5 5526f4a3781315bcc4c5bbcc30f0e3b0
BLAKE2b-256 058c348c628b3ff33af6640394dbc2aafe41bb552392d1d333e3673b2818249e

See more details on using hashes here.

File details

Details for the file orkestr-0.1.2-py3-none-any.whl.

File metadata

  • Download URL: orkestr-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 17.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.13

File hashes

Hashes for orkestr-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 2f4bea533dcd76560dcc281de9c8235cfcee3f97a11a1578dfd8c6ae8964975a
MD5 64b52787b41f9fded954481f98584d49
BLAKE2b-256 39f68eca84bfdcea52f81a59ff3947bf3c818599b5e97f769de94d5dbfec44c9

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