/ 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"]