Tino

App de finanças pessoais para Android onde os dados vivem cifrados no aparelho, sem cadastro nem backend de usuários — construída, publicada e operada em produção por uma única pessoa: código, compliance legal e monitoramento.

Rol: Desenvolvedor mobile (sozinho, de ponta a ponta)2025–2026Sozinho — código, compliance e operação
React Native 0.85 (New Architecture / Fabric + Hermes)Expo SDK 56TypeScript (strict)Drizzle ORM + op-sqlite (SQLCipher)Zustandexpo-routerCloudflare Workers (Hono)Claude API (Anthropic)Sentry + Android Vitals + UptimeRobot
100%
local-first, sem backend de usuários
180+
testes (unit + componente)
73
commits atômicos

01 · Contexto e problema

Tino é um copiloto financeiro para Android: os dados do usuário vivem cifrados no próprio aparelho, sem cadastro nem backend de usuários — a privacidade é uma propriedade da arquitetura, não uma promessa na política de privacidade.

Não é um repo de prática: eu levei do código à produção sozinho. Isso incluiu application id, LGPD/CDC, Termos de Uso e Política de Privacidade (site próprio), a ficha completa na Play Console (Data Safety, classificação de conteúdo, declaração de funções financeiras), um pipeline de identidade visual gerado por script, e monitoramento real em produção.

O problema técnico central: categorizar gastos com IA sem mandar cada transação para um LLM (custo, latência) nem expor dados sensíveis (CPF/CNPJ, cartão, PIX, e-mail, telefone) — numa app sem backend próprio para se apoiar.

Restrições

Local-first: sem cadastro nem backend de usuáriosDados financeiros cifrados em repouso (SQLCipher)PII nunca crua rumo ao provedor de IAGasto de IA limitado do lado do servidorUm único desenvolvedor: código, jurídico e operaçãoAndroid gama média-baixa incluída

02 · Decisões técnicas

1

Armazenamento local cifrado

Problema

Os dados financeiros deviam viver só no aparelho, mas continuar consultáveis com relações estruturadas (categorias, orçamentos, transações) e cifrados em repouso, sem componente de servidor para o modelo de dados.

Opções

AsyncStorage/MMKV plano · Realm cifrado · SQLite sem cifrar + camada própria · Drizzle ORM sobre op-sqlite (SQLCipher)

Decisão ✓

Drizzle ORM sobre op-sqlite com SQLCipher: banco relacional cifrado em repouso, migrações tipadas, sem depender de um backend para o schema de dados.

Trade-off

O migrador que vem com o ORM tinha um bug real com DDL + defaults em SQLite, encontrado validando contra um banco real. Resolvido escrevendo um migrador próprio em vez de confiar no padrão.

2

Categorização com IA sem vazar PII nem pagar por cada transação

Problema

Queria categorização inteligente de gastos, mas mandar cada transação para um LLM é caro, lento e expõe dados sensíveis (CPF/CNPJ, cartão, PIX, e-mail, telefone) que um copiloto financeiro não pode filtrar.

Opções

IA por transação sem filtro · regras fixas sem IA · motor híbrido (regras → cache → fila de IA em lote) com anonimização prévia

Decisão ✓

Motor híbrido: regras determinísticas resolvem a maioria das transações sem tocar rede; o que não bate entra numa fila e é enviado em lote — Haiku para classificação, Opus para o chat —, sempre depois de uma função pura de anonimização de PII, testada isoladamente.

Trade-off

Mais peças do que 'manda tudo pra IA', mas a maioria das transações resolve de graça e no aparelho, e nenhum dado sensível sai cru.

3

Backend mínimo: proxy stateless em vez de backend de usuários

Problema

Precisava usar a API da Claude sem expor a key no cliente, para um app que se declara local-first e não quer construir um backend de usuários completo.

Opções

Backend completo com contas · SDK de IA embutido no cliente (key exposta) · Cloudflare Workers como proxy stateless

Decisão ✓

Cloudflare Workers (Hono) como proxy stateless: a key da IA vive só no Worker, com rate limiting e teto de gasto do lado do servidor; o Worker não persiste dados do usuário, só repassa a chamada já anonimizada.

Trade-off

Um salto de rede extra para a IA, em troca de zero superfície de backend de usuários para manter e gasto de IA limitado mesmo que o cliente seja comprometido.

Arquitetura

UI · expo-router (file-based)
App · Zustand + hook de reatividade local próprio
Domain · motor de categorização (regras → cache → fila IA)
Infra · Drizzle/op-sqlite (SQLCipher) on-device + Cloudflare Worker (proxy IA)

03 · Publicar sozinho, de ponta a ponta: quando o trabalho depois do código é metade do projeto

Levar o Tino de um repo à Play Store sozinho significou tratar tudo o que normalmente se divide entre papéis diferentes (jurídico, design, DevOps, PM) com o mesmo rigor do código.

Isso foi application id, LGPD/CDC, Termos de Uso e Política de Privacidade publicados em site próprio, e a ficha completa na Play Console: Data Safety, classificação de conteúdo e a declaração específica de funções financeiras.

Também um pipeline de identidade visual (ícone, splash e feature graphic gerados por script a partir de uma única definição) para não manter assets na mão, e monitoramento real em produção: Sentry configurado opt-in com um scrubber de PII —não é negociável num app que lida com dados financeiros—, Android Vitals e UptimeRobot vigiando o backend.

Resultado: um app financeiro em produção na Play Store, com prova fechada com testers reais em andamento, operado de ponta a ponta por uma única pessoa.

04 · De um repo à Play Store, sozinho

Application id, LGPD/CDC, Termos de Uso e Política de Privacidade em site próprio — não templates genéricos, adaptados ao que o app realmente faz com dados financeiros.

Ficha completa na Play Console: formulário de Data Safety, classificação de conteúdo e a declaração de funções financeiras que o Google exige para esse tipo de app.

Pipeline de identidade visual: ícone, splash e feature graphic gerados por script a partir de uma única definição, para não depender de retocar assets à mão a cada release.

Monitoramento de produção real: Sentry com scrubber de PII (opt-in), Android Vitals para estabilidade e desempenho, e UptimeRobot vigiando o backend — além de uma prova fechada com testers reais em andamento.

Demo interativa

Em breve: execute o código e veja-o rodando ao vivo, sem instalar nada.

main.dart
Run

Editor ao vivo (DartPad) — em breve

Em breve

04 · Resultados · antes / depois

testes (unit + componente)
0
180+
commits atômicos
73
PII exposta à IA
sem filtro
0 (anonimizada antes de qualquer chamada)
cobertura no núcleo (CI)
sem gate
gate obrigatório

05 · Retrospectiva

Escreveria o migrador próprio desde o dia 1 em vez de confiar no do ORM: o bug com DDL + defaults em SQLite só apareceu validando contra um banco real, e custou tempo debugar algo que não era minha lógica.

Trataria o scrubber de PII como função pura e testada isolada desde o primeiro commit de IA, não depois: é a peça que menos margem de erro tolera, e por isso mereceu esse cuidado extra desde o início.

Escolheria de novo um proxy stateless num Worker em vez de um backend completo: num app que se vende como local-first, qualquer peça de servidor que não seja estritamente necessária é uma promessa quebrada.

Caso anterior
Próximo caso