- Python 51.8%
- Shell 41.2%
- Dockerfile 7%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| .forgejo/workflows | ||
| docker | ||
| docs | ||
| scripts | ||
| tests | ||
| .dockerignore | ||
| .env.example | ||
| .gitignore | ||
| compose.docker.yaml | ||
| compose.yaml | ||
| config.example.yaml | ||
| Dockerfile | ||
| LICENSE | ||
| README.md | ||
| renovate.json | ||
| sources.env | ||
Agent Zoo Docker
A small container deployment for Agent Zoo and its authenticated mobile gateway. The default Compose service runs azo-gateway, which launches and resumes headless Agent Zoo sessions and transparently proxies their canonical websocket frames.
The image pins and builds five mirrored repositories:
agent-zoo— session runtime and websocket backendagent-utils— agent pipeline abstractionstmux-pilot— local process and session coordinationazo-gateway— authenticated project/session API and websocket proxy required byazo-mobileazo-tui— terminal frontend for attaching to a live Agent Zoo backend
The Android application remains separate client software. The runtime includes Python, Bash, Git, tmux, OpenSSH, curl, jq, ripgrep, common file/process utilities, the Agent Zoo TUI, and the Docker CLI with Buildx and Compose. Coding agents can use a normal shell in /workspace; host Docker access remains opt-in.
Quick start
Prerequisites:
- Docker Engine with Compose v2.
- A Codex subscription authenticated by the Codex CLI. Run
codex loginon the Docker host and confirm${HOME}/.codex/auth.jsonexists.
cp .env.example .env
docker compose up -d
docker compose ps
docker compose logs agent-zoo
The safe default publishes the gateway only on 127.0.0.1:8787. The startup log contains its bearer token, manual base URL, pairing URI, and QR code. When AZO_GATEWAY_TOKEN is blank, the entrypoint generates a strong token once and persists it at /data/.azo-gateway/token.
Agent Zoo state, writable configuration, userspace plugins, plugin configuration, and the persistent Python extension environment are stored in agent-zoo-data. Coding projects are stored in agent-zoo-workspace.
docker compose down
docker compose up -d
Recreating the service keeps both volumes and the generated gateway token.
Connect the Android app
The mirrored azo-mobile source requires the matching standalone gateway. It does not connect directly to a raw ws://HOST:8765/session endpoint.
Set the gateway's published and advertised addresses in .env:
AZO_GATEWAY_BIND_ADDRESS=0.0.0.0
AZO_GATEWAY_PORT=8787
AZO_GATEWAY_ADVERTISE_HOST=192.0.2.10
Replace 192.0.2.10 with the Docker host's LAN or VPN address, then recreate the service:
docker compose up -d --force-recreate
docker compose logs agent-zoo
Scan the logged QR code, paste the azo://gateway/connect?... pairing URI, or enter http://192.0.2.10:8787 and the bearer token manually in the app.
The gateway authenticates project, filesystem, session, and proxied websocket routes. The bearer token controls every project and session under this gateway's Agent Zoo state root. Plain HTTP still exposes traffic to the network, so use it only on a trusted LAN or VPN. Use an authenticated TLS reverse proxy for untrusted networks; do not publish the gateway directly to the internet.
See docs/container-interaction.md for Android pairing, creating or fuzzy-selecting a gateway session with azo-tui-session, and the exact limits of tmux terminal observation.
Codex subscription authentication
Bundled gpt-5.5-*-codex profiles read the Codex CLI OAuth file at ~/.codex/auth.json. Compose mounts ${CODEX_HOME} there. The mount is writable because Agent Zoo may refresh an expired OAuth token and atomically update the file.
Set AGENT_ZOO_UID and AGENT_ZOO_GID in .env to the host account that owns the Codex directory. Never commit auth.json, copy it into an image, or expose it through environment variables.
On the first start, the image copies config.example.yaml to the persistent, writable /data/config.yaml. It selects the high-reasoning Codex profile, disables direct API-key profiles, starts in interactive pace, and uses headless terminal tools. Later image or repository updates never overwrite that file. Edit it in the volume or through the running agent; gateway-launched processes discover it through Agent Zoo's normal layered configuration loader.
Runtime user and extension packages
The image follows the Hermes Agent startup model:
- Docker starts the entrypoint as container root.
- The entrypoint maps the
agent-zooaccount toAGENT_ZOO_UID:AGENT_ZOO_GIDand prepares/dataand/workspace. gosupermanently drops privileges before the gateway or backend starts.
The defaults are 1000:1000. The smoke suite also exercises 12345:12346, checks the effective service-process UID, and verifies persistent writes as that account. This accommodates rootless Docker or Podman mappings; ownership changes that a rootless engine rejects are warnings rather than fatal errors.
Set optional whitespace-separated Debian package names in .env:
AGENT_ZOO_APT_PACKAGES=make gcc
At startup, the root bootstrap validates package names, checks them with dpkg-query, and runs apt-get only when a requested package is missing. These changes live in the disposable container layer and are repeated after recreation; use a derived Dockerfile for large or permanent toolchains.
A Python virtual environment at /data/python-env is created automatically and backed by the persistent data volume. The Agent Zoo backend and gateway-launched sessions run through it, so packages installed there remain visible after recreation without mutating the image's base Python installation. Install Python dependencies directly as the runtime account:
docker compose exec --user "${AGENT_ZOO_UID:-1000}:${AGENT_ZOO_GID:-1000}" \
agent-zoo /data/python-env/bin/python -m pip install 'httpx==0.28.1' 'beautifulsoup4>=4.13'
docker compose restart agent-zoo
Pin requirements for repeatable deployments. Installation requires repository/network access.
Persistent plugins and configuration
Agent Zoo's documented source-plugin directories are all under the state volume:
/data/plugins/{common,default,rlm}
/data/plugin-sources
/data/plugin-configs
/data/plugin-installs
Install and inspect userspace plugins as the runtime account:
docker compose exec --user "${AGENT_ZOO_UID:-1000}:${AGENT_ZOO_GID:-1000}" \
agent-zoo azo-plugin install --copy /workspace/my_plugin.py
docker compose restart agent-zoo
Pure source plugins persist directly in /data. Install any additional import requirements into /data/python-env with the command above; restart after changing plugin code or dependencies so new Agent Zoo processes load them. The image supports the userspace extension contract and deliberately does not make its baked core source writable.
Optional host Docker access
The Docker CLI, Buildx, and Compose plugin are installed, but no daemon or socket is mounted by default. Opt in with the dedicated override:
docker compose -f compose.yaml -f compose.docker.yaml up -d
docker compose -f compose.yaml -f compose.docker.yaml exec agent-zoo docker version
DOCKER_SOCKET_PATH selects the host socket path. The entrypoint detects its numeric group ID and adds the runtime user to a matching supplementary group; set DOCKER_SOCKET_GID only when the runtime cannot inspect the mounted socket.
Access to a conventional Docker socket is effectively root-equivalent control of the Docker host. Enable it only for an agent you trust with the host, and prefer a constrained remote builder or socket proxy when full host control is unnecessary.
Raw backend mode
Protocol clients that do not need the mobile control plane can run one raw backend session:
docker run --rm \
-p 127.0.0.1:8765:8765 \
-v agent-zoo-data:/data \
-v agent-zoo-workspace:/workspace \
code.mehalter.com/clawlter/agent-zoo-docker:dev backend
Connect to ws://127.0.0.1:8765/session. This raw protocol has no authentication; keep it on loopback, a trusted private network, or behind an authenticated proxy. The Compose gateway is the recommended mobile and multi-session deployment.
Common operations
# Follow gateway and launched-backend logs
docker compose logs -f agent-zoo
# Verify the active gateway/backend health contract
docker compose exec agent-zoo agent-zoo-healthcheck
# Update after a new main build is published
docker compose pull
docker compose up -d
One gateway manages one Agent Zoo state root and can keep multiple sessions active. Use another Compose project with different ports and volumes for an independent state root.
Build and test locally
Image builds use authenticated Forgejo mirror checkouts prepared outside Docker. If the mirrored repositories are already sibling directories, no token is needed:
AGENT_ZOO_SOURCE_ROOT=/path/to/agent-zoo-landscape ./scripts/prepare-sources.sh
docker build -t agent-zoo-docker:local .
./tests/smoke.sh agent-zoo-docker:local
AGENT_ZOO_TEST_MODE=gateway ./tests/smoke.sh agent-zoo-docker:local
AGENT_ZOO_TEST_MODE=gateway AGENT_ZOO_TEST_INTERACTION=1 \
./tests/smoke.sh agent-zoo-docker:local
AGENT_ZOO_TEST_MODE=gateway \
AGENT_ZOO_TEST_EXTENSIONS=1 AGENT_ZOO_TEST_PERSISTENCE=1 \
AGENT_ZOO_TEST_UID=12345 AGENT_ZOO_TEST_GID=12346 \
./tests/smoke.sh agent-zoo-docker:local
Otherwise set FORGEJO_TOKEN to a token with mirror read access and run ./scripts/prepare-sources.sh. The script exports only pinned commits into ignored .build-sources/; credentials never enter the Docker build context or image.
The backend smoke performs a real protocol-v1 websocket hello handshake. Gateway smoke verifies unauthenticated health, authenticated API access, project creation, backend launch, a proxied websocket hello, generated-token persistence across restart, the effective non-root service UID, writable configuration, source-plugin persistence, and persistent paths. Interaction mode launches the bundled TUI in a real tmux pseudo-terminal, attaches it through the authenticated gateway proxy, and requires the TUI to adopt the backend session title. Extension mode additionally installs a Debian package and Python requirement, then exercises the mounted host Docker socket as the remapped non-root user. Every run uses isolated temporary Docker resources and cleans up only its own objects.
Source updates
sources.env pins full commit IDs for agent-zoo, agent-utils, tmux-pilot, azo-gateway, and azo-tui. Before updating, confirm the Forgejo mirrors have synchronized and review compatibility across the gateway, Android app, and TUI. Build and test both service modes before publication.
renovate.json is ready for a Forgejo Renovate bot. It tracks Docker base images, compatible Forgejo Actions workflows, and the main digest of each source mirror. Source pins and Actions are grouped for reviewed, smoke-tested pull requests; dependency automerge is intentionally disabled. The bot needs normal write access to this repository and read access to the five mirrored source repositories.
The mirrors synchronize every eight hours and may lag GitHub. Their configured GitHub locations require access this environment does not have, so the authenticated Forgejo mirrors are the build source. See docs/upstream-landscape.md for the full 14-repository map and source-boundary notes.
Image publication
Pull requests build and smoke-test both service modes. Pushes to main publish:
code.mehalter.com/clawlter/agent-zoo-docker:dev
Forgejo Actions uses the account-level PACKAGE_TOKEN secret for registry publication.