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.1.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.1-py3-none-any.whl (281.3 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: axern_sdk-0.4.1.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.1.tar.gz
Algorithm Hash digest
SHA256 d40d37ea69bcb6775233193422bd183674f6fe01ca4faee4491076b9fc263215
MD5 97d6bf321efbcdbcbe1cd45bad851fb4
BLAKE2b-256 5291123f21f3b23b9a08b5ef794c4fcef94cf6d483df34419f5bf36b8e672b9c

See more details on using hashes here.

File details

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

File metadata

  • Download URL: axern_sdk-0.4.1-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.1-py3-none-any.whl
Algorithm Hash digest
SHA256 e18f41f338078f938ec546c97ea906be4212e4dca6f87236f713a112571c5acb
MD5 7f1a543929fb6736fcb87499392e2768
BLAKE2b-256 6d6eeb606860e135a27478a75dcc99df6f691f3b418d2510853f5eda80e6cd19

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

This release

0.4.1 This release

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