Skip to main content

Python SDK for Tenki Sandbox

Project description

Tenki Sandbox Python SDK

Python SDK for Tenki Sandbox: cloud sandboxes for AI agents and code execution.

pip install tenki-sandbox
from tenki_sandbox import Sandbox

# create() waits by default via a single server-held request and returns an
# exec-ready sandbox with data-plane access primed (wait=False to skip).
with Sandbox.create(project_id="proj_123", cpu_cores=2, memory_mb=4096) as sb:
    result = sb.exec("python3", "-c", "print('hello')")
    result.check()
    print(result.stdout_text)

    # fs paths are relative to the sandbox workdir (absolute paths must stay inside it)
    sb.fs.write_text("input.txt", "data")
    print(sb.fs.read_text("input.txt"))

    preview = sb.expose_port(3000, ttl=3600)
    print(preview.url)

Async (asyncio)

AsyncClient / AsyncSandbox expose the same surface with native async/await, built on grpc.aio — no asyncio.to_thread wrapping required. Use it inside any asyncio server (FastAPI, aiohttp, etc.).

import asyncio
from tenki_sandbox import AsyncSandbox


async def main():
    async with await AsyncSandbox.create(project_id="proj_123", cpu_cores=2) as sb:
        result = await sb.exec("python3", "-c", "print('hello')")
        result.check()
        print(result.stdout_text)

        await sb.fs.write_text("input.txt", "data")
        print(await sb.fs.read_text("input.txt"))

        proc = await sb.start("bash", "-lc", "read name; echo hi $name")
        await proc.write_stdin("tenki\n")
        await proc.close_stdin()
        async for chunk in proc.stdout:
            print(chunk.decode(), end="")
        (await proc.wait()).check()


asyncio.run(main())

The sync Client / Sandbox remain available for non-async consumers. SSH, dial, and host-port tunnels are also async: AsyncSandbox.ssh() returns an AsyncSSHConn (raw async read/write/close, matching the TS/Go SDKs; needs the tenki-sandbox[async] extra for websockets), dial() returns an AsyncDialConn, and expose_host_port() / expose_host_port_resilient() return async tunnels with await tunnel.terminated.wait() and on_terminated callbacks.

Auth

Auth resolution:

  1. auth_token= passed to Client or Sandbox.create
  2. TENKI_AUTH_TOKEN
  3. TENKI_API_KEY

TENKI_API_ENDPOINT overrides the API URL; legacy TENKI_API_URL is also accepted.

Process API

exec collects stdout/stderr and returns a result:

result = sb.exec("npm", "test", cwd="project", timeout=60, env={"CI": "1"})
print(result.stdout_text)
result.check()

start returns a live process:

proc = sb.start("bash", "-lc", "read name; echo hello $name")
proc.write_stdin("tenki\n")
proc.close_stdin()
for chunk in proc.stdout:
    print(chunk.decode(), end="")
proc.wait().check()

Use shell() when you want shell parsing:

sb.shell("python3 -m http.server 3000 >/tmp/server.log 2>&1 &")

Process cwd values follow the guest contract: relative paths are normalized under the sandbox guest workdir (/home/tenki by default), absolute paths are used unchanged, and missing or non-directory targets fail before the process starts.

Sandbox lifetime

Long-lived sandboxes are a parameter choice at create(), not a separate API:

sb = client.create(
    project_id="proj_123",
    sticky=True,              # long-lived session: not reaped on idle
    idle_timeout_minutes=120, # or: generous idle window before auto-pause
    max_duration=8 * 3600,    # total lifetime cap (seconds)
    pause_retention=24 * 3600,# how long a paused session is kept resumable
)
  • max_duration caps total lifetime; sb.extend(seconds) pushes the deadline (sb.info.timeout_at) while running.
  • idle_timeout_minutes auto-pauses an idle sandbox; sb.resume() brings it back with the filesystem intact.
  • sticky=True opts the session out of idle reaping for keep-warm use cases (workspaces cap concurrent sticky sessions).
  • client.list(sticky=True) filters for long-lived sessions.

SSH

For tools that speak SSH (paramiko, scp, IDE remote dev), the SDK can mint a short-lived OpenSSH user certificate for your session and open a transport to the sandbox SSH gateway. No keys are provisioned into the guest; the engine signs your local public key and the gateway verifies the certificate.

Requires pip install websocket-client paramiko (websocket-client for the transport, paramiko if you want a client in-process).

import subprocess
import paramiko

# 1. local keypair (any OpenSSH key works; ed25519 shown)
subprocess.run(["ssh-keygen", "-t", "ed25519", "-N", "", "-q", "-f", "id_tenki"], check=True)

# 2. engine signs a short-lived user cert for this sandbox
cert = sb.issue_ssh_cert(open("id_tenki.pub").read(), ttl=600)
open("id_tenki-cert.pub", "w").write(cert.ssh_cert)

# 3. open the gateway transport and run a paramiko session over it
pkey = paramiko.Ed25519Key.from_private_key_file("id_tenki")
pkey.load_certificate("id_tenki-cert.pub")

