Conteúdo detalhado
🧩 As três camadas
Olhar a árvore de pastas dá o mapa imediato: src/ (core Rust), app/src-tauri/ (shell Tauri) e app/src/ (React). Cada uma tem regra própria de o que pode ou não morar lá.
📂 Layout do repo (resumido)
💡 Mnemônico
Core decide, shell entrega, app apresenta. Se você está mexendo em regra de negócio no shell ou tomando decisão de produto no React, parou no andar errado.
⚡ Core in-process
Até o PR #1061, o core era um binário separado (sidecar) disparado pelo Tauri. Hoje ele virou uma task tokio dentro do próprio processo do shell. Resultado: Cmd+Q mata tudo junto, sem processos zumbis.
✗ Antes (sidecar)
- ✗Binário
openhuman-corespawned como child - ✗Sobrevivia a crashes do shell — processo órfão
- ✗Stage step extra no build (
pnpm core:stage) - ✗IPC stdio frágil entre processos
✓ Agora (in-process)
- ✓Task tokio dentro do shell Tauri
- ✓Lifecycle unificado — Cmd+Q encerra tudo
- ✓
pnpm core:stagevirou no-op - ✓RPC via HTTP localhost com bearer
🔧 Escape hatch para debug
Quer atachar a um core rodando fora do Tauri (ex: ./target/debug/openhuman-core serve)? Use:
🔌 Transporte JSON-RPC
Frontend e core conversam por JSON-RPC 2.0 sobre HTTP localhost. Autenticação é por bearer token efêmero gerado a cada launch (variável OPENHUMAN_CORE_TOKEN).
📡 Forma de uma chamada
🚫 NÃO use fetch() direto
Chamar fetch('http://127.0.0.1:.../rpc') do React dispara CORS preflight e quebra. Sempre passe pelo core_rpc_relay (IPC Tauri) — ele é trampolim que evita o preflight e injeta o token.
💡 Convenção de naming
Métodos seguem openhuman.<dominio>_<funcao>. Ex: openhuman.threads_list, openhuman.memory_search, openhuman.cron_create. Esse padrão é gerado a partir do controller registry — não improvise.
🎨 CEF vs WebKit
Tauri padrão usa o WebView nativo do sistema (WebKit no macOS, WebView2 no Windows). OpenHuman trocou por CEF — Chromium Embedded Framework. Por quê? Para ter controle consistente sobre webviews de terceiros (Slack, WhatsApp, Telegram embarcados como contas).
✓ Por que CEF
- ✓Mesmo runtime nas 3 plataformas
- ✓CDP nativo para automação/scraping
- ✓Suporte completo a APIs modernas web
- ✓Compatível com origens estritas (Slack, etc.)
✗ Trade-offs
- ✗Binário maior (Chromium em
Contents/Frameworks/) - ✗Precisa
tauri-clivendored — stock quebra - ✗Setup inicial mais delicado
- ✗Mais RAM que WebKit puro
⚠️ Atenção: tauri-cli vendored
Se você instalar o @tauri-apps/cli stock por engano, o bundle vai panicar com cef::library_loader::LibraryLoader::new. Restaure com:
🛡️ Regra de ouro dos webviews de terceiros
NÃO injetar JavaScript em webviews que carregam Slack/Telegram/WhatsApp/LinkedIn. Use CEF handlers e CDP a partir do scanner Rust. JS injetado em origem de terceiro é superfície de ataque e fricção com o site.
🪜 Provider chain do React
A árvore de providers no App.tsx determina o que está disponível em cada parte da UI. Conhecer a ordem é diferença entre debugar em minutos ou em horas.
Sentry.ErrorBoundary
Captura crashes de render — sempre por fora de tudo.
Redux Provider + PersistGate
Estado global. Tokens de auth NÃO ficam aqui — vivem no core.
BootCheckGate
Espera o core estar pronto antes de prosseguir.
CoreStateProvider
Snapshot do core via fetchCoreAppSnapshot(). Substitui o antigo UserProvider/AIProvider.
SocketProvider + ChatRuntimeProvider
Realtime e runtime de chat. Daqui pra baixo, componentes podem confiar em socket conectado.
HashRouter → CommandProvider → AppShell
Roteamento, paleta de comandos, e finalmente a UI.
🚧 Separation of concerns
A regra existe para uma coisa: poder substituir uma camada sem reescrever as outras. Manter o shell descartável é o que permite, por exemplo, ter futuramente um cliente CLI ou web compartilhando o mesmo core.
✓ Lógica que pertence ao core
- ✓Regras de domínio (cron, threads, channels…)
- ✓Persistência e migrações
- ✓Tomada de decisão do agente
- ✓Crypto, auth, encryption
✗ NÃO mora no React
- ✗Decisão de quando enviar webhook
- ✗Cálculo de cota / billing
- ✗Lógica de roteamento de mensagem
- ✗Validação autoritativa de inputs
🌐 i18n é regra do app
Toda string visível na UI passa por useT() do I18nContext. Hardcoded em JSX/label/placeholder não passa no CI (pnpm i18n:check). Chaves vão em en.ts + chunk files de TODAS as locales.
💡 Como decidir
Pergunta-teste: "Se eu trocasse a UI por uma CLI, isso ainda precisaria existir?" Se sim → core. Se é só sobre como apresentar para o usuário → app.
🌱 Resumo do Módulo
Próximo Módulo:
1.3 — 🔄 Domínios e fluxo de dados (mapa do core, event bus, controllers, memória)