Web interfaces

Interface de controle

A Control UI é um pequeno aplicativo de página única Vite + Lit servido pelo Gateway:

  • padrão: http://<host>:18789/
  • prefixo opcional: defina gateway.controlUi.basePath (por exemplo, /openclaw)

Ela se comunica diretamente com o WebSocket do Gateway na mesma porta.

Abertura rápida (local)

Se o Gateway estiver em execução no mesmo computador, abra:

Se a página não carregar, inicie o Gateway primeiro: openclaw gateway.

A autenticação é fornecida durante o handshake do WebSocket via:

  • connect.params.auth.token
  • connect.params.auth.password
  • cabeçalhos de identidade do Tailscale Serve quando gateway.auth.allowTailscale: true
  • cabeçalhos de identidade de proxy confiável quando gateway.auth.mode: "trusted-proxy"

O painel de configurações do dashboard mantém um token para a sessão atual da aba do navegador e a URL do gateway selecionada; senhas não são persistidas. O onboarding geralmente gera um token de gateway para autenticação por segredo compartilhado na primeira conexão, mas autenticação por senha também funciona quando gateway.auth.mode é "password".

Pareamento de dispositivo (primeira conexão)

Quando você se conecta à Control UI de um novo navegador ou dispositivo, o Gateway geralmente exige uma aprovação de pareamento única. Esta é uma medida de segurança para impedir acesso não autorizado.

