Docker Desktop's default noexec on tmpfs broke the "download a CLI to /tmp, chmod +x, run it" workflow — exactly what the doc-gardener inspector needs to verify docs.mcpproxy.app against the real mcpproxy binary. Previously the agent spent ~10 minutes in a self- debug loop discovering the noexec, falling back to /home/agent, running into externally-managed Python, missing python3-venv, etc. With /tmp exec, the inspector's own install pipeline works on the first try: curl | tar | chmod | run. First real run produced a 72-claim drift report (21 matched / 1 drifted / 50 missing) against mcpproxy v0.24.4 in ~8 minutes, no REVISE loop. The 64m → 128m bump gives breathing room for curl'd tarballs that need a temp extraction directory alongside the final binary. Inspector prompt updated to tell the agent about the /tmp install path explicitly and forbid the previous /home/agent detours. Also updated the coordinator brief template to match. Note the image itself is UNCHANGED — we deliberately do NOT bake mcpproxy (or any other domain-specific tool) into synapbus-agent. The image stays a blank Linux shell with Node + Python + core tools, and each example's prompt teaches its agent how to install whatever it needs. This keeps the gardener universal: swap in any other docs domain and the inspector figures out what to install on demand. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
SynapBus container images
The docker harness backend (internal/harness/docker/) runs each agent
inside an ephemeral container. This directory holds the canonical agent
image SynapBus's bundled examples reference.
synapbus-agent
The default image. Debian bookworm-slim base with:
geminiCLI (@google/gemini-cli)claudeCLI (@anthropic-ai/claude-code)tinias PID 1 (signal forwarding + zombie reaping)- Standard tooling the example wrappers use:
jq,sqlite3,curl,git,python3 - Non-root
agentuser (uid 1000, gid 1000) matching the typical host user
No SynapBus binary lives in the image. Agents reach the SynapBus MCP
server on the host at host.docker.internal:<port> — the harness
rewrites .gemini/settings.json URLs from 127.0.0.1 to the gateway
hostname automatically.
Build
Local single-arch:
docker build -t synapbus-agent:latest image-build/synapbus-agent
Multi-arch via buildx (recommended for sharing the image):
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t synapbus-agent:latest \
--load \
image-build/synapbus-agent
Pin specific CLI versions with build args:
docker build \
--build-arg GEMINI_CLI_VERSION=0.37.1 \
--build-arg CLAUDE_CODE_VERSION=1.0.0 \
-t synapbus-agent:0.37.1 \
image-build/synapbus-agent
Wire an agent to use it
In harness_config_json add a docker block:
{
"gemini_md": "...",
"mcp_servers": [...],
"env": {...},
"docker": {
"image": "synapbus-agent:latest",
"memory": "1g",
"cpus": "1.0",
"network": "bridge"
}
}
The reactor will pick the docker backend automatically when it sees the
docker.image field. Default security posture: --cap-drop=ALL,
--security-opt=no-new-privileges, --read-only root with tmpfs
/tmp, --pids-limit=512, --user=<host uid:gid>. Override via the
typed fields in the docker block (memory, cpus, cap_add,
extra_mounts, read_only_root, user).