> ## Documentation Index
> Fetch the complete documentation index at: https://usefused.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Docker extension

> Run framework-neutral Python sandbox workloads through the Fused-maintained Docker Harnest Extension.

The official Docker Harnest Extension moves Docker SDK and container ownership out of Harnest core. It implements the provider-neutral sandbox contract for managed ADK and LangGraph agents. It is a Harnest Extension, not an Agent Plugin, browser tool, or general container orchestrator.

## Install the extension

From the agent folder:

```bash theme={null}
harnest extensions install docker
# Equivalent: harnest extensions install harnest-extension-docker
harnest env sync .
```

For local extension development, point the same command at a checkout:

```bash theme={null}
harnest extensions install ../official-extensions/docker --force
harnest env sync .
```

Harnest validates and copies the package without importing its code. Use Python 3.11 or newer with the current Harnest release. Version `0.4.2` requires Harnest `>=1.0.0,<2` and `docker>=7.1,<8`. The host must provide a reachable Docker daemon.

## Declare a Docker sandbox

Create `sandbox/python.py`. The exported variable must match the filename:

```python sandbox/python.py theme={null}
from harnest.extensions.docker import docker, DockerScope
from harnest.sandbox import SandboxBudget, SandboxNetworkPolicy


python = docker.sandbox(
    image="python:3.12-slim@sha256:<approved-digest>",
    network_policy=SandboxNetworkPolicy.none(),
    scope=DockerScope.EXECUTION,
    timeout_seconds=120,
    max_output_bytes=1_048_576,
    budget=SandboxBudget(
        cpu=1.0,
        memory_bytes=512 * 1024 * 1024,
        pids=64,
        scratch_bytes=64 * 1024 * 1024,
    ),
)
```

Add `"python"` to each consuming agent's `sandboxes=[...]` grant. Call it from an authored tool through `context.sandboxes["python"]`. Exactly one of `image` or `docker_path` is required; prefer an immutable image digest in deployments.

### Sandbox configuration

| Option                  | Default            | Purpose                                                         |
| ----------------------- | ------------------ | --------------------------------------------------------------- |
| `image` / `docker_path` | None               | Select exactly one registry image or local Docker build context |
| `base_url`              | Docker SDK default | Connect to an explicitly configured daemon                      |
| `network_policy`        | `none()`           | Grant no egress or supported unrestricted egress                |
| `services` / `network`  | Empty              | Declare a private multi-container topology                      |
| `timeout_seconds`       | 300                | Bound queueing, startup, and execution                          |
| `max_output_bytes`      | 1 MiB              | Bound captured output while streaming                           |
| `scope`                 | `EXECUTION`        | Choose execution, invocation, or session reuse                  |
| `budget`                | Harnest defaults   | Bound CPU, memory, processes, and scratch space                 |
| `max_scopes`            | 8                  | Bound retained invocation or session identities                 |

`options` carries supported ADK parsing and retry options, while `metadata` is attached to the portable sandbox declaration. Construction validates immutable configuration without contacting Docker; the first execution starts provider resources lazily.

## Add service containers

Attach bounded services when the primary Python container needs another process:

```python sandbox/worker.py theme={null}
from harnest.extensions.docker import docker, DockerScope
from harnest.sandbox import SandboxBudget, SandboxNetworkPolicy


queue = docker.service(
    name="queue",
    image="redis:7-alpine@sha256:<approved-digest>",
    command=["redis-server", "--save", ""],
    ports=[6379],
    readiness=docker.readiness(command=["redis-cli", "ping"]),
    budget=SandboxBudget(memory_bytes=128 * 1024 * 1024),
)

worker = docker.sandbox(
    image="python:3.12-slim@sha256:<approved-digest>",
    services=[queue],
    network=docker.network(internal=True),
    network_policy=SandboxNetworkPolicy.none(),
    scope=DockerScope.SESSION,
)
```

The extension creates one uniquely named bridge for the primary container and up to eight services. Use each service name or alias as its internal DNS name. Declared ports describe the container-to-container contract and are never published on the host.

