Optimize Hermes Nix service configuration

This commit is contained in:
2026-05-23 08:55:05 +02:00
parent 1bd78b5de8
commit b49d5c4f72
3 changed files with 33 additions and 52 deletions
+4
View File
@@ -41,6 +41,10 @@
tuwunel = 3024; tuwunel = 3024;
honcho = 3025; honcho = 3025;
# Agent infrastructure
hermes-api = 8642;
hermes-dashboard = 9119;
# Home automation # Home automation
homarr = 7575; homarr = 7575;
+18 -45
View File
@@ -7,31 +7,6 @@
# Edge TTS: Seraphina — friendly, multilingual German female voice (free, no API key) # Edge TTS: Seraphina — friendly, multilingual German female voice (free, no API key)
edgeVoice = "de-DE-SeraphinaMultilingualNeural"; edgeVoice = "de-DE-SeraphinaMultilingualNeural";
# Hermes v0.14 moved Matrix from matrix-nio to lazy-installed mautrix.
# Lazy pip installs cannot work in the read-only Nix Python environment, so
# provide the Matrix runtime deps declaratively and put their site-packages on
# the gateway process PYTHONPATH at interpreter startup.
matrixPython = pkgs.python312.withPackages (ps: let
# Hermes lazy_deps pins this exact version. nixpkgs currently ships an
# older aiosqlite, and lazy_deps treats version mismatches as missing.
aiosqlite_0_22_1 = ps.aiosqlite.overridePythonAttrs (_old: rec {
version = "0.22.1";
src = pkgs.fetchFromGitHub {
owner = "omnilib";
repo = "aiosqlite";
tag = "v${version}";
hash = "sha256-voOOFo1OwaRQ3JsDHlBrngP+8ajf0kTNKXJyOaJiTs4=";
};
});
in [
(ps.mautrix.override {withOlm = true;})
ps.markdown
aiosqlite_0_22_1
ps.asyncpg
ps.aiohttp-socks
]);
matrixPythonPath = "${matrixPython}/lib/python3.12/site-packages";
# Build skills using agents flake lib for hermes user # Build skills using agents flake lib for hermes user
hermesSkills = inputs.agents.lib.mkSkills { hermesSkills = inputs.agents.lib.mkSkills {
inherit pkgs; inherit pkgs;
@@ -54,17 +29,10 @@
in { in {
virtualisation.docker.enable = true; virtualisation.docker.enable = true;
# Matrix E2EE uses libolm via python-olm. libolm is archived upstream and
# marked insecure in nixpkgs, but Hermes Matrix encrypted rooms currently
# still require it through mautrix[encryption].
nixpkgs.config.permittedInsecurePackages = [
"olm-3.2.16"
];
systemd.tmpfiles.rules = [ systemd.tmpfiles.rules = [
"d /home/hermes/.config 0755 hermes hermes -" "d /var/lib/hermes/.config 0755 hermes hermes -"
"d /home/hermes/.config/tea 0755 hermes hermes -" "d /var/lib/hermes/.config/tea 0755 hermes hermes -"
"L+ /home/hermes/.config/tea/yml - - - - ${pkgs.writeText "tea-yml" '' "L+ /var/lib/hermes/.config/tea/yml - - - - ${pkgs.writeText "tea-yml" ''
logins: logins:
- name: m3ta - name: m3ta
url: https://code.m3ta.dev url: https://code.m3ta.dev
@@ -88,24 +56,29 @@ in {
''; '';
}; };
# Ensure 'uv' is in the hermes-agent service PATH so CronJobs and terminal
# sessions can use 'uv run' for PEP 723 scripts (e.g. garmin-daily.py).
systemd.services.hermes-agent = {
path = [pkgs.uv];
environment = {
PYTHONPATH = matrixPythonPath;
};
};
services.hermes-agent = { services.hermes-agent = {
enable = true; enable = true;
addToSystemPackages = true; addToSystemPackages = true;
# v0.14 lazy-installs heavy optional backends by default. In the sealed
# Nix package, include the backends this host config actively uses so the
# gateway, Matrix bridge, memory, web search, and TTS work
# without runtime pip/uv mutation.
extraDependencyGroups = [
"matrix"
"honcho"
"exa"
"edge-tts"
];
extraPackages = with pkgs; [ extraPackages = with pkgs; [
docker docker
git git
curl
jq
tea tea
nix nix
python3Minimal
uv
zellij zellij
]; ];
@@ -134,7 +107,7 @@ in {
# Bind to 0.0.0.0 so the Netbird interface can reach it. # Bind to 0.0.0.0 so the Netbird interface can reach it.
API_SERVER_ENABLED = "true"; API_SERVER_ENABLED = "true";
API_SERVER_HOST = "0.0.0.0"; API_SERVER_HOST = "0.0.0.0";
API_SERVER_PORT = "8642"; API_SERVER_PORT = toString (config.m3ta.ports.get "hermes-api");
}; };
# ── Container mode (podman) ────────────────────────────────────────── # ── Container mode (podman) ──────────────────────────────────────────
+11 -7
View File
@@ -7,6 +7,8 @@
# Netbird mesh VPN range — dashboard only accessible from mesh peers. # Netbird mesh VPN range — dashboard only accessible from mesh peers.
# m3-atlas Traefik proxies to this port over Netbird. # m3-atlas Traefik proxies to this port over Netbird.
netbirdRange = "100.64.0.0/16"; netbirdRange = "100.64.0.0/16";
apiPort = config.m3ta.ports.get "hermes-api";
dashboardPort = config.m3ta.ports.get "hermes-dashboard";
# Reference the hermes-agent package from the running service config # Reference the hermes-agent package from the running service config
hermesPkg = config.services.hermes-agent.package or (inputs.hermes-agent.packages.${pkgs.stdenv.hostPlatform.system}.default or pkgs.hermes-agent); hermesPkg = config.services.hermes-agent.package or (inputs.hermes-agent.packages.${pkgs.stdenv.hostPlatform.system}.default or pkgs.hermes-agent);
@@ -14,10 +16,10 @@ in {
# ── Hermes Dashboard systemd service ─────────────────────────────────── # ── Hermes Dashboard systemd service ───────────────────────────────────
# Web UI for managing Hermes Agent — sessions, config, kanban, cron, etc. # Web UI for managing Hermes Agent — sessions, config, kanban, cron, etc.
# #
# Flow: Browser → dash.m3ta.dev (TLS via m3-atlas Traefik) → Netbird → :9119 # Flow: Browser → dash.m3ta.dev (TLS via m3-atlas Traefik) → Netbird → :${toString dashboardPort}
# #
# --insecure is required to bind 0.0.0.0 (hermes refuses non-localhost otherwise). # --insecure is required to bind 0.0.0.0 (hermes refuses non-localhost otherwise).
# Safe because firewall restricts port 9119 to Netbird mesh only. # Safe because firewall restricts the dashboard/API ports to Netbird mesh only.
systemd.services.hermes-dashboard = { systemd.services.hermes-dashboard = {
description = "Hermes Agent Web Dashboard"; description = "Hermes Agent Web Dashboard";
after = ["network.target" "hermes-agent.service"]; after = ["network.target" "hermes-agent.service"];
@@ -29,7 +31,7 @@ in {
User = "hermes"; User = "hermes";
Group = "hermes"; Group = "hermes";
ExecStart = "${hermesPkg}/bin/hermes dashboard --host 0.0.0.0 --port 9119 --no-open --insecure"; ExecStart = "${hermesPkg}/bin/hermes dashboard --host 0.0.0.0 --port ${toString dashboardPort} --no-open --insecure";
# Environment matching the hermes-agent service # Environment matching the hermes-agent service
Environment = [ Environment = [
@@ -51,15 +53,17 @@ in {
}; };
}; };
# ── Firewall: Dashboard only from Netbird mesh ───────────────────────── # ── Firewall: Hermes network endpoints only from Netbird mesh ──────────
networking.firewall = { networking.firewall = {
extraCommands = '' extraCommands = ''
# Allow Hermes Dashboard (9119/tcp) only from Netbird mesh VPN # Allow Hermes Dashboard and OpenAI-compatible API only from Netbird mesh VPN
ip46tables -A nixos-fw -p tcp --dport 9119 -s ${netbirdRange} -j nixos-fw-accept ip46tables -A nixos-fw -p tcp --dport ${toString dashboardPort} -s ${netbirdRange} -j nixos-fw-accept
ip46tables -A nixos-fw -p tcp --dport ${toString apiPort} -s ${netbirdRange} -j nixos-fw-accept
''; '';
extraStopCommands = '' extraStopCommands = ''
ip46tables -D nixos-fw -p tcp --dport 9119 -s ${netbirdRange} -j nixos-fw-accept 2>/dev/null || true ip46tables -D nixos-fw -p tcp --dport ${toString dashboardPort} -s ${netbirdRange} -j nixos-fw-accept 2>/dev/null || true
ip46tables -D nixos-fw -p tcp --dport ${toString apiPort} -s ${netbirdRange} -j nixos-fw-accept 2>/dev/null || true
''; '';
}; };
} }