Finance Web.
Money management for families and businesses, with the database itself keeping each organization apart.


The problem
Rework · Missing metrics
A family or a small business wants to know where the money goes without spreading accounts, cards and the month’s shopping across separate spreadsheets. When many organizations share one database, a single forgotten filter in a query is enough to show one organization’s statement to another. And an AI summary cannot get a value wrong, because in a financial report a made-up number is worse than none.
What I built
I built it alone, from the API to the front end. It has accounts, categories, transfers and recurring entries. Credit cards have open, closed and paid bills, installments up to 48x and refunds. Shopping lists turn into expenses, notifications arrive in real time, and every month the AI writes a diagnosis of the finances. Sign-up is invite-only, with admin, manager and member roles, and whoever runs the SaaS gets a console to suspend organizations and review the audit log.
Technical decisions
01Postgres keeps organizations apart
Every transaction sets the organization id with set_config, and 12 Postgres RLS policies filter everything by it. A suspended organization gets read-only transactions. A test makes sure writes outside that window are refused. If someone forgets a filter in a query, it still sees only its own organization.
Where it lives in the code: apps/api/src/shared/tenancy/tenant.ts
02An AI that never writes numbers
In the monthly diagnosis, the model does not write values. It uses references like {{v:month.income}} that point to the real data, and a validator throws out any value the model made up. The wording can come from the AI, but the numbers come from the database.
Where it lives in the code: modules/ai/domain/prompt.ts · validator.ts
03Events commit with the write
Each write records its event in an outbox table inside the same transaction. If the write fails, the event goes with it. If it commits, the notification goes out. The API, realtime and worker run as separate processes.
04A modular monolith
The code is split into identity, finance, shopping, notifications and ai, and every module has the same four layers: domain, application, infra and http. You can find any rule knowing only its module and layer.
Where it lives in the code: apps/api/src/modules/*
05One permission rule for API and UI
Permissions live in a package shared by the API and the web app. The button the UI hides and the route the API blocks come from the same rule.
Where it lives in the code: packages/access
Numbers
214
commits, single author
~2k
tests
19
migrations
~31
tables across 5 schemas
Stack
- Next.js 16
- React 19
- TypeScript
- Tailwind 4
- shadcn/ui
- Recharts
- Fastify 5
- Zod 4
- Drizzle
- Better Auth
- Socket.io
- PostgreSQL 17
- Redis 8
- Vitest
- Node 24
Screens




AtendeLink