Dein Hermes-Workspace im Browser-Tab — ohne Installation, ohne Relay

Du sitzt an einem geliehenen Laptop, an einer gesperrten Firmenmaschine oder an einer Dev-Box, die du per SSH erreichst, und eigentlich willst du deinen Hermes-Workspace — Session-Liste, Terminal, Dateibaum, Preview-Pane — und keine Chat-Box. Die Desktop-App zu installieren ist nicht erlaubt oder schlicht nicht möglich, und den eigenen Kontext von Hand in einer nackten CLI nachzubauen fühlt sich an wie Tippen mit Handschuhen. Der alte Ausweg war das Dashboard, und das ist ein anderes Tier: ein separater Prozess mit eigener Sicht darauf, was gerade läuft, der zufällig ähnlich aussieht und nichts von dem Tab weiß, den du zu Hause offen gelassen hast.
Die Webapp-Änderung nimmt den einfacheren Weg. Der gateway liefert den Desktop-Workspace selbst aus (#141864, gemergt am 25. Oktober 2026) und führt damit die Richtung von #93508 fort — ohne separaten Webapp-Server und ohne Relay dazwischen.
Was die Webapp tatsächlich ist
Kurzfassung: der Desktop-Workspace, gehostet von dem Prozess, den du sowieso laufen hast.
Der gateway ist der langlaufende Hintergrundprozess, mit dem sich die CLI, die Desktop-App und jede Messaging-Plattform verbinden. Sessions in der Webapp laufen auf diesem gateway, innerhalb des profile, auf das du es gerichtet hast — buchstäblich derselbe Prozess, der deinem Terminal, deinem Telegram-Adapter und deinem Desktop-Fenster antwortet. Dieselbe Unterhaltung, dieselbe Queue. Welche Oberfläche du auch anschaust: es gibt genau eine Kopie des Zustands.
Diese eine Eigenschaft erklärt fast alles, was folgt. Ein Tab ist kein Spiegel einer Desktop-Session, sondern ein weiterer Client desselben Backends. Starte einen Turn im Browser, wechsle ins Terminal, und die Arbeit ist schon da. Nichts abzugleichen, weil nichts doppelt existiert — und nichts zusätzlich zu betreiben: kein Webapp-Server, kein zweiter Port, kein Relay-Hop, der erst gesund sein muss.
Einschalten
Der Schalter liegt in der config.yaml des profile, unter dem das gateway läuft, und ist standardmäßig aus:
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 wird beim Start des gateway gelesen, also umlegen und mit hermes gateway restart neu starten. Danach der Befehl, in dem du leben wirst:
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 ist mehr als „öffne eine URL“: es baut das Frontend-Bundle neu, wenn das auf der Platte veraltet ist, startet das gateway, falls es nicht läuft, und reicht dir erst dann einen einmaligen Anmelde-Link.
host ist strenger, als es aussieht, und genau hier verlieren die meisten zehn Minuten. Nur 127.0.0.1, ::1 und localhost bedeuten „nur diese Maschine“. 0.0.0.0 und :: bedeuten, dass andere Maschinen herankommen — was einen Sign-in-Provider für das Dashboard voraussetzt; ohne einen bedient das gateway weiter ausschließlich native Clients, und hermes webapp --status sagt dir das auch. Jeder andere Wert, auch etwas harmlos Wirkendes wie 127.0.0.2, lässt die Webapp ausgeschaltet.
port ist absichtlich fest, damit deine Browser-URL Neustarts überlebt und du den Tab tagelang offen lassen kannst. Bei einem eigenständigen profile ohne gesetzten Port wählt das Panel Settings → Web Access der Desktop-App (PR #142220) einen freien Port — oberhalb von 9120, von keinem anderen profile belegt — und schreibt ihn in port zurück. Einen Port, den du selbst gesetzt hast, überschreibt es nie. Ein gateway pro Host ist der Ort, an dem du klärst, welches profile ihn bedienen soll.
public_url ist für den Fall, dass du bereits einen Reverse Proxy oder Tunnel hast. Füllst du es aus, wird der Dashboard-Sign-in auch bei einem Loopback-host aktiv, und es wird der einzige externe Hostname, auf den die Webapp antwortet — der Sign-in-Callback wird daraus gebaut, nicht aus der Anfrage. Es muss http:// oder https:// sein, ohne Credentials, Query-String oder Fragment. Eine Falle, die es wert ist, benannt zu werden: dashboard.public_url regelt nur hermes dashboard und gilt für die Webapp nie.
Die drei Arten, wie sie sich weigert aufzugehen
| Was dir auffällt | Was passiert ist | Was du tun solltest |
|---|---|---|
Der Browser bekommt nichts, --status lehnt ab |
host hat einen Wert, den die Webapp nicht akzeptiert, z. B. 127.0.0.2 |
127.0.0.1, ::1, localhost verwenden oder einen Wildcard zusammen mit einem Dashboard-Sign-in-Provider |
| CLI und Desktop laufen, der Browser bleibt dunkel | Port belegt, Sign-in-Gate abgelehnt, oder die Browser-Credential-Datei ist nicht les- oder schreibbar | Den Port freigeben und hermes webapp erneut ausführen |
| Der Tab lief gestern, heute ist er tot | Das gateway startete neu, während der Port belegt war, also wurde das Browser-Credential rotiert | hermes webapp erneut für den Tab ausführen, den du schon offen hast |
Die mittlere Zeile überrascht Leute, weil das gateway dabei nicht umfällt. Kann der Webapp-Listener nicht starten, behält es den ursprünglichen privaten Loopback-Listener und bedient weiter die nativen Clients — CLI, TUI und Desktop sind unberührt —, tauscht aber das Browser-Credential aus. Dieser Tausch ist Absicht: war der Port belegt, sitzt jetzt vielleicht ein anderes Programm darauf, und einem unbekannten Zuhörer ein altes Credential zu überreichen wäre schlimmer als ein toter Tab. Sobald der Port frei wird, öffnet hermes webapp einen Tab, den du bereits hast, erneut. Die dritte Zeile ist derselbe Mechanismus von der anderen Seite: ein Neustart allein ist kein Grund, den Browser neu zu laden — einer, der passierte, während der Port gehalten wurde, schon.
Was reines HTTP stillschweigend wegnimmt
Alles oben kann über http:// funktionieren und dir trotzdem einen Workspace mit fehlenden Teilen hinterlassen — und die Fehlermeldungen waren bis vor kurzem nutzlos.
Browser stellen crypto.subtle nur in einem sicheren Kontext bereit. Auf einer reinen HTTP-Adresse scheiterte der Bot Screen (bot screen / noVNC) früher mit Cannot read properties of undefined (reading 'digest'), was dir überhaupt nichts sagt. Jetzt steht dort, was falsch ist: du brauchst HTTPS oder einen SSH-Tunnel auf 127.0.0.1. Dasselbe bei Live-Voice — in einem unsicheren Kontext gibt es kein navigator.mediaDevices, getUserMedia ist undefined, und die Meldung lautet jetzt schlicht „Mikrofon wird nicht unterstützt“ statt eines Stacktrace aus dem Inneren des Clients (#144722).
Der Launcher und die Doku listen inzwischen auf, was reines HTTP kostet: Lesen der Zwischenablage, Wake Word, Bot Screen, Live-Voice. Ist die Adresszeile weder 127.0.0.1 noch HTTPS, rechne mit genau diesen vier Ausfällen — setz einen TLS-Reverse-Proxy davor und public_url, oder tunele den Port.
Dateien gehören zur sandbox der Session, nicht zu deinem Laptop
Ein Browser-Tab lässt den Dateibaum lokal wirken, und genau das ist die Falle, die #144741 als Security-Fix geschlossen hat: Datei- und Media-Anfragen werden jetzt gegen die sandbox der jeweiligen Session aufgelöst. Eine Session in einer Docker-sandbox liefert Dateien aus dem Container; eine Session in einer SSH-Umgebung liefert sie aus dem entfernten Workspace. Beides gegen das Arbeitsverzeichnis des Hosts aufzulösen ist nicht kosmetisch — es bedeutet, einen Baum herauszugeben, der zu einer anderen Maschine mit einem anderen Berechtigungsmodell gehört. Bei containerisierten Sessions ist die Grenze, die der setuid-Strip in Docker-sandboxes beschreibt, dieselbe, die der Dateibaum jetzt respektiert.
Den Rest des Batches merkst du mehr, als dass du ihn liest: große HTML-Previews öffnen richtig, SSH-profile können Preview-Text bereitstellen, und der browser-seitige Export des profile ist dokumentiert (#144718); das Browser-Terminal zeichnet neu und exit funktioniert (#144719); ein Tab schließt sauber, wenn die Input-Queue voll ist (#144665); Downloads aus Gruppenchat-Previews sind gedeckelt (#144557). Das Preview-Pane hat außerdem echte Browser-Steuerung bekommen, beschrieben in den Browser aus dem Preview-Pane steuern.
Ein Routing-Detail, über das Integrationsarbeit stolpert: die eigenen Routen der Webapp — staged uploads, file tickets — existieren nur auf dem Webapp-Listener. Das eigenständige Dashboard und hermes serve antworten darauf mit 404.
Wer den einmaligen Link bekommen kann
Der Link, den hermes webapp ausgibt, gilt 60 Sekunden und ist genau einmal verwendbar. Der Browser tauscht ihn gegen einen Login-Zustand, der im sessionStorage dieses Tabs liegt — deshalb landet nichts Credential-Förmiges in deinem Browserverlauf oder auf einem Screenshot.
Nur dein eigener OS-Benutzer sollte den Link bekommen können, und das gateway antwortet dafür über einen privaten Control-Socket statt über das Netzwerk. Was das praktisch heißt, hängt von der Plattform ab:
- Linux — das gateway prüft, ob die Verbindung, die den Link anfragt, zum aktuellen Benutzer gehört.
- Windows — keine Prüfung. Der Link erscheint nur in der Kommandozeile des Browser-Launchers, lesbar für denselben Benutzer, für SYSTEM und für erhöhte Administratoren.
- macOS, BSD, illumos, WSL — ebenfalls keine Prüfung. Stattdessen schreibt der Befehl den Link in eine Seite
webapp/launch-*.htmlunter dem gateway-Home, Verzeichnis0700und Datei0600, mit<meta refresh>, einerdefault-src 'none'-CSP undno-referrer. Beim Öffnen werden die vorherigen Launch-Seiten gelöscht, dann wird der Link ausgegeben.
Sign-in ist bedingt: es greift, wenn host gleich 0.0.0.0 oder :: ist, oder wenn public_url gesetzt ist, und nutzt dein Dashboard-Konto. Native Clients melden sich nie an — die Listener von CLI, TUI und Desktop verlangen es nie, egal was in der Config steht — und der einmalige Link öffnet nur eine Webapp, die keinen Sign-in verlangt. Die Übergabe beim Sign-in behandelt diesen Pfad.
Bevor du dich auf die Suche nach dem Schalter machst
Zur Verfügbarkeit, direkt: #141864 und dieser Batch wurden am 25. und 26.10.2026 gemergt, nachdem v0.22.5 am 23.10.2026 erschienen ist. Sie sind nur auf main — kein Release enthält sie bisher. Fehlt hermes webapp in deinem Build, ist das der Grund und keine kaputte Installation. Folge main oder warte auf die nächste Version; dieser Beitrag beschreibt main, und bis du ihn liest, kann das Feature durchaus schon ausgeliefert sein.
Zwei Dinge noch. Das Tracking-Issue #144648 ist noch offen, und sein Titel sagt, was es ist — Post-Merge-Follow-ups —, der Batch oben ist also das Aufgeräumte, keine Behauptung, die Oberfläche sei fertig. Und unter Windows besitzt das gateway den festen Port exklusiv, ein Blick auf „da läuft schon etwas auf 9120“ lohnt sich also, bevor du die Config beschuldigst.
Die ehrliche Zusammenfassung: das ersetzt einen zweiten Server und ein Relay durch einen Config-Block und einen Befehl. Es macht einen unsicheren Kontext nicht sicher — das bleibt dein TLS-Terminator oder dein SSH-Tunnel.