Retour au blog
HermesTailscaleTroubleshootingSelf-Hosting

Hermes Agent avec Tailscale ne se connecte pas : solutions

Hermes Desktop n'atteint pas votre gateway distant via Tailscale ? Quatre causes couvrent presque tous les cas, du bind sur localhost au regex CORS.

Par Hermify Team||9 min de lecture
Scène sombre avec le wordmark Tailscale au-dessus d'un ordinateur portable tentant d'atteindre un gateway Hermes distant à travers un maillage, avec le texte en gras 'Tailscale Not Connecting'

Le Tailnet Est Actif et Hermes Ne Répond Toujours Pas

Vous avez installé Tailscale sur le VPS, rejoint le tailnet depuis votre ordinateur portable et confirmé que les deux côtés se pinguent sur leurs adresses 100.x.x.x. hermes serve tourne sur l'hôte avec le port ouvert, et l'application Hermes Desktop sur le portable reste bloquée sur "Could not connect to Hermes gateway." Rien dans le log du gateway ne semble en colère. Rien dans Tailscale n'est en rouge.

Cet échec silencieux vient presque toujours de l'une de quatre causes, et trois d'entre elles échouent en silence par conception. Ce post parcourt chacune, comment confirmer laquelle est la vôtre, et le correctif exact. Commencez par le haut : la première cause attrape la plupart des configurations distantes fraîchement montées, et chaque cause suivante suppose que les précédentes sont écartées.

Cause 1 : hermes serve Est Lié à 127.0.0.1

hermes serve se lie à 127.0.0.1 par défaut. C'est le bon défaut pour une configuration ordinateur-portable-uniquement et le mauvais défaut pour tout ce que vous voulez atteindre via le tailnet. Un processus lié au loopback ne répond qu'aux requêtes qui viennent de la même machine, et un peer Tailscale n'est pas la même machine. Le port est ouvert, le firewall est correct, le tunnel est actif, et le socket refuse la connexion.

Symptôme : depuis le portable, curl -v http://<hermes-vps>:8642/api/health renvoie Connection refused ou reste bloqué jusqu'au timeout. Depuis une session SSH sur le VPS, le même curl http://127.0.0.1:8642/api/health répond instantanément. Si le loopback répond et pas le tailnet, c'est votre cause.

Le correctif est de lier hermes serve à l'IP Tailscale de l'hôte explicitement :

TAILSCALE_IP=$(tailscale ip -4)
hermes serve --host "$TAILSCALE_IP" --port 8642

Lier à l'interface du tailnet plutôt qu'à 0.0.0.0 est la forme que vous voulez. 0.0.0.0 fonctionne aussi et c'est ce que beaucoup de guides suggèrent, mais cela expose le socket sur chaque interface qu'a la machine, y compris toute interface accidentellement publique, et remet toute l'histoire d'authentification sur la couche applicative. Lier à l'IP Tailscale, c'est de la défense en profondeur : le socket n'est joignable que depuis l'intérieur du tailnet.

