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.5.0.tar.gz (230.6 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.5.0-py3-none-any.whl (295.5 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: axern_sdk-0.5.0.tar.gz
  • Upload date:
  • Size: 230.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","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.5.0.tar.gz
Algorithm Hash digest
SHA256 49dd0f21c6f8d1f6e226516603feb82d21d38c49fe54973c18812d39c7a1c666
MD5 7ebc9a70ff8e0defd7ff95c596e0b02b
BLAKE2b-256 d2fbf802a7242341cdb7b3bf21704e8b033b66fd54b230b215bb710e78e397f5

See more details on using hashes here.

File details

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

File metadata

  • Download URL: axern_sdk-0.5.0-py3-none-any.whl
  • Upload date:
  • Size: 295.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","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.5.0-py3-none-any.whl
Algorithm Hash digest
SHA256 a3ed11c1ded5cb8a8ec3d9564fb56faa2eac18daea124d8ed142bfc98c03b911
MD5 e3728ee5b869f7cebcbd8d85363b9953
BLAKE2b-256 60b93ee50d418ffc1cda2c78dbd36ab840194841e39e99685f4f7fd3d0344740

See more details on using hashes here.

Release history Release notifications | RSS feed

0.6.2

2 files

0.5.1

2 files

This release

0.5.0 This release

2 files

0.4.1

2 files

0.4.0

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