Construí o touri, um Grok Bot open source: bots de IA que trabalham no seu computador, com as suas contas (agora também no Linux)
Nos últimos meses eu construí o touri, um app desktop open source (Apache-2.0) para ter "colegas de trabalho" de IA rodando no seu próprio computador. Esta semana ele passou a rodar também no Linux, então resolvi contar aqui como ele funciona, as decisões de produto por trás dele e alguns bugs que me ensinaram bastante.
Repositório: https://github.com/touri-ai/touri · Site: https://touri.ai
O problema que eu queria resolver
Eu já usava Codex e Claude Code no terminal o dia inteiro. Eles são ótimos para uma tarefa por vez, mas eu queria algo diferente:
- um bot que continua existindo depois da conversa, com memória, função e rotina;
- vários bots com funções diferentes (um para suporte, um para leads, um para revisar PRs);
- a possibilidade de pedir algo pelo celular e o trabalho acontecer na minha máquina;
- e tudo isso sem entregar meus arquivos e minhas contas para mais um serviço na nuvem.
Se você viu o Grok Bot, a ideia é parecida: bots com nome, função e um computador para trabalhar. A diferença é que lá o agente roda numa máquina deles, com assinatura e o modelo deles. O touri faz o caminho oposto: é open source, roda na sua máquina e usa a sua conta. Pense nele como um Grok Bot open source e local-first.
A ideia em uma frase
Cada bot do touri é um agente persistente que usa o CLI que você já tem e a conta que você já paga (Codex, Claude Code ou OpenCode), rodando no seu computador.
Algumas consequências disso:
- O touri não revende modelo. Se você já tem ChatGPT Plus/Pro ou Claude Pro/Max, o bot usa essa conta.
- Não tem telemetria. Conversas, memória, rotinas e segredos ficam num SQLite local. Logins vão para o Keychain do macOS ou para o Secret Service no Linux, nunca para o banco ou para os logs.
- Você está no comando. Ação arriscada (escrever numa pasta nova, rodar um comando fora do esperado) vira um cartão de aprovação em português, com o que vai acontecer e o alcance. Você aprova, nega ou libera "sempre neste bot".
Como é usar
Criar um bot é uma tela só: nome, o que ele faz e um mascote. Tem um catálogo de funções prontas ("Responde no suporte", "Olha os PRs e revisa", "Organiza minhas notas"…) que já define o papel e as instruções. Provedor, modelo, nível de acesso, máquina, projeto e MCP ficam atrás de um botão "Avançado". O touri escolhe padrões seguros: usa o primeiro CLI logado que encontrar e começa pedindo permissão antes de mudar arquivos.
Uma coisa que aprendi testando com gente não técnica: cada palavra técnica na tela é um ponto de abandono. A primeira versão tinha um assistente de 5 etapas e 12 campos. Mesmo para quem entende, "sandbox", "workspace-write" ou "MiB" na hora de criar o primeiro bot só atrapalham. Hoje são 4 cliques e dois campos (nome e a primeira mensagem) até a primeira resposta.
No chat, o bot mostra o que está fazendo (lendo um arquivo, rodando um comando, esperando a IA), e você pode parar a qualquer momento. Se a espera passa de alguns segundos, a tela diz isso honestamente em vez de ficar num "Pensando…" eterno.
Rotinas: "todo dia às 9h, me dê uma ideia de post" se cria escolhendo dias e hora, sem escrever cron. Você revisa e ativa; o bot nunca ativa uma rotina sozinho.
Grupos: vários bots na mesma conversa, e você chama um deles com @menção.
Máquina por bot (opcional): um bot pode rodar dentro da própria VM, com Apple container no macOS ou Podman/Docker no Linux. Ele tem navegador próprio e você pode acompanhar a tela, assumir o controle e devolver.
Modo servidor: touri serve roda o mesmo app num servidor seu (o meu é um container na rede de casa) e você acessa pelo navegador do celular via Tailscale, depois de parear com um código de uso único. O trabalho continua acontecendo no servidor mesmo com a aba fechada.
Por baixo do capô
- App: Tauri 2 (Rust) + React. O processo pesado é um sidecar em TypeScript compilado com Bun (
bun build --compile). - Estado: SQLite com migrações versionadas. O app nativo confere a versão do esquema no handshake com o sidecar, o que já me salvou de um bug (mais abaixo).
- Eventos: o sidecar publica um log de eventos com sequência contínua. A UI assina via WebSocket e reconecta a partir do último número que viu.
- Isolamento: no macOS o CLI roda dentro de um perfil do Seatbelt do próprio touri. No Linux, o confinamento das pastas liberadas com
bwrap(bubblewrap) está em revisão agora. - Ferramentas para os bots: um servidor MCP local expõe ferramentas como "pedir acesso a uma pasta", "vincular um projeto" e "criar rotina" (que nasce pausada). Toda ação sensível passa pela fila de aprovação.
Bugs que valeram o post
1. O chat que "não respondia". Mandei um "oi" para um bot. A notificação chegou com a resposta, mas o chat ficou em "Pensando…". O log de eventos tinha 120 MB, porque cada atualização de mensagem durante o streaming gravava o corpo inteiro de novo. Ao abrir o app, o cliente pedia o replay desde o início. O WebSocket se fechava sozinho por backpressure aos 8 MB e reconectava, umas 60 vezes, e os eventos novos ficavam presos atrás desse backlog. A notificação usava outra conexão, sem backlog, por isso chegava na hora.
A correção:
- acima de um limite (2.000 eventos ou 2 MB), o servidor manda um único frame de resync e o cliente recarrega o estado por REST;
- no replay, cada mensagem vai só na versão mais recente, e as antigas viram um marcador pequeno, para a sequência continuar contínua;
- o log guarda um ponteiro para cada versão de mensagem em vez de copiar o corpo inteiro.
2. Segfault no início de cada turno. O servidor caía de forma intermitente dentro do servidor HTTP do Bun (uncork num socket que já tinha sido fechado, endereço 0x0). A causa era um inocente Bun.spawnSync(["ps", ...]) para rastrear os processos do CLI, chamado dentro do handler que inicia o turno. No Bun 1.2.21, enquanto o spawnSync espera, o event loop continua rodando callbacks HTTP e timers de outras conexões, e o estado interno do uWS fica inconsistente. Num teste mínimo foram 54 reentradas de handler e 72 timers em 2 segundos dentro de um spawnSync.
A correção foi trocar as quatro chamadas por uma versão assíncrona, criar um teste de estresse (quatro bots iniciando turnos em loop com clientes reconectando: falhava 6 de 6 vezes, agora passa 20 de 20) e um teste que falha se alguém voltar a usar spawnSync no servidor.
3. O app que não abria depois de atualizar. Um PR adicionou a migração 18 do banco, mas esqueceu de atualizar a constante de versão esperada no Rust. Resultado: "schema mismatch: shell expects 17, sidecar reported 18". O teste que pegaria isso existia, mas o job de Rust do CI estava vermelho havia dias por outro motivo (o cargo deny nem estava instalado), e ninguém olhava mais para ele. Lição velha, mas que sempre volta: um CI que vive vermelho é um CI desligado. Corrigi a constante e fiz o job ficar verde de verdade.
4. Logins que sumiam depois de recompilar. No macOS, um item do Keychain confia na assinatura do app que o criou. Builds assinados ad-hoc mudam de assinatura a cada compilação, então o app novo perdia o acesso aos logins salvos e a leitura travava esperando um diálogo que nunca aparecia (o sidecar roda em segundo plano). Agora a leitura tem prazo, o erro vira um botão "Permitir acesso ao cofre" no chat que abre a autorização do macOS em primeiro plano, e os builds locais usam uma identidade de assinatura estável.
Como eu testo
Cada rodada termina com um agente usando o app de verdade por computer use, como um usuário novo, e anotando passo a passo o que funcionou, quanto tempo levou e onde travou. A primeira rodada desta semana terminou com 1 bug que impedia o uso. A última, com 0 bloqueios e 80 de 96 passos aprovados. Todo bug vira issue pública com evidência, e todo PR passa por uma revisão independente antes do merge.
Onde está e como testar
- Código: https://github.com/touri-ai/touri (Apache-2.0)
- Site: https://touri.ai
- macOS: Apple Silicon. Precisa do Codex, Claude Code ou OpenCode já logado.
- Linux: x86_64 (arm64 em validação). Instruções e requisitos na seção "Linux install" do README; o pacote .deb da próxima release vai ficar linkado lá. Também roda como servidor com
touri serve.
Ainda é cedo: tem arestas, principalmente no Linux, que acabou de chegar. Feedback, issues e PRs são muito bem-vindos. Se você usa Linux e testar, me conta como foi.
