Retour d’expérience sur l’installation d’Hermes Agent : durcissement Ubuntu 24.04, installation native avec systemd, providers IA, Telegram, voice mode, mémoire persistante et automatisations planifiées. Le but : montrer la démarche, les choix et les limites, sans exposer une infrastructure réelle.
Hermes Agent (Nous Research) est un agent IA self-hosted : tu lui parles en français depuis Telegram (texte ou vocal), il a accès à des outils (web search, browser, code, image gen, TTS), il garde une mémoire persistante de qui tu es et de ce que vous avez fait ensemble, et il exécute des tâches automatiques selon un planning que tu définis.
Contrairement à un chat web classique, Hermes vit en permanence sur le serveur, garde le contexte d'une session à l'autre, sait apprendre de nouvelles procédures (skills), et peut t'envoyer proactivement des informations sans que tu n'aies à le solliciter.
Décisions d'architecture prises pour cette installation : install native sur le VPS plutôt que Docker (plus simple, lisible, et systemd gère la persistance) · polling Telegram plutôt que webhook (pas de domaine ni de cert HTTPS à maintenir) · clés API directes auprès de chaque provider (pas de gateway intermédiaire).
Garde-fous anti-emballement intégrés : sessions cron isolées sans mémoire entre runs, modèle léger Gemini Flash Lite pour les jobs automatiques, modèle puissant DeepSeek V4 Pro réservé aux conversations interactives, credit limit fixé côté OpenRouter.
Disclaimer. Cette page est un retour d’expérience personnel et anonymisé, pas une procédure officielle ni une garantie de sécurité. Les commandes, architectures et choix présentés doivent être adaptés, vérifiés et testés dans votre propre contexte. Si vous les réutilisez, vous le faites sous votre responsabilité : coûts cloud/API, exposition réseau, gestion des secrets, conformité, sauvegardes et sécurité opérationnelle restent à votre charge.
Doctrine clés. Un modèle cher sur un job automatique récurrent + une fréquence courte = facture qui explose pendant que tu dors. Hermes sépare les deux usages : tu gardes la qualité quand tu interagis en direct, tu paies presque rien quand l'agent travaille tout seul en arrière-plan.
Pile complète, du métal jusqu'à Telegram, dans l'ordre des couches :
User non-root hermesuser avec sudo · SSH key-only ed25519 · PermitRootLogin no · UFW deny-incoming · fail2ban · unattended-upgrades. Surface d'attaque réduite à un seul port : 22/tcp avec auth par clé seule.
Installés par le script Hermes officiel dans ~/.hermes/hermes-agent/venv. Plus ripgrep, ffmpeg, Playwright/Chromium pour le browser tool. Aucun apt-get manuel, tout passe par l'installer.
Le binaire hermes dans ~/.local/bin/ · gateway systemd user service avec linger activé · scheduler cron tick toutes les 60s · état persistant dans ~/.hermes/state.db (SQLite).
OpenRouter pour l'inférence (DeepSeek V4 Pro chat, Gemini Flash Lite cron) · Tavily pour le web search · FAL pour l'image gen · Edge TTS gratuit + Groq Whisper pour le voice mode · GitHub token pour le Skills Hub.
Bot créé via @BotFather · allowlist stricte avec un seul user ID approuvé via pairing code (ABC12345) · home channel pour la livraison des cron jobs · texte + vocal supportés des deux côtés.
VPS fraîchement réinstallé, root SSH par mot de passe, /root/.ssh/authorized_keys vide. Première tâche : créer un user non-root, lui basculer la clé SSH ed25519 générée localement, désactiver le login root et l'auth password, puis fermer le firewall.
adduser hermesuser usermod -aG sudo hermesuser mkdir -p ~/.ssh echo "ssh-ed25519 AAAA... hermesuser@vps-example" > ~/.ssh/authorized_keys chown hermesuser:hermesuser ~/.ssh/authorized_keys chmod 600 ~/.ssh/authorized_keys
# /etc/ssh/sshd_config PermitRootLogin no PasswordAuthentication no PubkeyAuthentication yes
sudo apt install -y ufw fail2ban unattended-upgrades sudo ufw default deny incoming sudo ufw allow OpenSSH sudo ufw enable
Pourquoi cet ordre : on installe la clé sur le user non-root avant de désactiver l'auth password de root, et on ajoute UFW après avoir vérifié que la nouvelle session SSH marche dans un autre terminal. Le vieux principe « ne ferme jamais la dernière porte avant d'avoir testé la nouvelle ».
Hermes ne se compile pas, ne se docker-compose pas. Un script bash officiel installe Python 3.11, Node, uv, ripgrep, ffmpeg, le venv, et la commande hermes globale. Puis un wizard interactif demande tout le reste.
curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash
Le wizard pose ~15 questions : provider d'inférence (OpenRouter), modèle, TTS, terminal backend (local), tools à activer, search provider (Tavily), image gen (FAL), browser (local Playwright), cron settings, messaging (Telegram), allowlist, home channel, install systemd. Total : 10 minutes les doigts dans le nez.
~/.hermes/
├── config.yaml # toute la config en YAML (v23)
├── .env # clés API (chmod 600)
├── SOUL.md # personnalité de l'agent
├── memories/
│ ├── USER.md # qui tu es (durable)
│ └── MEMORY.md # contexte session (réutilisable)
├── skills/ # ~85 skills builtin + custom
├── cron/ # cron jobs en JSON
├── sessions/ # historique conversations
└── state.db # SQLite, état runtimeLe test canonique de la persistance : on dit à l'agent quelque chose dans une session, on quitte, on relance, et on vérifie qu'il s'en souvient. Hermes a écrit dans USER.md les faits durables (nom, âge, métier, ville) et dans MEMORY.md le contexte courant.
hermes > Je suis David, 49 ans, architecte Data & IA. Mémorise ça. > /quit hermes > Comment je m'appelle ? < Tu es David.
Côté skills, hermes skills list a affiché ~85 skills builtin déjà disponibles : arxiv, github-pr-workflow, systematic-debugging, research-paper-writing, obsidian, notion, blogwatcher, etc. L'agent les charge automatiquement quand le contexte s'y prête.
Bot créé via @BotFather, token collé dans le wizard. Le point critique : une allowlist Telegram mal renseignée peut ouvrir le bot à un utilisateur non prévu, avec consommation de crédits API à la clé. La correction doit être faite proprement dans la configuration, sans dépendre d’un numéro de ligne fragile :
hermes config edit # puis définir explicitement : # messaging.telegram.allowed_chats: "<TELEGRAM_USER_ID>"
Au premier /start, Hermes ne reconnaît pas l'utilisateur et exige un appairage cryptographique : il génère un code à usage unique (ABC12345) qu'il faut approuver côté serveur. Belle pratique : impossible de bypasser l'allowlist, même avec le token volé.
hermes pairing approve telegram ABC12345 ✓ Approved! User David (<TELEGRAM_USER_ID>) on telegram can now use the bot
Voice mode : Edge TTS (gratuit, voix Microsoft cloud) en sortie + Groq Whisper Large v3 Turbo (free tier large) en entrée. Tu envoies un vocal Telegram → Groq transcrit en français → l'agent répond → Edge TTS reformule en MP3 (converti en Opus par ffmpeg pour Telegram).
Doctrine clés API : tu n'actives que ce dont tu te sers. Pas de Discord, pas de Spotify, pas de Home Assistant. Chaque clé est dans ~/.hermes/.env avec chmod 600, lue au démarrage du gateway.
# ~/.hermes/.env
OPENROUTER_API_KEY=***
TELEGRAM_BOT_TOKEN=***
FAL_KEY=***
TAVILY_API_KEY=***
GITHUB_TOKEN=***
GROQ_API_KEY=***Voir la section Tools ci-dessous pour le détail provider par provider, tier, usage Hermes.
Règle de fer pour les jobs automatiques : jamais de modèle cher sur un job récurrent. Les trois cron jobs tournent sur google/gemini-2.5-flash-lite (≈0.10$/M tokens en input). Le chat interactif garde DeepSeek V4 Pro pour la qualité.
Bonus de la session : pendant que l'agent créait les crons, il a aussi sauvegardé un skill custom create-news-cron qui encapsule la procédure (table heure Paris ↔ UTC été/hiver, template prompt, règle SILENT, pièges). Réutilisable par simple commande en français.
~/.hermes/skills/productivity/create-news-cron/SKILL.mdVoir la section Cron ci-dessous pour les trois jobs en détail.
Chaque bloc ci-dessous a un bouton Copier en haut à droite. Tu peux dérouler la session intégrale en suivant l'ordre. Ce qu'il faut adapter pour ton propre cas est entre <CHEVRONS> ou en MAJUSCULES.
Vérifier ce qui existe déjà dans ~/.ssh/ sur ta machine Windows.
ls -la ~/.ssh/
Générer une nouvelle clé ed25519 dédiée au VPS (passphrase fortement recommandée).
ssh-keygen -t ed25519 -C "hermesuser@vps-example" -f ~/.ssh/id_ed25519_vps_demo
Récupérer le contenu de la clé publique à coller sur le serveur.
cat ~/.ssh/id_ed25519_vps_demo.pub
Enregistrer un alias SSH vps dans ~/.ssh/config pour ne plus avoir à taper l'IP ni le chemin de clé. Remplace l'IP par la tienne avant de coller.
cat >> ~/.ssh/config << 'EOF' Host vps HostName 666.66.6.66 User hermesuser IdentityFile ~/.ssh/id_ed25519_vps_demo EOF
Tester l'alias et la connexion avec la clé.
ssh vps
Créer le user hermesuser et l'ajouter au groupe sudo.
adduser hermesuser usermod -aG sudo hermesuser
Installer la clé publique pour l'auth SSH key-only. Remplace la chaîne ssh-ed25519 AAAA... par TA clé publique générée à l'étape A.
mkdir -p ~/.ssh echo "ssh-ed25519 AAAA... hermesuser@vps-example" > ~/.ssh/authorized_keys chown -R hermesuser:hermesuser ~/.ssh chmod 700 ~/.ssh chmod 600 ~/.ssh/authorized_keys
Sauvegarder la config SSH avant modification.
sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak
Éditer /etc/ssh/sshd_config et fixer ces 3 directives. Décommenter en virant le # au besoin.
sudo nano /etc/ssh/sshd_config
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yesRecharger sshd. Garde une autre session ouverte au cas où, et teste la nouvelle connexion dans une 2ᵉ Git Bash avant de fermer la première.
sudo systemctl restart ssh
Installer UFW + fail2ban, fermer tout sauf SSH, activer.
sudo apt update && sudo apt install -y ufw fail2ban sudo ufw default deny incoming sudo ufw default allow outgoing sudo ufw allow OpenSSH sudo ufw enable
Vérifier l'état des deux services.
sudo ufw status verbose sudo systemctl status fail2ban --no-pager
Installer et activer les mises à jour de sécurité automatiques.
sudo apt install -y unattended-upgrades sudo dpkg-reconfigure --priority=low unattended-upgrades
Vérifier que le service tourne.
sudo systemctl status unattended-upgrades --no-pager | head -5
Lancer l'installer officiel. Le script demande l'autorisation pour ripgrep + ffmpeg → répondre Y. À la fin il propose le wizard de setup → choisir Full setup.
curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash
Recharger le shell pour avoir la commande hermes dans le PATH.
source ~/.bashrc
Vérifier que tout est OK (config, providers, packages, services).
hermes doctor
Stopper le gateway le temps de corriger.
hermes gateway stop
Trouver les lignes liées à Telegram et l'allowlist dans la config.
grep -n -i "telegram\|allow\|user_id" ~/.hermes/config.yaml
Définir explicitement ton user ID Telegram dans allowed_chats. Évite les commandes dépendantes d’un numéro de ligne YAML : ouvre la config et modifie la clé concernée.
hermes config edit # messaging.telegram.allowed_chats: "<TELEGRAM_USER_ID>"
Vérifier la modification.
sed -n '374,385p' ~/.hermes/config.yaml
Redémarrer le gateway.
hermes gateway start hermes gateway status
Au premier /start côté Telegram, le bot affiche un code de pairing. L'approuver ici (remplace ABC12345 par le code reçu).
hermes pairing approve telegram ABC12345
~/.hermes/.env (chmod 600).
Ajouter le token GitHub (rate limit du Skills Hub : 60→5000 req/h). Remplace par ta vraie clé, copiée depuis github.com/settings/tokens.
echo 'GITHUB_TOKEN=GITHUB_TOKEN_EXEMPLE' >> ~/.hermes/.env chmod 600 ~/.hermes/.env grep '^GITHUB' ~/.hermes/.env
Ajouter la clé Groq (transcription Whisper pour le voice mode). Remplace par ta clé, depuis console.groq.com/keys.
echo 'GROQ_API_KEY=GROQ_KEY_EXEMPLE' >> ~/.hermes/.env grep '^GROQ_API_KEY' ~/.hermes/.env
Ajouter la section stt sous la config voice: pour utiliser Groq comme provider de transcription.
nano ~/.hermes/config.yaml
Bloc YAML à insérer juste après les directives voice: existantes (attention aux indentations 2/4/6 espaces, jamais de tabulation).
silence_duration: 3.0
stt:
provider: groq
groq:
model: whisper-large-v3-turboVérifier le résultat.
grep -A 10 "^voice:" ~/.hermes/config.yaml
Redémarrer le gateway pour qu'il prenne la nouvelle config voice.
hermes gateway restart
Vérifier la liste finale des clés présentes (valeurs masquées).
grep -v '^#' ~/.hermes/.env | grep -v '^$' | sed 's/=.*/=***/'
Créer un zip complet de ~/.hermes/. Génère ~/hermes-backup-YYYY-MM-DD-HHMMSS.zip.
hermes backup
Depuis Git Bash local (pas le VPS), rapatrier le zip dans ~/Downloads/. Adapte le nom de fichier à celui généré.
scp vps:~/hermes-backup-YYYY-MM-DD-HHMMSS.zip ~/Downloads/
En cas de besoin de restauration, sur un VPS neuf après install.sh.
hermes import hermes-backup-YYYY-MM-DD-HHMMSS.zip
~/.hermes/config.yaml dans $EDITOR.hermes config set image_gen.model fal-ai/flux-2-pro).hermes pairing approve telegram ABC12345).~/.hermes/ (config, env, skills, cron, sessions, mémoires).hermes-agent/ + reinstall venv)./model anthropic/claude-haiku-4.5).| Provider | Tier | Variable .env | Usage Hermes |
|---|---|---|---|
| OpenRouter INFÉRENCE |
Pay-per-use (credit limit fixé) | OPENROUTER_API_KEY | Routage vers DeepSeek V4 Pro (chat) et Gemini 2.5 Flash Lite (cron). Permet de switcher de modèle sans changer de fournisseur. |
| Telegram MESSAGING |
Gratuit (BotFather) | TELEGRAM_BOT_TOKEN | Bot Telegram pour le chat 24/7 depuis le phone. Allowlist stricte côté config.yaml. |
| Tavily WEB |
Free 1000 searches/mois | TAVILY_API_KEY | Search + extract en un seul provider. Lit le contenu complet des pages, pas juste les snippets. |
| FAL IMAGE |
Pay-per-use (10$ chargés) | FAL_KEY | Génération d'images via flux-2-pro par défaut (~0.03$/MP, qualité photoréaliste). Switchable à la demande vers nano-banana-pro pour la qualité max. |
| Groq VOICE |
Free tier large | GROQ_API_KEY | Whisper Large v3 Turbo pour la transcription des vocaux Telegram. Latence faible, qualité top sur le français. |
| GitHub SKILLS |
Gratuit (PAT fine-grained) | GITHUB_TOKEN | Passe l'API GitHub de 60 req/h (anonyme) à 5000 req/h. Évite les blocages quand Hermes pulle des skills depuis le hub agentskills.io. |
Tools sans clé. Le browser tool tourne sur Playwright/Chromium local (gratuit, isolé). Edge TTS (Microsoft) ne demande pas de clé. La code execution s'appuie sur le Python du venv. La memory est en SQLite local. Le cron scheduler est natif. Au total, sur les 18 tools activés, seulement 6 demandent une clé externe.
Trois cron jobs distincts, un par moment de la journée, avec des sources différentes pour éviter la redondance. Chacun tourne en session isolée (pas de mémoire entre runs), sur Gemini Flash Lite, et délivre directement dans la conversation Telegram. Si rien à dire, l'agent répond [SILENT] et rien n'est envoyé.
Rappel calendrier. Les schedules sont en UTC. Au passage à l'heure d'hiver fin octobre 2026, il faudra décaler tous les schedules d'une heure (ex : 0 5 → 0 6). L'agent l'a noté dans le skill create-news-cron et le rappellera spontanément. Sinon, une simple phrase au bot suffit : « passe les 3 crons à l'heure d'hiver ».
Pour ajouter un nouveau cron, plus besoin de connaître la syntaxe : tu parles au bot en français.
Toi → bot:
« Ajoute un cron tous les vendredis à 18h pour me résumer
l'actu agentic AI de la semaine via Tavily. »
Bot → action:
[charge le skill create-news-cron]
[crée le cron via cronjob(action='create', ...)]
[confirme avec ID + horaire UTC + récap]ufw allow OpenSSH est plus lisible qu'une règle iptables raw.apt update manuellement pour les CVE critiques.linger activé permet au service de tourner même quand le user n'est pas connecté en SSH.cp à travers SSH. Combiné avec un alias dans ~/.ssh/config, permet de bouger des fichiers VPS↔Windows en une commande./start d'un utilisateur inconnu. Doit être approuvé côté serveur (hermes pairing approve) pour que le user soit ajouté à l'allowlist en runtime.SKILL.md qui décrit une procédure réutilisable (template, paramètres, pièges). Hermes les charge automatiquement quand le contexte le justifie.[SILENT] et rien d'autre, le gateway supprime la livraison. Permet aux crons d'être silencieux quand il n'y a rien à dire.Model Context Protocol = standard pour exposer des données externes à l'agent. Hermes est compatible client + serveur. Candidats utiles : Gmail (lire les mails Outlook pro), Google Calendar, Notion, Linear, GitHub. Chacun = un OAuth + une ligne dans config.yaml.
Le SOUL.md par défaut est neutre. Le réécrire pour : ton dense français sans blabla, pousse-back sur les imprécisions, références architectes/SI bancaire, refus du mirroring excessif, format laconique préféré. Faisable en 10 minutes.
Fichier markdown projet-par-projet, lu quand l'agent navigue dans un dossier. Idéal pour : un AGENTS.md dans ton repo site-exemple.fr (rappel des conventions CSP, structure assets), un autre pour les notes de recherche World Models JEPA.
Hermes est censé améliorer ses skills à l'usage. Concrètement, après chaque session complexe, il peut écrire une post-mortem dans le SKILL.md correspondant. À surveiller sur les premiers cas réels (debug, écriture, recherche papers).
Pour l'instant, backup manuel + scp ponctuel. Faisable mieux : un cron weekly qui zip ~/.hermes/ et le push vers un bucket S3 chiffré (ou rclone vers Drive). Le skill backup-rotate ferait ça en quelques lignes.
Cron quotidien (mode no_agent) qui curl openrouter.ai/api/v1/credits, et alerte Telegram si la conso du jour dépasse un seuil (genre 0.50$/jour). Garde-fou anti-runaway en plus du credit limit côté provider.
État final. Hermes Agent est opérationnel, durci et supervisable pour un usage personnel. Les conversations Telegram texte + vocal sont disponibles 24/7, les automatisations sont planifiées, et un plan de restauration existe via hermes import hermes-backup-YYYY-MM-DD-HHMMSS.zip.