Votre espace de travail Hermes dans un onglet de navigateur — sans installation, sans relais


Vous êtes sur un portable emprunté, une machine professionnelle verrouillée, ou une machine de dev atteinte en SSH, et ce que vous voulez vraiment, c’est votre espace de travail Hermes — liste des sessions, terminal, arborescence de fichiers, volet d’aperçu — pas une boîte de chat. Installer l’application Desktop n’est ni autorisé ni possible, et reconstruire votre contexte à la main dans un CLI nu revient à taper avec des gants. L’ancien repli était le dashboard, un animal différent : un processus séparé avec sa propre vue de ce qui tourne, qui ressemble par accident à la même chose et ne sait rien de l’onglet que vous avez laissé ouvert chez vous.

Le changement webapp emprunte une route plus simple. Le gateway sert lui-même l’espace de travail Desktop (#141864, fusionnée le 25 octobre 2026), en prolongeant la direction de #93508 — sans serveur Webapp séparé et sans relais au milieu.

Ce qu’est réellement le webapp

Version courte : l’espace de travail Desktop, hébergé par le processus que vous faites déjà tourner.

Le gateway est le processus d’arrière-plan de longue durée auquel se connectent le CLI, l’application desktop et chaque plateforme de messagerie. Les sessions du webapp tournent sur ce gateway, à l’intérieur du profile que vous avez désigné — littéralement le même processus qui répond à votre terminal, à votre adaptateur Telegram et à votre fenêtre desktop. Même conversation, même file d’attente. Quelle que soit la surface que vous regardez, il n’existe qu’une seule copie de l’état.

Cette propriété unique explique la majeure partie du comportement qui suit. Un onglet n’est pas le miroir d’une session desktop, c’est un client de plus du même backend. Lancez un tour dans le navigateur, basculez sur le terminal : le travail est là. Rien à réconcilier, puisque rien n’est dupliqué — et rien de plus à faire tourner : pas de serveur Webapp, pas de second port, pas de saut de relais qu’il faudrait maintenir en vie avant tout le reste.

L’activer

L’interrupteur se trouve dans le config.yaml du profile sous lequel tourne le gateway, et il est désactivé par défaut :

webapp:
  enabled: false     # true: the gateway serves the Webapp
  host: 127.0.0.1    # 127.0.0.1 / ::1 / localhost; 0.0.0.0 or :: exposes it to other machines (sign-in required)
  port: 9120         # fixed port, so the browser URL survives a gateway restart
  public_url: ""     # the http(s) address your reverse proxy or tunnel serves (enables sign-in)

enabled est lu au démarrage du gateway : basculez-le, puis redémarrez avec hermes gateway restart. Vient ensuite la commande dans laquelle vous allez vivre :

hermes webapp            # open the Desktop workspace in a browser
hermes webapp --status   # current state, and the reason if it declined to start
hermes webapp --no-open  # print the link, don't launch a browser
hermes webapp -p work    # target a specific profile

hermes webapp fait bien plus qu’« ouvrir une URL » : il reconstruit le bundle frontend quand celui du disque est périmé, démarre le gateway s’il ne tourne pas, et ne vous remet qu’ensuite un lien de connexion à usage unique.

host est plus strict qu’il n’y paraît, et c’est là que la plupart des gens perdent dix minutes. Seuls 127.0.0.1, ::1 et localhost signifient « cette machine uniquement ». 0.0.0.0 et :: signifient que d’autres machines peuvent l’atteindre — ce qui exige un provider de connexion dashboard ; sans lui, le gateway continue de ne servir que les clients natifs, et hermes webapp --status vous le dit. Toute autre valeur, y compris quelque chose d’apparemment inoffensif comme 127.0.0.2, laisse le Webapp désactivé.

port est fixe à dessein, pour que l’URL de votre navigateur survive aux redémarrages et que vous puissiez garder l’onglet ouvert des jours. Pour un profile autonome sans port défini, le panneau Settings → Web Access du Desktop (PR #142220) en choisit un libre — au-dessus de 9120, non utilisé par un autre profile — et l’écrit dans port. Il n’écrase jamais un port que vous avez fixé à la main. Un seul gateway par hôte est l’endroit où déterminer quel profile doit le servir.

public_url sert quand vous avez déjà un reverse proxy ou un tunnel. Le renseigner active la connexion dashboard même avec un host en loopback, et il devient le seul nom d’hôte externe auquel le Webapp répond — le callback de connexion est construit à partir de lui, pas à partir de la requête. Il doit être en http:// ou https://, sans identifiants, sans query string ni fragment. Un piège qui mérite d’être nommé : dashboard.public_url ne régit que hermes dashboard et ne s’applique jamais au Webapp.

Les trois façons dont il refuse de s’ouvrir

Ce que vous constatez Ce qui s’est passé Ce qu’il faut faire
Le navigateur n’obtient rien, --status refuse host a une valeur que le Webapp n’accepte pas, par exemple 127.0.0.2 Utiliser 127.0.0.1, ::1, localhost, ou un wildcard avec un provider de connexion dashboard
CLI et desktop OK, navigateur noir Port occupé, contrôle de connexion refusé, ou fichier d’identifiant navigateur illisible ou non inscriptible Libérer le port, puis relancer hermes webapp
L’onglet marchait hier, mort ce matin Le gateway a redémarré alors que le port était occupé : l’identifiant navigateur a donc été renouvelé Relancer hermes webapp pour l’onglet déjà ouvert

La ligne du milieu surprend, parce que le gateway ne tombe pas. Quand l’écouteur Webapp ne peut pas démarrer, il conserve l’écouteur loopback privé d’origine et continue de servir les clients natifs — CLI, TUI, desktop intacts — tout en remplaçant l’identifiant navigateur. Ce remplacement est délibéré : si le port était occupé, un autre programme l’occupe peut-être à présent, et remettre un ancien identifiant à qui écoute serait pire qu’un onglet mort. Une fois le port libéré, hermes webapp rouvre un onglet que vous avez déjà. La troisième ligne relève du même mécanisme vu de l’autre côté : un redémarrage seul ne justifie pas de recharger le navigateur, mais un redémarrage survenu pendant que le port était pris, si.

Ce que le HTTP simple vous retire sans bruit

Tout ce qui précède peut fonctionner en http:// et vous laisser malgré tout un espace de travail incomplet — et les messages d’erreur étaient, jusqu’à récemment, inutilisables.

Les navigateurs n’exposent crypto.subtle qu’en contexte sécurisé. Sur une adresse HTTP simple, l’écran de bot (bot screen / noVNC) échouait avec Cannot read properties of undefined (reading 'digest'), ce qui ne vous apprend rien. Il dit maintenant ce qui ne va pas : il vous faut du HTTPS, ou un tunnel SSH vers 127.0.0.1. Idem pour la voix en direct — pas de navigator.mediaDevices en contexte non sécurisé, getUserMedia est indéfini, et le message est désormais un simple « microphone non pris en charge » au lieu d’une pile d’appels interne au client (#144722).

Le lanceur et la doc listent maintenant ce que le HTTP simple vous coûte : lecture du presse-papiers, wake word, écran de bot, voix en direct. Si la barre d’adresse n’est ni 127.0.0.1 ni du HTTPS, attendez-vous à ces quatre pertes — placez un reverse proxy TLS devant et définissez public_url, ou tunnelisez le port.

Les fichiers appartiennent au sandbox de la session, pas à votre portable

Un onglet de navigateur donne à l’arborescence de fichiers une apparence locale, et c’est précisément le piège que #144741 a refermé en correctif de sécurité : les requêtes de fichiers et de médias se résolvent désormais contre le sandbox propre à la session. Une session dans un sandbox Docker sert les fichiers depuis l’intérieur du conteneur ; une session dans un environnement SSH les sert depuis l’espace de travail distant. Résoudre l’un ou l’autre contre le répertoire de travail de l’hôte n’est pas cosmétique : cela revient à distribuer une arborescence qui appartient à une autre machine, avec un autre modèle de permissions. Pour les sessions conteneurisées, la frontière décrite dans le retrait de setuid dans les sandbox Docker est la même que l’arborescence respecte maintenant.

Le reste du lot se ressent plus qu’il ne se lit : les gros aperçus HTML s’ouvrent correctement, les profiles SSH peuvent préparer du texte d’aperçu, et l’export de profile côté navigateur est documenté (#144718) ; le terminal du navigateur se repeint et exit fonctionne (#144719) ; un onglet se ferme proprement quand la file d’entrée est pleine (#144665) ; les téléchargements d’aperçu en groupe sont plafonnés (#144557). Le volet d’aperçu a aussi gagné un vrai contrôle du navigateur, traité dans piloter le navigateur depuis le volet d’aperçu.

Un détail de routage qui fait trébucher le travail d’intégration : les routes propres au Webapp — uploads préparés, tickets de fichiers — n’existent que sur l’écouteur Webapp. Le dashboard autonome et hermes serve renvoient 404.

Qui peut obtenir le lien à usage unique

Le lien qu’imprime hermes webapp est valable 60 secondes et utilisable une seule fois. Le navigateur l’échange contre un état de connexion conservé dans le sessionStorage de cet onglet, ce qui explique que rien de ressemblant à un identifiant n’atterrisse dans votre historique ni dans une capture d’écran.

Seul votre propre utilisateur système devrait pouvoir obtenir ce lien, et le gateway répond via un socket de contrôle privé plutôt que par le réseau. Ce que cela signifie en pratique dépend de la plateforme :

  • Linux — le gateway vérifie que la connexion qui demande le lien appartient à l’utilisateur courant.
  • Windows — aucun contrôle. Le lien n’apparaît que dans la ligne de commande du lanceur de navigateur, lisible par le même utilisateur, par SYSTEM et par les administrateurs élevés.
  • macOS, BSD, illumos, WSL — aucun contrôle non plus. La commande écrit à la place le lien dans une page webapp/launch-*.html sous le répertoire du gateway, répertoire en 0700 et fichier en 0600, portant un <meta refresh>, une CSP default-src 'none' et no-referrer. L’ouvrir efface les pages de lancement précédentes, puis le lien est imprimé.

La connexion est conditionnelle : elle s’active quand host vaut 0.0.0.0 ou ::, ou quand public_url est défini, et elle utilise votre compte dashboard. Les clients natifs ne se connectent jamais — les écouteurs CLI, TUI et desktop ne l’exigent jamais, quoi que dise la config — et le lien à usage unique n’ouvre qu’un Webapp qui n’exige pas de connexion. Le parcours de passation de connexion couvre ce chemin.

Avant de chercher l’interrupteur

La disponibilité, sans détour : #141864 et ce lot ont été fusionnés le 2026-10-25 et le 2026-10-26, après la sortie de la v0.22.5 le 2026-10-23. Ils sont uniquement sur main — aucune release ne les contient encore, donc si hermes webapp manque dans votre build, c’est la raison, pas une installation cassée. Suivez main ou attendez la prochaine version ; cet article décrit main, et le temps que vous le lisiez, la fonctionnalité aura peut-être été livrée.

Deux choses encore. L’issue de suivi #144648 est toujours ouverte, et son titre dit ce qu’elle est — des suivis post-fusion — donc le lot ci-dessus est ce qui a été nettoyé, pas l’affirmation que la surface est terminée. Et sous Windows, le gateway possède le port fixe en exclusivité : « quelque chose occupe déjà le 9120 » mérite donc une vérification avant d’accuser la config.

Le résumé honnête : ceci remplace un second serveur et un relais par un bloc de config et une commande. Cela ne rend pas un contexte non sécurisé sécurisé — pour ça, il vous faut toujours votre terminateur TLS ou votre tunnel SSH.