From 7738269fb3ef5d48f3d9da6ecfff30e16b588808 Mon Sep 17 00:00:00 2001 From: Thierry Pouplier Date: Wed, 29 Apr 2026 21:12:35 +0000 Subject: [PATCH 1/3] feat(ai): add Dockerfile with curl, poppler-utils, imagemagick Add Dockerfile for building custom Hermes Agent image. Packages (PR 1 of 5): - curl: HTTP client - poppler-utils: PDF tools - imagemagick: Image manipulation --- ai/Dockerfile | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 ai/Dockerfile diff --git a/ai/Dockerfile b/ai/Dockerfile new file mode 100644 index 0000000..39c9def --- /dev/null +++ b/ai/Dockerfile @@ -0,0 +1,64 @@ +FROM ghcr.io/astral-sh/uv:0.11.6-python3.13-trixie@sha256:b3c543b6c4f23a5f2df22866bd7857e5d304b67a564f4feab6ac22044dde719b AS uv_source +FROM tianon/gosu:1.19-trixie@sha256:3b176695959c71e123eb390d427efc665eeb561b1540e82679c15e992006b8b9 AS gosu_source +FROM debian:13.4 + +# Disable Python stdout buffering to ensure logs are printed immediately +ENV PYTHONUNBUFFERED=1 + +# Store Playwright browsers outside the volume mount so the build-time +# install survives the /opt/data volume overlay at runtime. +ENV PLAYWRIGHT_BROWSERS_PATH=/opt/hermes/.playwright + +# Install system dependencies in one layer, clear APT cache +# tini reaps orphaned zombie processes (MCP stdio subprocesses, git, bun, etc.) +# that would otherwise accumulate when hermes runs as PID 1. See #15012. +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + build-essential nodejs npm python3 ripgrep ffmpeg gcc python3-dev libffi-dev procps git openssh-client docker-cli tini \ + curl poppler-utils imagemagick && \ + rm -rf /var/lib/apt/lists/* + +# Non-root user for runtime; UID can be overridden via HERMES_UID at runtime +RUN useradd -u 10000 -m -d /opt/data hermes + +COPY --chmod=0755 --from=gosu_source /gosu /usr/local/bin/ +COPY --chmod=0755 --from=uv_source /usr/local/bin/uv /usr/local/bin/uvx /usr/local/bin/ + +WORKDIR /opt/hermes + +# ---------- Layer-cached dependency install ---------- +# Copy only package manifests first so npm install + Playwright are cached +# unless the lockfiles themselves change. +COPY package.json package-lock.json ./ +COPY web/package.json web/package-lock.json web/ + +RUN npm install --prefer-offline --no-audit && \ + npx playwright install --with-deps chromium --only-shell && \ + (cd web && npm install --prefer-offline --no-audit) && \ + npm cache clean --force + +# ---------- Source code ---------- +# .dockerignore excludes node_modules, so the installs above survive. +COPY --chown=hermes:hermes . . + +# Build web dashboard (Vite outputs to hermes_cli/web_dist/) +RUN cd web && npm run build + +# ---------- Permissions ---------- +# Make install dir world-readable so any HERMES_UID can read it at runtime. +# The venv needs to be traversable too. +USER root +RUN chmod -R a+rX /opt/hermes +# Start as root so the entrypoint can usermod/groupmod + gosu. +# If HERMES_UID is unset, the entrypoint drops to the default hermes user (10000). + +# ---------- Python virtualenv ---------- +RUN uv venv && \ + uv pip install --no-cache-dir -e ".[all]" + +# ---------- Runtime ---------- +ENV HERMES_WEB_DIST=/opt/hermes/hermes_cli/web_dist +ENV HERMES_HOME=/opt/data +ENV PATH="/opt/data/.local/bin:${PATH}" +VOLUME [ "/opt/data" ] +ENTRYPOINT [ "/usr/bin/tini", "-g", "--", "/opt/hermes/docker/entrypoint.sh" ] From 38f67f1bd66d89de380d2576f018ee3f9d72a8cd Mon Sep 17 00:00:00 2001 From: Hermes Date: Wed, 20 May 2026 14:27:58 -0400 Subject: [PATCH 2/3] fix: trim Dockerfile to minimal base image (PR 1 of 5) Replace the full Hermes agent Dockerfile with the minimal debian:13.4 base image as specified in the task: - debian:13.4 base - uv installed from astral-sh/uv:latest - curl, poppler-utils, imagemagick only - No other packages (PR 1 of 5) --- ai/Dockerfile | 71 +++++++++++---------------------------------------- 1 file changed, 15 insertions(+), 56 deletions(-) diff --git a/ai/Dockerfile b/ai/Dockerfile index 39c9def..39b8eb3 100644 --- a/ai/Dockerfile +++ b/ai/Dockerfile @@ -1,64 +1,23 @@ -FROM ghcr.io/astral-sh/uv:0.11.6-python3.13-trixie@sha256:b3c543b6c4f23a5f2df22866bd7857e5d304b67a564f4feab6ac22044dde719b AS uv_source -FROM tianon/gosu:1.19-trixie@sha256:3b176695959c71e123eb390d427efc665eeb561b1540e82679c15e992006b8b9 AS gosu_source FROM debian:13.4 -# Disable Python stdout buffering to ensure logs are printed immediately -ENV PYTHONUNBUFFERED=1 - -# Store Playwright browsers outside the volume mount so the build-time -# install survives the /opt/data volume overlay at runtime. -ENV PLAYWRIGHT_BROWSERS_PATH=/opt/hermes/.playwright - -# Install system dependencies in one layer, clear APT cache -# tini reaps orphaned zombie processes (MCP stdio subprocesses, git, bun, etc.) -# that would otherwise accumulate when hermes runs as PID 1. See #15012. +# Install uv (Python package manager), curl, poppler-utils, and imagemagick RUN apt-get update && \ apt-get install -y --no-install-recommends \ - build-essential nodejs npm python3 ripgrep ffmpeg gcc python3-dev libffi-dev procps git openssh-client docker-cli tini \ - curl poppler-utils imagemagick && \ + curl \ + poppler-utils \ + imagemagick && \ rm -rf /var/lib/apt/lists/* -# Non-root user for runtime; UID can be overridden via HERMES_UID at runtime -RUN useradd -u 10000 -m -d /opt/data hermes +# Install uv if not already present (debian:13.4 doesn't ship it) +COPY --from=ghcr.io/astral-sh/uv:latest /usr/local/bin/uv /usr/local/bin/uv +RUN uv --version -COPY --chmod=0755 --from=gosu_source /gosu /usr/local/bin/ -COPY --chmod=0755 --from=uv_source /usr/local/bin/uv /usr/local/bin/uvx /usr/local/bin/ +# Verify all expected tools are available +RUN curl --version && \ + pdftotext -v 2>&1 | head -1 && \ + pdfinfo -v 2>&1 | head -1 && \ + pdftoppm -v 2>&1 | head -1 && \ + convert --version | head -1 && \ + identify --version | head -1 -WORKDIR /opt/hermes - -# ---------- Layer-cached dependency install ---------- -# Copy only package manifests first so npm install + Playwright are cached -# unless the lockfiles themselves change. -COPY package.json package-lock.json ./ -COPY web/package.json web/package-lock.json web/ - -RUN npm install --prefer-offline --no-audit && \ - npx playwright install --with-deps chromium --only-shell && \ - (cd web && npm install --prefer-offline --no-audit) && \ - npm cache clean --force - -# ---------- Source code ---------- -# .dockerignore excludes node_modules, so the installs above survive. -COPY --chown=hermes:hermes . . - -# Build web dashboard (Vite outputs to hermes_cli/web_dist/) -RUN cd web && npm run build - -# ---------- Permissions ---------- -# Make install dir world-readable so any HERMES_UID can read it at runtime. -# The venv needs to be traversable too. -USER root -RUN chmod -R a+rX /opt/hermes -# Start as root so the entrypoint can usermod/groupmod + gosu. -# If HERMES_UID is unset, the entrypoint drops to the default hermes user (10000). - -# ---------- Python virtualenv ---------- -RUN uv venv && \ - uv pip install --no-cache-dir -e ".[all]" - -# ---------- Runtime ---------- -ENV HERMES_WEB_DIST=/opt/hermes/hermes_cli/web_dist -ENV HERMES_HOME=/opt/data -ENV PATH="/opt/data/.local/bin:${PATH}" -VOLUME [ "/opt/data" ] -ENTRYPOINT [ "/usr/bin/tini", "-g", "--", "/opt/hermes/docker/entrypoint.sh" ] +CMD ["/bin/bash"] From d0031e5c57d165aafd18a733a06f9369d612bc18 Mon Sep 17 00:00:00 2001 From: Hermes Date: Wed, 20 May 2026 14:28:01 -0400 Subject: [PATCH 3/3] feat: add Paperclip env example file with placeholder secrets Add env/.env.example.paperclip documenting the two required environment variables for the Paperclip agent orchestrator services: - PAPERCLIP_DB_PASSWORD -- PostgreSQL password for paperclip-db - PAPERCLIP_AUTH_SECRET -- Better Auth secret key for token signing Users copy this to .env and fill in the secrets before deploying. --- env/.env.example.paperclip | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 env/.env.example.paperclip diff --git a/env/.env.example.paperclip b/env/.env.example.paperclip new file mode 100644 index 0000000..a80936f --- /dev/null +++ b/env/.env.example.paperclip @@ -0,0 +1,26 @@ +# Paperclip Environment Variables +# Copy this file to your .env (at the compose root or docker-compose working directory) +# and fill in the secrets. +# +# cp env/.env.example.paperclip .env +# +# Then reference it from compose.yml: +# env_file: +# - path: .env +# required: true + +# --------------------------------------------------------------------------- +# Database +# --------------------------------------------------------------------------- +# PostgreSQL password for the paperclip-db service. +# Generate a strong random password: +# openssl rand -base64 32 +PAPERCLIP_DB_PASSWORD=change_me_to_a_strong_random_password + +# --------------------------------------------------------------------------- +# Authentication +# --------------------------------------------------------------------------- +# Secret key used by Better Auth for signing and verifying tokens. +# Generate a strong random secret: +# openssl rand -base64 32 +PAPERCLIP_AUTH_SECRET=change_me_to_a_strong_random_secret