/ Dockerfile
Dockerfile
1 # ============================================================ 2 # UI Build Stages - Run in parallel with separate caches 3 # ============================================================ 4 # These stages use registry cache with mode=max, which means: 5 # - Each stage is cached independently in the registry 6 # - Only stages with changed dependencies will rebuild 7 # - Cache mounts persist across builds for faster npm installs 8 # - Changes to docs/ won't invalidate config_portal or webui caches 9 # ============================================================ 10 11 # Build Config Portal UI 12 FROM node:25.5.0-trixie-slim AS ui-config-portal 13 WORKDIR /build/config_portal/frontend 14 COPY config_portal/frontend/package*.json ./ 15 RUN --mount=type=cache,target=/root/.npm \ 16 npm ci 17 COPY config_portal/frontend ./ 18 RUN npm run build 19 20 # Build WebUI 21 FROM node:25.5.0-trixie-slim AS ui-webui 22 WORKDIR /build/client/webui/frontend 23 COPY client/webui/frontend/package*.json ./ 24 RUN --mount=type=cache,target=/root/.npm \ 25 npm ci 26 COPY client/webui/frontend ./ 27 RUN npm run build 28 29 # Build Documentation 30 FROM node:25.5.0-trixie-slim AS ui-docs 31 WORKDIR /build/docs 32 COPY docs/package*.json ./ 33 RUN --mount=type=cache,target=/root/.npm \ 34 npm ci 35 COPY docs ./ 36 COPY README.md ../README.md 37 COPY cli/__init__.py ../cli/__init__.py 38 RUN npm run build 39 40 # Stage to extract Node.js binaries for use in Python stages 41 FROM node:25.5.0-trixie-slim AS node-binaries 42 43 # ============================================================ 44 # Python Build Stage 45 # ============================================================ 46 # This stage uses registry cache with mode=max for optimal caching: 47 # - uv cache mount (/root/.cache/uv) speeds up package downloads 48 # - Lock file changes only rebuild dependency installation layer 49 # - Source code changes only rebuild the wheel build layer 50 # - Independent from UI build stages - Python changes don't rebuild UI 51 # ============================================================ 52 FROM python:3.13.11-slim-trixie AS builder 53 54 # Copy Node.js 25 from the official node image - Revert to NodeSource when useful (25.5 or 26) version is available 55 COPY --from=node-binaries /usr/local/bin/node /usr/local/bin/node 56 COPY --from=node-binaries /usr/local/bin/npm /usr/local/bin/npm 57 COPY --from=node-binaries /usr/local/bin/npx /usr/local/bin/npx 58 COPY --from=node-binaries /usr/local/lib/node_modules /usr/local/lib/node_modules 59 60 # Install system dependencies and uv 61 # Add unstable repo with APT pinning to only upgrade libtasn1-6 (CVE-2025-13151 fix) 62 # Pin libc6=2.41-12+deb13u2 to fix CVE-2026-0861, CVE-2026-0915, CVE-2025-15281 (glibc vulnerabilities) 63 # Pin dpkg=1.22.22 to fix CVE-2026-2219 (denial of service via zstd-compressed .deb archives) 64 RUN echo "deb http://deb.debian.org/debian unstable main" > /etc/apt/sources.list.d/unstable.list && \ 65 printf "Package: *\nPin: release a=unstable\nPin-Priority: 50\n\nPackage: libtasn1-6\nPin: release a=unstable\nPin-Priority: 900\n" > /etc/apt/preferences.d/99pin-libtasn1 && \ 66 apt-get update && \ 67 apt-get install -y --no-install-recommends \ 68 build-essential \ 69 curl \ 70 dpkg=1.22.22 \ 71 ffmpeg=7:7.1.3-0+deb13u1 \ 72 git \ 73 libc6=2.41-12+deb13u2 \ 74 libtasn1-6/unstable \ 75 libpng16-16t64=1.6.48-1+deb13u4 \ 76 libsqlite3-0=3.46.1-7+deb13u1 \ 77 libssl3t64=3.5.5-1~deb13u2 \ 78 libvpx9=1.15.0-2.1+deb13u1 \ 79 openssl=3.5.5-1~deb13u2 && \ 80 curl -LsSf https://astral.sh/uv/install.sh | sh && \ 81 mv /root/.local/bin/uv /usr/local/bin/uv && \ 82 rm -rf /var/lib/apt/lists/* /etc/apt/sources.list.d/unstable.list /etc/apt/preferences.d/99pin-libtasn1 && \ 83 python3 -m venv /opt/venv && \ 84 uv pip install --system "virtualenv<21" hatch 85 86 WORKDIR /app 87 88 ENV VIRTUAL_ENV=/opt/venv 89 ENV UV_LINK_MODE=copy 90 ENV UV_COMPILE_BYTECODE=1 91 92 # Sync dependencies from lock file directly into venv (cached layer) 93 # This is the expensive step with 188 packages (~8s with cache, ~280MB downloads without) 94 # Using lock file ensures reproducible builds with exact versions 95 # --frozen ensures lock file isn't modified, --no-dev skips dev dependencies 96 RUN --mount=type=cache,target=/root/.cache/uv \ 97 --mount=type=bind,source=uv.lock,target=uv.lock \ 98 --mount=type=bind,source=pyproject.toml,target=pyproject.toml \ 99 uv sync \ 100 --frozen \ 101 --no-dev \ 102 --active \ 103 --no-install-project 104 105 # Copy Python source code and essential files (skip UI source code) 106 COPY src ./src 107 COPY cli ./cli 108 COPY evaluation ./evaluation 109 COPY templates ./templates 110 COPY config_portal/__init__.py ./config_portal/__init__.py 111 COPY config_portal/backend ./config_portal/backend 112 COPY .github/helper_scripts ./.github/helper_scripts 113 114 # Copy pre-built UI static assets from UI build stages 115 COPY --from=ui-config-portal /build/config_portal/frontend/static ./config_portal/frontend/static 116 COPY --from=ui-webui /build/client/webui/frontend/static ./client/webui/frontend/static 117 COPY --from=ui-docs /build/docs/build ./docs/build 118 119 COPY LICENSE ./LICENSE 120 COPY README.md ./README.md 121 COPY pyproject.toml ./pyproject.toml 122 123 # Build the project wheel with cache mount 124 # Set SAM_SKIP_UI_BUILD to skip npm builds since we already have static assets 125 RUN --mount=type=cache,target=/root/.cache/uv \ 126 SAM_SKIP_UI_BUILD=true hatch build -t wheel 127 128 # Install only the wheel package (not dependencies, they're already installed) 129 # This is fast since all deps are already in the venv 130 RUN --mount=type=cache,target=/root/.cache/uv \ 131 uv pip install /app/dist/solace_agent_mesh-*.whl 132 133 # Runtime stage 134 FROM python:3.13.11-slim-trixie AS runtime 135 136 ENV PYTHONUNBUFFERED=1 137 ENV PATH="/opt/venv/bin:$PATH" 138 139 # Build argument to control LibreOffice installation for binary artifact preview 140 # LibreOffice is NOT installed by default to keep image size smaller 141 # To enable binary artifact preview (DOCX/PPTX), set: 142 # docker build --build-arg INSTALL_LIBREOFFICE=true -t sam . 143 # Or via environment variable: 144 # INSTALL_LIBREOFFICE=true docker build --build-arg INSTALL_LIBREOFFICE -t sam . 145 # 146 # IMPORTANT: LibreOffice is a separate open-source application licensed under MPL-2.0. 147 # See THIRD_PARTY_LICENSES/LIBREOFFICE.md for full license and attribution details. 148 # Source code: https://www.libreoffice.org/download/source-code/ 149 ARG INSTALL_LIBREOFFICE 150 151 # Copy Node.js 25 from the official node image 152 COPY --from=node-binaries /usr/local/bin/node /usr/local/bin/node 153 COPY --from=node-binaries /usr/local/bin/npm /usr/local/bin/npm 154 COPY --from=node-binaries /usr/local/bin/npx /usr/local/bin/npx 155 COPY --from=node-binaries /usr/local/lib/node_modules /usr/local/lib/node_modules 156 157 # Install minimal runtime dependencies (no uv for licensing compliance, no curl - due to vulnerabilities) 158 # LibreOffice is optionally installed for document conversion (DOCX/PPTX to PDF for preview) 159 # Add unstable repo with APT pinning to only upgrade libtasn1-6 (CVE-2025-13151 fix) 160 # Pin libc6=2.41-12+deb13u2 to fix CVE-2026-0861, CVE-2026-0915, CVE-2025-15281 (glibc vulnerabilities) 161 # Pin dpkg=1.22.22 to fix CVE-2026-2219 (denial of service via zstd-compressed .deb archives) 162 RUN echo "deb http://deb.debian.org/debian unstable main" > /etc/apt/sources.list.d/unstable.list && \ 163 printf "Package: *\nPin: release a=unstable\nPin-Priority: 50\n\nPackage: libtasn1-6\nPin: release a=unstable\nPin-Priority: 900\n" > /etc/apt/preferences.d/99pin-libtasn1 && \ 164 apt-get update && \ 165 apt-get install -y --no-install-recommends \ 166 dpkg=1.22.22 \ 167 ffmpeg=7:7.1.3-0+deb13u1 \ 168 git \ 169 libatomic1 \ 170 libc6=2.41-12+deb13u2 \ 171 libtasn1-6/unstable \ 172 libpng16-16t64=1.6.48-1+deb13u4 \ 173 libsqlite3-0=3.46.1-7+deb13u1 \ 174 libssl3t64=3.5.5-1~deb13u2 \ 175 libvpx9=1.15.0-2.1+deb13u1 \ 176 openssl=3.5.5-1~deb13u2 && \ 177 if [ "${INSTALL_LIBREOFFICE}" = "true" ]; then \ 178 echo "============================================================" && \ 179 echo "NOTICE: Installing LibreOffice - a separate open-source application" && \ 180 echo "LibreOffice is licensed under Mozilla Public License 2.0 (MPL-2.0)" && \ 181 echo "License: https://www.mozilla.org/en-US/MPL/2.0/" && \ 182 echo "Source: https://www.libreoffice.org/download/source-code/" && \ 183 echo "See THIRD_PARTY_LICENSES/LIBREOFFICE.md for full attribution" && \ 184 echo "============================================================" && \ 185 apt-get install -y --no-install-recommends \ 186 libreoffice-writer-nogui \ 187 libreoffice-impress-nogui \ 188 libreoffice-calc-nogui; \ 189 else \ 190 echo "Skipping LibreOffice installation (set INSTALL_LIBREOFFICE=true to enable binary artifact preview)"; \ 191 fi && \ 192 apt-get clean && \ 193 rm -rf /var/lib/apt/lists/* /etc/apt/sources.list.d/unstable.list /etc/apt/preferences.d/99pin-libtasn1 194 195 # Node 25.5.0 ships with npm 11.8.0; upgrade to pick up security fixes in bundled dependencies 196 RUN node /usr/local/lib/node_modules/npm/bin/npm-cli.js install -g npm@11.11.1 197 198 # Install playwright temporarily just for browser installation (cached layer) 199 # This is separate from the full venv to keep this layer cached 200 # We'll use the playwright from the full venv at runtime 201 RUN --mount=type=cache,target=/root/.cache/pip \ 202 --mount=type=cache,target=/root/.cache/playwright \ 203 python3 -m pip install playwright && \ 204 playwright install-deps chromium && \ 205 playwright install chromium && \ 206 python3 -m pip uninstall playwright -y 207 208 # Create non-root user and Playwright cache directory 209 RUN groupadd -r solaceai && useradd --create-home -r -g solaceai solaceai && \ 210 mkdir -p /var/cache/playwright && \ 211 chown -R solaceai:solaceai /var/cache/playwright 212 213 WORKDIR /app 214 RUN chown -R solaceai:solaceai /app 215 216 # Copy the pre-built virtual environment from builder 217 # This avoids slow pip install in runtime (UV already did it) 218 # Copied AFTER Playwright setup so Playwright layers stay cached 219 COPY --from=builder /opt/venv /opt/venv 220 221 COPY preset /preset 222 223 USER solaceai 224 # Required environment variables 225 ENV CONFIG_PORTAL_HOST=0.0.0.0 226 ENV FASTAPI_HOST=0.0.0.0 227 ENV FASTAPI_PORT=8000 228 ENV NAMESPACE=sam/ 229 ENV SOLACE_DEV_MODE=True 230 231 # Set the following environment variables to appropriate values before deploying 232 ENV SESSION_SECRET_KEY="REPLACE_WITH_SESSION_SECRET_KEY" 233 234 LABEL org.opencontainers.image.source=https://github.com/SolaceLabs/solace-agent-mesh 235 236 EXPOSE 5002 8000 237 238 # CLI entry point 239 ENTRYPOINT ["solace-agent-mesh"] 240 241 # Default command to run the preset agents 242 CMD ["run", "/preset/agents"]