DisPatch: Um aplicativo de chat auto-hospedado para agentes de IA locais

Publicado em
11 julho 2026
Atualizado em
9 outubro 2026
Por
Jacob Lloyd — escrito com ajuda de IA, depois do projeto
Tempo de leitura
18 min de leitura

Em termos simples: O DisPatch é um aplicativo de chat que roda no seu próprio computador e funciona em qualquer telefone ou laptop da casa. Você o usa para conversar com assistentes de IA que também rodam nesse computador, além de poder enviar arquivos para que os assistentes possam trabalhar com eles. Um PIN mantém esses poderosos assistentes protegidos; assim, um dispositivo compartilhado pela família só tem acesso a um conjunto seguro de funcionalidades.

O DisPatch é o aplicativo que criei para não precisar mais manter uma instalação do Rocket.Chat com 30 containers apenas para trocar mensagens com meus próprios agentes de IA. Trata-se de um único processo em Python e um arquivo SQLite. Qualquer dispositivo na casa consegue acessá-lo; os agentes respondem em threads, assim como as pessoas fazem. Tudo o que eu envio do meu celular chega à máquina onde os agentes estão hospedados. A versão 2.0.0 foi lançada em 09/10/2026: o download é um arquivo zip de 3 MB cujo conjunto de testes executa 1.915 verificações, todas com sucesso. Esta página explica o que o DisPatch faz, como as respostas chegam ao seu celular exatamente uma vez, e como o bloqueio por PIN impede que um tablet da família seja acessado por agentes capazes de realizar alterações reais.

Resumindo:

  • O que é: um chat web auto-hospedado para agentes de IA locais (um gateway OpenClaw, ou uma API de modelo para a qual você pode apontar o DisPatch), além de funcionalidade de arrastar e soltar arquivos; utilizável em qualquer dispositivo capaz de acessar o servidor.
  • Quanto custa: $0. Sem taxas por uso, sem necessidade de conta.
  • O que é necessário: Linux com Python 3.12+ e uv, ou Docker. O OpenClaw é opcional, caso você queira agentes capazes de utilizar ferramentas externas.
  • O que você obtém: threads individuais para cada bot, respostas em tempo real com botão de interrupção, opções de regenerar e editar mensagens, suporte a arrastar arquivos, ferramentas e aplicativos embutíveis, dez temas visuais, um Modo Seguro controlado por PIN e uma API de push para tarefas agendadas.
  • O download: a versão 2.0.0, idêntica à tag v2.0.0 no repositório público. Licença MIT.

O que ele faz

As capturas de tela são de uma cópia em ambiente isolado com dados fictícios. As quatro primeiras são da versão 2.0; as imagens do Modo Seguro e do Servidor de Arquivos são da versão de julho, e funcionam da mesma forma na versão 2.0.

