Two things found while asking "what version of Claude Code do missions run?" Mission containers are created from CLAWMATES_RUNTIME_IMAGE, which on both stacks still pointed at clawmates-runtime:hooks — zeroclaw 0.8.4, Claude Code 2.1.237 (2.1.228 locally), built 2026-08-21. The v0.8.5 upgrade on 09-06 rebuilt only the persistent clawmates-runtime container, which container-tier missions do not drive their turns through. Every measured mission this month ran on 0.8.4/2.1.237; the measurements stand (one image throughout) but the version attached to them in the handoff and in memory was wrong, and has been corrected there. The drift itself came from `npm install -g @anthropic-ai/claude-code` with no version: each rebuild takes whatever npm has that day, so two builds three weeks apart shipped two CLIs and nothing recorded either. The changelog shows why that is not merely untidy — 2.1.265 and 2.1.275 each broke every turn on ANTHROPIC_BASE_URL endpoints, the path the GLM and Kimi backends use — and today's floating local build silently took Kimi 2.0.1, a major version. Both are ARGs now, defaulting to what was verified. Verified before promoting: two local missions on 0.8.5 + 2.1.276 — tool arguments recorded on 40/40 and 123/123 calls, gate.installed, skill reads, judge met and independent with the correction loop closing, spend rows with provider and model, and with delegation forced, 4 Agent spawns → 45 subagent calls across 4 ids all typed general-purpose (44/4 on the old CLI). The hook payload did not move. Prod's .env now names clawmates-runtime:v085-cc276, built on tank from the fork at 57635deb with this Dockerfile. The local override's CLAWMATES_RUNTIME_IMAGE points at :toolchain, which is the same lineage plus cmake/python3-dev for `cargo test` on cmake-driven deps. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01WZb5A2kfVfjpdwSochkuHz
162 lines
8.7 KiB
Docker
162 lines
8.7 KiB
Docker
# Slim Clawmates runtime = the zeroclaw daemon (gateway API), API-only.
|
|
# Simple full-source build (avoids the upstream Dockerfile's stale manifest-
|
|
# prefetch step). web/dist may be empty → dashboard is omitted, /api/* works.
|
|
# Requires BuildKit (cache mounts). Build context = the zeroclaw source tree.
|
|
# syntax=docker/dockerfile:1
|
|
|
|
# bookworm-pinned so the binary's glibc matches the bookworm runtime stage.
|
|
# 1.98 tracks upstream: v0.8.5 moved ZeroClaw's own container builders to
|
|
# rust:1.98-slim (#9527) and keeps 1.96 only as the declared SOURCE floor, so
|
|
# the floor is what the crates promise, not what upstream actually builds with.
|
|
FROM rust:1.98-slim-bookworm AS build
|
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
pkg-config build-essential cmake libssl-dev ca-certificates git \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
WORKDIR /app
|
|
# gw-04 is the only reachable x86_64 host AND the production host, so the build
|
|
# must not take every core from the services it is running beside.
|
|
ARG CARGO_BUILD_JOBS=6
|
|
ENV CARGO_BUILD_JOBS=${CARGO_BUILD_JOBS}
|
|
COPY . .
|
|
# Ensure the gateway's include_dir!("../../web/dist") target exists at compile time.
|
|
RUN mkdir -p web/dist
|
|
RUN --mount=type=cache,id=cm-cargo-registry,target=/usr/local/cargo/registry,sharing=locked \
|
|
--mount=type=cache,id=cm-cargo-git,target=/usr/local/cargo/git,sharing=locked \
|
|
--mount=type=cache,id=cm-target-bookworm,target=/app/target,sharing=locked \
|
|
cargo build --release --locked --bin zeroclaw \
|
|
&& cp target/release/zeroclaw /usr/local/bin/zeroclaw
|
|
|
|
FROM debian:bookworm-slim
|
|
# ca-certificates for TLS; Node 22 (Kimi Code needs >=22.19; Claude Code is fine
|
|
# on it too) to install BOTH agent CLIs that the subprocess providers spawn:
|
|
# `claude -p` (claude_cli, Claude subscription) and `kimi -p` (kimi_cli, Kimi
|
|
# membership) — subscription-backed, no per-minute API TPM ceiling.
|
|
# PINNED. These two binaries are what every mission agent runs, and an
|
|
# unpinned install takes whatever npm has on build day: prod's mission image
|
|
# (`:hooks`, built 2026-08-21) carried Claude Code 2.1.237 for a month while the
|
|
# persistent runtime, rebuilt on 2026-09-06, got 2.1.263 — two versions in one
|
|
# deployment and nothing recording either. Worse, 2.1.265 and 2.1.275 each
|
|
# broke every turn on ANTHROPIC_BASE_URL endpoints (HTTP 400), which is how the
|
|
# GLM and Kimi backends reach `claude`; a rebuild landing on either day would
|
|
# have shipped that silently. Bump these on purpose, run a mission on the
|
|
# result (attribution, gate, tap, skills, judge), then promote.
|
|
ARG CLAUDE_CODE_VERSION=2.1.276
|
|
ARG KIMI_CODE_VERSION=0.41.0
|
|
RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates curl gnupg git \
|
|
&& curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \
|
|
&& apt-get install -y --no-install-recommends nodejs \
|
|
&& npm install -g @anthropic-ai/claude-code@${CLAUDE_CODE_VERSION} @moonshot-ai/kimi-code@${KIMI_CODE_VERSION} \
|
|
&& npm cache clean --force \
|
|
&& rm -rf /var/lib/apt/lists/* /root/.npm
|
|
|
|
# Upstream Gitea SDLC tooling for team-scoped agents that operate on
|
|
# git.redclaw.dev. `tea` covers shell-level ops (git remote helpers,
|
|
# clone/push, PR checkout); `gitea-mcp` gives the LLM a structured
|
|
# MCP tool surface (create_pr, list_repo_issues, create_file, etc.).
|
|
# Both are official upstream binaries — no custom bundle to maintain.
|
|
ARG TEA_VERSION=0.14.2
|
|
ARG GITEA_MCP_VERSION=1.3.0
|
|
RUN set -eux; \
|
|
arch="$(dpkg --print-architecture)"; \
|
|
case "$arch" in \
|
|
amd64) mcp_asset="Linux_x86_64" ;; \
|
|
arm64) mcp_asset="Linux_arm64" ;; \
|
|
*) echo "unsupported arch: $arch"; exit 1 ;; \
|
|
esac; \
|
|
curl -fsSL "https://dl.gitea.com/tea/${TEA_VERSION}/tea-${TEA_VERSION}-linux-${arch}" \
|
|
-o /usr/local/bin/tea && chmod +x /usr/local/bin/tea; \
|
|
tmp="$(mktemp -d)" && \
|
|
curl -fsSL "https://gitea.com/gitea/gitea-mcp/releases/download/v${GITEA_MCP_VERSION}/gitea-mcp_${mcp_asset}.tar.gz" \
|
|
-o "$tmp/gitea-mcp.tgz" && \
|
|
tar -xzf "$tmp/gitea-mcp.tgz" -C "$tmp" && \
|
|
install -m 0755 "$tmp/gitea-mcp" /usr/local/bin/gitea-mcp && \
|
|
rm -rf "$tmp"; \
|
|
/usr/local/bin/tea --version | head -1; \
|
|
/usr/local/bin/gitea-mcp --version 2>&1 | head -1 || true
|
|
|
|
# ── Mission toolchain ────────────────────────────────────────────────
|
|
# Agents and the phase evaluator both run project checks inside this image:
|
|
# `templates/teams/rust_sdlc.toml` tells the coder to run `cargo test`, the
|
|
# `done_when` evaluator runs the project's own suite to verify a claim rather
|
|
# than believe it, and `security_scan.rs` shells out to four scanners.
|
|
#
|
|
# None of it was here. A Rust mission's `cargo build` failed, and every
|
|
# security scan produced four `<tool>:tool_error` task rows instead of
|
|
# findings — a scan that scanned nothing and reported cleanly.
|
|
#
|
|
# Measured cost on top of the 864 MB base: scanners +350 MB, Rust +1.23 GB,
|
|
# semgrep +680 MB. This image is NOT in `AGENT_IMAGES`, so it never ships to
|
|
# fleet nodes — only gw-04 holds it, against 112 GB free. The real cost is a
|
|
# slower `docker save | load` on each runtime rebuild, which is worth paying
|
|
# for missions that can actually compile and test what they write.
|
|
#
|
|
# Ordered cheapest-and-most-stable first so a version bump lower down doesn't
|
|
# invalidate the expensive layers above it.
|
|
ARG GITLEAKS_VERSION=8.30.1
|
|
ARG TRIVY_VERSION=0.72.0
|
|
RUN set -eux; \
|
|
arch="$(dpkg --print-architecture)"; \
|
|
case "$arch" in \
|
|
amd64) gl_arch=x64; tv_arch=64bit ;; \
|
|
arm64) gl_arch=arm64; tv_arch=ARM64 ;; \
|
|
*) echo "unsupported arch: $arch"; exit 1 ;; \
|
|
esac; \
|
|
curl -fsSL "https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/gitleaks_${GITLEAKS_VERSION}_linux_${gl_arch}.tar.gz" \
|
|
| tar -xz -C /usr/local/bin gitleaks; \
|
|
curl -fsSL "https://github.com/aquasecurity/trivy/releases/download/v${TRIVY_VERSION}/trivy_${TRIVY_VERSION}_Linux-${tv_arch}.tar.gz" \
|
|
| tar -xz -C /usr/local/bin trivy; \
|
|
gitleaks version; trivy --version | head -1
|
|
|
|
# semgrep in its own venv so its pinned dependency tree can never collide with
|
|
# anything else installed here.
|
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
python3 python3-pip python3-venv \
|
|
&& python3 -m venv /opt/semgrep \
|
|
&& /opt/semgrep/bin/pip install --no-cache-dir semgrep \
|
|
&& ln -s /opt/semgrep/bin/semgrep /usr/local/bin/semgrep \
|
|
&& rm -rf /var/lib/apt/lists/* \
|
|
&& semgrep --version
|
|
|
|
# Rust last: the largest layer and the one most likely to be bumped, so it
|
|
# sits where a rebuild costs the least cache.
|
|
ENV RUSTUP_HOME=/usr/local/rustup \
|
|
CARGO_HOME=/usr/local/cargo \
|
|
PATH=/usr/local/cargo/bin:$PATH
|
|
# `cmake` and `build-essential` (for `g++`) are NOT optional here, even though
|
|
# the builder stage above already has them: a mission compiles the USER's repo
|
|
# in this stage, and a great many Rust crates drive a C/C++ build from their
|
|
# build script. `clawhdf5` is one — `libz-ng-sys` shells out to cmake, so
|
|
# `cargo test` died in 13 seconds with "is `cmake` not installed?" and exit 101.
|
|
#
|
|
# That exit is read as `TestOutcome::Failed(101)` — a RED SUITE — so the
|
|
# `on_green_tests` gate reported the repo's tests as failing when in truth they
|
|
# never compiled. A missing toolchain must not be indistinguishable from broken
|
|
# code.
|
|
#
|
|
# `python3-dev` is here for the same reason one layer down: `clawhdf5-py` is a
|
|
# pyo3 crate, so the link step wants `-lpython3.11` and fails with
|
|
# "cannot find -lpython3.11" without the dev package. `python3` alone ships no
|
|
# shared library to link against.
|
|
#
|
|
# `images/agent-toolchain/Dockerfile` (the microVM path) has installed
|
|
# `cmake build-essential` all along. Its own header warns about exactly this
|
|
# divergence: "if `cargo` is present in one image and absent in another, the
|
|
# same mission passes or fails depending on which backend it landed on, and
|
|
# nothing says why." This is that, one package down.
|
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
gcc libc6-dev pkg-config libssl-dev make cmake build-essential python3-dev \
|
|
&& curl -fsSL https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain stable \
|
|
&& cargo install cargo-audit --locked --no-default-features \
|
|
&& rm -rf /var/lib/apt/lists/* "$CARGO_HOME/registry" "$CARGO_HOME/git" \
|
|
&& chmod -R a+rX "$RUSTUP_HOME" "$CARGO_HOME" \
|
|
&& rustc --version && cargo audit --version
|
|
|
|
COPY --from=build /usr/local/bin/zeroclaw /usr/local/bin/zeroclaw
|
|
ENV HOME=/zeroclaw-data \
|
|
ZEROCLAW_WORKSPACE=/zeroclaw-data/workspace \
|
|
ZEROCLAW_GATEWAY_PORT=42617
|
|
RUN mkdir -p /zeroclaw-data/workspace
|
|
EXPOSE 42617
|
|
ENTRYPOINT ["zeroclaw"]
|
|
CMD ["daemon"]
|