A história do Capivara — do hub bagunçado ao painel central com 396 testes
🐷 Capivara·

A história do Capivara — do hub bagunçado ao painel central com 396 testes

📖 6 min de leitura← Voltar para timeline

⚡ O hub que começou como bagunça financeira

A necessidade original não tinha nada de glamouroso: eu precisava entender pra onde meu dinheiro ia. O Capivara nasceu como um hub pessoal pra organizar minhas finanças — mas quem acompanha este blog sabe que ele virou muito mais que isso.

Hoje, o Capivara é o painel central do meu ecossistema: monitora Arachne, TatuEngine e serviços, mostra receita do Dogwalk, responde perguntas sobre meus projetos com RAG local, e mantém backup offsite em duas nuvens diferentes.

E o mais impressionante: 396 testes (131 backend + 248 frontend + 17 browser/E2E) garantindo que nada disso quebra.

🧠 Contexto — um painel que virou infraestrutura

O commit inicial veio com um nome honesto:

a4eef5d feat: capivara hub — FastAPI + React + Cloudflare Tunnel

FastAPI no backend, React no frontend, SQLite de banco, e um Cloudflare Tunnel pra expor tudo com HTTPS sem abrir porta. A primeira versão tinha um dashboard simples de status dos projetos — zero mock data, limpeza total (commit 35f411a).

O Capivara cresceu em ondas. Primeiro os convites temporários (1h-7d com permissões), depois o dashboard analytics com Umami, os proxies pras outras aplicações (Portifólio staging, Umami, Dogwalk), e a autenticação endurecida com JWT + refresh token rotation + blacklist.

🔧 A luta — segurança, RAG local e o backup que não existia

A autenticação que precisava crescer

No começo era um login simples. Mas um painel que controla infraestrutura não pode ter senha fraca. Então vieram:

# backend/auth.py — JWT + bcrypt + 2FA TOTP
def create_access_token(user_id: int) -> str:
    expire = datetime.utcnow() + timedelta(hours=24)
    return jwt.encode(
        {"sub": str(user_id), "exp": expire},
        settings.SECRET,
        algorithm="HS256",
    )

E o 2FA TOTP — o mesmo algoritmo do Google Authenticator. Cada login exige o código de 6 dígitos gerado no app. Já teve dia que eu tentei logar sem o celular por perto e fiquei travado na porta — sinal de que a segurança estava funcionando.

O Segundo Cérebro local

Aí veio a parte divertida: um chat RAG local no admin. ChromaDB como vector store + Ollama com qwen3.5:4b como LLM. As memórias são ingeridas automaticamente dos 6 projetos — hoje são 430 memórias no banco vetorial.

# backend/routers/chat.py — busca no ChromaDB
def search_memories(query: str, top_k: int = 5):
    results = collection.query(
        query_texts=[query],
        n_results=top_k,
        include=["documents", "metadatas", "distances"],
    )
    return results

O diferencial: um bot no Telegram conectado ao mesmo cérebro. Mando pergunta no chat, o webhook consulta o RAG e responde com as fontes. Meus projetos respondem perguntas sobre si mesmos 24/7.

O backup que quase me pegou

O momento mais tenso: descobri que o backup offsite não existia de verdade. De 14/07 a 31/07, o R2 estava desativado na conta — o script rodava, dizia que fazia backup, mas não subia nada. Se o WSL morresse naquele período, o banco inteiro sumia.

A correção teve duas frentes:

  1. Cloudflare R2 — bucket capivara-backups, snapshot validado a cada 6h via cron no_agent
  2. Cloudflare D1 disaster recovery — cópia do SQLite no edge Cloudflare, sync a cada 6h
# scripts/backup_to_r2.sh — o que salvou o banco
rclone copy backend/capivara.db r2:capivara-backups/$(date +%F)/ --progress

Tokens rotacionados, creds no Bitwarden SM, e o dump validado manualmente antes de dormir tranquilo de novo.

💡 Resolução — o painel que virou operação

O Capivara deixou de ser “meu painel” e virou parte da operação. Hoje ele:

  • Expõe staging do Portifólio e proxy do Umami (porta 3001→3100 pra não conflitar)
  • Mostra o Stripe dashboard do Dogwalk (receita, assinaturas, payouts, gráfico 12 meses)
  • Monitora o WSL em tempo real (CPU, RAM, GPU, discos, Docker, serviços, processos)
  • Manda push notifications com PWA offline completo (VAPID keys)

A refatoração do frontend foi brutal: Dashboard de 994→262 linhas (-74%), AdminPage de 1091→97 linhas (-91%), initial bundle de 668→238 kB (-64%) com code splitting.

📊 Métricas

Métrica Nascimento Hoje
Commits 1 107
Testes backend (pytest) 0 131
Testes frontend (Vitest) 0 248+
Testes browser + E2E 0 17
Memórias no RAG (ChromaDB) 0 430
Backups offsite 0 2 (R2 + D1)
Rate limit ausente IP + API key dual window

🎯 Aprendizados

O Capivara me ensinou que painel pessoal também é produção. O dia que o backup “silencioso” falhou sem eu saber foi um alerta: se uma automação é crítica, ela precisa de validação, não só de execução. Foi isso que levou ao snapshot validado do R2 e ao sync no_agent — sistemas que se corrigem sozinhos.

E a segunda lição: RAG local com dados pessoais funciona muito bem. Ter meu Segundo Cérebro respondendo sobre meus próprios projetos, com fontes, mudou como eu trabalho — e virou um padrão que o Arachne depois levou ao extremo.

~/lifelog — bash
$cat about.txt
╔══════════════════════════════════════╗
║  Samuel Medeiros                    ║
║  Senior Software Engineer           ║
║  Stack: Python · TypeScript · Rust  ║
║  Projetos: Arachne, Dogwalk,        ║
║            Capivara, TatuEngine      ║
╚══════════════════════════════════════╝
      
$