DisPatch 2.0 em um desktop com o tema Glacier: uma barra lateral com três avatares de bots, a lista de conversas do bot planejador agrupada por dia, e uma conversa na qual ele responde com um plano de passeio em formato de lista e uma tabela comparativa classificável.
Versão desktop, 1440px, tema Glacier: barra lateral de bots, lista de conversas e painel de chat. O plano e a tabela são renderizados em markdown; a tabela permite ordenação por coluna.
Uma resposta no tema Forest contendo uma tabela de checklist com quatro tarefas domésticas; cada linha possui uma caixa de seleção e colunas classificáveis: Tarefa, Quem e Quando.
Tabela de checklist, novidade na versão 2.0: um bot a publica, qualquer pessoa pode marcar as tarefas como concluídas, e essas marcações são sincronizadas em todos os dispositivos.
DisPatch em resolução 390px: uma conversa em um único painel, com lista numerada e lista de compras em formato markdown, botão “Copiar” e menu sob cada mensagem; barra inferior com abas para Bots, Conversas e Mensagens.
O mesmo aplicativo em resolução 390px: a barra lateral se transforma numa barra de abas inferior. Cada mensagem exibe o botão “Copiar” e um menu ⋯ com opções como regenerar, editar, citar, etc.
Configurações na aba Tema: prévias em miniatura de seis temas principais e uma opção “Mais temas” com quatro adicionais.
Configurações → Tema: dez temas com prévias dinâmicas, selecionáveis por dispositivo.
DisPatch no Modo Seguro após definição de PIN: a barra lateral de bots é reduzida para apenas três bots aprovados; há um botão de cadeado âmbar na parte inferior para desbloquear os demais.
Modo Seguro com PIN definido: apenas os bots marcados como “seguros” são exibidos. O servidor nunca envia os demais.
  • Conversas individuais por bot em todos os dispositivos. Abra-a no celular durante uma conversa e será a mesma thread com todo o histórico. As threads podem ser fixadas, arquivadas, renomeadas e pesquisadas.
  • Respostas em markdown em tempo real, com destaque de sintaxe para código, botões “Copiar”, prévias de imagens e vídeos via lightbox, além de suporte a PWA instalável.
  • Painel de execução em tempo real e botão “Parar”. Uma linha de status indica o que o agente está fazendo (carregando modelo, escrevendo, usando uma ferramenta). Clique nos pontos de digitação para acompanhar o raciocínio do agente e as chamadas a ferramentas; pressione “Parar” para interromper a execução.
  • Controles comuns de apps de chat. A opção “Regenerar” mantém as respostas anteriores; é possível editar uma mensagem e reexecutar a partir dela, citar uma resposta, dar avaliação ou remover uma mensagem do contexto. Cada thread permite definir o modelo e o nível de raciocínio desejado; um medidor indica o quanto o contexto está preenchido.
  • Rascunhos e caixa de saída offline. Um rascunho não enviado permanece vinculado à sua thread; mensagens enviadas sem conexão ficam em espera e são transmitidas assim que a conexão for restabelecida.
  • Ferramentas e apps. O botão “Ferramentas” abre projetos próprios dentro do app: uma pasta estática, uma página web ou um pacote de app confiável com backend e bot próprios. O Job Board é o primeiro app disponível.
  • Duas formas de conectar a IA. Basta indicar diretamente na configuração a API do modelo (LM Studio, Ollama, OpenAI, Anthropic ou qualquer outro compatível com OpenAI); também é possível conectar um gateway OpenClaw para agentes que utilizam ferramentas e agem diretamente no sistema.
  • API de push. O POST /api/inject permite que um cron job ou outro agente insira uma mensagem numa thread. É assim que recebo relatórios matinais no celular sem precisar pedir.
  • Sistema de bloqueio em duas camadas. O PIN separa os agentes com plenos poderes dos bots considerados “seguros”. Mais detalhes abaixo.
  • Painel de controle do servidor. Exibe saúde do sistema, armazenamento, integridade do banco de dados e status dos backups; também lista o que está mal configurado.
  • Oito idiomas de interface, incluindo árabe (escrito da direita para a esquerda, e dez temas.
  • Visualizador local de arquivos. Ao tocar num caminho de arquivo numa mensagem, o arquivo é aberto dentro do app, inclusive no celular. Nada é servido sem que você defina uma pasta; segredos e caminhos do sistema jamais são expostos.

Os agentes também podem inserir imagens numa thread. Um bot escreve um marcador [[pic:prompt|caption]] na sua resposta; imediatamente aparece um espaço reservado, que se transforma na imagem assim que a renderização termina. As minhas imagens vêm de uma máquina com GPU localizada em outra parte da rede. Se essa máquina estiver fora do ar, o espaço reservado informa isso, em vez de falhar silenciosamente.

O Servidor de Arquivos

Este é o recurso que mais uso. Basta abrir o DisPatch em qualquer dispositivo, arrastar um arquivo para lá, e o arquivo fica no servidor, agrupado por dia, com miniaturas para imagens e vídeos.

Visualização do Servidor de Arquivos do DisPatch: uma zona de upload na parte superior, seguida de arquivos agrupados por dia com miniaturas e informações sobre o tipo de arquivo.
O Servidor de Arquivos: zona de upload no topo; os arquivos são agrupados por dia abaixo. Num celular, este é todo o fluxo de trabalho.

O grande benefício está no local onde os arquivos são armazenados: na mesma máquina onde os agentes rodam. Uma foto enviada do meu celular pode ser lida diretamente do disco por um agente segundos depois; assim, posso descrevê-la, arquivá-la ou colocá-la num site, sem precisar de serviços em nuvem ou cabos.

O código previne os modos de falha mais comuns:

  • O tamanho do arquivo a ser enviado é verificado antes que o FastAPI processe o corpo da requisição; assim, um celular não consegue esgotar a memória do servidor.
  • O armazenamento total é limitado a 20 GB por padrão (DISPATCH_FILES_TOTAL_MAX; valor 0 desativa esse limite).
  • Cada upload é gravado num arquivo temporário, sincronizado e depois renomeado para o local final; portanto, uma interrupção na conexão nunca deixa um arquivo incompleto.
  • Um dispositivo bloqueado só pode fazer uploads. Ele consegue enviar um arquivo, mas não pode navegar ou baixar nada que esteja no Servidor de Arquivos.

Por que eu o criei em vez de usar uma interface existente?

O Rocket.Chat funcionava, mas um banco de dados e vários containers representam muita infraestrutura para algo tão simples quanto “enviar texto, receber texto de volta e sincronizá-lo em todos os lugares”.

O Open WebUI e o LibreChat são as soluções mais comuns para “chat de IA auto-hospedado”, e são bons em se comunicar com modelos. Minhas necessidades, porém, eram diferentes. Eu queria agentes que participassem dos threads compartilhados por toda a casa, um mecanismo de bloqueio imposto pelo servidor para dispositivos usados por crianças, um endpoint HTTP onde scripts pudessem enviar dados, além de arquivos que ficassem em locais acessíveis aos agentes. Essa combinação é o que o DisPatch oferece. Se você só precisa de uma interface bonita para um modelo, comece por um desses.

Além disso, não há nenhum passo de compilação. O frontend utiliza módulos JS puros e CSS/HTML simples; não há bundler nem npm run build. Meu requisito inicial era “ser fácil para um agente de IA modificar posteriormente”: basta editar um arquivo e atualizar a página. Eu faria a mesma escolha novamente.

O que vem no download

O arquivo zip é dispatch-2026-10.zip, a versão 2.0.0 do DisPatch, idêntico byte a byte ao tag v2.0.0 no repositório público. Medi as dimensões para esta revisão:

ItemNo zip
Tamanho do download2.992.647 bytes (2,9 MB); 319 arquivos, 7,9 MB após descompactação
SHA-256e9824d3c13bb3acd2d2eebcf2b162b14ae26eb86f56ca0e4ac8aa20f686a2398
BackendPython 3.12+, FastAPI + uvicorn, um arquivo SQLite, gerenciado via uv. São 36.501 linhas em backend/app/ (main.py possui 12.331 linhas), além de 5.497 linhas no aplicativo Job Board incluído
Testes30.726 linhas de testes em Python; o pytest relata 1.915 testes aprovados, e 473 testes front-end são executados via node --test
FrontendJavaScript puro, sem compilação: cerca de 21.000 linhas fora das bibliotecas e testes incluídos, além de aproximadamente 9.300 linhas de CSS e HTML
Bibliotecas embarcadasmarked, highlight.js, DOMPurify. Não há chamadas a CDNs
DadosFora do código-fonte, por padrão em ~/.local/share/local-chat/ (nome antigo do projeto): o banco de dados, os arquivos config.yaml e security.yaml, mídias, arquivos e backups rotacionados do banco
LicençaMIT

O que mudou desde o zip 1.0.0 de agosto:

  • As respostas agora seguem um caminho mais rápido e não se perdem. As mensagens são encaminhadas para o gateway do agente através do WebSocket já aberto, em vez de iniciar um novo processo openclaw por mensagem. Isso reduziu cerca de um segundo no tempo de resposta (de 2,26 s para 1,12 s, conforme medição). O texto chega à medida que o modelo o gera. Cada solicitação aceita pelo gateway é rastreada até sua conclusão; assim, a resposta ainda é enviada mesmo após queda de conexão ou reinicialização do servidor.
  • Licença. A versão 1.0.0 usava AGPL-3.0. Em 30/08/2026 o projeto foi relicenciado para MIT, e a versão 2.0.0 segue essa licença. Os zips antigos de julho e agosto permanecem nos endereços originais, cada um com seu próprio arquivo LICENSE.
  • O terminal de codificação foi removido. A versão 1.0.0 incluía um terminal opcional no navegador (um PTY do servidor transmitido para o xterm.js). Eu o usava para um agente de programação que depois retirei; por isso, a versão 2.0 elimina esse recurso e sua biblioteca embarcada. O painel DeepSeek Harness, ativado quando o dsh está instalado, substitui essa função e agora permite iniciar, monitorar e interromper sessões ao vivo.
  • Novidades: controles de conversa, rascunhos e caixa de saída, ferramentas e aplicativos, tabelas de checklist, visualizador local, marcadores de imagem, dez temas em vez do simples alternador claro/escuro, além de linhas de mensagem mais discretas. A lista completa está no arquivo CHANGELOG.md do repositório.

Configuração

Este é um resumo extraído do README.md e da pasta docs/ dentro do arquivo zip. Trata-se de um projeto normal do uv:

unzip dispatch-2026-10.zip
cd DisPatch-2.0.0/backend
uv sync --frozen
uv run pytest -q          # 1,915 passed on my machine
uv run uvicorn app.main:app --host 0.0.0.0 --port 8765

Ou, para pular o Python: execute cp .env.example .env && docker compose up -d a partir da raiz da pasta descompactada. A pasta deploy/systemd/ contém os arquivos de configuração para a versão que roda continuamente; já o arquivo docs/deploy-bare-metal.md explica um passo que todo mundo esquece: loginctl enable-linger. Sem isso, o serviço do seu usuário para de funcionar assim que você encerra a sessão SSH.

  • Defina um PIN primeiro (ícone de engrenagem, depois “Segurança”). Ambos os comandos acima escutam conexões em 0.0.0.0; portanto, até que um PIN seja definido, qualquer pessoa na sua rede terá acesso total. Use --host 127.0.0.1 (ou BIND_ADDR=127.0.0.1 no Docker) enquanto explora o sistema, e não redirecione a porta para a internet.
  • Edite o arquivo config.yaml na pasta de dados para que os IDs dos bots id correspondam aos IDs dos agentes do OpenClaw. O arquivo é lido novamente sempre que sofre alterações.
  • As configurações vêm de variáveis de ambiente cujo prefixo é DISPATCH_; todas estão listadas em .env.example: HOST, PORT, DATA_DIR, AGENT_TIMEOUT (900 s), MAX_CONCURRENCY (3) e GATEWAY_WS.
  • O parâmetro DISPATCH_MAX_CONCURRENCY limita quantas execuções de agentes podem ocorrer simultaneamente. O valor padrão, 3, protege máquinas que executam modelos locais. Uma quarta solicitação aguardará sua vez; ela não é descartada.

O DisPatch não inclui o OpenClaw. Para os agentes, ele espera a presença de um gateway do OpenClaw no mesmo host. Mesmo sem esse gateway, o aplicativo inicia normalmente e registra um aviso nos logs. As threads, a API de injeção, o servidor de arquivos, o bloqueio por PIN, as ferramentas e as APIs diretas dos modelos continuam funcionando. É assim que faço os testes preliminares do pacote: descompacto-o, executo uv sync, uso uma porta livre e não tenho nenhum gateway configurado.

Como uma resposta chega até você

A maior parte do trabalho de engenharia foi dedicada aos casos de falha: a conexão cai no meio da resposta, o servidor reinicia com um processo em andamento ou um subagente responde dez minutos depois. O transporte é escolhido pelo parâmetro DISPATCH_GATEWAY_WS. Se não estiver definido, cada mensagem inicia um processo do openclaw agent, que devolve a resposta ao finalizar. O modo shadow conecta-se e registra o que seria enviado, sem efetivamente entregar a mensagem. O valor 1 indica que as mensagens são enviadas pelo socket do gateway, com as respostas retornando por esse mesmo canal. Eu uso 1. O pacote zip vem com essa opção desativada; portanto, nada muda na entrega das respostas até que você a ative.

Com o socket ativado, todo processo aceito pelo gateway é registrado numa tabela de processos em andamento e finalizado por meio da chamada “aguarde este processo” do próprio gateway. Mesmo se a transmissão ao vivo for interrompida, a resposta chega assim que o processo termina; além disso, os processos que estavam em andamento quando o servidor caiu são recuperados na inicialização. Apenas os processos que o gateway nunca aceitou são retransmitidos, evitando assim envios duplicados.

Há uma ressalva quanto ao mecanismo de backup. A varredura de transcrições lê os arquivos de sessão do OpenClaw; porém, versões mais recentes do OpenClaw armazenam as sessões num banco de dados. Nesses casos, o DisPatch informa na inicialização que os mecanismos de backup baseados em arquivos não podem ser executados, sendo o socket o único caminho de entrega. Esse é mais um motivo para ativar o socket.

Qualquer que seja o caminho escolhido, toda resposta potencial passa por um único canal que verifica se já foi entregue; assim, mesmo que uma resposta apareça duas vezes, ela é exibida apenas uma vez. Mesmo em caso de erro na CLI, a varredura é executada antes de desistir, pois o modelo frequentemente escreve sua resposta na transcrição antes do processo ser encerrado.

O bloqueio de dois níveis: modo normal vs. Modo Seguro

O lado desbloqueado não é uma brincadeira. Esses agentes podem editar código, enviar alterações para o meu site ao vivo, executar ferramentas e gerenciar a máquina onde estão instalados. O lado bloqueado permite apenas conversas comuns; ainda é útil para tirar dúvidas e obter ajuda, mas não tem acesso a nada mais. O telefone da família nem sempre está nas mãos de adultos, então o servidor impõe esse bloqueio. Veja o que acontece com uma solicitação:

O recurso de lembrar dispositivos fica desativado até que você o ative nas configurações de Segurança, definindo o período de confiança em 30 dias. Ao pressionar ‘Bloquear’ num dispositivo lembrado, esse dispositivo é removido da lista; alterar o PIN apaga todos os dispositivos memorizados. O servidor armazena no máximo 10 dispositivos confiáveis. Se o arquivo de dispositivos confiáveis estiver corrompido, nenhum dispositivo será considerado confiável, mas o PIN continuará funcionando.

Se você se bloquear acidentalmente, há duas maneiras de reverter a situação: um arquivo de código de recuperação ou a edição direta do arquivo security.yaml. Caso o security.yaml esteja danificado, o aplicativo permanece bloqueado, impedindo que qualquer pessoa na rede local tenha acesso. Essa foi uma decisão intencional.

Armadilhas comuns

  • O campo da API de injeção é content. O endpoint POST /api/inject espera um corpo do tipo {"bot_id": "...", "content": "..."}. Enviar apenas o texto (text) foi o erro de integração mais comum, inclusive por minha parte.
  • Não passe mensagens longas como argumentos da linha de comando (isso ainda é importante no caminho de fallback). O Linux limita o tamanho de um único argumento a 128 KB (MAX_ARG_STRLEN); uma mensagem muito longa gera erro E2BIG ao tentar executá-la. O código grava a mensagem num arquivo temporário e passa o caminho via --message-file. Não “simplifique” isso voltando a usar um argumento comum.
  • As chaves de sessão são minúsculas; os IDs dos bots não. O OpenClaw converte as chaves de sessão para minúsculas, mas seus IDs podem ter letras maiúsculas e minúsculas (Alpha, My_Bot). Num sistema sensível a maiúsculas/minúsculas, o painel de controle e o envio de respostas atrasadas simplesmente param de funcionar, embora tudo o mais continue normal. A solução é fazer uma comparação exata primeiro; caso contrário, usar a versão em minúsculas como fallback.
  • A linha de comando retorna apenas o último bloco de texto. Sem conexão via socket, tudo o que um agente diz entre chamadas de ferramentas fica registrado apenas no seu histórico. Por isso existe o processo de varredura do histórico; ele continua sendo executado mesmo quando a conexão via socket está ativa.
  • Verifique o cabeçalho Origin nos WebSockets. O middleware HTTP não é aplicado às rotas WebSocket. O DisPatch realiza a autenticação diretamente e rejeita qualquer handshake cujo host no Origin não corresponda ao valor de Host, evitando ataques de sequestro de WebSockets entre sites. Clientes que não são navegadores não enviam o cabeçalho Origin e, portanto, são aceitos.
  • Nunca sirva arquivos SVG enviados como imagens. Um arquivo SVG aberto diretamente pode executar scripts no seu domínio, criando assim uma vulnerabilidade de tipo XSS armazenado. O DisPatch rejeita SVG como mídia e envia arquivos desse tipo com políticas CSP que garantem isolamento e o parâmetro nosniff.
  • As atualizações do service-worker deixam abas antigas rodando versões antigas do JavaScript. Isso parece exatamente um problema de regressão. A solução é realizar uma recarga automática ao disparar o evento controllerchange.

Replicando a ideia

Mesmo que você nunca execute meu pacote zip, a arquitetura é composta por quatro componentes substituíveis:

  • Um backend de chat que gerencia o histórico: um pequeno servidor web (neste caso, FastAPI e um arquivo SQLite no modo WAL), um WebSocket para atualizações em tempo real e um endpoint HTTP simples para o envio de mensagens. Isso substitui o banco de dados e toda a pilha de containers.
  • Um gateway de agentes com o qual o backend se comunica: aqui uso o OpenClaw, mas qualquer serviço que aceite um ID de sessão e uma mensagem e retorne texto também serve. Como processo separado, ele pode falhar, ser atualizado ou substituído sem afetar o aplicativo de chat.
  • Servidores de modelos por trás do gateway (LM Studio, vLLM, Ollama). Eles são responsabilidade do gateway, não do aplicativo de chat.
  • Serviços de mídia opcionais, como um gerador de imagens em outra máquina, que os agentes podem utilizar como ferramentas. O backend apenas armazena o resultado dessas chamadas.

Essas lições são válidas independentemente da sua pilha tecnológica:

  • Exija o modo “bloqueado” no servidor, nunca via CSS.
  • Considere que “a resposta chegou ao usuário exatamente uma vez” como um problema de entrega que deve ser resolvido com deduplicação.
  • Mantenha o frontend sem necessidade de compilação, caso agentes de IA venham a mantê-lo.

Relacionado: minha pilha de agentes de IA local usando OpenClaw · primeiras impressões do DeepSeek Harness · gerenciando meus sites com uma caixa de agentes de IA local · configurando seu assistente LLM

Downloads

Gratuito para uso pessoal. Se isso te economizar uma tarde, o botão do café está logo ali.


← Mais de IA e LLM Local