DisPatch: Um aplicativo de chat auto-hospedado para agentes de IA locais
- Categoria
- IA e LLM Local
- 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.0no 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.





- 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/injectpermite 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.

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:
| Item | No zip |
|---|---|
| Tamanho do download | 2.992.647 bytes (2,9 MB); 319 arquivos, 7,9 MB após descompactação |
| SHA-256 | e9824d3c13bb3acd2d2eebcf2b162b14ae26eb86f56ca0e4ac8aa20f686a2398 |
| Backend | Python 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 |
| Testes | 30.726 linhas de testes em Python; o pytest relata 1.915 testes aprovados, e 473 testes front-end são executados via node --test |
| Frontend | JavaScript 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 embarcadas | marked, highlight.js, DOMPurify. Não há chamadas a CDNs |
| Dados | Fora 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ça | MIT |
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
openclawpor 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
dshestá 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.mddo 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(ouBIND_ADDR=127.0.0.1no Docker) enquanto explora o sistema, e não redirecione a porta para a internet. - Edite o arquivo
config.yamlna pasta de dados para que os IDs dos botsidcorrespondam 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) eGATEWAY_WS. - O parâmetro
DISPATCH_MAX_CONCURRENCYlimita 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 endpointPOST /api/injectespera 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
Originnão corresponda ao valor deHost, 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.