Oracle3
Open-source trading engine and MCP server for prediction markets. Fee-aware no-arbitrage trading across related contracts, live on Kalshi, Polymarket and Solana or on paper.
App gratuito de finanças pessoais. Anote seus gastos em segundos e entenda seu mês de um jeito simples, visual e sem planilha.
Translation pending
FinanceTypeScript · PLpgSQLGitHub @valorizeag-createOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Loading the full README. The saved excerpt is shown below.
Aplicativo de finanças pessoais e familiares, pensado primeiro para o celular. A Íris mostra para onde o dinheiro vai: você anota entradas e gastos em segundos e enxerga o mês inteiro, sem planilhas, sem jargão e sem julgamento. • Next.js 16 (App Router) + React 19 + TypeScript • Tailwind CSS 4 • Supabase (Postgres + Auth), rodando localmente via Docker durante o desenvolvimento • Zod para validação • Vitest (regras de negócio, componentes, banco) e Playwright (ponta a ponta)Read the full README ↗
Aplicativo de finanças pessoais e familiares, pensado primeiro para o celular. A Íris mostra para onde o dinheiro vai: você anota entradas e gastos em segundos e enxerga o mês inteiro, sem planilhas, sem jargão e sem julgamento.
O ambiente local usa uma instância do Supabase rodando em Docker — nenhum dado de desenvolvimento fica em um projeto na nuvem.
npm installnpx supabase start — sobe Postgres, Auth e demais serviços localmente.npx supabase status -o env mostra as credenciais locais. Copie para .env.local (baseado em .env.example) mapeando:
API_URL → NEXT_PUBLIC_SUPABASE_URLPUBLISHABLE_KEY (ou ANON_KEY) → NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEYSECRET_KEY (ou SERVICE_ROLE_KEY) → SUPABASE_SECRET_KEYNEXT_PUBLIC_SITE_URL=http://localhost:3000npx supabase db reset — aplica as migrações e a seed no banco local.npm run dev → http://localhost:3000SUPABASE_SECRET_KEY só é usada pelos testes de banco e end-to-end (para criar/apagar usuários de teste) e pelo script local scripts/configurar-tarefa.mjs. Ela nunca é usada no código do app, nunca vai para a Netlify e nunca recebe o prefixo NEXT_PUBLIC_. As variáveis do Plano 8 (push, e-mail, tarefa) são opcionais: sem elas, a parte correspondente fica desligada e o app funciona igual (veja "PWA e notificações (local)").
O login por e-mail funciona sem nenhuma configuração extra. Já "Continuar com Google" só funciona depois de configurar [auth.external.google] em supabase/config.toml com as credenciais OAuth do Google (client_id e secret) via variáveis de ambiente — nunca comitadas.
Tudo funciona sem conta em nenhum serviço. Cada parte liga com variáveis em .env.local (lista comentada em .env.example); sem elas, a parte fica desligada.
npm run dev ele só registra com NEXT_PUBLIC_REGISTER_SW=1 (o Playwright já liga sozinho). Ele não guarda nenhuma tela, dado ou arquivo do app: só a página "Sem conexão" e um ícone. Para ver o aviso à mão: npm run build && npm start e desligue a rede no navegador.npx web-push generate-vapid-keys. Copie a chave pública para NEXT_PUBLIC_VAPID_PUBLIC_KEY (a única com esse prefixo, porque vai para o navegador), a privada para VAPID_PRIVATE_KEY e defina VAPID_SUBJECT=mailto:seu-email. Em Configurações → Lembretes, "Ativar lembretes". O endereço de push de cada aparelho é dado pessoal: nada o lê pela API, e o app só envia para serviços de push conhecidos (Google, Mozilla, Apple e Microsoft).SMTP_HOST=127.0.0.1, SMTP_PORT=54325, MAIL_FROM=Íris <[email protected]>, sem usuário e senha. Os e-mails aparecem na caixa local, em http://127.0.0.1:54324, e não saem do computador. A recuperação de senha também passa por essa caixa (o modelo está em supabase/templates/recovery.html; é o Supabase que envia)./api/jobs/notificacoes) que o banco chama; ela só abre com um código de disparo.
node -e "console.log(require('crypto').randomBytes(32).toString('base64url'))". Coloque o valor em JOB_SECRET no .env.local.npm run job:configurar registra no banco local só o resumo (SHA-256) do segredo e mostra dois comandos do Vault (endereço da rota e código de disparo). Cole-os no SQL Editor, em http://127.0.0.1:54323. O script nunca mostra nem grava o segredo.npx supabase db reset (o Vault e os resumos são esvaziados).npm run job:notificacoes -- manha (enfileira os lembretes da manhã e entrega), , (gera as contas do dia) ou sem argumento (só entrega o que está na fila). Esse atalho usa a chave publicável e o segredo; é só para a máquina local.Segurança da tarefa, em poucas palavras: o banco guarda só o SHA-256 do segredo; quem chama a rota (pg_net) leva um código derivado do segredo, do qual não se volta ao segredo e que só pede à rota para entregar um lote; a rota usa a chave publicável e o segredo do próprio ambiente, nunca a chave de serviço. O código de disparo continua valendo até o segredo ser trocado. Não ligue log_statement = 'all' nem pgaudit para os papéis da API (os parâmetros das chamadas iriam para o registro).
npm test — regras de dinheiro, datas, formulários e componentes (Vitest); no Plano 8 também tipos de aviso, textos e destinos, envio de push e de e-mail (simulados), rota da tarefa, service worker, manifest e página "Sem conexão"; no Plano 9 também as células do CSV, os textos jurídicos, as ações de excluir o cadastro e trocar o e-mail e a rota de download; no Plano 10 também os textos da landing, a regra da seção Confiança, robots e sitemap, as colunas do desktop, o pular para o conteúdo e a conferência de textos (scripts/conferir-textos/core.test.mjs)npm run test:db — privacidade e regras no banco local (usa SUPABASE_SECRET_KEY; requer npx supabase start); o Plano 8 acrescenta preferências, inscrições e fila de avisos, tarefa diária das contas, convite por e-mail e o segredo da tarefa; o Plano 9 acrescenta plano9 (entrada recente, exclusão completa tabela por tabela, isolamento, família e limites de convite que sobrevivem à exclusão). Durante a execução o agendador local (pg_cron) fica pausado; se uma execução for interrompida antes do fim, rode select public.job_set_paused(false); no SQL Editornpm run test:e2e — fluxo completo no navegador, celular e desktop (Playwright; requer npx supabase start e .env.local preenchidos, e sobe o npm run dev automaticamente, já com NEXT_PUBLIC_REGISTER_SW=1; se um npm run dev já estiver rodando, reinicie-o). Os testes de push, da tarefa e do convite por e-mail do Plano 8 rodam quando NEXT_PUBLIC_VAPID_PUBLIC_KEY, JOB_SECRET e SMTP_HOST estão em .env.local; sem eles ficam marcados como pulados. Os testes da tarefa e do convite por e-mail só rodam contra o Supabase local e a caixa de e-mail local (NEXT_PUBLIC_SUPABASE_URL e SMTP_HOST em localhost ou 127.0.0.1): com um banco hospedado ou um servidor de e-mail de verdade em .env.local eles ficam pulados, para nenhum aviso ou e-mail de teste chegar a pessoas de verdade. A caixa de e-mail é lida em http://127.0.0.1:54324 (mude com E2E_MAILBOX_URL). O Plano 9 acrescenta plano9.spec.ts: páginas públicas de Termos e Privacidade, download dos dados, exclusão do cadastro, sessão antiga e troca de e-mail; o Plano 10 acrescenta plano10.spec.ts (landing, SEO, / para quem entrou, colunas a 1440 e 800 px, 375 px) e acessibilidade.spec.ts; a troca de e-mail só roda com o banco local (NEXT_PUBLIC_SUPABASE_URL em localhost ou 127.0.0.1) e o teste da sessão antiga também precisa do no contêiner do banco localnpx playwright test tests/e2e/acessibilidade.spec.ts roda o axe (WCAG 2.0, 2.1 e 2.2, níveis A e AA) nas páginas públicas e nas principais telas do app, no celular e no desktop, e confere que nada rola na horizontal. As telas do app criam um cadastro de teste, então precisam do Docker e do .env.local. Sem Docker: npx playwright test --grep @publico roda só os testes públicos (landing, Entrar, Criar cadastro, Termos e Privacidade, SEO, pular para o conteúdo e 375 px); os que precisam de banco ficam de fora ou pulados. A execução completa é npx supabase db reset && npm run test:e2e. Uma violação aparece com a regra, o impacto e o elemento; a correção é sempre na tela, nunca desligando a regra.
npm run textos compara todo texto visível do app (src/, public/sw.js, public/sem-conexao.html, supabase/templates/) com a cópia da copy e com as listas de textos (docs/etapa-2-requisitos.md, docs/etapa-5-ui.md, docs/decisoes-para-revisao.md e a seção "Textos novos" de cada plano). Termina com erro e lista arquivo:linha "texto" quando um texto não está em nenhuma delas; no fim mostra quantos textos conferiu, quantos casaram com a copy, com as listas ou com o ignorar.json, e quantos ficaram de fora. Código 0: tudo coberto; 1: há textos fora das listas; 2: falta a cópia da copy.
docs/copy/documento-base.md, mantendo a primeira linha com a revisão e a data. A pasta docs/copy/ está no .gitignore: o arquivo nunca vai para o git, e os testes usam uma cópia fictícia. Quando a copy mudar, exporte de novo.docs/decisoes-para-revisao.md (para a sua aprovação); se não aparece para ninguém (identificador, formato, nome técnico), coloque em scripts/conferir-textos/ignorar.json com o motivo. Uma entrada sem "motivo", ou que já não casa com nada, também faz o comando falhar.A publicação só acontece quando a pessoa responsável pelo projeto decidir — não faz parte do fluxo automático de desenvolvimento.
npx supabase link para associar o repositório a esse projeto.npx supabase db push para aplicar as migrações no banco de produção.netlify.toml para build e publicação).NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY e NEXT_PUBLIC_SITE_URL (com a URL pública do site). SUPABASE_SECRET_KEY não é necessária em produção — ela só é usada pelos testes locais de banco e end-to-end.NEXT_PUBLIC_SITE_URL de produção.Na Vercel (hospedagem atual): as variáveis são as mesmas do passo 5. O servidor do site precisa rodar na mesma região do banco: cada tela faz várias consultas em sequência, e cada uma atravessa a distância entre os dois. O vercel.json fixa a região em gru1 (São Paulo), a mesma do projeto Supabase atual (sa-east-1), perto de quem usa o app. Se o banco mudar de região, troque também a região no vercel.json (Oregon/us-west-2 é pdx1; Virgínia/us-east-1 é iad1).
Nada disto é necessário para desenvolver e testar localmente. Reúne o que ficou para você nos Planos 8, 9 e 10: contas e chaves que só você pode criar (itens 1 a 11, todas com plano gratuito), decisões de custo e de hospedagem (12, 13, 17), configurações do Supabase hospedado (5, 8, 14, 15), a revisão jurídica (16), a primeira execução com Docker (18) e as aprovações (11, 19 e 22), a liberação da landing (20) e o endereço dela (21). A SUPABASE_SECRET_KEY não entra nesta lista: ela não vai para a Netlify, nunca.
SMTP_HOST, SMTP_PORT, SMTP_SECURE, SMTP_USER, SMTP_PASS e MAIL_FROM na Netlify e as mesmas credenciais no Supabase, em Authentication → SMTP (é o que envia a recuperação de senha). Sem isso, convite e resumo por e-mail ficam desligados (o convite por link continua) e a recuperação de senha tem limite baixo de envios.supabase/templates/recovery.html e o assunto "Crie uma nova senha na Íris". O arquivo do repositório só vale no ambiente local.npx web-push generate-vapid-keys (outro par, diferente do local) para NEXT_PUBLIC_VAPID_PUBLIC_KEY, VAPID_PRIVATE_KEY e VAPID_SUBJECT na Netlify. Trocar as chaves depois desliga os lembretes de quem já ativou (a pessoa ativa de novo).JOB_SECRET (comando acima) e coloque na Netlify. Depois, uma vez, rode npm run job:configurar -- producao https://SEU-DOMINIO na sua máquina (com o mesmo JOB_SECRET no .env.local) e cole os três comandos que ele mostra no SQL Editor do Supabase hospedado. Nenhum deles contém o segredo.pg_cron, pg_net e o Vault antes de npx supabase db push. O Postgres do projeto precisa ser 17 ou mais novo (o local usa 17). O papel anon tem limite curto de tempo por comando: a produção depende do pg_cron (que chama as funções direto), não do atalho job_trigger do script local.BUDGET_MS em src/features/notificacoes/deliver.ts) e que os trechos que rodam depois da resposta (after(): avisos de planejado e de meta) de fato rodam lá. Se não rodarem, é preciso entregá-los por waitUntil.fcm.googleapis.com, *.push.services.mozilla.com, *.notify.windows.com, *.push.apple.com) é rígida e, se um navegador usar outro endereço, o lembrete não é salvo. Confira com um endereço real de cada navegador (o Chrome pode usar um endereço diferente de ). A lista está em e, igual, na migração.| Documento | Conteúdo |
|---|---|
docs/etapa-2-requisitos.md | Regras de negócio e requisitos (aprovados) |
docs/etapa-3-arquitetura.md | Modelo de dados, privacidade, páginas e fluxos (aprovada) |
docs/etapa-5-ui.md | Identidade visual e tokens (aprovada) |
docs/etapa-7-roteiro.md | Roteiro de desenvolvimento em 10 planos |
docs/superpowers/plans/ | Planos de implementação detalhados |
-- noite-- ocorrenciasnpm run icons gera os PNG a partir do logo provisório.docker execnpm run typecheck — checagem de tipos (TypeScript)npm run lint — checagem de estilo e boas práticas (ESLint)npm run textos — conferência de textos (abaixo)fcm.googleapis.comsrc/features/notificacoes/endpoint.tsnext mais longo (use o curinga …/**).scripts/gerar-icones.mjs e rodar npm run icons. Hoje os ícones usam o logo provisório.docs/decisoes-para-revisao.md).NEXT_PUBLIC_SITE_URL, o "Site URL" e os endereços de retorno em Supabase → Authentication → URL Configuration, e o retorno do login com o Google.NEXT_PUBLIC_SITE_URL de produção. O link do e-mail de troca usa {{ .SiteURL }}.supabase/templates/email_change.html, com o assunto "Confirme a troca de e-mail na Íris". O link precisa ser exatamente {{ .SiteURL }}/confirmar-email?token_hash={{ .TokenHash }}. Se decidir ligar "Confirm email" no cadastro, colar também supabase/templates/confirmation.html em "Confirm signup" (assunto "Confirme seu e-mail na Íris") e testar antes o caminho do convite da família (pendência em docs/progresso.md).src/features/legal/content.ts são um rascunho fiel ao que o app faz. Antes de lançar: (a) preencher em src/features/legal/controller.ts quem é o responsável (você ou a sua empresa), o e-mail de contato para assuntos de dados (que também é o contato do encarregado), o nome do provedor de e-mail e a data da revisão — enquanto um deles estiver vazio, as páginas mostram "Rascunho em revisão", marcam o que falta com "[a definir antes do lançamento]" e não são indexadas; nunca invente nomes ou números ali; (b) decidir os pontos marcados em docs/decisoes-para-revisao.md (Plano 9, "Para a revisão jurídica"): base legal, servidores da Netlify fora do Brasil, idade mínima, limite de responsabilidade, aviso em caso de incidente, prazos das cópias de segurança, lei aplicável e foro, gratuidade; (c) confirmar se os e-mails de confirmação e de nova senha (enviados pelo Supabase Auth) usam o mesmo provedor de e-mail do item 1 — a política diz que isso ainda está a confirmar.npx supabase db reset && npm run test:db && npm run test:e2e. Esperado do Plano 9: banco plano9 com 19 testes; ponta a ponta plano9.spec.ts com 6 testes (12 entradas em --list); os testes dos Planos 7 e 8 continuam passando (em especial plano7-familia, plano7-saida e plano8-convite, que passam pelas mudanças do Plano 9). Conferências que nenhum teste automático faz de ponta a ponta:
20261003000001_seus_dados.sql: se parar no primeiro bloco, o papel que aplica as migrações não pode apagar em auth.users (ou ler auth.sessions, ou tem a segurança por linha ligada nas tabelas de auth). Nesse caso não publique e não use a chave de serviço no app: fale comigo. No banco hospedado, npx supabase db push para nesse ponto com uma mensagem clara.plano9.spec.ts envelhece a sessão pelo psql do contêiner do banco local (supabase_db_Planilha_financeira; mude com E2E_DB_CONTAINER) e fica marcado como pulado se o contêiner não puder ser alcançado. Sem ele, à mão: update auth.sessions set created_at = now() - interval '16 minutes' where user_id = 'ID';, depois a tela Excluir meu cadastro deve pedir para sair e entrar de novo, e delete_my_account chamada com o token dessa pessoa deve recusar com "Entrada recente necessária." e deixar o cadastro intacto.plano9.spec.ts baixa o arquivo com uma sessão de verdade. Confira também num deploy de prévia da Netlify, com um cadastro grande, e o que acontece se a conexão cair no meio do envio (o download deve falhar, não terminar "completo"). Confira ainda, no mesmo deploy, que os trechos after() rodam (item 6).auth.jwt() ->> 'session_id' vem preenchido; verifyOtp({ type: 'email_change', token_hash }) só devolve sessão na segunda confirmação; cada endereço recebe um {{ .TokenHash }} diferente.authenticated tem limite de tempo por comando, 8 segundos no Supabase hospedado; o risco de estourar está no tamanho do registro de acessos de autenticação, auth.audit_log_entries, varrido sem índice; se estourar, a exclusão volta atrás inteira).select * from public.account_leftovers('ID-DO-CADASTRO', 'e-mail') deve voltar vazio. Compare também select tablename from pg_tables where schemaname = 'auth' com o que o papel consegue ler: uma tabela sem acesso não é conferida.docs/decisoes-para-revisao.md, seção "Plano 9").DATA_RIGHTS_RELEASED em src/features/landing/release.ts, depois dos itens 16, 17 e 18 (e com os campos de src/features/legal/controller.ts preenchidos — sem eles o item não aparece, mesmo ligado). Antes de ligar, aprove a frase nova "Você pode baixar o que registrou ou excluir seu cadastro quando quiser." (decisão 160).NEXT_PUBLIC_SITE_URL define o endereço canônico, o sitemap.xml e a imagem de compartilhamento. Termos e Privacidade só entram no sitemap.xml quando deixam de ser rascunho (item 16). O logo definitivo (item 10) também troca a imagem de compartilhamento (npm run icons).docs/decisoes-para-revisao.md, seção "Plano 10"), e a landing e os layouts de desktop vistos por você no celular e no computador (a landing não tinha protótipo; confira também as colunas a exatamente 1024 px). Rode npm run textos com a sua cópia da copy (acima) e resolva o que ele listar.src/features/cartoes/brand-mark.tsx só para identificar o cartão da própria pessoa. Antes do lançamento, confira o manual de marca de cada bandeira (cores, área de respiro, uso permitido) ou troque pelos arquivos oficiais. Aprove também os textos novos da bandeira (docs/decisoes-para-revisao.md, "Bandeira do cartão").src/domain/importacao/dicionario.ts) e a leitura de cada formato com o que aparecer. Faça a revisão jurídica da frase nova da Política de privacidade ("Se você importar o extrato do seu banco, o arquivo é lido no seu próprio aparelho e não é enviado nem guardado pela Íris…") e aprove os textos de "Importar extrato" (docs/decisoes-para-revisao.md, seção "Importar extrato (2026-10-10)"). Rode npm run textos com a sua cópia da copy.