Skip to main content

Axern Python SDK

The Axern Python SDK is the first-class programmable interface for Axern sandboxes. It exposes both the control plane client and a high-level Sandbox API for lifecycle, command execution, attached processes, file operations, directory transfer, tunnels, capability discovery, and diagnostics.

Install

Install the published package:

uv add axern-sdk

For repository development, build and run examples through the root uv workspace:

uv build --no-sources sdk/python
uv run --package axern-sdk python sdk/python/examples/sandbox_programming.py

Connect

from axern_sdk import AxernClient

client = AxernClient.from_context("~/.config/axern/config.json")

TLS-enabled local compose setups can pass certificate paths directly:

client = AxernClient(
    "127.0.0.1:25000",
    tls_ca_cert=".dev/certs/ca.crt",
    tls_cert=".dev/certs/client.crt",
    tls_key=".dev/certs/client.key",
)

AsyncAxernClient provides the same control-plane surface for asyncio code. Constructors are explicit and never read the user directory. from_context() loads a named CLI context from the supplied path; from_env() is available for environment-driven automation and reads AXERN_ENDPOINT plus the gateway TLS and proxy variables.

Sandbox Sources

Create a sandbox from exactly one source:

  • image="docker.io/library/python:3.12-slim" for an OCI image.
  • template_id="python311" for a catalog template.
  • environment_id="..." for an existing environment.
from axern_sdk import AxernClient, Sandbox

client = AxernClient("127.0.0.1:25000")

with Sandbox(client=client, image="docker.io/library/python:3.12-slim") as sandbox:
    print(sandbox.metadata.allocation_id)

client.close()

Volumes

Use VolumeMount to attach Service V1 volumes to service-backed sandboxes. The SDK passes volume intent through the public control plane; storage resolution, node publish, mount injection, and release remain owned by Axern's Storage V1 runtime flow.

from axern_sdk import Sandbox, VolumeMount

with Sandbox(
    client=client,
    image="docker.io/library/python:3.12-slim",
    volumes=[
        VolumeMount("data", "/data"),
        VolumeMount("cache", "/cache", readonly=True, options=("rbind",)),
    ],
) as sandbox:
    result = sandbox.exec("ls /data /cache", text=True, check=True)
    print(result.stdout)

Function Manifests

Function.from_file() loads and validates an axern/v1 Function resource. Function.package() creates a deterministic tar bundle, and Function.deploy() packages the source, uploads it with FunctionControl.UploadFunctionBundle, and then calls FunctionControl.DeployFunction. The python311 runtime image includes the SDK Function worker module used by controld-managed warm workers. Function.invoke() calls the dedicated Function invocation API and returns a decoded invocation result.

from axern_sdk import AxernClient, Function

client = AxernClient.from_context("~/.config/axern/config.json")
function = Function.from_file(client, "examples/function-hello/function.yaml")
deployment = function.deploy(labels={"team": "runtime"})

print(function.name)
print(function.spec.handler)
print(deployment.function.id)

Exec

Use exec() for command-result workflows. Set text=True to decode stdout and stderr; set check=True to raise SandboxExecError on non-zero exit.

with Sandbox(client=client, image="docker.io/library/python:3.12-slim") as sandbox:
    result = sandbox.exec("python -c \"print('hello')\"", text=True, check=True)
    print(result.stdout)

Use exec_stream() when stdout/stderr should be consumed as events:

for event in sandbox.exec_stream(["python", "-u", "-c", "print('streamed')"]):
    if event.stream == "stdout":
        print(event.text(), end="")

Attached Process

Use process() when your program needs to control stdin, observe output, wait, or terminate a running command.

with sandbox.process(["python", "-u", "-c", "import sys; print(sys.stdin.read().upper())"]) as process:
    process.write("hello process\n")
    process.close_stdin()

    for event in process.events():
        if event.stream == "stdout":
            print(event.text(), end="")

    result = process.wait()
    print(result.exit_code)

AsyncSandbox.process() returns AsyncSandboxProcess with async equivalents of write(), close_stdin(), events(), wait(), terminate(), and kill().

Image-Backed Processes

Use exec_image() or process_image() to run a tool from a separate image against explicit host-backed sandbox paths. OCI and Nydus image refs use the same image field. When mounts=None, the SDK requests /workspace -> /workspace; pass mounts=[] for no shared paths. Use Sandbox(image=...) when the image should be the sandbox rootfs with normal files, exec, process, tunnel, and lifecycle APIs; image-backed processes are temporary side processes attached to an existing sandbox.

from axern_sdk import workspace_mount

result = sandbox.exec_image(
    "ghcr.io/cofy-x/agent:latest",
    "tool run",
    mounts=[workspace_mount("/workspace")],
    check=True,
    text=True,
)
print(result.stdout)

Files

Single-file APIs are byte-safe. Text helpers only encode/decode at the SDK boundary.

sandbox.write_text("/tmp/message.txt", "payload\n")
print(sandbox.read_text("/tmp/message.txt"))

sandbox.write_bytes("/tmp/blob.bin", b"\x00\x01")
data = sandbox.read_bytes("/tmp/blob.bin")

Platform file operations are handled by the node/runtime file service, not by SDK-side shell fallbacks:

sandbox.copy("/tmp/message.txt", "/tmp/message-copy.txt", overwrite=True)
sandbox.move("/tmp/message-copy.txt", "/tmp/message-final.txt")
sandbox.chmod("/tmp/message-final.txt", 0o600)
sandbox.touch("/tmp/message-final.txt")

info = sandbox.stat("/tmp/message-final.txt")
entries = sandbox.list_dir("/tmp")
exists = sandbox.exists("/tmp/message-final.txt")

Directory Transfer