O que você verá: "desconectado (1008): pareamento necessário"

  • Listar solicitações pendentes

    openclaw devices list
    
  • Aprovar pelo ID da solicitação

    openclaw devices approve <requestId>
    
  • Se o navegador tentar parear novamente com detalhes de autenticação alterados (função/escopos/chave pública), a solicitação pendente anterior será substituída e um novo requestId será criado. Execute openclaw devices list novamente antes da aprovação.

    Se o navegador já estiver pareado e você alterá-lo de acesso de leitura para acesso de escrita/admin, isso será tratado como uma atualização de aprovação, não como uma reconexão silenciosa. O OpenClaw mantém a aprovação antiga ativa, bloqueia a reconexão mais ampla e pede que você aprove explicitamente o novo conjunto de escopos.

    Depois de aprovado, o dispositivo é lembrado e não exigirá nova aprovação, a menos que você o revogue com openclaw devices revoke --device <id> --role <role>. Consulte a CLI de dispositivos para rotação e revogação de tokens.

    Identidade pessoal (local ao navegador)

    A Control UI oferece suporte a uma identidade pessoal por navegador (nome de exibição e avatar) anexada às mensagens enviadas para atribuição em sessões compartilhadas. Ela fica no armazenamento do navegador, é limitada ao perfil atual do navegador e não é sincronizada com outros dispositivos nem persistida no servidor além dos metadados normais de autoria da transcrição nas mensagens que você de fato envia. Limpar os dados do site ou trocar de navegador a redefine para vazio.

    O mesmo padrão local ao navegador se aplica à substituição do avatar do assistente. Avatares de assistente enviados sobrepõem a identidade resolvida pelo gateway apenas no navegador local e nunca fazem ida e volta por config.patch. O campo de configuração compartilhado ui.assistant.avatar ainda está disponível para clientes sem UI que gravam o campo diretamente (como gateways com scripts ou dashboards personalizados).

    Endpoint de configuração de runtime

    A Control UI busca suas configurações de runtime em /__openclaw/control-ui-config.json. Esse endpoint é protegido pela mesma autenticação do gateway que o restante da superfície HTTP: navegadores não autenticados não conseguem buscá-lo, e uma busca bem-sucedida exige um token/senha de gateway já válido, identidade do Tailscale Serve ou uma identidade de proxy confiável.

    Suporte a idiomas

    A Control UI pode se localizar no primeiro carregamento com base no idioma do seu navegador. Para substituir isso depois, abra Visão geral -> Acesso ao Gateway -> Idioma. O seletor de idioma fica no card Acesso ao Gateway, não em Aparência.

    • Idiomas compatíveis: en, zh-CN, zh-TW, pt-BR, de, es, ja-JP, ko, fr, ar, it, tr, uk, id, pl, th, vi, nl, fa
    • Traduções que não são em inglês são carregadas sob demanda no navegador.
    • O idioma selecionado é salvo no armazenamento do navegador e reutilizado em visitas futuras.
    • Chaves de tradução ausentes voltam para inglês.

    As traduções da documentação são geradas para o mesmo conjunto de idiomas que não são em inglês, mas o seletor de idiomas integrado do site de documentação do Mintlify é limitado aos códigos de idioma aceitos pelo Mintlify. A documentação em tailandês (th) e persa (fa) ainda é gerada no repositório de publicação; ela pode não aparecer nesse seletor até que o Mintlify ofereça suporte a esses códigos.

    Temas de aparência

    O painel Aparência mantém os temas integrados Claw, Knot e Dash, além de um slot de importação tweakcn local ao navegador. Para importar um tema, abra o editor tweakcn, escolha ou crie um tema, clique em Compartilhar e cole o link do tema copiado em Aparência. O importador também aceita URLs de registro https://tweakcn.com/r/themes/<id>, URLs de editor como https://tweakcn.com/editor/theme?theme=amethyst-haze, caminhos relativos /themes/<id>, IDs de tema brutos e nomes de tema padrão como amethyst-haze.

    Temas importados são armazenados apenas no perfil atual do navegador. Eles não são gravados na configuração do gateway e não são sincronizados entre dispositivos. Substituir o tema importado atualiza o único slot local; limpá-lo muda o tema ativo de volta para Claw se o tema importado estava selecionado.

    O que ela pode fazer (hoje)

    Chat e conversa por voz
    • Converse por chat com o modelo via Gateway WS (chat.history, chat.send, chat.abort, chat.inject).
    • Atualizações do histórico de chat solicitam uma janela recente limitada com limites de texto por mensagem, para que sessões grandes não obriguem o navegador a renderizar um payload de transcrição completo antes que o chat fique utilizável.
    • Converse por sessões em tempo real no navegador. A OpenAI usa WebRTC direto, o Google Live usa um token de navegador limitado a um único uso por WebSocket, e plugins de voz em tempo real somente de backend usam o transporte de relay do Gateway. Sessões de provedor pertencentes ao cliente começam com talk.client.create; sessões de relay do Gateway começam com talk.session.create. O relay mantém as credenciais do provedor no Gateway enquanto o navegador transmite PCM do microfone por talk.session.appendAudio e encaminha chamadas de ferramenta do provedor openclaw_agent_consult por talk.client.toolCall para a política do Gateway e o modelo OpenClaw maior configurado.
    • Transmita chamadas de ferramentas + cards de saída de ferramenta ao vivo no Chat (eventos do agente).
    Canais, instâncias, sessões, sonhos
    • Canais: status de canais integrados e de plugin bundled/externos, login por QR e configuração por canal (channels.status, web.login.*, config.patch).
    • Atualizações de sondagem de canais mantêm o snapshot anterior visível enquanto verificações lentas do provedor terminam, e snapshots parciais são rotulados quando uma sondagem ou auditoria excede seu orçamento de UI.
    • Instâncias: lista de presença + atualização (system-presence).
    • Sessões: lista + substituições por sessão de modelo/pensamento/rápido/verboso/trace/raciocínio (sessions.list, sessions.patch).
    • Sonhos: status de Dreaming, alternância para ativar/desativar e leitor do Diário de Sonhos (doctor.memory.status, doctor.memory.dreamDiary, config.patch).
    Cron, Skills, nós, aprovações de exec
    • Jobs do Cron: listar/adicionar/editar/executar/ativar/desativar + histórico de execução (cron.*).
    • Skills: status, ativar/desativar, instalar, atualizações de chave de API (skills.*).
    • Nós: lista + limites (node.list).
    • Aprovações de exec: editar allowlists do gateway ou do nó + política de solicitação para exec host=gateway/node (exec.approvals.*).
    Configuração
    • Veja/edite ~/.openclaw/openclaw.json (config.get, config.set).
    • Aplique + reinicie com validação (config.apply) e desperte a última sessão ativa.
    • Gravações incluem uma proteção de hash-base para evitar sobrescrever edições concorrentes.
    • Gravações (config.set/config.apply/config.patch) fazem uma pré-verificação da resolução de SecretRef ativo para refs no payload de configuração enviado; refs enviados ativos não resolvidos são rejeitados antes da gravação.
    • Renderização de esquema + formulário (config.schema / config.schema.lookup, incluindo title / description de campo, dicas de UI correspondentes, resumos imediatos de filhos, metadados de documentação em nós aninhados de objeto/curinga/array/composição, além de esquemas de plugin + canal quando disponíveis); o editor JSON bruto fica disponível apenas quando o snapshot tem uma ida e volta bruta segura.
    • Se um snapshot não puder fazer ida e volta segura de texto bruto, a Control UI força o modo Formulário e desativa o modo Bruto para esse snapshot.
    • O editor JSON bruto "Redefinir para salvo" preserva a forma criada no bruto (formatação, comentários, layout de $include) em vez de renderizar novamente um snapshot achatado, para que edições externas sobrevivam a uma redefinição quando o snapshot puder fazer ida e volta com segurança.
    • Valores estruturados de objeto SecretRef são renderizados como somente leitura em entradas de texto do formulário para impedir corrupção acidental de objeto para string.
    Depuração, logs, atualização
    • Depuração: snapshots de status/integridade/modelos + log de eventos + chamadas RPC manuais (status, health, models.list).
    • O log de eventos inclui tempos de atualização/RPC da Control UI, tempos lentos de renderização de chat/configuração e entradas de responsividade do navegador para quadros de animação longos ou tarefas longas quando o navegador expõe esses tipos de entrada PerformanceObserver.
    • Logs: cauda ao vivo dos logs de arquivo do gateway com filtro/exportação (logs.tail).
    • Atualização: execute uma atualização de pacote/git + reinício (update.run) com um relatório de reinício e depois consulte update.status após reconectar para verificar a versão do gateway em execução.
    Observações do painel de jobs do Cron
    • Para jobs isolados, a entrega usa por padrão anunciar resumo. Você pode mudar para nenhuma se quiser execuções apenas internas.
    • Campos de canal/destino aparecem quando anunciar está selecionado.
    • O modo Webhook usa delivery.mode = "webhook" com delivery.to definido como uma URL de webhook HTTP(S) válida.
    • Para jobs de sessão principal, os modos de entrega webhook e nenhuma estão disponíveis.
    • Controles avançados de edição incluem excluir após execução, limpar substituição de agente, opções exatas/escalonadas de cron, substituições de modelo/pensamento do agente e alternâncias de entrega em melhor esforço.
    • A validação do formulário é inline com erros em nível de campo; valores inválidos desativam o botão de salvar até serem corrigidos.
    • Defina cron.webhookToken para enviar um token bearer dedicado; se omitido, o webhook é enviado sem cabeçalho de autenticação.
    • Fallback obsoleto: jobs legados armazenados com notify: true ainda podem usar cron.webhook até serem migrados.

    Comportamento do chat

    Semântica de envio e histórico
    • chat.send é não bloqueante: confirma imediatamente com { runId, status: "started" } e a resposta é transmitida por eventos chat.
    • Uploads de chat aceitam imagens e arquivos que não sejam vídeo. Imagens mantêm o caminho de imagem nativo; outros arquivos são armazenados como mídia gerenciada e exibidos no histórico como links de anexo.
    • Reenviar com o mesmo idempotencyKey retorna { status: "in_flight" } durante a execução, e { status: "ok" } após a conclusão.
    • As respostas de chat.history têm limite de tamanho para segurança da UI. Quando entradas da transcrição são grandes demais, o Gateway pode truncar campos de texto longos, omitir blocos pesados de metadados e substituir mensagens grandes demais por um placeholder ([chat.history omitted: message too large]).
    • Imagens do assistente/geradas são persistidas como referências de mídia gerenciada e servidas de volta por URLs de mídia autenticadas do Gateway, então recarregamentos não dependem de payloads brutos de imagem em base64 permanecerem na resposta do histórico de chat.
    • Ao renderizar chat.history, a Control UI remove tags de diretivas inline apenas de exibição do texto visível do assistente (por exemplo [[reply_to_*]] e [[audio_as_voice]]), payloads XML de chamadas de ferramenta em texto simples (incluindo <tool_call>...</tool_call>, <function_call>...</function_call>, <tool_calls>...</tool_calls>, <function_calls>...</function_calls> e blocos truncados de chamadas de ferramenta), e tokens de controle do modelo vazados em ASCII/largura completa, e omite entradas do assistente cujo texto visível inteiro seja apenas o token silencioso exato NO_REPLY / no_reply ou o token de confirmação de Heartbeat HEARTBEAT_OK.
    • Durante um envio ativo e a atualização final do histórico, a visualização de chat mantém mensagens locais otimistas do usuário/assistente visíveis se chat.history retornar brevemente um snapshot mais antigo; a transcrição canônica substitui essas mensagens locais quando o histórico do Gateway alcança o estado atual.
    • Eventos chat ao vivo são estado de entrega, enquanto chat.history é reconstruído a partir da transcrição durável da sessão. Após eventos finais de ferramenta, a Control UI recarrega o histórico e mescla apenas uma pequena cauda otimista; o limite da transcrição está documentado em WebChat.
    • chat.inject anexa uma nota do assistente à transcrição da sessão e transmite um evento chat para atualizações apenas de UI (sem execução de agente, sem entrega por canal).
    • O cabeçalho do chat mostra o filtro de agente antes do seletor de sessão, e o seletor de sessão é escopado pelo agente selecionado. Trocar de agente mostra apenas sessões vinculadas a esse agente e recai para a sessão principal desse agente quando ele ainda não tem sessões de painel salvas.
    • Em larguras de desktop, os controles de chat permanecem em uma única linha compacta e se recolhem ao rolar para baixo na transcrição; rolar para cima, voltar ao topo ou chegar ao fim restaura os controles.
    • Mensagens consecutivas duplicadas somente de texto são renderizadas como um único balão com um selo de contagem. Mensagens que contêm imagens, anexos, saída de ferramenta ou prévias de canvas não são recolhidas.
    • Os seletores de modelo e raciocínio do cabeçalho do chat aplicam patch imediatamente à sessão ativa por meio de sessions.patch; são substituições persistentes da sessão, não opções de envio para apenas um turno.
    • Digitar /new na Control UI cria e alterna para a mesma sessão nova de painel que New Chat. Digitar /reset mantém a redefinição explícita in-place do Gateway para a sessão atual.
    • O seletor de modelo de chat solicita a visualização de modelos configurada do Gateway. Se agents.defaults.models estiver presente, essa lista permitida controla o seletor. Caso contrário, o seletor mostra entradas explícitas de models.providers.*.models mais provedores com autenticação utilizável. O catálogo completo permanece disponível pelo RPC de depuração models.list com view: "all".
    • Quando relatórios recentes de uso da sessão do Gateway mostram alta pressão de contexto, a área do compositor de chat mostra um aviso de contexto e, em níveis recomendados de Compaction, um botão compacto que executa o caminho normal de Compaction da sessão. Snapshots obsoletos de tokens ficam ocultos até o Gateway relatar uso recente novamente.
    Modo de fala (tempo real no navegador)

    O modo de fala usa um provedor de voz em tempo real registrado. Configure a OpenAI com talk.realtime.provider: "openai" mais talk.realtime.providers.openai.apiKey, ou configure o Google com talk.realtime.provider: "google" mais talk.realtime.providers.google.apiKey. O navegador nunca recebe uma chave de API padrão do provedor. A OpenAI recebe um segredo efêmero de cliente Realtime para WebRTC. O Google Live recebe um token de autenticação de Live API restrito de uso único para uma sessão WebSocket do navegador, com instruções e declarações de ferramenta travadas no token pelo Gateway. Provedores que expõem apenas uma ponte de tempo real de backend executam pelo transporte de relay do Gateway, então credenciais e sockets de fornecedores ficam no servidor enquanto o áudio do navegador passa por RPCs autenticados do Gateway. O prompt da sessão Realtime é montado pelo Gateway; talk.client.create não aceita substituições de instruções fornecidas pelo chamador.

    No compositor de chat, o controle Talk é o botão de ondas ao lado do botão de ditado por microfone. Quando Talk inicia, a linha de status do compositor mostra Connecting Talk..., depois Talk live enquanto o áudio está conectado, ou Asking OpenClaw... enquanto uma chamada de ferramenta em tempo real consulta o modelo maior configurado por meio de talk.client.toolCall.

    Smoke ao vivo para mantenedores: OPENAI_API_KEY=... GEMINI_API_KEY=... node --import tsx scripts/dev/realtime-talk-live-smoke.ts verifica a troca SDP de WebRTC do navegador da OpenAI, a configuração de WebSocket do navegador com token restrito do Google Live e o adaptador de navegador do relay do Gateway com mídia falsa de microfone. O comando imprime apenas o status do provedor e não registra segredos.

    Parar e abortar
    • Clique em Stop (chama chat.abort).
    • Enquanto uma execução está ativa, acompanhamentos normais entram na fila. Clique em Steer em uma mensagem na fila para injetar esse acompanhamento no turno em execução.
    • Digite /stop (ou frases autônomas de aborto como stop, stop action, stop run, stop openclaw, please stop) para abortar fora de banda.
    • chat.abort oferece suporte a { sessionKey } (sem runId) para abortar todas as execuções ativas dessa sessão.
    Retenção parcial de aborto
    • Quando uma execução é abortada, texto parcial do assistente ainda pode ser mostrado na UI.
    • O Gateway persiste texto parcial abortado do assistente no histórico da transcrição quando existe saída em buffer.
    • Entradas persistidas incluem metadados de aborto para que consumidores da transcrição consigam distinguir parciais abortadas de saída de conclusão normal.

    Instalação PWA e web push

    A Control UI inclui um manifest.webmanifest e um service worker, então navegadores modernos podem instalá-la como uma PWA independente. Web Push permite que o Gateway acorde a PWA instalada com notificações mesmo quando a aba ou janela do navegador não está aberta.

    Superfície O que faz
    ui/public/manifest.webmanifest Manifesto PWA. Navegadores oferecem "Instalar app" quando ele fica acessível.
    ui/public/sw.js Service worker que trata eventos push e cliques em notificações.
    push/vapid-keys.json (no diretório de estado do OpenClaw) Par de chaves VAPID gerado automaticamente usado para assinar payloads de Web Push.
    push/web-push-subscriptions.json Endpoints de assinatura do navegador persistidos.

    Substitua o par de chaves VAPID por variáveis de ambiente no processo do Gateway quando quiser fixar chaves (para implantações multi-host, rotação de segredos ou testes):

    • OPENCLAW_VAPID_PUBLIC_KEY
    • OPENCLAW_VAPID_PRIVATE_KEY
    • OPENCLAW_VAPID_SUBJECT (o padrão é mailto:openclaw@localhost)

    A Control UI usa estes métodos do Gateway com escopo restrito para registrar e testar assinaturas do navegador:

    • push.web.vapidPublicKey — busca a chave pública VAPID ativa.
    • push.web.subscribe — registra um endpoint mais keys.p256dh/keys.auth.
    • push.web.unsubscribe — remove um endpoint registrado.
    • push.web.test — envia uma notificação de teste para a assinatura do chamador.

    Embeds hospedados

    Mensagens do assistente podem renderizar conteúdo web hospedado inline com o shortcode [embed ...]. A política de sandbox do iframe é controlada por gateway.controlUi.embedSandbox:

    strict

    Desabilita a execução de scripts dentro de embeds hospedados.

    scripts (default)

    Permite embeds interativos mantendo o isolamento de origem; este é o padrão e geralmente é suficiente para jogos/widgets de navegador autocontidos.

    trusted

    Adiciona allow-same-origin sobre allow-scripts para documentos no mesmo site que precisam intencionalmente de privilégios mais fortes.

    Exemplo:

    {
      gateway: {
        controlUi: {
          embedSandbox: "scripts",
        },
      },
    }
    

    URLs externas absolutas de embed http(s) permanecem bloqueadas por padrão. Se você quiser intencionalmente que [embed url="https://..."] carregue páginas de terceiros, defina gateway.controlUi.allowExternalEmbedUrls: true.

    Largura de mensagens de chat

    Mensagens de chat agrupadas usam uma largura máxima padrão legível. Implantações em monitores largos podem substituí-la sem aplicar patch ao CSS empacotado definindo gateway.controlUi.chatMessageMaxWidth:

    {
      gateway: {
        controlUi: {
          chatMessageMaxWidth: "min(1280px, 82%)",
        },
      },
    }
    

    O valor é validado antes de chegar ao navegador. Valores compatíveis incluem comprimentos simples e porcentagens como 960px ou 82%, além de expressões de largura restritas min(...), max(...), clamp(...), calc(...) e fit-content(...).

    Acesso à tailnet (recomendado)

    Integrated Tailscale Serve (preferred)

    Mantenha o Gateway em loopback e deixe o Tailscale Serve encaminhá-lo por proxy com HTTPS:

    openclaw gateway --tailscale serve
    

    Abra:

    • https://<magicdns>/ (ou seu gateway.controlUi.basePath configurado)

    Por padrão, solicitações Serve da Control UI/WebSocket podem se autenticar por cabeçalhos de identidade do Tailscale (tailscale-user-login) quando gateway.auth.allowTailscale é true. O OpenClaw verifica a identidade resolvendo o endereço x-forwarded-for com tailscale whois e comparando-o ao cabeçalho, e aceita isso apenas quando a solicitação chega ao loopback com os cabeçalhos x-forwarded-* do Tailscale. Para sessões de operador da Control UI com identidade de dispositivo do navegador, esse caminho Serve verificado também pula a ida e volta de pareamento de dispositivo; navegadores sem dispositivo e conexões com função de nó ainda seguem as verificações normais de dispositivo. Defina gateway.auth.allowTailscale: false se quiser exigir credenciais explícitas de segredo compartilhado mesmo para tráfego Serve. Então use gateway.auth.mode: "token" ou "password".

    Para esse caminho assíncrono de identidade Serve, tentativas de autenticação com falha para o mesmo IP de cliente e escopo de autenticação são serializadas antes das escritas de limite de taxa. Portanto, novas tentativas ruins concorrentes do mesmo navegador podem mostrar retry later na segunda solicitação em vez de duas incompatibilidades simples competindo em paralelo.

    Bind to tailnet + token

    openclaw gateway --bind tailnet --token "$(openssl rand -hex 32)"
    

    Então abra:

    • http://<tailscale-ip>:18789/ (ou seu gateway.controlUi.basePath configurado)

    Cole o segredo compartilhado correspondente nas configurações da UI (enviado como connect.params.auth.token ou connect.params.auth.password).

    HTTP inseguro

    Se você abrir o painel por HTTP simples (http://<lan-ip> ou http://<tailscale-ip>), o navegador será executado em um contexto não seguro e bloqueará WebCrypto. Por padrão, o OpenClaw bloqueia conexões da UI de controle sem identidade do dispositivo.

    Exceções documentadas:

    • compatibilidade com HTTP inseguro apenas para localhost com gateway.controlUi.allowInsecureAuth=true
    • autenticação bem-sucedida da UI de controle do operador por meio de gateway.auth.mode: "trusted-proxy"
    • escape de emergência gateway.controlUi.dangerouslyDisableDeviceAuth=true

    Correção recomendada: use HTTPS (Tailscale Serve) ou abra a UI localmente:

    • https://<magicdns>/ (Serve)
    • http://127.0.0.1:18789/ (no host do gateway)
    Comportamento do alternador de autenticação insegura
    {
      gateway: {
        controlUi: { allowInsecureAuth: true },
        bind: "tailnet",
        auth: { mode: "token", token: "replace-me" },
      },
    }
    

    allowInsecureAuth é apenas um alternador de compatibilidade local:

    • Ele permite que sessões da UI de controle em localhost prossigam sem identidade do dispositivo em contextos HTTP não seguros.
    • Ele não contorna verificações de pareamento.
    • Ele não afrouxa os requisitos de identidade do dispositivo remoto (não localhost).
    Somente escape de emergência
    {
      gateway: {
        controlUi: { dangerouslyDisableDeviceAuth: true },
        bind: "tailnet",
        auth: { mode: "token", token: "replace-me" },
      },
    }
    
    Observação sobre proxy confiável
    • A autenticação bem-sucedida por proxy confiável pode admitir sessões da UI de controle de operador sem identidade do dispositivo.
    • Isso não se estende a sessões da UI de controle com função de nó.
    • Proxies reversos de loopback no mesmo host ainda não satisfazem a autenticação por proxy confiável; consulte Autenticação por proxy confiável.

    Consulte Tailscale para orientações de configuração de HTTPS.

    Política de segurança de conteúdo

    A UI de controle é fornecida com uma política img-src restrita: somente ativos de mesma origem, URLs data: e URLs blob: geradas localmente são permitidos. URLs de imagem remotas http(s) e relativas a protocolo são rejeitadas pelo navegador e não emitem buscas de rede.

    O que isso significa na prática:

    • Avatares e imagens servidos em caminhos relativos (por exemplo, /avatars/<id>) ainda são renderizados, incluindo rotas de avatar autenticadas que a UI busca e converte em URLs blob: locais.
    • URLs inline data:image/... ainda são renderizadas (úteis para cargas no protocolo).
    • URLs blob: locais criadas pela UI de controle ainda são renderizadas.
    • URLs de avatar remotas emitidas pelos metadados do canal são removidas pelos auxiliares de avatar da UI de controle e substituídas pelo logotipo/selo integrado, de modo que um canal comprometido ou malicioso não possa forçar buscas arbitrárias de imagens remotas a partir do navegador de um operador.

    Você não precisa alterar nada para obter esse comportamento — ele está sempre ativo e não é configurável.

    Autenticação da rota de avatar

    Quando a autenticação do gateway está configurada, o endpoint de avatar da UI de controle exige o mesmo token do gateway que o restante da API:

    • GET /avatar/<agentId> retorna a imagem do avatar somente para chamadores autenticados. GET /avatar/<agentId>?meta=1 retorna os metadados do avatar sob a mesma regra.
    • Solicitações não autenticadas para qualquer uma das rotas são rejeitadas (igual à rota irmã de mídia do assistente). Isso impede que a rota de avatar vaze a identidade do agente em hosts que, de outra forma, estão protegidos.
    • A própria UI de controle encaminha o token do gateway como um cabeçalho bearer ao buscar avatares e usa URLs blob autenticadas para que a imagem ainda seja renderizada nos painéis.

    Se você desativar a autenticação do gateway (não recomendado em hosts compartilhados), a rota de avatar também ficará não autenticada, em linha com o restante do gateway.

    Autenticação da rota de mídia do assistente

    Quando a autenticação do gateway está configurada, as pré-visualizações de mídia local do assistente usam uma rota em duas etapas:

    • GET /__openclaw__/assistant-media?meta=1&source=<path> exige a autenticação normal de operador da UI de controle. O navegador envia o token do gateway como um cabeçalho bearer ao verificar a disponibilidade.
    • Respostas de metadados bem-sucedidas incluem um mediaTicket de curta duração com escopo para esse caminho de origem exato.
    • URLs de imagem, áudio, vídeo e documento renderizadas pelo navegador usam mediaTicket=<ticket> em vez do token ou senha ativos do gateway. O tíquete expira rapidamente e não pode autorizar uma origem diferente.

    Isso mantém a renderização normal de mídia compatível com elementos de mídia nativos do navegador sem colocar credenciais reutilizáveis do gateway em URLs de mídia visíveis.

    Compilando a UI

    O Gateway serve arquivos estáticos a partir de dist/control-ui. Compile-os com:

    pnpm ui:build
    

    Base absoluta opcional (quando você quiser URLs de ativos fixas):

    OPENCLAW_CONTROL_UI_BASE_PATH=/openclaw/ pnpm ui:build
    

    Para desenvolvimento local (servidor de desenvolvimento separado):

    pnpm ui:dev
    

    Então aponte a UI para a URL WS do seu Gateway (por exemplo, ws://127.0.0.1:18789).

    Depuração/teste: servidor de desenvolvimento + Gateway remoto

    A UI de controle é composta por arquivos estáticos; o destino WebSocket é configurável e pode ser diferente da origem HTTP. Isso é útil quando você quer o servidor de desenvolvimento do Vite localmente, mas o Gateway é executado em outro lugar.

  • Inicie o servidor de desenvolvimento da UI

    pnpm ui:dev
    
  • Abra com gatewayUrl

    http://localhost:5173/?gatewayUrl=ws%3A%2F%2F<gateway-host>%3A18789
    

    Autenticação opcional de uso único (se necessário):

    http://localhost:5173/?gatewayUrl=wss%3A%2F%2F<gateway-host>%3A18789#token=<gateway-token>
    
  • Observações
    • gatewayUrl é armazenado em localStorage após o carregamento e removido da URL.
    • Se você passar um endpoint ws:// ou wss:// completo via gatewayUrl, codifique o valor de gatewayUrl para URL para que o navegador analise a string de consulta corretamente.
    • token deve ser passado pelo fragmento da URL (#token=...) sempre que possível. Fragmentos não são enviados ao servidor, o que evita vazamento por logs de solicitação e Referer. Parâmetros de consulta legados ?token= ainda são importados uma vez para compatibilidade, mas apenas como fallback, e são removidos imediatamente após o bootstrap.
    • password é mantido apenas na memória.
    • Quando gatewayUrl está definido, a UI não faz fallback para credenciais de configuração ou ambiente. Forneça token (ou password) explicitamente. Credenciais explícitas ausentes são um erro.
    • Use wss:// quando o Gateway estiver atrás de TLS (Tailscale Serve, proxy HTTPS etc.).
    • gatewayUrl só é aceito em uma janela de nível superior (não incorporada) para impedir clickjacking.
    • Implantações da UI de controle que não sejam loopback devem definir gateway.controlUi.allowedOrigins explicitamente (origens completas). Isso inclui configurações de desenvolvimento remoto.
    • A inicialização do Gateway pode semear origens locais como http://localhost:<port> e http://127.0.0.1:<port> a partir do bind e da porta efetivos em tempo de execução, mas origens de navegador remotas ainda precisam de entradas explícitas.
    • Não use gateway.controlUi.allowedOrigins: ["*"] exceto para testes locais rigidamente controlados. Isso significa permitir qualquer origem de navegador, não "corresponder a qualquer host que eu esteja usando".
    • gateway.controlUi.dangerouslyAllowHostHeaderOriginFallback=true habilita o modo de fallback de origem por cabeçalho Host, mas é um modo de segurança perigoso.

    Exemplo:

    {
      gateway: {
        controlUi: {
          allowedOrigins: ["http://localhost:5173"],
        },
      },
    }
    

    Detalhes de configuração de acesso remoto: Acesso remoto.

    Relacionados