Seu workspace do Hermes numa aba do navegador — sem instalar nada, sem relay


Você está num notebook emprestado, numa máquina corporativa travada ou num servidor de desenvolvimento acessado por SSH, e o que você quer de verdade é o seu workspace do Hermes — lista de sessions, terminal, árvore de arquivos, painel de preview — não uma caixa de chat. Instalar o app Desktop não é permitido, ou não é possível, e reconstruir seu contexto à mão num CLI pelado parece digitar de luvas. O plano B antigo era o dashboard, que é outra criatura: um processo separado, com a própria visão do que está rodando, que por acaso se parece com o app e não sabe nada sobre a aba que você deixou aberta em casa.

A mudança do webapp pega um caminho mais simples. O gateway serve o próprio workspace do Desktop (#141864, mesclado em 25 de outubro de 2026), dando continuidade à direção da #93508 — sem servidor Webapp separado e sem relay no meio.

O que o webapp é de fato

Versão curta: o workspace do Desktop, hospedado pelo processo que você já roda.

O gateway é aquele processo de fundo de longa duração ao qual o CLI, o app desktop e todas as plataformas de mensagem se conectam. Sessions no webapp rodam nesse gateway, dentro do profile para o qual você o apontou — literalmente o mesmo processo que responde ao seu terminal, ao seu adaptador de Telegram e à sua janela do desktop. Mesma conversa, mesma fila. Não importa qual superfície você olhe: existe uma única cópia do estado.

Essa propriedade sozinha explica quase todo o comportamento que vem a seguir. Uma aba não é o espelho de uma session do desktop; é mais um cliente do mesmo backend. Comece um turno no navegador, mude para o terminal e o trabalho está lá. Nada a reconciliar, porque nada é duplicado — e nada extra para rodar: nenhum servidor Webapp, nenhuma segunda porta, nenhum salto de relay que precise estar saudável antes.

Como ligar

O interruptor mora no config.yaml do profile sob o qual o gateway roda, e vem desligado por padrão:

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 é lido quando o gateway sobe, então troque o valor e reinicie com hermes gateway restart. Depois vem o comando com o qual você vai conviver:

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 faz bem mais do que “abrir uma URL”: ele reconstrói o bundle do frontend quando o que está em disco está velho, sobe o gateway se ele não estiver rodando e só então te entrega um link de login de uso único.

host é mais rígido do que parece, e é aqui que a maioria das pessoas perde dez minutos. Só 127.0.0.1, ::1 e localhost significam “só esta máquina”. 0.0.0.0 e :: significam que outras máquinas podem alcançar o serviço — e isso exige um provider de login do dashboard; sem um, o gateway continua servindo apenas clientes nativos, e hermes webapp --status diz exatamente isso. Qualquer outro valor, inclusive algo aparentemente inofensivo como 127.0.0.2, deixa o Webapp desligado.

port é fixa de propósito, para que a URL do navegador sobreviva a reinícios e você possa deixar a aba aberta por dias. Num profile avulso sem porta definida, o painel Settings → Web Access do Desktop (PR #142220) escolhe uma porta livre — acima de 9120 e não usada por outro profile — e grava o valor de volta em port. Ele nunca sobrescreve uma porta que você definiu à mão. Um gateway por host é o lugar para decidir qual profile deve servir o quê.

public_url serve para quando você já tem um proxy reverso ou um túnel. Preenchê-la habilita o login do dashboard mesmo com um host de loopback, e ela passa a ser o único hostname externo ao qual o Webapp responde — o callback de login é montado a partir dela, não a partir da requisição. Precisa ser http:// ou https://, sem credenciais, query string ou fragmento. Uma armadilha que vale nomear: dashboard.public_url só governa o hermes dashboard e nunca se aplica ao Webapp.

As três maneiras de ele se recusar a abrir

O que você nota O que aconteceu O que fazer
O navegador não recebe nada e o --status nega host tem um valor que o Webapp não aceita, por exemplo 127.0.0.2 Use 127.0.0.1, ::1, localhost ou um wildcard com um provider de login do dashboard
CLI e desktop funcionam, o navegador fica no escuro Porta ocupada, barreira de login recusada ou arquivo de credencial do navegador que não pode ser lido ou gravado Libere a porta e rode hermes webapp de novo
A aba funcionava ontem e amanheceu morta O gateway reiniciou com a porta ocupada, então a credencial do navegador foi rotacionada Rode hermes webapp de novo para a aba que você já tem aberta

A linha do meio surpreende porque o gateway não cai. Quando o listener do Webapp não consegue subir, ele mantém o listener privado de loopback original e segue atendendo clientes nativos — CLI, TUI e desktop intactos — enquanto troca a credencial do navegador. A troca é deliberada: se a porta estava ocupada, algum outro programa pode estar ali agora, e entregar uma credencial antiga a quem estiver escutando seria pior do que uma aba morta. Assim que a porta libera, hermes webapp reabre uma aba que você já tem. A terceira linha é o mesmo mecanismo pelo outro lado: um reinício sozinho não é motivo para recarregar o navegador, mas um que aconteceu com a porta tomada é.

O que o HTTP puro tira de você sem avisar

Tudo isso pode funcionar sobre http:// e ainda te deixar com um workspace faltando peças — e as mensagens de erro, até pouco tempo atrás, não ajudavam em nada.

Navegadores só expõem crypto.subtle em contexto seguro. Num endereço HTTP puro, a tela de bot (bot screen / noVNC) falhava com Cannot read properties of undefined (reading 'digest'), o que não te diz nada. Agora ela diz o que está errado: você precisa de HTTPS ou de um túnel SSH para 127.0.0.1. O mesmo vale para voz ao vivo — não existe navigator.mediaDevices em contexto inseguro, getUserMedia fica indefinido, e a mensagem agora é simplesmente “microphone not supported” em vez de um stack trace vindo de dentro do cliente (#144722).

O launcher e a documentação passaram a listar o que o HTTP puro custa: leitura de clipboard, wake word, tela de bot, voz ao vivo. Se a barra de endereço não é 127.0.0.1 nem HTTPS, conte com esses quatro ausentes — coloque um proxy reverso com TLS na frente e defina public_url, ou faça um túnel da porta.

Arquivos pertencem ao sandbox da session, não ao seu notebook

Uma aba de navegador faz a árvore de arquivos parecer local, e é justamente essa a armadilha que a #144741 fechou como correção de segurança: requisições de arquivo e mídia agora são resolvidas contra o sandbox da própria session. Uma session num sandbox Docker serve arquivos de dentro do container; uma session em ambiente SSH serve do workspace remoto. Resolver qualquer uma delas contra o diretório de trabalho do host não é cosmético — significa entregar uma árvore que pertence a outra máquina, com outro modelo de permissão. Para sessions em container, a fronteira descrita em o corte de setuid nos sandboxes Docker é a mesma que a árvore de arquivos agora respeita.

Do resto do lote você sente mais do que lê: previews grandes de HTML abrem direito, profiles SSH conseguem preparar texto de preview e o Export profile do lado do navegador está documentado (#144718); o terminal do navegador repinta e exit funciona (#144719); uma aba fecha limpo quando a fila de entrada está cheia (#144665); downloads de preview de group chat têm limite (#144557). O painel de preview também ganhou controle real de navegador, coberto em dirigindo o navegador pelo painel de preview.

Um detalhe de roteamento que atrapalha trabalho de integração: as rotas do próprio Webapp — uploads em stage, tickets de arquivo — existem apenas no listener do Webapp. O dashboard avulso e o hermes serve devolvem 404 para elas.

O link que hermes webapp imprime vale por 60 segundos e serve exatamente uma vez. O navegador o troca por um estado de login guardado no sessionStorage daquela aba, e é por isso que nada com cara de credencial vai parar no histórico do navegador nem num screenshot.

Só o seu próprio usuário do sistema operacional deveria conseguir obter o link, e o gateway responde por um socket de controle privado, não pela rede. O que isso significa na prática depende da plataforma:

  • Linux — o gateway verifica que a conexão pedindo o link pertence ao usuário atual.
  • Windows — sem verificação. O link aparece apenas na linha de comando do launcher do navegador, legível pelo mesmo usuário, pelo SYSTEM e por administradores elevados.
  • macOS, BSD, illumos, WSL — também sem verificação. Em vez disso, o comando grava o link numa página webapp/launch-*.html sob o home do gateway, diretório 0700 e arquivo 0600, carregando <meta refresh>, uma CSP default-src 'none' e no-referrer. Abrir a página limpa as páginas de lançamento anteriores e então o link é impresso.

O login é condicional: ele liga quando host é 0.0.0.0 ou ::, ou quando public_url está definida, e usa a sua conta do dashboard. Clientes nativos nunca fazem login — os listeners de CLI, TUI e desktop nunca exigem isso, diga o que disser a config — e o link de uso único só abre um Webapp que não exige login. O passo a passo da passagem de login cobre esse caminho.

Antes de sair procurando o interruptor

Disponibilidade, sem rodeios: a #141864 e este lote foram mesclados em 2026-10-25 e 2026-10-26, depois da v0.22.5, que saiu em 2026-10-23. Eles estão apenas na main — nenhuma release os contém ainda, então se hermes webapp não existe no seu build, o motivo é esse, e não uma instalação quebrada. Acompanhe a main ou espere a próxima versão; este texto descreve a main, e até você lê-lo o recurso pode muito bem já ter saído.

Mais duas coisas. A issue de acompanhamento #144648 continua aberta, e o título dela diz o que é — follow-ups pós-merge —, então o lote acima é o que já foi limpo, não uma afirmação de que a superfície está pronta. E no Windows o gateway detém a porta fixa com exclusividade, então “tem outra coisa na 9120” vale uma checagem antes de culpar a config.

O resumo honesto: isto substitui um segundo servidor e um relay por um bloco de config e um comando. Não torna um contexto inseguro em seguro — isso continua sendo o seu terminador TLS ou o seu túnel SSH.