docker/agent-runtime/README.md
Container images for running coding-agent harnesses in sandboxed environments (for example the kubernetes sandbox provider, stage 1 of the k8s contribution). Images are named agent-runtime-{harness}:{version} and published to ghcr.io/paperclipai/ by the agent-runtime-images workflow. The registry is overridable: every reference flows through the REGISTRY bake variable.
agent-runtime-base: Foundation. Ubuntu 22.04 + Node 22 + git + tini + non-root user (uid 1000) + the agent shim.agent-runtime-opencode: Extends base with opencode-ai globally installed.agent-runtime-pi: Extends base with @mariozechner/pi-coding-agent.agent-runtime-codex: Extends base with @openai/codex.agent-runtime-gemini: Extends base with @google/gemini-cli plus headless auth-mode settings.agent-runtime-claude: Extends base with @anthropic-ai/claude-code (symlinked as claude-code).agent-runtime-acpx / agent-runtime-hermes: Dockerfiles included in the bake group, not in the default publish scope (hermes is a stub until a CLI package exists).OS & Runtime:
paperclip (uid/gid 1000)Paperclip Binaries:
/usr/local/bin/paperclip-agent-shim: Go binary compiled from tools/agent-shim/. Reads /run/paperclip/runtime-command.json and syscall.Execs the harness CLI.Defaults:
USER: 1000:1000 (paperclip, non-root)WORKDIR: /workspace (mount workspace volumes here)ENTRYPOINT: /usr/bin/tini -- (PID-1 reaper, forwards signals)CMD: /usr/local/bin/paperclip-agent-shimAll targets build linux/amd64 by default (see buildx-bake.hcl). Derived images chain off the base target through bake contexts, so the literal registry in each FROM line is overridden at build time and the whole family builds in one pass without pushing intermediates.
docker buildx bake -f docker/agent-runtime/buildx-bake.hcl --load
REGISTRY=myregistry VERSION=mytag \
docker buildx bake -f docker/agent-runtime/buildx-bake.hcl --load
Build and verify the agent-runtime-claude image runs locally:
docker buildx bake -f docker/agent-runtime/buildx-bake.hcl base claude --load
docker run --rm ghcr.io/paperclipai/agent-runtime-claude:dev claude-code --version
The main agent process runs as the shim (PID 1 under tini). The shim:
/run/paperclip/runtime-command.json (path overridable via -spec), a JSON file mounted by whatever schedules the run{ "command", "args" }: the harness CLI and argumentssyscall.Execs it, replacing itselfruntime-command.json Contract:
{
"command": "claude-code",
"args": ["--token", "xyz", "--workspace", "/workspace"]
}
The shim makes no assumptions about command structure; it is harness-agnostic. New harnesses swap the command/args; the base image stays the same.
/workspace + /tmp mounts).github/workflows/agent-runtime-images.yml builds and pushes the default scope (base, opencode, pi, codex, gemini, claude) on workflow_dispatch (with an explicit version tag) or on pushes to master touching these paths, then signs each digest with cosign keyless OIDC.