Skip to main content

Docker sandbox tools for Axio

Project description

axio-tools-docker

PyPI Python License: MIT

Docker sandbox tools for axio.

DockerSandbox is an async context manager that spins up an isolated Docker container on entry and removes it on exit. Inside the context it exposes six axio tools - the same shell, write_file, read_file, list_files, run_python, and patch_file as axio-tools-local, but every operation runs inside the container, never on the host.

Requirements

Docker must be installed and running:

docker info   # should succeed

The package talks to the Docker Engine API directly via aiodocker - the docker CLI is not required.

Installation

pip install axio-tools-docker

Quick start

import asyncio
from axio.agent import Agent
from axio.context import MemoryContextStore
from axio.testing import StubTransport, make_text_response
from axio_tools_docker import DockerSandbox

async def main() -> None:
    transport = StubTransport([make_text_response("Done.")])
    async with DockerSandbox(image="python:3.12-alpine") as sandbox:
        agent = Agent(
            system="You are a coding assistant. Use the sandbox tools to run code safely.",
            tools=sandbox.tools,
            transport=transport,
        )
        ctx = MemoryContextStore()
        result = await agent.run("Print hello from Python.", ctx)
        print(result)

asyncio.run(main())

Sandbox tools

These mirror axio-tools-local exactly - same names and field schemas:

Tool Description
shell Run a shell command; returns stdout + stderr. Supports timeout, cwd, stdin.
write_file Create or overwrite a file. Parent directories are created automatically.
read_file Read a file with optional start_line/end_line, line_numbers, max_chars.
list_files List a directory; directories first with a trailing /.
run_python Execute a Python snippet in a subprocess. Supports timeout, cwd, stdin.
patch_file Replace lines from_line..to_line (1-indexed, inclusive). to_line = from_line - 1 inserts.

Container lifecycle

The container is created on __aenter__ and removed on __aexit__ (docker rm -f). Cleanup runs even when the body raises an exception:

from axio_tools_docker import DockerSandbox
from axio import Agent

async def run(ctx):
    agent = Agent(
        system="You are a coding assistant. Use the sandbox tools to run code safely.",
        tools=ctx.sandbox.tools,
    )

    async with DockerSandbox(image="alpine:latest") as sandbox:
        await agent.run("...", ctx)

    # container removed here (unless remove=False)

The image is pulled automatically if not present locally. If the Docker daemon is unreachable, __aenter__ raises immediately:

RuntimeError: Docker daemon not available at 'unix:///var/run/docker.sock': ...

The running container's ID is available as sandbox.container_id inside the async with block.

Named containers and reuse

Pass name= to give the container a fixed name. If a container with that name already exists, the sandbox starts it if needed and attaches instead of creating a new one. Attached containers are never removed on exit - regardless of remove:

import asyncio
from axio_tools_docker import DockerSandbox

async def first_session() -> None:
    async with DockerSandbox(image="python:3.12-slim", name="my-sandbox", remove=False) as sb:
        await sb.exec("pip install requests")

async def second_session() -> None:
    async with DockerSandbox(name="my-sandbox") as sb:
        result = await sb.exec("python3 -c 'import requests; print(requests.__version__)'")

asyncio.run(first_session())
asyncio.run(second_session())

Named volumes

Named volumes are managed by the Docker daemon and persist across container restarts. Use them to share state between sandbox sessions:

import asyncio
from axio_tools_docker import DockerSandbox

async def main() -> None:
    async with DockerSandbox(
        image="python:3.12-alpine",
        named_volumes={"/data": "my-project-data"},
    ) as sb:
        await sb.write_file("/data/state.json", '{"count": 1}')
    # Container removed, volume survives.

    async with DockerSandbox(
        image="python:3.12-alpine",
        named_volumes={"/data": "my-project-data"},
        volumes_remove=True,   # delete the volume on exit
    ) as sb:
        raw = await sb.read_file_bytes("/data/state.json")
        assert raw.decode() == '{"count": 1}'

asyncio.run(main())

Docker creates the volume automatically if it does not exist yet.

Configuration

import os
from axio_tools_docker import DockerSandbox

sandbox = DockerSandbox(
    os.getenv("DOCKER_HOST", "unix:///var/run/docker.sock"),  # Docker daemon URL, optional
    image="python:3.12-slim",
    memory="512m",  # memory limit: "256m", "1g", …
    cpus="2.0",  # CPU limit
    network=False,  # False=none, True=default, str=explicit mode
    workdir="/workspace",
    volumes={"/workspace": "/tmp/host-dir"},  # {container_path: host_path}
    named_volumes={"/data": "my-project-data"},  # named Docker volumes
    volumes_remove=False,  # remove named volumes on exit
    env={"PYTHONPATH": "/app"},
    user="nobody",
    name="my-agent-sandbox",
    remove=False,
    read_only=True,  # read-only root filesystem
    shm_size="64m",  # /dev/shm size
    cap_add=["NET_ADMIN"],  # add Linux capabilities
    cap_drop=["ALL"],  # drop Linux capabilities
    privileged=False,
    ulimits={"nofile": (1024, 65536), "nproc": 512},
    tmpfs={"/tmp": "size=128m,mode=1777"},
    ports={8080: 8080},  # {container_port: host_port}
    platform="linux/amd64",
    extra_hosts={"host.docker.internal": "host-gateway"},
    devices=["/dev/net/tun", "/dev/sda:/dev/xvda:r"],
    dns=["8.8.8.8", "1.1.1.1"],
)

