Deixe uma Extensão de Navegador Assumir com Segurança as Ferramentas de Navegador do Hermes: o Guia do Controller Autenticado


Você tem uma página aberta no navegador e quer que o Hermes trabalhe nela por você — preencher o formulário, virar as páginas, capturar evidências em screenshot. A resposta antiga era apontá-lo para um “browser backend”: um navegador em nuvem ou CDP local. Mas a aba que você realmente usa e o “browser backend” que o Hermes inicia são duas coisas diferentes. O formato ideal é: a extensão de navegador que você instalou vira o canal de controle, e o agente dirige exatamente a página à sua frente. Isso levanta uma questão de segurança afiada — por que uma extensão deveria ser confiável, e onde exatamente fica o limite do que ela pode fazer?

A resposta do Hermes é uma via autenticada chamada browser extension controller: uma extensão se registra como o controller exato das ferramentas browser_* de uma sessão por meio de um ticket WebSocket de uso único, e as capacidades são filtradas por uma allowlist estrita — sem CDP bruto, sem avaliação arbitrária de scripts, sem acesso ao console. Depois de vinculado, ele falha fechado (fail-closed): um controller ausente, ambíguo, desconectado ou incapaz faz a chamada falhar em vez de trocar silenciosamente para outro browser backend. O recurso chega no PR #91535 (um salvamento do #85351 com os commits do autor original cherry-picked e preservados), mesclado no upstream main — ainda não está em nenhuma tag de release.

Por que uma via de “controller” afinal

As ferramentas de navegador do Hermes (browser_navigate, browser_click, browser_snapshot, …) ficam em vários backends: Browserbase cloud, Browser Use, CDP Chromium local, Camofox… — todos são “um navegador que o Hermes inicia sozinho”. Mas o navegador que você usa é de outra espécie: não é iniciado pelo Hermes, é a instância que você dirige todos os dias.

O extension controller preenche exatamente essa lacuna: a extensão segura sua aba real do navegador, o Hermes envia comandos browser_* por um canal WebSocket autenticado, e a extensão os executa na página real e reporta de volta. Comandos, capacidades e propriedade são todos restringidos por uma allowlist no servidor e por tickets de uso único.

Habilitando a configuração

O recurso vem desligado por padrão e exige opt-in — e o caminho da API local adicionalmente exige a bearer key do API server:

browser:
  extension_control:
    enabled: true

Um controller só pode se registrar para uma sessão de servidor existente; o principal do controller é derivado do estado autenticado do servidor, e um principal_id fornecido pelo cliente é ignorado — identidade não é algo que o cliente possa declarar.

A allowlist de capacidades: 11 itens, sem CDP puro

GET /v1/capabilities expõe se o recurso está habilitado, a versão do protocolo, os nomes de transporte e a allowlist exata de capacidades:

controller.noop
browser_back
browser_click
browser_navigate
browser_press
browser_screenshot
browser_scroll
browser_snapshot
browser_tab_activate
browser_tabs
browser_type

Capacidades solicitadas fora dessa lista são filtradas. Note o trade-off deliberado: CDP bruto, avaliação arbitrária de scripts, acesso ao console, uploads, extração de imagens e visão não fazem parte do protocolo do controller — a extensão pode ajudar o agente a clicar, digitar, rolar e capturar screenshots, mas não pode executar JavaScript arbitrário na página. Esse é um limite de segurança deliberadamente estreito: capaz, mas sem sustos.

Registro e conexão: ticket de uso único, TTL de 30 segundos

O fluxo tem três passos (registro na API local):

  1. Envie um POST /v1/browser-control/register autenticado com protocol_version, session_id, controller_id, browser_profile_id e as capabilities solicitadas;
  2. O Hermes retorna um ticket de uso único (TTL de 30 segundos) com o escopo do controller filtrado e vinculado ao servidor;
  3. Abra GET /v1/browser-control/ws com ambos os subprotocolos WebSocket: hermes-browser-control-v1 e hermes-browser-control-ticket.<ticket>.

O ticket nunca é aceito na query string; tickets desconhecidos, expirados, reutilizados ou malformados falham antes do upgrade WebSocket. Uma vez conectado, o Hermes envia frames browser.controller.command (com command_id, action, arguments imutáveis e o tool_call_id de origem), e o controller responde com browser.controller.result (mesmo command_id, booleano ok exato e result ou error). Cancelamento e timeouts emitem browser.controller.cancel; resultados atrasados são ignorados.

Fail-closed: sem troca silenciosa de navegador depois de vinculado

Esta é a parte que merece mais destaque:

  • Sem identidade de controller vinculada, ou com o recurso desabilitado → o Hermes preserva o browser backend existente;
  • Uma vez que o gateway vincula um principal de controller e uma família de transporte à requisição, essa via de extensão é autoritativa: controllers ausentes, ambíguos, desconectados ou incapazes falham fechado em vez de trocar silenciosamente para um navegador local/em nuvem diferente;
  • Depois que um controller exato é selecionado, seu resultado ou erro é autoritativo, e o Hermes nunca tenta a mesma ação por outro backend.

Em outras palavras: uma sessão de “controle esta aba” nunca vai pular silenciosamente para um navegador em nuvem pelas suas costas. Uma perda inesperada de socket é tratada como desconexão recuperável (o trabalho em andamento sobrevive até o deadline de cada comando); um browser.controller.detach explícito no transporte autenticado é a separação dura, cancelando o trabalho pendente imediatamente. Um id de controller ou browser profile diferente na mesma via de sessão autenticada é uma substituição dura: o trabalho pendente antigo é cancelado antes que o sucessor se torne roteável.

Status atual e como testar

  • Código: feat(browser): authenticated extension controller (salvage #85351), PR #91535, mesclado no main em 2026-08-21.
  • Status de release: ainda não está em nenhuma tag de release — a tag v2026.8.19 da v0.20.5 foi cortada antes dessa mesclagem. Hoje vive apenas no main; quando sair com uma release futura, o hermes update o traz.
  • Fluxo complementar: o fluxo de pairing loopback da extensão de navegador (PR #88203, a metade do gateway do “um clique de aprovação gera um token com escopo”) ainda está aberto — então a experiência de instalar e clicar para aprovar ainda não foi mesclada; por enquanto você passa pela autenticação do API server + o fluxo de registro você mesmo.

Para testar: pegue um build do main que inclua o PR, habilite browser.extension_control.enabled, configure a bearer key do API server e use o cliente WebSocket de sua preferência para registrar um controller de teste conforme os três passos acima, começando com controller.noop para validar o canal. Para mais sobre seleção e modos de browser backend, veja nosso guia de modos do Browser Use CLI 3.0 e o guia do browser de leitura/preview do desktop.

O valor real deste recurso não é “mais uma forma de conectar” — é que o navegador que você realmente usa se torna, pela primeira vez, um ambiente de execução seguramente controlável para o agente: a allowlist encolhe a superfície de ataque, o ticket de uso único bloqueia replay e o fail-closed torna impossível o drift silencioso de backend. Quando o fluxo de pairing chegar, instale a extensão, clique em aprovar e o Hermes trabalha na página à sua frente.