Back home

Finman — On-Device AI Personal CFO

June 2026

Finman turns the chaos of transactional SMS into three honest numbers — income, expenses, savings — and reconciles every account to the rupee, without a single raw message ever leaving the phone.

A privacy-first Android app that parses transactional bank/UPI SMS on the device and turns them into a clean, reconciled personal ledger — income, expenses, savings, and per-account balances. Built solo, end-to-end: a framework-free TypeScript reference engine, a Flutter/Dart device port kept in lockstep by golden vectors, and a NestJS + Prisma + PostgreSQL server that only ever sees redacted skeletons. Passion project — Android-only by necessity, foundation complete, source open.

Android-only passion project · not distributed · code is open

Role

Solo — design, engine, server, device, tests

Timeline

June 2026 · one focused week

Platform

Android only (iOS blocks SMS access)

Status

Paused · foundation complete

Why I built it

Every month my real spending lived scattered across dozens of bank and UPI text messages — different formats, OTPs and promos mixed in, balances that quietly drifted. The apps that promise to organise it want read access to your entire financial life. I wanted the opposite: three honest numbers, computed on my own phone, with the raw messages never leaving it.

So I built Finman as a passion project — partly to have the tool I wanted, partly as an excuse to do the engineering properly: a privacy model that holds up, money math that can't round wrong, and parsing logic tested hard enough that I'd trust the numbers.

And yes — I know Finman is a terrible name. I was deep in a Marvel binge at the time, so the finance tracker got named like a superhero. I'm sorry. (I'm not. Finman answers to no one.)

The problem

Dozens of banks, dozens of formats, one-time passwords and promos in the mix, and available balances that drift between messages. The signal is all there; it just isn't legible — and it's too sensitive to hand to a third party. Finman parses every transactional SMS on-device and turns it into a clean, reconciled ledger.

How it works

SMS (device inbox)
Gate — drop OTP/promo
Fingerprint — mask → SHA-256
Parse — template + classify
Ledger — idempotent entry
income · expenses · savings

Every message runs the same deterministic pipeline into an idempotent ledger entry — keyed (UUIDv5) so a re-sync or a duplicate SMS can never double-count. 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 full diagram is in the walkthrough below.

The privacy line

Raw — device-local only

Rs.4,510 spent on Card xx1234 at ZOMATO on 02-06. Avl Bal Rs.18,240. Ref 559210

mask == redact

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 key — no shared account, no cross-user leak

Engineering highlights

Privacy as a hard constraint

Raw SMS never leaves the phone; only a redacted skeleton syncs — and the masker and redactor are one code path, so they can’t drift.

server re-checks redaction · 422

Two-language golden-vector lockstep

Parsing lives twice — TypeScript engine and Dart port — kept honest by language-neutral input → expected fixtures run in both.

engine matrix + 109 device tests green

Per-account balance reconciliation

A balance belongs to the account, not the card — deposit sightings collapse to one line per issuer and chain to the rupee; unexplained moves are flagged, not hidden.

fixed the “off by ₹1,000” fragmentation bug

Fail-first TDD + distribution hardening

Every fix began red. The server is hardened with fail-closed JWT, rate limiting, security headers, per-install keys, and no cloud backup.

12 server e2e tests green

The app

7-slide walkthrough — auto-plays, or step with ← → · open full-screen ↗

Stack & decisions

Device appFlutter / Dart · drift (SQLite) · offline outbox + sync client
Reference engine@finman/engine — framework-free TypeScript parsing/ledger logic
ServerNestJS 10 · Prisma 5 · PostgreSQL 16 · JWT auth
TestingJest + ts-jest · fast-check (property/invariant) · golden vectors
Money modelNUMERIC(14,2) at rest, integer paise in compute — never a float
InfraAWS EC2 + PM2 + RDS Postgres (deliberately no k8s/ECS)

A few decisions were deliberate. Money is integer paise, never a float — the one rule I refused to bend. Parsing was duplicated across two languages on purpose and kept honest by golden vectors, because the device must work offline and the server can't be the only source of truth. And the infra is intentionally boring, because a passion project shouldn't need a Kubernetes cluster.

What I learned

Privacy is a design constraint, not a feature

Deciding up front that raw messages could never leave the device shaped every layer that followed.

Two implementations only stay in sync if a test forces them to

The golden-vector approach was the single most valuable engineering decision in the project.

Property-based tests earn their keep on money

fast-check invariants — like by-tag totals reconciling to by-category totals — caught bugs example-based tests missed.

Status & what's next

Why it's Android-only, and why it's paused. The whole idea rests on reading transactional SMS on the device, and Apple doesn't allow apps to read your messages — so an iOS version isn't possible. It's a passion project, so I built the core loop end-to-end — capture → ledger → the three numbers → per-account reconciliation — then chose to stop there rather than ship something I'd have to support.

I'm not distributing an APK; the goal was the engineering, not a launch. The full source is open (linked above). Future phases — payee/tag learning, settlement, liabilities, Drive backup, real Google Sign-In, TLS — are sketched if I ever revisit.

Device app repo ↗Server + engine repo ↗or say hello

Tools & Technologies

Flutter
Dart
TypeScript
NestJS
Prisma
PostgreSQL
Jest