From 48245518a174d4b6c33de0afb1ff9d4a00a5022a Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Tue, 5 May 2026 01:17:14 +0000 Subject: [PATCH 1/9] fix: load iptables kernel modules for WireGuard NAT wg-easy needs iptable_nat and iptable_filter to set up masquerading for VPN traffic. These modules must be loaded at boot for the container to access iptables. --- hosts/lazyworkhorse/configuration.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hosts/lazyworkhorse/configuration.nix b/hosts/lazyworkhorse/configuration.nix index a74ec09..7fdf7ba 100644 --- a/hosts/lazyworkhorse/configuration.nix +++ b/hosts/lazyworkhorse/configuration.nix @@ -36,7 +36,7 @@ "transparent_hugepage=always" # because mucho ram ]; # 2. Load the specific drivers found by sensors-detect - boot.kernelModules = [ "nct6775" "lm96163" ]; + boot.kernelModules = [ "nct6775" "lm96163" "iptable_nat" "iptable_filter" ]; # 3. Force the nct6775 driver to recognize the chip if it's stubborn boot.extraModprobeConfig = '' options nct6775 force_id=0xd280 From 7d0b72a51380939b780c2e54cd4ed14fcd9bc1ac Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Tue, 5 May 2026 01:18:13 +0000 Subject: [PATCH 2/9] chore: update compose submodule to linuxserver/wireguard --- assets/compose | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/compose b/assets/compose index 293429a..4a57ca6 160000 --- a/assets/compose +++ b/assets/compose @@ -1 +1 @@ -Subproject commit 293429a124f72e75e4f29620bb3fb9ec201d03d3 +Subproject commit 4a57ca69b2683a3f32274a3fc98d48eb33723104 From 92bcf1cc041ef955b8c087dac3dbb64518f37424 Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Tue, 5 May 2026 01:21:19 +0000 Subject: [PATCH 3/9] chore: update compose submodule to wireguard-vpn --- assets/compose | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/compose b/assets/compose index 4a57ca6..a0484e9 160000 --- a/assets/compose +++ b/assets/compose @@ -1 +1 @@ -Subproject commit 4a57ca69b2683a3f32274a3fc98d48eb33723104 +Subproject commit a0484e9df99f7f72907c5a00e41d8c9425a5f1b6 From a42b2ff65de5f7d5dc00742994da66cec964b3ee Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Tue, 5 May 2026 01:21:34 +0000 Subject: [PATCH 4/9] chore: update compose submodule to wireguard-vpn (fix ref) --- assets/compose | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/compose b/assets/compose index a0484e9..eea6db3 160000 --- a/assets/compose +++ b/assets/compose @@ -1 +1 @@ -Subproject commit a0484e9df99f7f72907c5a00e41d8c9425a5f1b6 +Subproject commit eea6db3ceba56424e57b2b32f2c2e7268dd88e45 From e0068260cb58c1bef110039a5c63e55567cf6182 Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Tue, 5 May 2026 01:43:42 +0000 Subject: [PATCH 5/9] chore: move Hermes Dockerfile to compose repo, add WireGuard tools - Move Dockerfile.full from infra/docker/hermes to compose/ai/Dockerfile - Add wireguard-tools and openresolv to Hermes image - Remove stray docker/hermes directory from infra --- assets/compose | 2 +- docker/hermes/Dockerfile.full | 71 ----------------------------------- 2 files changed, 1 insertion(+), 72 deletions(-) delete mode 100644 docker/hermes/Dockerfile.full diff --git a/assets/compose b/assets/compose index eea6db3..b021d0d 160000 --- a/assets/compose +++ b/assets/compose @@ -1 +1 @@ -Subproject commit eea6db3ceba56424e57b2b32f2c2e7268dd88e45 +Subproject commit b021d0dba7136ad30809f78b37fcb9cf1859809a diff --git a/docker/hermes/Dockerfile.full b/docker/hermes/Dockerfile.full deleted file mode 100644 index 1edd524..0000000 --- a/docker/hermes/Dockerfile.full +++ /dev/null @@ -1,71 +0,0 @@ -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 \ - chromium xvfb fonts-noto-color-emoji fonts-unifont fonts-liberation fonts-ipafont-gothic fonts-wqy-zenhei fonts-tlwg-loma-otf fonts-freefont-ttf \ - libasound2t64 libatk-bridge2.0-0t64 libatk1.0-0t64 libatspi2.0-0t64 libcairo2 libcups2t64 libdbus-1-3 libdrm2 libgbm1 libglib2.0-0t64 libnspr4 libnss3 libpango-1.0-0 libx11-6 libxcb1 libxcomposite1 libxdamage1 libxext6 libxfixes3 libxkbcommon0 libxrandr2 \ - texlive-latex-base texlive-latex-extra texlive-fonts-recommended texlive-xetex texlive-science \ - qemu-user-static binfmt-support qemu-user-binfmt \ - emacs-nox \ - libportaudio2 && \ - 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]" && \ - uv pip install --no-cache-dir sounddevice numpy faster-whisper - -# ---------- 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 b9289a149dabdaf616cdc1a1036d6e66be3df8e2 Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Tue, 5 May 2026 01:48:24 +0000 Subject: [PATCH 6/9] chore: update compose submodule for Hermes NET_ADMIN + WireGuard Dockerfile --- assets/compose | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/compose b/assets/compose index b021d0d..acf45ac 160000 --- a/assets/compose +++ b/assets/compose @@ -1 +1 @@ -Subproject commit b021d0dba7136ad30809f78b37fcb9cf1859809a +Subproject commit acf45acdd961a99c7b3edc3134009e5c1f9d9407 From cf279c4fb0abc42af9d3f2ed9d22a437e12a415b Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Tue, 5 May 2026 02:11:41 +0000 Subject: [PATCH 7/9] feat: add host-level WireGuard client via networking.wireguard - Add wg0 interface config with agenix-managed secrets - Revert compose submodule to remove NET_ADMIN from Hermes - WireGuard runs at host level, all containers inherit the tunnel --- assets/compose | 2 +- hosts/lazyworkhorse/configuration.nix | 35 +++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/assets/compose b/assets/compose index acf45ac..bc49391 160000 --- a/assets/compose +++ b/assets/compose @@ -1 +1 @@ -Subproject commit acf45acdd961a99c7b3edc3134009e5c1f9d9407 +Subproject commit bc49391b4f67f1db5d9bfcd35a299367210da330 diff --git a/hosts/lazyworkhorse/configuration.nix b/hosts/lazyworkhorse/configuration.nix index 7fdf7ba..69e113e 100644 --- a/hosts/lazyworkhorse/configuration.nix +++ b/hosts/lazyworkhorse/configuration.nix @@ -49,6 +49,27 @@ networking.networkmanager.enable = true; # Easiest to use and most distros use this by default. networking.hostId = "deadbeef"; + # WireGuard VPN client -- always up, connects to wg-easy server + # Before deploying, create age-encrypted secrets: + # echo -n "IOWDh8tH19DGphAkEr46zN0pRl61tmbAynrMkaFo30M=" | agenix -e secrets/wireguard_private_key.age + # echo -n "TIE9hcyOESofAiyJ1Md4CcPruTRXG63rItV9rmV3UDk=" | agenix -e secrets/wireguard_preshared_key.age + networking.wireguard.interfaces = { + wg0 = { + ips = [ "10.8.0.3/24" ]; + privateKeyFile = config.age.secrets.wireguard_private_key.path; + peers = [ + { + publicKey = "rY9zII3AOm8rog2rv02PyA3Bq7zdvTOGkZapfCV1DkE="; + presharedKeyFile = config.age.secrets.wireguard_preshared_key.path; + allowedIPs = [ "0.0.0.0/0" "::/0" ]; + endpoint = "vpn.lazyworkhorse.net:51820"; + persistentKeepalive = 25; + } + ]; + dns = [ "1.1.1.1" "8.8.8.8" ]; + }; + }; + # Set your time zone. time.timeZone = "America/Montreal"; @@ -269,6 +290,20 @@ mode = "0440"; path = "/run/secrets/openclaw_gateway_token"; }; + wireguard_private_key = { + file = ../../secrets/wireguard_private_key.age; + owner = "root"; + group = "root"; + mode = "0400"; + path = "/run/secrets/wireguard_private_key"; + }; + wireguard_preshared_key = { + file = ../../secrets/wireguard_preshared_key.age; + owner = "root"; + group = "root"; + mode = "0400"; + path = "/run/secrets/wireguard_preshared_key"; + }; }; }; From 94a7c7195a02de26315ed5bd59e0d0e8b85dbab7 Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Tue, 5 May 2026 02:12:55 +0000 Subject: [PATCH 8/9] fix: remove exposed keys from comments --- hosts/lazyworkhorse/configuration.nix | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hosts/lazyworkhorse/configuration.nix b/hosts/lazyworkhorse/configuration.nix index 69e113e..953c64e 100644 --- a/hosts/lazyworkhorse/configuration.nix +++ b/hosts/lazyworkhorse/configuration.nix @@ -50,9 +50,9 @@ networking.hostId = "deadbeef"; # WireGuard VPN client -- always up, connects to wg-easy server - # Before deploying, create age-encrypted secrets: - # echo -n "IOWDh8tH19DGphAkEr46zN0pRl61tmbAynrMkaFo30M=" | agenix -e secrets/wireguard_private_key.age - # echo -n "TIE9hcyOESofAiyJ1Md4CcPruTRXG63rItV9rmV3UDk=" | agenix -e secrets/wireguard_preshared_key.age + # Create age-encrypted secrets before deploying (run on the host): + # echo -n "" | agenix -e secrets/wireguard_private_key.age + # echo -n "" | agenix -e secrets/wireguard_preshared_key.age networking.wireguard.interfaces = { wg0 = { ips = [ "10.8.0.3/24" ]; From 5c481d664ab1a8dcba257ad73254a5edd66092a1 Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Tue, 5 May 2026 02:41:29 +0000 Subject: [PATCH 9/9] fix: split tunnel on host VPN - only route 10.8.0.0/24 --- hosts/lazyworkhorse/configuration.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hosts/lazyworkhorse/configuration.nix b/hosts/lazyworkhorse/configuration.nix index 953c64e..2f13320 100644 --- a/hosts/lazyworkhorse/configuration.nix +++ b/hosts/lazyworkhorse/configuration.nix @@ -61,7 +61,7 @@ { publicKey = "rY9zII3AOm8rog2rv02PyA3Bq7zdvTOGkZapfCV1DkE="; presharedKeyFile = config.age.secrets.wireguard_preshared_key.path; - allowedIPs = [ "0.0.0.0/0" "::/0" ]; + allowedIPs = [ "10.8.0.0/24" ]; endpoint = "vpn.lazyworkhorse.net:51820"; persistentKeepalive = 25; }