assert sandbox.image == "python:3.12-slim"
assert sandbox.memory == "512m"
assert sandbox.cpus == "2.0"
assert sandbox.network == False
Parameter Type Default Description
url str "unix:///var/run/docker.sock" Docker daemon URL (unix socket or TCP). Positional.
image str "python:latest" Container image. Pulled automatically if not present.
memory str "256m" Memory limit. Accepts k/m/g suffixes.
cpus str "1.0" CPU limit as a decimal string.
network bool | str False Network mode. Falsenone. True → Docker default. String → explicit NetworkMode (e.g. "host", "bridge", "my-net").
workdir str "/workspace" Working directory inside the container.
volumes dict[str, str] {} Bind mounts as {container_path: host_path}.
named_volumes dict[str, str] {} Named Docker volumes as {container_path: volume_name}. Created automatically if absent.
volumes_remove bool False Remove named volumes on exit. No effect when attached to an existing container.
env dict[str, str] {} Environment variables passed to all commands.
user str "" User to run as (e.g. "nobody", "1000").
name str "" Container name. Attaches to existing container if found; creates new one otherwise.
remove bool True Remove container on exit. No effect when attached to an existing container.
read_only bool False Read-only root filesystem. Combine with tmpfs for writable scratch paths.
shm_size str "" /dev/shm size (e.g. "64m"). Useful for PyTorch / shared-memory IPC.
cap_add list[str] [] Linux capabilities to add (e.g. ["NET_ADMIN", "SYS_PTRACE"]).
cap_drop list[str] [] Linux capabilities to drop (e.g. ["ALL"]).
privileged bool False Extended privileges - full capability set and device access. Use with care.
ulimits dict[str, int | tuple[int, int]] {} Resource limits. {"nofile": 1024} → soft=hard=1024. {"nofile": (1024, 65536)} → soft/hard split.
tmpfs dict[str, str] {} Tmpfs mounts as {path: options} (e.g. {"/tmp": "size=128m,mode=1777"}).
ports dict[int, int] {} Port bindings as {container_port: host_port}. Only meaningful when network != False.
platform str "" Platform override (e.g. "linux/amd64", "linux/arm64").
extra_hosts dict[str, str] {} Extra /etc/hosts entries as {hostname: ip}.
devices list[str] [] Host devices to expose. Format: "/dev/sda", "/dev/sda:/dev/xvda", "/dev/sda:/dev/xvda:r".
dns list[str] [] DNS servers (e.g. ["8.8.8.8"]). Only meaningful when network != False.

Part of the axio ecosystem

axio · axio-tools-local · axio-tools-mcp · axio-tui

License

MIT

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

axio_tools_docker-0.9.5.tar.gz (49.7 kB view details)

Uploaded Source

Built Distribution

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

axio_tools_docker-0.9.5-py3-none-any.whl (13.0 kB view details)

Uploaded Python 3

File details

Details for the file axio_tools_docker-0.9.5.tar.gz.

File metadata

  • Download URL: axio_tools_docker-0.9.5.tar.gz
  • Upload date:
  • Size: 49.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for axio_tools_docker-0.9.5.tar.gz
Algorithm Hash digest
SHA256 4e35ce5e203cd4e6d3c57cd769fc6150fb4891d4326b5144206cfae52cb1e330
MD5 7c79d112d223fa0859def00bb68edd94
BLAKE2b-256 a458d1450246debe9a5a5ec62525a5f9dd833bd656535b518d122195f49aff4b

See more details on using hashes here.

Provenance

The following attestation bundles were made for axio_tools_docker-0.9.5.tar.gz:

Publisher: publish.yml on mosquito/axio-agent

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

File details

Details for the file axio_tools_docker-0.9.5-py3-none-any.whl.

File metadata

File hashes

Hashes for axio_tools_docker-0.9.5-py3-none-any.whl
Algorithm Hash digest
SHA256 b332bf0398ef755ae90ce9d3874348757d41171d265d4194c4e3e24a7c9e4f37
MD5 eeb0b5dff1daf8462321a4359cf97951
BLAKE2b-256 c0f87d7037e6bfeabf893d7641e71a1b684d3bc017eb7ee2e6aa580943fac082

See more details on using hashes here.

Provenance

The following attestation bundles were made for axio_tools_docker-0.9.5-py3-none-any.whl:

Publisher: publish.yml on mosquito/axio-agent

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