# 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 FROM rust:1.96-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 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. 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 @moonshot-ai/kimi-code \ && 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_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 RUN apt-get update && apt-get install -y --no-install-recommends \ gcc libc6-dev pkg-config libssl-dev make \ && 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"]