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.jsonfingerprint.jsonfields.jsongate-outcome.jsoninput → expected · verified in BOTH languages
Fail-first TDD:🔴 write the regression test →
watch it RED →
fix →
🟢 green in boththis 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
ⓘ Showing “since you installed”. Your full picture builds as you go.
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
⚖️ Balance rose ₹12 with no message — likely interest. Needs review.