Rendez le changement permanent en mettant le même flag dans l'unit systemd ou dans le command du docker-compose.yml. Si vous tournez sous Docker, publiez le port directement sur l'IP Tailscale avec -p ${TAILSCALE_IP}:8642:8642 plutôt que le -p 8642:8642 par défaut (qui publie sur toutes les interfaces de l'hôte).

Pour l'installation initiale complète avec Tailscale, le guide d'accès distant sécurisé Hermes Agent + Tailscale parcourt la recette de bout en bout.

Cause 2 : Le Regex CORS du Dashboard Rejette Votre Origine Tailscale

Vous liez le gateway à l'IP Tailscale, l'API répond sur /api/health, et le dashboard web charge son HTML depuis http://<hermes-vps>:8642/. Ensuite chaque appel d'API que fait le dashboard échoue avec une erreur CORS dans la console du navigateur : has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

Ce qui se passe : d'anciens builds Hermes livraient un allow_origin_regex en dur dans le dashboard qui ne matchait que ^https?://(localhost|127\.0\.0\.1)(:\d+)?$. Le regex était sûr sur un portable et silencieusement inutile partout ailleurs. Un hostname Tailscale comme http://hermes-vps:8642 ou une IP comme http://100.64.1.5:8642 ne matche jamais, donc le preflight échoue et le navigateur abandonne le fetch. La feature request qui suit le correctif a l'historique complet.

Le correctif est une variable d'environnement :

export HERMES_DASHBOARD_CORS_ORIGINS="http://hermes-vps:8642,http://100.64.1.5:8642"
hermes serve --host "$TAILSCALE_IP" --port 8642

Listez chaque origine depuis laquelle vous chargez réellement le dashboard : le nom MagicDNS, l'IP Tailscale brute, et tout alias Funnel ou serve que vous auriez ajouté. Les jokers sont supportés (http://*.tail1a2b3.ts.net:8642) si vous préférez matcher tout le nom du tailnet plutôt que lister chaque appareil.

Deux boutons connexes qui font trébucher :

  • HERMES_DASHBOARD_HOST écrase l'adresse que le dashboard annonce au navigateur. Si vous l'avez laissée sur localhost, le dashboard rend des liens qui pointent vers http://localhost:8642/api/... et le navigateur essaie de joindre son propre loopback plutôt que le tailnet. Réglez-la sur votre hostname ou IP Tailscale.
  • L'app Hermes Desktop porte aussi une Origin. Si vous utilisez le desktop packagé plutôt que le dashboard du navigateur, son renderer envoie Origin: null (Electron charge via file://). Les anciens builds l'acceptaient uniquement quand le serveur était lié au loopback, ce qui est l'exclusivité mutuelle décrite dans l'issue #38412. Les builds récents acceptent null quand il est présent dans HERMES_DASHBOARD_CORS_ORIGINS à côté de vos vraies origines : ajoutez la chaîne littérale null à la liste pour autoriser le client desktop.

Redémarrez hermes serve après chaque changement de ces env vars. Les valeurs sont lues au démarrage, pas par requête.

Cause 3 : Le Tunnel Tailscale Retombe sur DERP ou Ne Monte Pas

Si le dashboard finit par charger mais que chaque message met plusieurs secondes à partir et que les notes vocales hachent, le tunnel est monté mais lent. Tailscale relaye chaque paquet par un serveur DERP jusqu'à votre VPS, et le round-trip est dominé par ce saut supplémentaire plutôt que par le modèle. Si rien ne passe du tout, le tunnel n'est probablement jamais monté.

Confirmez lequel des deux c'est avec tailscale status. Un peer en bonne santé affiche direct <ip>:<port> sur sa ligne. Un peer relayé par DERP affiche relay "<region>". Si le peer est absent ou marqué offline, le tunnel ne s'est jamais établi.

Le correctif change selon le cas :

  • Bloqué sur DERP. Ouvrez UDP 41641 en sortie à la fois sur le firewall de l'hôte VPS et sur le firewall du réseau client. C'est le port que Tailscale utilise pour les tunnels WireGuard directs ; si l'un des côtés bloque la sortie UDP, les deux peers retombent sur DERP même si la paire est authentifiée. Confirmez avec sudo ufw allow 41641/udp sur le VPS et en repinguant le peer après tailscale down && tailscale up. Les réseaux d'entreprise et le Wi-Fi d'hôtel sont les suspects habituels du blocage de la sortie UDP. Si une connexion directe reste impossible, DERP suffit pour le texte mais vous le sentez sur la voix.
  • Peer marqué offline ou tunnel jamais monté. La clé du nœud a expiré. Tailscale renouvelle les clés tous les 180 jours par défaut, et un appareil qui est resté hors ligne pendant la fenêtre de renouvellement revient "offline" dans la console admin jusqu'à ce que vous réauthentifiiez. Corrigez avec tailscale up --force-reauth du côté concerné et reconnectez-vous via le navigateur. Pour éviter le renouvellement complètement sur les installations VPS côté serveur, taguez le nœud (tailscale up --advertise-tags=tag:server) et désactivez l'expiration de clé pour ce tag dans la console admin Tailscale : les nœuds tagués sautent la vérification des 180 jours par défaut.
  • Le mode économiseur de batterie a tué le client sur le portable. macOS et Windows laissent le système d'exploitation mettre en pause les services en arrière-plan dans les modes agressifs, et l'app de barre des tâches Tailscale peut se déconnecter toute seule en silence. Si le tailnet s'est éteint juste après avoir débranché, regardez l'icône dans la barre avant de diagnostiquer autre chose.

Cause 4 : Vous Visez une URL Localhost depuis un Client Distant

Le dernier cas silencieux est celui où chaque couche fonctionne et où le client pose la mauvaise question. Si vous avez configuré la Remote Gateway URL de Hermes Desktop en http://localhost:8642 ou http://127.0.0.1:8642, l'app essaie d'atteindre sa propre interface de loopback au lieu de traverser le tailnet, et aucun correctif côté serveur ne changera rien.

Symptôme : sur le portable, l'app desktop affiche "Could not connect." Depuis ce même portable, curl http://<hermes-vps>:8642/api/health répond en bonne santé.

Le correctif est un seul paramètre. Dans Hermes Desktop, ouvrez Settings puis Connection et réglez la Remote Gateway URL sur l'un de :

  • http://<magic-dns-name>:8642 - préféré, survit aux changements d'IP Tailscale.
  • http://<tailscale-ip>:8642 - l'adresse brute 100.x.x.x. Assez stable pour une configuration fixe.

Le nom MagicDNS est ce que tailscale status affiche dans la première colonne pour la ligne du VPS. Si vous n'avez jamais activé MagicDNS, faites-le dans la console admin sous DNS : c'est un seul toggle et cela vous épargne chaque session de debug pour un changement d'IP pendant toute la vie du tailnet.

Pendant que vous êtes dans Settings, vérifiez le champ des identifiants. Si le gateway est derrière un token (HERMES_AUTH_TOKEN), le client a besoin du même token, et un token périmé produit un 4403 sur le WebSocket qui ressemble beaucoup à un échec de connexion. L'issue du WebSocket 4403 donne plus de détail sur ce mode d'échec spécifique.

Ordre de Diagnostic qui Économise du Temps

Quand le tailnet est monté et que Hermes ne répond pas, parcourez les causes dans cet ordre plutôt que de reconstruire votre configuration Tailscale :

  1. hermes serve est-il lié au loopback ? curl http://<tailscale-ip>:8642/api/health depuis le client répond en une seconde. Le meilleur taux de touche sur les configurations distantes fraîchement montées.
  2. Le regex CORS du dashboard rejette-t-il votre origine ? Ouvrez les devtools du navigateur sur le dashboard et cherchez une entrée CORS en rouge dans l'onglet réseau. Si elle est présente, réglez HERMES_DASHBOARD_CORS_ORIGINS et redémarrez.
  3. Le tunnel est-il direct ou relayé ? tailscale status affiche direct ou relay par peer. Offline signifie que la clé du nœud a expiré et qu'il faut --force-reauth.
  4. Le client demande-t-il localhost ? Ouvrez les paramètres de connexion de l'app desktop et confirmez que la Remote Gateway URL pointe sur le hostname du tailnet, pas sur localhost.

Pour la recette Docker sous-jacente sur le VPS, voyez le guide Docker Hermes Agent. Si vous préférez sauter complètement le maillage, self-hosting vs Hermes Agent géré couvre les compromis.

Quand Vous Préférez Ne Pas Opérer un Maillage

Tailscale est la bonne forme pour un Hermes auto-hébergé quand vous voulez garder la boîte sur votre propre VPS et l'atteindre depuis n'importe où. C'est aussi un système de plus à garder en vie : une fenêtre de renouvellement de clés, une env var CORS, une règle de firewall pour UDP 41641, et un paramètre client qui doit correspondre au nom du tailnet du jour. Si votre lecture est qu'un assistant IA personnel ne devrait pas exiger un VPN maillé et une session de debug dans la console du navigateur pour vous dire bonjour, commencez avec Hermify. Hermify exécute un Hermes Agent géré sur Telegram avec la même mémoire et les mêmes skills, en ligne en une minute environ, sans port à ouvrir ni tailnet à opérer.

Sources

Lancez votre propre agent Hermes

Apportez votre clé API, connectez Telegram et obtenez un agent IA auto-améliorant opérationnel en 60 secondes.

Commencer