Two-stage build, agent tools, Node 24, claude CLI, data dirs owned by app, DOTNET_EnableDiagnostics=0. Adds the MCP spec's deferred test that the Dockerfile's PLAYWRIGHT_MCP_VERSION equals PlaywrightPreset.DefaultPackage. Spec change: the image keeps root as its user and the entrypoint drops to app with setpriv. With USER app, compose's docker-init (PID 1) runs as app and its /proc/1/environ exposes every secret to the agent's bash. The spec also lists the csprojs the restore layer copies (GoogleCalendar, Mcp, Scheduler included) and sets HOME=/home/app in the image.
77 lines
4.5 KiB
Docker
77 lines
4.5 KiB
Docker
# The Telegram host as one container. User guide: docs/docker.md.
|
|
# Spec: docs/superpowers/specs/2026-09-26-docker-deployment-design.md
|
|
|
|
FROM mcr.microsoft.com/dotnet/sdk:10.0-noble AS build
|
|
WORKDIR /src
|
|
|
|
# Restore layer: only the project files the host needs, cached until one of them changes.
|
|
COPY Directory.Build.props VolcanicAgenticCore.slnx ./
|
|
COPY src/Volcanic.AgenticCore.Abstractions/Volcanic.AgenticCore.Abstractions.csproj src/Volcanic.AgenticCore.Abstractions/
|
|
COPY src/Volcanic.AgenticCore/Volcanic.AgenticCore.csproj src/Volcanic.AgenticCore/
|
|
COPY extensions/Volcanic.AgenticCore.Extensions.ClaudeCode/Volcanic.AgenticCore.Extensions.ClaudeCode.csproj extensions/Volcanic.AgenticCore.Extensions.ClaudeCode/
|
|
COPY extensions/Volcanic.AgenticCore.Extensions.ClaudeCode.Bridge/Volcanic.AgenticCore.Extensions.ClaudeCode.Bridge.csproj extensions/Volcanic.AgenticCore.Extensions.ClaudeCode.Bridge/
|
|
COPY extensions/Volcanic.AgenticCore.Extensions.GoogleCalendar/Volcanic.AgenticCore.Extensions.GoogleCalendar.csproj extensions/Volcanic.AgenticCore.Extensions.GoogleCalendar/
|
|
COPY extensions/Volcanic.AgenticCore.Extensions.Mcp/Volcanic.AgenticCore.Extensions.Mcp.csproj extensions/Volcanic.AgenticCore.Extensions.Mcp/
|
|
COPY extensions/Volcanic.AgenticCore.Extensions.Scheduler/Volcanic.AgenticCore.Extensions.Scheduler.csproj extensions/Volcanic.AgenticCore.Extensions.Scheduler/
|
|
COPY hosts/Volcanic.AgenticCore.Telegram/Volcanic.AgenticCore.Telegram.csproj hosts/Volcanic.AgenticCore.Telegram/
|
|
RUN dotnet restore hosts/Volcanic.AgenticCore.Telegram/Volcanic.AgenticCore.Telegram.csproj
|
|
|
|
COPY src/ src/
|
|
COPY extensions/ extensions/
|
|
COPY hosts/ hosts/
|
|
# Framework-dependent: claude starts the bridge as `dotnet <Bridge>.dll`, so the loose dll and its runtimeconfig must exist.
|
|
RUN dotnet publish hosts/Volcanic.AgenticCore.Telegram/Volcanic.AgenticCore.Telegram.csproj -c Release --no-restore -o /out \
|
|
&& test -f /out/Volcanic.AgenticCore.Extensions.ClaudeCode.Bridge.dll \
|
|
&& test -f /out/Volcanic.AgenticCore.Extensions.ClaudeCode.Bridge.runtimeconfig.json
|
|
|
|
FROM mcr.microsoft.com/dotnet/runtime:10.0-noble
|
|
ARG DEBIAN_FRONTEND=noninteractive
|
|
ARG NODE_MAJOR=24
|
|
ARG CLAUDE_CODE_VERSION=2.1
|
|
ARG INSTALL_PLAYWRIGHT=false
|
|
# Must equal the version in PlaywrightPreset.DefaultPackage (the single source of truth);
|
|
# PlaywrightPresetTests.Dockerfile_version_matches_DefaultPackage fails when they differ.
|
|
ARG PLAYWRIGHT_MCP_VERSION=0.0.82
|
|
|
|
# What the agent's bash tool can run, then Node from NodeSource (Ubuntu noble's nodejs is too old).
|
|
RUN apt-get update \
|
|
&& apt-get install -y --no-install-recommends \
|
|
bash git ripgrep python3 python-is-python3 python3-venv curl ca-certificates tzdata jq less procps openssh-client gnupg \
|
|
&& mkdir -p /etc/apt/keyrings \
|
|
&& curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg \
|
|
&& echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_${NODE_MAJOR}.x nodistro main" > /etc/apt/sources.list.d/nodesource.list \
|
|
&& apt-get update \
|
|
&& apt-get install -y --no-install-recommends nodejs \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
# The global prefix stays root-owned, so claude's auto-updater can't replace it at runtime: an upgrade changes what
|
|
# system/init validation sees and invalidates the prompt cache, so it only arrives with a rebuild.
|
|
RUN npm install -g "@anthropic-ai/claude-code@${CLAUDE_CODE_VERSION}" \
|
|
&& claude --version \
|
|
&& npm cache clean --force
|
|
|
|
# Set even without Playwright, so an MCP server started with the host environment finds browsers here.
|
|
ENV PLAYWRIGHT_BROWSERS_PATH=/opt/ms-playwright
|
|
# INSTALL_PLAYWRIGHT block (T2)
|
|
|
|
# The entrypoint hard-codes uid/gid 1654. Named volumes copy this ownership on first mount.
|
|
RUN test "$(id -u app)" = 1654 && test "$(id -g app)" = 1654 \
|
|
&& mkdir -p /data/workspaces /data/agent /data/claude /home/app \
|
|
&& chown -R app:app /data /home/app
|
|
|
|
COPY --from=build /out /app
|
|
WORKDIR /app
|
|
|
|
ENV Telegram__WorkspaceRoot=/data/workspaces \
|
|
VOLCANIC_AGENT_DIR=/data/agent \
|
|
CLAUDE_CONFIG_DIR=/data/claude \
|
|
HOME=/home/app \
|
|
DOTNET_CLI_TELEMETRY_OPTOUT=1 \
|
|
DOTNET_NOLOGO=1 \
|
|
TZ=UTC \
|
|
DOTNET_EnableDiagnostics=0
|
|
|
|
# No USER: PID 1 (docker-init with compose's init: true) must stay root, or the agent could read every secret from
|
|
# /proc/1/environ. setpriv drops to app and execs dotnet, so Environment.ProcessPath is the muxer the bridge needs.
|
|
ENTRYPOINT ["setpriv", "--reuid=1654", "--regid=1654", "--init-groups", "--inh-caps=-all", "--", "dotnet", "Volcanic.AgenticCore.Telegram.dll"]
|