MÓDULO 1.2

🏗️ Arquitetura

Como o core Rust roda in-process dentro do Tauri host, conversa via JSON-RPC autenticado e serve a UI React. Sem sidecar, sem mistério.

6
Tópicos
40
Minutos
Básico
Nível
Teoria
Tipo

Conteúdo detalhado

1

🧩 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)

openhuman/ ├── src/ # Crate Rust 'openhuman' + bin 'openhuman-core' │ ├── core/ # Transporte: HTTP, JSON-RPC, CLI, event bus │ └── openhuman/ # Domínios: agent, memory, channels, ... ├── app/ # Workspace pnpm 'openhuman-app' │ ├── src/ # React + Vite (UI) │ └── src-tauri/ # Shell Tauri (Rust) └── Cargo.toml # Core crate

💡 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
src/
SHELL
app/src-tauri/
APP
app/src/
REGRA
Lógica = core
2

⚡ 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-core spawned 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:stage virou 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:

OPENHUMAN_CORE_REUSE_EXISTING=1 pnpm dev:app
HANDLE
CoreProcessHandle
RUNTIME
Tokio task
LIFECYCLE
Junto com GUI
ESCAPE
REUSE_EXISTING
3

🔌 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

// app/src/services/coreRpcClient.ts await invoke('core_rpc_relay', { method: 'openhuman.threads_list', params: { limit: 50 } }); // internamente → POST http://127.0.0.1:<port>/rpc // Headers: Authorization: Bearer <OPENHUMAN_CORE_TOKEN> // Body: {"jsonrpc":"2.0","method":"...","params":{...},"id":1}

🚫 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.

PROTOCOLO
JSON-RPC 2.0
TRANSPORTE
HTTP localhost
AUTH
Bearer per-launch
BRIDGE
core_rpc_relay
4

🎨 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-cli vendored — 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:

cargo install --locked --path app/src-tauri/vendor/tauri-cef/crates/tauri-cli

🛡️ 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.

RUNTIME
Chromium
CLI
Vendored
SCRAPING
CDP nativo
REGRA
Zero JS injection
5

🪜 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.

1

Sentry.ErrorBoundary

Captura crashes de render — sempre por fora de tudo.

2

Redux Provider + PersistGate

Estado global. Tokens de auth NÃO ficam aqui — vivem no core.

3

BootCheckGate

Espera o core estar pronto antes de prosseguir.

4

CoreStateProvider

Snapshot do core via fetchCoreAppSnapshot(). Substitui o antigo UserProvider/AIProvider.

5

SocketProvider + ChatRuntimeProvider

Realtime e runtime de chat. Daqui pra baixo, componentes podem confiar em socket conectado.

6

HashRouter → CommandProvider → AppShell

Roteamento, paleta de comandos, e finalmente a UI.

TOPO
Sentry
ESTADO
Redux + Persist
CORE
CoreStateProvider
UI
AppShell
6

🚧 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.

LÓGICA
Core
UX
App React
SHELL
Descartável
i18n
useT() obrigatório

🌱 Resumo do Módulo

Três camadas — src/ (core), app/src-tauri/ (shell), app/src/ (UI)
Core in-process — task tokio dentro do shell, PR #1061 removeu sidecar
JSON-RPC — HTTP localhost + bearer, sempre via core_rpc_relay
CEF — runtime Chromium para webviews; tauri-cli vendored
Provider chain — Sentry → Redux → BootCheck → CoreState → Socket → UI
Onde mora cada coisa — lógica no core, UX no app, shell apenas entrega

Próximo Módulo:

1.3 — 🔄 Domínios e fluxo de dados (mapa do core, event bus, controllers, memória)