Directory upload/download uses archive streaming. The SDK packages local directories with tarfile and safely extracts downloaded archives; remote file semantics remain owned by the platform file service.

from pathlib import Path

source = Path("example-upload")
source.mkdir(exist_ok=True)
source.joinpath("data.txt").write_text("directory payload\n")

sandbox.upload_dir(source, "/tmp/example-upload", overwrite=True)
sandbox.download_dir("/tmp/example-upload", "example-download", overwrite=True)

Local symlinks are rejected during upload. Download extraction rejects absolute paths, parent traversal, symlinks, and hardlinks.

Tunnel

Pass upstream to expose a local TCP service to code running inside the sandbox. The SDK owns the tunnel connector and renews finite tunnel TTLs while the sandbox is active.

from axern_sdk import Sandbox

with Sandbox(
    client=client,
    image="docker.io/library/python:3.12-slim",
    upstream="127.0.0.1:8080",
    remote_port=8786,
) as sandbox:
    print(sandbox.bound_addr)

Metadata

Sandbox.state is the lightweight runtime state. Sandbox.metadata is stable for logs and diagnostics:

metadata = sandbox.metadata
print(metadata.environment_id, metadata.service_id, metadata.allocation_id)
print(metadata.node_id, metadata.runtime_class, metadata.tunnel_session_id)

Capabilities

Use capability_status() to discover baseline and optional sandboxd-backed providers before calling desktop or browser APIs:

status = sandbox.capability_status()
print(status.ready, status.capabilities)

for provider in status.providers:
    print(provider.name, provider.state, provider.available, provider.reason)

Errors

SDK exceptions expose fields for programmatic handling:

from axern_sdk import (
    SandboxConnectionError,
    SandboxPermissionError,
    SandboxPreconditionError,
    SandboxRpcError,
)

try:
    sandbox.exec("python -V", check=True)
except SandboxConnectionError as exc:
    if exc.retryable:
        print("temporary node/control-plane connectivity issue")
    raise
except SandboxPermissionError as exc:
    print("credentials do not permit this operation", exc.operation)
    raise
except SandboxPreconditionError as exc:
    if exc.capability:
        print(
            exc.capability.capability,
            exc.capability.provider,
            exc.capability.provider_state,
            exc.capability.missing_dependencies,
        )
    raise
except SandboxRpcError as exc:
    print(exc.operation, exc.code, exc.details, exc.allocation_id)
    raise

Common error classes:

  • SandboxNotStartedError: operation requires an active sandbox.
  • SandboxExecError: exec(..., check=True) observed non-zero exit.
  • SandboxConnectionError: transport or connectivity failure.
  • SandboxPermissionError: authentication or authorization failure.
  • SandboxRpcError: gRPC status mapped from node/runtime APIs.
  • SandboxTimeoutError: SDK-side timeout.

Sandboxd-backed capability failures keep their normal SDK exception class and also expose exc.capability when the node returns provider diagnostics. That object contains capability, provider, provider_state, reason, and missing_dependencies, so callers can branch on missing browser or computer-use dependencies without parsing the full error string.

Async

Async APIs mirror the synchronous shape:

from axern_sdk import AsyncAxernClient, AsyncSandbox

async with AsyncAxernClient("127.0.0.1:25000") as client:
    async with AsyncSandbox(client=client, template_id="python311") as sandbox:
        result = await sandbox.exec("python -c \"print('hello async')\"", text=True, check=True)
        print(result.stdout)

        async with await sandbox.process(["python", "-u", "-c", "import sys; print(sys.stdin.read())"]) as process:
            await process.write("async input\n")
            await process.close_stdin()
            async for event in process.events():
                if event.stream == "stdout":
                    print(event.text(), end="")

Examples

Runnable examples live in examples:

Examples expect a reachable Axern gateway control edge at 127.0.0.1:25000. service_gateway.py also expects AXERN_SERVICE_URL, for example http://127.0.0.1:25080. It accepts AXERN_NAMESPACE, AXERN_IMAGE, AXERN_RUNTIME_CLASS, AXERN_REQUEST_CPU, AXERN_REQUEST_MEMORY, AXERN_LIMIT_CPU, and AXERN_LIMIT_MEMORY for service configuration.

Validation

make test-py
make lint-py
make sdk-python-verify
make local-compose-python-sdk-e2e

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

axern_sdk-0.4.0.tar.gz (216.2 kB view details)

Uploaded Source

Built Distribution

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

axern_sdk-0.4.0-py3-none-any.whl (281.3 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: axern_sdk-0.4.0.tar.gz
  • Upload date:
  • Size: 216.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.1 {"installer":{"name":"uv","version":"0.12.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for axern_sdk-0.4.0.tar.gz
Algorithm Hash digest
SHA256 50461425611f7a250888eb7cc977c811401250f0ca4140b05b604aec63888ee7
MD5 21c490e50f8d5cf9d8bd9a3ba1a2469a
BLAKE2b-256 e467ded3431eaef29ff6e229271b99fd657ea053afe55eda0865976b1774616c

See more details on using hashes here.

File details

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

File metadata

  • Download URL: axern_sdk-0.4.0-py3-none-any.whl
  • Upload date:
  • Size: 281.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.1 {"installer":{"name":"uv","version":"0.12.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for axern_sdk-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 0c19fe9878f928042cd3b58e0132c42d65e90f98d382a2d52498b7170dec228f
MD5 791a3fcc4123cce330aa30033c79b0d2
BLAKE2b-256 2152f3cdb478ecae592d36dbced99c74e45e3067f2e0124beeda8af2039abe1a

See more details on using hashes here.

Release history Release notifications | RSS feed

0.6.2

2 files

0.5.1

2 files

0.5.0

2 files

0.4.1

2 files

This release

0.4.0 This release

2 files

0.3.1

2 files

0.2.1

2 files

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