A private money app you run on your own server. Bring the transactions from all your bank accounts into one place (read-only, it can never move money), sort your spending into categories, set monthly budgets, and keep track of trips and recurring payments. Everyone on the server gets their own separate data, and you can send monthly totals to your own Google Sheet if you want.
No FinKit cloud, no accounts, no telemetry. Everything runs on your machine or server.
- Browser: analytics, budget progress and trip suggestions are computed locally from your transactions.
- Your API (SQLite on your host): per-user rules, settings, budgets, trips, contracts, bank tokens and synced bank transactions. Bank and trip details are encrypted at rest. Disconnecting a bank removes its synced data.
- Optional outbound calls, only if you enable them: Google Sheets, Enable Banking (
BANKING_PROVIDER=enablebanking), and Cloudflare Access as the production login gate.
- Bank sync: connect banks through Open Banking (Enable Banking), with several accounts per person, balances and back-filled history
- Transactions: one table with inline edits, bulk actions, merchant pages and categories picked from a popover
- Review: queues for transactions that need a category or a second look
- Categorize: rules, recurring detection and merchant rules learned from your edits
- Budgets: monthly limit per category with rollover, an overall monthly cap and a month switcher
- Trips: group flights, stays and other spending, split costs between travelers, and compare against a trip budget
- Recurring payments: detects recurring payments and lets you add your own
- Income and analytics: trends, category breakdowns and top expenses, computed in the browser
- Multi-user: allowlist via
ALLOWED_USER_EMAILS, each person sees only their own data - Anonymize: optionally mask names, IBANs and emails in the UI
- Export: monthly totals to Google Sheets
Needs Node.js 26.13+ (see .nvmrc).
npm run install:all # api + web dependencies
cp api/.env.example api/.env
npm run dev # API :3003, web :5173Open http://localhost:5173/app/. Locally you are dev@localhost; send X-Dev-User: other@example.com to act as another allowlisted user (ignored in production).
npm test # api + web
npm run check # lint + format check
npm run lint:fix && npm run format
npm run buildCopy .env.example to .env, set ALLOWED_USER_EMAILS, CF_ACCESS_TEAM_DOMAIN, CF_ACCESS_AUD and ALLOWED_ORIGIN, then:
docker compose -f docker-compose.prod.yml pull
docker compose -f docker-compose.prod.yml up -dThe API won't start in production without Cloudflare Access config. Details, including key files: deployment.
- Deployment: Docker, secrets, key generation
- Users and access: Cloudflare Access, allowlist, per-user data
- Enable Banking setup: bank sync
- Google Sheets: export setup
- Web architecture: frontend layout and conventions
- Budgets: monthly limits per category
- Trips: getting transactions into a trip
React, Vite, TanStack Router/Query, Tailwind, Recharts · NestJS, SQLite, Zod · Cloudflare Access JWT auth
MIT