An internal topology network remains inside the sandbox boundary, so services can communicate when `network_policy` denies external egress. Use `docker.network(internal=False)` only with `SandboxNetworkPolicy.unrestricted()`. That choice gives every topology container the same unrestricted egress authority.

Services start in declaration order. A declared readiness command becomes a bounded Docker health check, and submitted Python does not run until every service is healthy. Each service has its own `SandboxBudget`. Cleanup removes the primary container, service containers in reverse order, and then the owned network. If cleanup cannot be confirmed, the topology remains poisoned and replacement is blocked until cleanup succeeds.

## Choose a reuse scope

| Scope                    | Container ownership                                          |
| ------------------------ | ------------------------------------------------------------ |
| `DockerScope.EXECUTION`  | Fresh topology for every call; this is the default           |
| `DockerScope.INVOCATION` | Reuse only for the same agent, user, session, and invocation |
| `DockerScope.SESSION`    | Reuse only for the same agent, user, and session             |

Retained scopes reuse the container and its `/tmp` scratch files while that identity remains cached, but not live processes or durable storage. Every successful call stops remaining processes. `max_scopes` defaults to `8` and evicts the least recently used retained container before admitting another identity.

## Set network authority

The extension supports `SandboxNetworkPolicy.none()` and `SandboxNetworkPolicy.unrestricted(block_private_networks=False)`. No-network mode is the default. Exact host or port allowlists, and unrestricted networking with private-network blocking, fail closed because this provider does not yet enforce those controls at Docker's network boundary.

Network policy is provider-enforced authority. Validating a URL in an Agent Tool does not replace it. Use another sandbox provider when a workload requires exact destination enforcement.

## Understand deadlines and cleanup

The all-in deadline covers queue admission, image and container startup, and execution. SDK transport timeouts for image preparation and container creation are constrained by the remaining deadline; a host watchdog and control checks bound execution. Output is bounded while streaming; timeout, cancellation, overflow, and failed startup poison the container instead of returning it to a reuse pool.

Cleanup receives a separate bounded five-second window. The provider retains ownership and blocks replacement when termination is uncertain. Startup errors identify the failed phase without exposing raw SDK details. Managed containers carry `dev.harnest.*` labels for operator inventory.

Docker startup, network, service, execution, and cleanup operations emit correlated logs and traces through Harnest observability. Every signal carries `harnest.extension.name=docker`. Topology IDs and service names make owned resources traceable without recording image references, commands, environment values, user/session identities, readiness output, or raw Docker errors.

## Security boundary

Docker daemon access is highly privileged. Protect its socket or remote API, restrict who can configure this extension, and use trusted digest-pinned images. Containers run as a non-root user with a read-only root filesystem, dropped capabilities, `no-new-privileges`, bounded `/tmp`, and no host mounts. Docker still shares the host kernel; choose a stronger provider for higher-risk isolation.

The extension does not transfer input or output files. Use stdout or a provider with an explicit file contract. See [Sandboxing](/docs/harnest/build/sandboxing) for agent grants, authored-tool usage, custom providers, and failure handling.

## Public API and limits

| API                                                 | Role                                                             |
| --------------------------------------------------- | ---------------------------------------------------------------- |
| `docker.sandbox(...)` / `docker_sandbox(...)`       | Create a portable Docker-backed `Sandbox`                        |
| `docker.service(...)`                               | Declare one topology service                                     |
| `docker.network(...)`                               | Declare the topology bridge and egress boundary                  |
| `docker.readiness(...)`                             | Declare a bounded service health command                         |
| `DockerScope`                                       | Select execution, invocation, or session ownership               |
| `DockerService`, `DockerNetwork`, `DockerReadiness` | Immutable topology contracts                                     |
| `DockerSandboxProvider`                             | Runtime adapter implementing Harnest's sandbox provider contract |

A topology supports up to eight services. Each service allows 16 aliases, 32 declared ports, 64 environment entries, and 64 command arguments; bounded authored string values may use up to 8 KiB. Readiness permits 1 to 100 attempts with finite timing values. Service names and aliases must be unique portable lowercase DNS labels. These checks run before the Docker SDK receives configuration.
