finmanAI Personal CFO
01 / 07
Personal finance · 100% on-device

An AI Personal CFO that
reads your bank SMS — privately.

Turns the chaos of transactional SMS into three honest numbers — income, expenses, savings — without a single raw message ever leaving the phone.
📲 Flutter / Dart device app 🧠 TypeScript reference engine 🗄️ NestJS · Prisma · Postgres
The problem

Your money story is already in your inbox —
just buried and unreadable.

HDFCBK · 14:32
Rs.4,510 spent on Card xx1234 at ZOMATO. Avl Bal Rs.18,240.
SBIINB · 09:01
Your a/c XXXX3456 credited INR 65,000.00 — SALARY.
PAYTM · 21:14
Rs 5,000 transferred to self via UPI. Ref 412345678901.
Dozens of banks, dozens of formats, OTPs and promos in the mix, and balances that drift. Finman parses it all on-device and reconciles it to the rupee.
How it's built · two-language lockstep

One parsing brain, two runtimes — kept honest by golden vectors.

@finman/engine ·TS

Framework-free reference spec
  • gate → drop OTP / promo / balance-only
  • fingerprint = mask(body) → SHA-256
  • parse + classify → ledger entry
  • Jest + fast-check invariants

Dart device port ·Flutter

Ships the same logic to the phone
  • Mirrors gate / fingerprint / redact
  • Offline-first: drift/sqlite + outbox
  • Same masker == same redactor
  • Must never drift from the engine
golden-vectors/redaction.json fingerprint.json fields.json gate-outcome.json input → expected · verified in BOTH languages
Fail-first TDD: 🔴 write the regression testwatch it REDfix🟢 green in both this session: 12 server e2e ✓ · 109 device ✓ · engine matrix ✓
Data flow · on the device

SMS in → three numbers out, deterministically.

✉️
SMS
device inbox
🚦
Gate
drop OTP / promo
🔑
Fingerprint
mask → SHA-256
🧩
Parse
template + classify
📒
Ledger
idempotent entry
3 numbers
income·exp·savings
🆔 Idempotent UUIDv5 — a re-sync or dual SMS never double-counts
🏦 Deposit accounts reconcile per issuer — balances match to the rupee
🚫 Transfers / top-ups never counted as income or expense
Unknown message shapes are induced server-side from a redacted skeleton only, cached as a template, then re-parsed on-device. The raw body never travels.
The privacy line · non-negotiable

Raw SMS never leaves the device. Ever.

raw SMS · device-local only
Rs.4,510 spent on Card xx1234 at ZOMATO on 02-06. Avl Bal Rs.18,240. Ref 559210
mask == redact
(same code path)
redacted skeleton · the ONLY thing that syncs
Rs.§AMT§ spent on Card §ACCT§
at §MERCHANT§ on §DATE§.
Avl Bal Rs.§AMT§. Ref §REF§
🔒 Raw bodies never persisted server-side — device + your own Drive only
🧮 Money is integer paise / NUMERIC(14,2) — never a float
🛡️ Server re-checks redaction — a surviving digit-run is rejected (422)
🔑 Per-install random device key — no shared account, no cross-user leak
🚪 Fail-closed JWT · rate-limited · helmet · no cloud backup of the ledger
The app

Period-honest, drill-downable, reconciled.

9:41▰▰▰ 100%
Dashboard
Income
₹65,000
Expenses
₹24,118
Savings
₹40,882
Spent
₹24,118
Three honest numbers + a spend donut
9:41▰▰▰ 100%
Food & Dining → platforms
Zomato · ₹9,210
Swiggy · ₹6,400
Blinkit · ₹3,180
AprMayJun
Drill down: category → platform → month
9:41▰▰▰ 100%
Account balances
HDFCBK ••1234₹18,240
SBIINB ••3456₹2,41,900
Slice₹1,012
Reconciled balances + learn-once categories
Built end-to-end · distribution-ready

Private by construction.
Correct to the rupee.

TypeScriptNestJSPrisma PostgreSQLFlutter · DartJest · fast-check
🔒 On-device privacy
🏦 Per-account reconciliation
🔑 Fail-closed auth + per-install keys
🧪 Two-language golden-vector lockstep
finman — an AI Personal CFO.
space pause · → next · ← back · F fullscreen · R restart
finman

Project walkthrough

7 slides · auto-advances in about 90 seconds.

F fullscreen · Space pause · ← → step · R restart