Tu workspace de Hermes en una pestaña del navegador, sin instalar nada y sin relay


Estás en un portátil prestado, en una máquina de trabajo con los permisos capados o en un servidor de desarrollo al que entraste por SSH, y lo que de verdad quieres es tu workspace de Hermes —lista de sesiones, terminal, árbol de archivos, panel de vista previa—, no una caja de chat. Instalar la app de Desktop no está permitido, o directamente no se puede, y reconstruir tu contexto a mano en una CLI pelada se parece demasiado a escribir con guantes. El recurso de antes era el dashboard, que es otro animal: un proceso separado con su propia idea de lo que está corriendo, que se le parece bastante y no sabe nada de la pestaña que dejaste abierta en casa.

El cambio del webapp va por un camino más simple. El gateway sirve el propio workspace de Desktop (#141864, fusionado el 25 de octubre de 2026), en la línea que abrió #93508: sin un servidor Webapp aparte y sin ningún relay en medio.

Qué es realmente el webapp

Versión corta: el workspace de Desktop, servido por el proceso que ya tienes corriendo.

El gateway es el proceso de fondo de larga vida al que se conectan la CLI, la app de escritorio y todas las plataformas de mensajería. Las sesiones del webapp corren sobre ese gateway, dentro del profile al que lo apuntaste: literalmente el mismo proceso que responde a tu terminal, a tu adaptador de Telegram y a tu ventana de escritorio. La misma conversación, la misma cola. Mires la superficie que mires, hay una sola copia del estado.

Esa única propiedad explica casi todo lo que viene después. Una pestaña no es un espejo de una sesión de escritorio; es otro cliente del mismo backend. Lanzas un turno en el navegador, te pasas a la terminal y el trabajo está ahí. Nada que reconciliar, porque no hay nada duplicado, y nada extra que ejecutar: ni servidor Webapp, ni segundo puerto, ni un salto de relay que tenga que estar sano primero.

Cómo activarlo

El interruptor vive en el config.yaml del profile bajo el que corre el gateway, y viene desactivado:

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 se lee cuando arranca el gateway, así que cámbialo y reinicia con hermes gateway restart. Luego viene el comando con el que vas a vivir:

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 es bastante más que “abrir una URL”: reconstruye el bundle del frontend cuando el que hay en disco está viejo, arranca el gateway si no está corriendo y solo entonces te entrega un enlace de acceso de un solo uso.

host es más estricto de lo que parece, y aquí es donde mucha gente pierde diez minutos. Solo 127.0.0.1, ::1 y localhost significan “solo esta máquina”. 0.0.0.0 y :: significan que otras máquinas pueden llegar, lo que exige un provider de sign-in del dashboard; sin uno, el gateway sigue sirviendo solo a los clientes nativos, y hermes webapp --status te lo dice. Cualquier otro valor, incluido uno que parezca inofensivo como 127.0.0.2, deja el Webapp apagado.

port es fijo a propósito, para que la URL del navegador sobreviva a los reinicios y puedas dejar la pestaña abierta durante días. En un profile independiente sin puerto definido, el panel Settings → Web Access del Desktop (PR #142220) elige uno libre —por encima de 9120 y que no use otro profile— y lo escribe de vuelta en port. Nunca pisa un puerto que hayas puesto a mano. Un gateway por host es el sitio donde decidir qué profile debería servirlo.

public_url es para cuando ya tienes un proxy inverso o un túnel. Rellenarlo activa el sign-in del dashboard incluso con un host de loopback, y pasa a ser el único hostname externo al que responde el Webapp: el callback del sign-in se construye a partir de él, no de la petición. Tiene que ser http:// o https://, sin credenciales, query string ni fragmento. Una trampa que conviene nombrar: dashboard.public_url solo gobierna hermes dashboard y nunca aplica al Webapp.

Las tres formas en que se niega a abrirse

Lo que notas Qué ha pasado Qué hacer
El navegador no recibe nada y --status se niega host tiene un valor que el Webapp no acepta, por ejemplo 127.0.0.2 Usa 127.0.0.1, ::1, localhost, o un wildcard con un provider de sign-in del dashboard
La CLI y el escritorio van bien, el navegador en negro Puerto ocupado, el control de sign-in rechazó, o el archivo de credencial del navegador no se puede leer ni escribir Libera el puerto y vuelve a ejecutar hermes webapp
La pestaña funcionaba ayer y esta mañana está muerta El gateway se reinició mientras el puerto estaba ocupado, así que la credencial del navegador se rotó Vuelve a ejecutar hermes webapp para la pestaña que ya tienes abierta

La fila del medio sorprende porque el gateway no se cae. Cuando el listener del Webapp no puede arrancar, conserva el listener privado de loopback original y sigue sirviendo a los clientes nativos —CLI, TUI y escritorio sin tocar— mientras cambia la credencial del navegador. Ese cambio es deliberado: si el puerto estaba ocupado, puede que ahora haya otro programa escuchando ahí, y entregarle una credencial vieja a quien sea que esté al otro lado sería peor que una pestaña muerta. Cuando el puerto se libera, hermes webapp vuelve a abrir una pestaña que ya tenías. La tercera fila es el mismo mecanismo visto desde el otro lado: un reinicio por sí solo no es motivo para recargar el navegador, pero uno que ocurrió con el puerto cogido sí lo es.

Lo que el HTTP plano te quita sin avisar

Todo lo anterior puede funcionar sobre http:// y dejarte igualmente un workspace con piezas de menos, y los mensajes de error fueron inútiles hasta hace poco.

Los navegadores solo exponen crypto.subtle en un contexto seguro. Sobre una dirección HTTP plana, la pantalla del bot (bot screen / noVNC) fallaba con Cannot read properties of undefined (reading 'digest'), que no te dice nada. Ahora dice qué pasa: necesitas HTTPS, o un túnel SSH a 127.0.0.1. Lo mismo con la voz en vivo: no hay navigator.mediaDevices en un contexto inseguro, getUserMedia está indefinido, y el mensaje ahora es un simple “micrófono no soportado” en vez de un stack trace desde dentro del cliente (#144722).

El launcher y la documentación ya enumeran lo que cuesta el HTTP plano: lectura del portapapeles, wake word, pantalla del bot, voz en vivo. Si la barra de direcciones no es 127.0.0.1 ni HTTPS, cuenta con perder esas cuatro cosas: pon un proxy inverso con TLS delante y define public_url, o tuneliza el puerto.

Los archivos son del sandbox de la sesión, no de tu portátil

Una pestaña del navegador hace que el árbol de archivos se sienta local, y esa es la trampa que #144741 cerró como arreglo de seguridad: las peticiones de archivos y medios se resuelven ahora contra el sandbox de la propia sesión. Una sesión en un sandbox de Docker sirve archivos desde dentro del contenedor; una sesión en un entorno SSH los sirve desde el workspace remoto. Resolver cualquiera de las dos contra el directorio de trabajo del host no es cosmético: significa repartir un árbol que pertenece a otra máquina con otro modelo de permisos. Para sesiones en contenedor, la frontera que describe el recorte de setuid en los sandboxes de Docker es la misma que ahora respeta el árbol de archivos.

Del resto del lote te enteras más de lo que lees: las vistas previas grandes de HTML se abren bien, los profiles SSH pueden preparar texto de vista previa y la exportación de profile del lado del navegador está documentada (#144718); la terminal del navegador se repinta y exit funciona (#144719); una pestaña se cierra limpiamente cuando la cola de entrada está llena (#144665); las descargas de vista previa en group chats tienen tope (#144557). El panel de vista previa también ha ganado control real del navegador, que se cuenta en manejar el navegador desde el panel de vista previa.

Un detalle de enrutado que lía el trabajo de integración: las rutas propias del Webapp —subidas preparadas, tickets de archivo— existen solo en el listener del Webapp. El dashboard independiente y hermes serve devuelven 404 para ellas.

Quién puede obtener el enlace de un solo uso

El enlace que imprime hermes webapp vale 60 segundos y se puede usar exactamente una vez. El navegador lo canjea por un estado de login que se guarda en el sessionStorage de esa pestaña, y por eso no acaba nada con forma de credencial en el historial del navegador ni en una captura de pantalla.

Solo tu propio usuario del sistema operativo debería poder obtener ese enlace, y el gateway responde por un socket de control privado en lugar de por la red. Qué significa eso en la práctica depende de la plataforma:

  • Linux — el gateway comprueba que la conexión que pide el enlace pertenece al usuario actual.
  • Windows — sin comprobación. El enlace aparece solo en la línea de comandos del launcher del navegador, legible por el mismo usuario, por SYSTEM y por administradores con privilegios elevados.
  • macOS, BSD, illumos, WSL — tampoco hay comprobación. En su lugar, el comando escribe el enlace en una página webapp/launch-*.html bajo el home del gateway, con directorio 0700 y archivo 0600, con <meta refresh>, una CSP default-src 'none' y no-referrer. Al abrirla se limpian las páginas de lanzamiento anteriores y entonces se imprime el enlace.

El sign-in es condicional: se activa cuando host es 0.0.0.0 o ::, o cuando hay public_url, y usa tu cuenta del dashboard. Los clientes nativos nunca hacen sign-in —los listeners de CLI, TUI y escritorio nunca lo piden, diga lo que diga la config— y el enlace de un solo uso solo abre un Webapp que no requiere sign-in. El recorrido del traspaso de sign-in cubre ese camino.

Antes de ir a buscar el interruptor

Disponibilidad, sin rodeos: #141864 y este lote se fusionaron el 2026-10-25 y el 2026-10-26, después de que v0.22.5 saliera el 2026-10-23. Están solo en main: ninguna release los contiene todavía, así que si hermes webapp no aparece en tu build, la razón es esa y no una instalación rota. Sigue main o espera la siguiente versión; este artículo describe main, y para cuando lo leas es muy posible que la función ya haya salido.

Dos cosas más. La incidencia de seguimiento #144648 sigue abierta, y su título dice lo que es: tareas posteriores a la fusión. Así que el lote de arriba es lo que ya se ha limpiado, no una afirmación de que la superficie esté terminada. Y en Windows el gateway es dueño exclusivo del puerto fijo, así que vale la pena comprobar lo de “ya hay algo en 9120” antes de culpar a la config.

El resumen honesto: esto sustituye un segundo servidor y un relay por un bloque de configuración y un comando. No convierte un contexto inseguro en seguro: eso sigue siendo cosa de tu terminador TLS o de tu túnel SSH.