transport = paramiko.Transport(sb.ssh())  # WebSocket-backed socket
transport.connect(username="tenki", pkey=pkey)
session = transport.open_session()
session.exec_command("echo hello-over-ssh")
print(session.makefile().read().decode())
transport.close()

Notes:

  • sb.ssh() / client.ssh(session_id) discover an active gateway and return SSHConn, an io.RawIOBase socket usable anywhere paramiko accepts one.
  • The SSH username is tenki.
  • TENKI_SANDBOX_GATEWAY_URL overrides the gateway WebSocket URL (it is otherwise derived from the API endpoint).
  • Certificate RPCs use the Connect protocol over HTTPS (same as the Go SDK and the tenki CLI), so they work through standard HTTP load balancers.
  • sb.update_ssh_authorized_keys([...]) additionally plants long-lived public keys in the guest's authorized_keys if you prefer key-based auth for the in-guest sshd.

Resource APIs

from tenki_sandbox import Client, GiB

client = Client()

volume = client.volumes.create(
    workspace_id="ws_123",
    name="cache",
    size_bytes=10 * GiB,
    project_id="proj_123",
)

preview = client.preview_urls.create(
    project_id="proj_123",
    slug="demo",
    session_id=sb.id,
    port=3000,
)

Registry

Delete an untagged, non-latest, unshared registry version by image and snapshot ID:

result = client.registry.delete_version(
    "11111111-1111-1111-1111-111111111111",
    "55555555-5555-5555-5555-555555555555",
)

Templates (typed builder)

TemplateSpec is an immutable typed recipe (every builder call returns a new value). Builds freeze the normalized spec + spec_hash server-side and return a private digest-addressed Image, the normal sandbox launch source.

from tenki_sandbox import Client, TemplateSpec

client = Client()

spec = (
    TemplateSpec()                     # defaults to base image "sandbox"
    .from_image("sandbox-v2")          # or .from_template(...) / .from_snapshot(...)
    .with_git_context("https://github.com/acme/node-api", "main")
    .run("npm ci", name="Install dependencies")
    .runtime_env({"NODE_ENV": "production"})
    .start(["npm", "start"], ready_when=[{"http": "http://localhost:3000/health"}])
)
spec.validate()  # aggregate local validation; server stays authoritative

template = client.templates.create(workspace_id="ws_123", name="node-api", spec=spec)

build = client.templates.build(
    template,                          # resource objects in, IDs as fallback
    build_secrets={"GITHUB_TOKEN": token},  # explicit request-time values
    wait_for_completion=True,
    on_event=lambda e: print(e),       # one ordered log/progress handler
)

sb = client.create(image=build.image, wait_for_runtime=True)

wait_build(build_id) reconnects to an existing build (ordered, deduplicated events). Local interruption (Ctrl-C or cancel_event) stops observation only; client.templates.cancel_build(build) cancels remotely. Waited failures raise TemplateBuildFailedError carrying the final redacted build; runtime waits raise TemplateRuntimeFailedError without terminating the running sandbox. Strict JSON import/export: spec.to_json() / TemplateSpec.from_json(text). See examples/template_builder.py for the full filesystem/memory workflow.

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

tenki_sandbox-0.4.0.tar.gz (179.4 kB view details)

Uploaded Source

Built Distribution

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

tenki_sandbox-0.4.0-py3-none-any.whl (155.2 kB view details)

Uploaded Python 3

File details

Details for the file tenki_sandbox-0.4.0.tar.gz.

File metadata

  • Download URL: tenki_sandbox-0.4.0.tar.gz
  • Upload date:
  • Size: 179.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for tenki_sandbox-0.4.0.tar.gz
Algorithm Hash digest
SHA256 2836e6e7100dfc81715b4d93ecfe80e3f055867498cb9f34be921a02969bb15f
MD5 098d201df0368d40b5f77a73ea7ad973
BLAKE2b-256 8d58f6527c63b2e4c94fd00a48ba4bd93ef52de22dbc72e247110949a17511f6

See more details on using hashes here.

Provenance

The following attestation bundles were made for tenki_sandbox-0.4.0.tar.gz:

Publisher: pypi-publish.yml on TenkiCloud/tenki-sdk-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file tenki_sandbox-0.4.0-py3-none-any.whl.

File metadata

  • Download URL: tenki_sandbox-0.4.0-py3-none-any.whl
  • Upload date:
  • Size: 155.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for tenki_sandbox-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 76d5ece2e1607b43705587d86277e983ef43601a86993077e379be8033a40b3e
MD5 d2951c0549eff5336b3f5eb35ca80509
BLAKE2b-256 3bf73c02da98793dc0e56230c520064d7d233c6e0edab13b410c031e1cfe7fd9

See more details on using hashes here.

Provenance

The following attestation bundles were made for tenki_sandbox-0.4.0-py3-none-any.whl:

Publisher: pypi-publish.yml on TenkiCloud/tenki-sdk-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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