feat(discord-hermes-bridge): pont Discord vers un gateway Hermes dédié

Gateway hermes-agent dédié (profil discord-bridge) sur 127.0.0.1:8643, API Server
OpenAI-compatible consommée par le cog hermes.py du discord-bot. Unité systemd écrite à
la main (le module NixOS ne gère pas d'instances) et même paquet que le bot Matrix,
donc mêmes groupes de deps (edge-tts, stt-whisper).

Clé d'API dans secrets/discord-hermes-bridge.yml, port jamais ouvert au firewall
(assertion). Côté discord-bot, HERMES_BRIDGE_URL et le nom de rôle viennent d'un
EnvironmentFile, seule la clé est secrète.
This commit is contained in:
Hermes Agent
2026-09-27 13:00:56 -04:00
parent 0000584366
commit 2622780459
5 changed files with 109 additions and 0 deletions
+4
View File
@@ -36,6 +36,10 @@ creation_rules:
key_groups:
- age:
- *tower
- path_regex: secrets/discord-hermes-bridge\.yml$
key_groups:
- age:
- *tower
- path_regex: secrets/hermes-dashboard\.yml$
key_groups:
- age:
+17
View File
@@ -0,0 +1,17 @@
hermes_discord_bridge_env: ENC[AES256_GCM,data:SJ2zitdGqLLt9seXzmxu3o8uVmJFG6k5pGaMACpZ2aWzqbgP6D8LwTPKlJZiSZ8lwNUnurJrQ3J9RBSJaIOkoKrRtwX0AMs6crxRJe+E6CU=,iv:czq1KzUHEQs2fdDObhA0WmGgQwNwjczVcctQimttFxQ=,tag:LLcMUdPc4gQWhnZdWYD7XQ==,type:str]
discord_bot_hermes_bridge_env: ENC[AES256_GCM,data:VHIQgLh0azzLBEB9vemDrSPjqqSvirlp4d1fL+1vXwAmAYWN4UPMpYlUAjWrar23JY+QZ7FkFjbGV18GoPta6UYSIK/2ICVmsPHhHo1G3hEw+BUYYF+X,iv:8lis93YMqPz8mJR7J/ifm/SyYi1k/5s5XZVjglqR8G4=,tag:8i4Ju5C78CD3hfL+KmJXYg==,type:str]
sops:
age:
- enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBqendZNnBBdW9FQ0lYOXJT
WmN1b1ZoL2htbG1XRndlM2hlU1BjZTNZemprCktteUkvREtZYlFCTCtIdVh5WkFR
L1MxblNxKy9pVnRCYU9ScjZZdGhkelEKLS0tIDBmdk0xeGVzY3MwbmZncnFya2Fv
Nm4wRFY2UFQ0eFNuYXlGUWFjdytaZDQKjhK7ZpoqiWjdMFEXZ3Fzsas1EdKbTKNq
/2rL0KJAmQu/7pn9qKAXdrO7W7qf6XEwiVSmoZ0Z4BATcI6xiX+UPQ==
-----END AGE ENCRYPTED FILE-----
recipient: age1usm3v0ugducrzcwud60dda7xfnj49fdhecj7kcsfm7dzwzrzeyqq2jc2hm
lastmodified: "2026-09-27T06:54:21Z"
mac: ENC[AES256_GCM,data:+tNIeBz2vsvMPNGFIYm/VNaBm/jzDc2XtvhiRZVxYC+3IGM5On+2F8qKEmFEDEejdLo9SkKU2+XI0OBuD3fcBo5Gb9hevQMxS48W7EIzjsQ8KxHsvuRPor8krw8Q0vNyvpxtLOf9SEhcFWoYiFJt5HQ9q6lhvV93ZgQdXiZk4uI=,iv:66NsoDMKqsJHBjD1OO7B5o6B7tbmQIKnHYCyfiz9bKg=,tag:D9Db4M6lLeiWGENN1nMXRA==,type:str]
unencrypted_suffix: _unencrypted
version: 3.13.3
+1
View File
@@ -36,6 +36,7 @@
./hermes-webui
./hermes-dashboard
./discord-bot
./discord-hermes-bridge
./eternal-terminal
./nextcloud-mcp
./amazon-mcp
+16
View File
@@ -1,6 +1,7 @@
{
inputs,
config,
lib,
...
}: {
imports = [inputs.discord-bot.nixosModules.default];
@@ -17,5 +18,20 @@
port = 8050;
};
# Pont vers le gateway Hermes dédié (services/discord-hermes-bridge).
# HERMES_BRIDGE_URL n'est pas secret (loopback local), seule la clé l'est.
systemd.services.discord-bot.serviceConfig.EnvironmentFile = lib.mkAfter [
config.sops.secrets."discord_bot_hermes_bridge_env".path
];
systemd.services.discord-bot.environment = {
HERMES_BRIDGE_URL = "http://127.0.0.1:8643/v1/chat/completions";
HERMES_BRIDGE_ROLE_NAME = "hermes";
};
sops.secrets."discord_bot_env" = {};
sops.secrets."discord_bot_hermes_bridge_env" = {
sopsFile = ../../secrets/discord-hermes-bridge.yml;
owner = "discord-bot";
group = "discord-bot";
};
}
@@ -0,0 +1,71 @@
# services/discord-hermes-bridge/default.nix
#
# Gateway Hermes dedie, profil "discord-bridge" (cree a la main sur tower via
# `hermes profile create discord-bridge`, meme mecanique que le profil pocket :
# pas de concept d'"instances" dans le module NixOS services.hermes-agent,
# donc unite systemd ecrite a la main ici plutot que via cette option).
#
# Reutilise le MEME paquet hermes-agent (config.services.hermes-agent.package)
# que le bot Matrix @hermes dans services/hermes : meme build, donc les memes
# groupes de deps optionnelles (edge-tts, stt-whisper...) sont deja presents,
# utiles pour la phase 2 (vocal) du pont Discord sans travail supplementaire.
#
# Expose seulement l'API Server OpenAI-compatible (/v1/chat/completions) en
# loopback, consommee par le cog hermes.py du bot (services/discord-bot).
# Jamais de port ouvert au firewall : c'est le meme host, meme boucle locale.
{
config,
lib,
...
}: let
package = config.services.hermes-agent.package;
port = 8643;
in {
sops.secrets."hermes_discord_bridge_env" = {
sopsFile = ../../secrets/discord-hermes-bridge.yml;
owner = "hermes";
group = "hermes";
};
systemd.services.hermes-discord-bridge = {
description = "Hermes Agent gateway dedie (profil discord-bridge, pont vers discord-bot)";
after = ["network-online.target"];
wants = ["network-online.target"];
wantedBy = ["multi-user.target"];
environment = {
HOME = "/var/lib/hermes";
HERMES_HOME = "/var/lib/hermes/.hermes";
HERMES_MANAGED = "true";
# API_SERVER_ENABLED/PORT/KEY sont les seules variables documentees
# (pas de API_SERVER_HOST : le bind loopback 127.0.0.1 est le defaut,
# non configurable). API_SERVER_KEY vient du fichier sops (EnvironmentFile).
API_SERVER_ENABLED = "true";
API_SERVER_PORT = toString port;
};
serviceConfig = {
Type = "simple";
User = "hermes";
Group = "hermes";
ExecStart = "${package}/bin/hermes -p discord-bridge gateway";
EnvironmentFile = [config.sops.secrets."hermes_discord_bridge_env".path];
Restart = "always";
RestartSec = 5;
NoNewPrivileges = true;
PrivateTmp = true;
ProtectHome = false;
ProtectSystem = "strict";
ReadWritePaths = ["/var/lib/hermes"];
UMask = "0007";
WorkingDirectory = "/var/lib/hermes/workspace";
};
};
assertions = [
{
assertion = !(lib.elem port config.networking.firewall.allowedTCPPorts);
message = "discord-hermes-bridge : le port ${toString port} (API server local) ne doit pas etre ouvert au firewall.";
}
];
}