Renommer un profile Hermes laissait un fantôme

Vous avez renommé le profile de votre bot Telegram de mybot à assistant. hermes profile rename mybot assistant s’est terminé sans broncher, le répertoire est passé de ~/.hermes/profiles/mybot à ~/.hermes/profiles/assistant, l’alias shell a suivi, et le bot a continué de répondre dans le groupe. Quelques jours plus tard, vous rouvrez une ancienne conversation et elle est toujours rangée sous mybot, un profile qui n’existe plus, tandis que la table de routing du gateway conserve des clés du type agent:mybot:*. Autre visage du même bug : vous supprimez un profile, et son répertoire réapparaît quelques jours après sur le disque, avec un fichier de cache dedans. Le PR #112653 corrige les deux (#111926 et #112592).
Trois mots d’abord. Un profile est un agent Hermes autonome — sa propre config, ses propres identifiants, sa mémoire, ses sessions — qui vit sous ~/.hermes/profiles/<nom>. Une routing key est la chaîne qui permet au gateway de décider à quel profile et à quelle session appartient un message entrant, de la forme agent:<profil>:<session>. Un gateway multiplexé est un seul processus gateway qui sert plusieurs profiles à la fois et garde l’index de routing en mémoire.
L’ancien rename déplaçait le dossier, pas l’identité
L’ancien code faisait la moitié du travail, celle du système de fichiers : renommer le dossier du profile, renommer l’alias shell. La moitié base de données n’était jamais touchée. Et ça fait mal, parce que beaucoup de lignes du state store de Hermes ont le nom du profile lui-même comme clé. Après un rename, ces lignes disaient encore mybot, alors que mybot n’existait plus sur le disque — elles pointaient vers un fantôme.
Rien n’explose tout de suite, ça suinte. Les anciennes sessions ne s’alignent plus, une ligne de heartbeat continue de déclarer un profile disparu, les delivery obligations (les enregistrements qui disent « ce message n’est pas encore livré et doit être retenté ») restent sous le nom mort, et la propriété des topics Telegram ne correspond plus. Ceux qui font tourner plusieurs profiles derrière un même gateway le voient en premier ; les limites de ce montage sont détaillées dans notre guide des profiles multi-instance.
Pourquoi les tables d’identité sont indexées par nom de profile
Pour la base de données, le nom du profile est l’identité. Changer le nom, c’est changer l’identité de l’agent dans le state store, donc la migration n’est pas un déplacement de fichiers mais un rename suivi d’une réécriture de chaque ligne indexée par ce nom. Le PR #112653 liste précisément les lignes concernées :
sessions.session_key,sessions.profile_name,sessions.origin_jsongateway_heartbeats.profiledelivery_obligations- les tables de topics Telegram
- l’index
gateway_routing(la clé et le JSON qu’elle embarque)
Il existe une seconde frontière utile à connaître — ce qui suit le profile et ce qui suit la session — et le guide des frontières de session la parcourt.
Ce qui migre désormais, une transaction plus un index en mémoire
Le côté base de données, c’est SessionDB.rekey_profile_state(old, new) dans hermes_state_gateway.py : une seule transaction qui réécrit au nouveau nom toutes les lignes indexées par nom de profile. Elle vérifie d’abord les collisions — si le nouveau nom porte déjà des lignes qui entreraient en conflit, elle refuse plutôt que d’écrire un fouillis. Elle est idempotente et saute les tables qui n’ont pas encore été créées.
Mais une réécriture limitée à la base ne suffit pas. Un multiplexer en cours d’exécution vide périodiquement ses _entries en mémoire vers la base, ce qui écraserait la réécriture. C’est le rôle de SessionStore.rekey_profile_routing(old, new) dans gateway/session.py : il rejoue les clés de l’index de routing en mémoire — les entrées et origin.profile — puis persiste le résultat. C’est pourquoi un gateway vivant pilote la migration — l’index vit dans son processus, il a donc le dernier mot.
La chaîne se déroule ainsi. rename_profile appelle _migrate_profile_identity dans hermes_cli/profile_identity.py. Si un multiplexer est actif, elle délègue au verbe migrate-profile-identity du control socket ; si aucun gateway ne tient le store, le CLI fait lui-même la réécriture durable. Dans les deux cas la migration n’est jamais fatale au rename — au pire vous obtenez un avertissement, un profile renommé, et des lignes d’identité restées sous l’ancien nom.
Renommer un profile sans casse, étape par étape
hermes profile list # voir ce que vous vous apprêtez à renommer
hermes profile rename mybot assistant
hermes profile show assistant # vérifier le nouveau nom et ses infos d'affichage
hermes profile list # la liste doit montrer assistant, pas mybot
Lisez la sortie du rename. Si elle avertit que le gateway vivant n’a pas pu migrer l’identité de session, ne laissez pas passer — corrigez avec la commande de la section suivante. Si vous ignorez si votre gateway est un multiplexer, suivez simplement ce que la commande imprime : sans avertissement, la migration est passée ; avec un avertissement, redémarrez le gateway et réessayez. Tous les flags de ces commandes figurent dans la référence de la commande hermes profile, et la page d’installation explique comment installer Hermes sur la machine.
Si la migration n’a pas pu tourner, reprenez avec migrate-identity
Le rename ne peut pas être rejoué — profiles/mybot a disparu. La reprise a donc sa propre commande :
hermes profile migrate-identity <old-name> <new-name>
Une vraie session ressemble à ceci :
hermes profile rename mybot assistant
# ⚠ Profile was renamed, but the live gateway could not migrate session identity (…).
# Restart the gateway, then run:
# hermes profile migrate-identity mybot assistant
hermes profile migrate-identity mybot assistant
# ✓ Session/routing identity migrated: mybot → assistant
Le premier bloc vous dit que le profile a bien été renommé, mais que le gateway vivant n’a pas pu migrer l’identité de session, et vous donne deux issues : redémarrer le gateway (il recharge l’index de routing depuis la base au démarrage, la migration est donc appliquée) ou l’arrêter — sans processus qui tient le store, la commande fait elle-même la réécriture durable.
Quelques propriétés à connaître :
- La migration est guidée par les lignes qui portent encore l’ancien nom, donc
profiles/mybotn’a pas besoin d’exister ; seul le nouveau nom est vérifié. - Elle est idempotente : relancer une migration terminée réussit, sans rien à rejouer.
- Elle sort avec un code non nul quand un gateway vivant refuse la migration, ou quand une base refuse la réécriture — une collision de routing, un lock, ou l’une des deux bases qui échoue pendant que l’autre réussit. Le message d’erreur nomme la base et le problème.
Renommer le profile default ne change qu’un nom d’affichage
Vous voulez peut-être un plus joli nom pour le profile default. Voici le piège : l’ID interne du profile default est toujours default, parce que ~/.hermes est la racine d’installation — il ne peut pas être vraiment renommé. Ce que la commande définit à la place, c’est un nom d’affichage (display name) :
hermes profile rename default Harumesu # Unicode accepté : 小助手
Ce nom apparaît dans hermes profile list et show, dans la commande de chat /profile, dans le dashboard et dans l’app de bureau, y compris le roster du Bot Mode. Il reste purement présentationnel : -p default, les noms de service, les tâches cron et toutes les autres références continuent d’utiliser l’ID canonique default. Il est stocké comme display_name dans ~/.hermes/profile.yaml — supprimez cette ligne pour revenir en arrière. Les profiles nommés peuvent aussi porter un display_name (il survit à un vrai rename), mais pour eux rename renomme le profile lui-même.
Un profile supprimé laisse une pierre tombale, rien ne doit le ressusciter
Supprimer ressemble à ceci :
hermes profile delete coder
La commande arrête le gateway, retire le service systemd/launchd, retire l’alias de commande et supprime toutes les données du profile. Elle vous demande de taper le nom du profile pour confirmer ; --yes saute cette étape. Le profile default ne peut pas être supprimé (c’est ~/.hermes lui-même) — pour tout retirer, utilisez hermes uninstall.
Ce qui partait de travers ensuite : un thread portant encore le chemin du home mort pouvait effectuer une écriture atomique tardive, et le mkdir(parents=True) qui la précède reconstruisait l’arborescence. Votre profile coder supprimé réapparaissait sur le disque, avec pour tout contenu un fichier de cache.
#112592 ferme ce chemin. utils._atomic_write ainsi que atomic_roundtrip_yaml_update / atomic_roundtrip_yaml_save créent désormais leurs dossiers parents via mkdir_under_hermes_home, le garde-fou des pierres tombales : toute écriture atomique JSON, texte, bytes ou YAML refuse avec FileNotFoundError un home nommé porteur d’une pierre tombale, et ne laisse rien derrière elle. Les mkdir(parents=True) disséminés en pré-écriture chez les appelants passent par le même garde-fou : le cache des modèles, l’ETag models.dev, le lifecycle sentinel, le cache de secrets, l’OAuth MCP, le memory store, plus le balayage mécanique des autres points de pré-écriture ; deux sites qui ont besoin d’un mode= explicite gardent leur mkdir derrière assert_named_profile_home_live.
Le garde-fou n’est pas systématique. Si le chemin cible n’a pas d’ancêtre profiles/<nom> démontrable, il ne fait rien : le home par défaut, les dossiers cachés .staging et les chemins qui contiennent profiles par hasard continuent d’écrire normalement.
Points de vigilance et résumé
- Lisez la sortie du rename. Une migration ratée ne fait jamais échouer le rename, elle avertit seulement — redémarrez le gateway, puis lancez
hermes profile migrate-identity <ancien> <nouveau>. - La reprise est idempotente, donc la lancer en cas de doute ne coûte rien.
- Un multiplexer vivant a la priorité, parce qu’il détient l’index de routing en mémoire ; si vous préférez arrêter le gateway, la commande fera la réécriture durable elle-même.
- Le profile default n’est jamais vraiment renommé — vous ne définissez qu’un nom d’affichage, l’ID interne reste
default. - Après une suppression, laissez-le supprimé. Le garde-fou des pierres tombales empêche une écriture tardive de retransformer un profile nommé supprimé en dossier sur le disque.