Kosh
A private, browser-only spend analyser for Indian bank statements.

Kosh imports CSV and PDF bank statements, parses and classifies every transaction locally in the browser, and turns them into categorised spend views, subscription detection and per-account summaries. It is a personal, single-owner tool for someone who wants to analyse their own Indian bank/UPI statements without handing the raw data to any server or third-party service.
Why it needed to exist
Bank statements are hard to read and normally require uploading sensitive financial data to a cloud service to analyse; Kosh does the entire parse-and-classify pipeline in the browser so the raw statement never leaves the device.
Inside the product
Local statement parsing
CSV, text-based PDFs and scanned/image PDFs (via in-browser OCR) are parsed client-side, including password-protected PDFs.
Transaction classifier
Merchant rules, UPI VPA parsing and running-balance checks sort rows into categories like food delivery, quick commerce, investment, income, transfer and self-transfer.
Subscription detection
Recognises recurring charges (including Google Play UPI-mandate and Apple Services) and separates them from EMIs, SIPs and rent.
Multi-account, bank and month filters
Groups statements by account and detected bank, with combined views and an individual-or-all month filter.
Keyword naming rules
Name an unrecognised merchant or mark a self-transfer once, and the rule re-applies to matching rows on every future import.
Bank & statement-period detection
Reads the letterhead of text PDFs to label each imported statement by bank and date range rather than by file name.
Encrypted local vault
Derived transactions, corrections and splits are stored AES-256-GCM encrypted in IndexedDB, with an encrypted backup export.
Needs-review + demo mode
Flags only genuinely ambiguous rows for review, and offers a synthetic demo with no login or real data.
End to end
- The owner logs in against a single configured user ID and Argon2id password hash, receiving a signed 12-hour session cookie.
- A statement file is read entirely in the browser; PDFs are extracted with PDF.js, falling back to on-device Tesseract OCR when a page has no text layer.
- Positioned text fragments are reconstructed into transaction rows, then classified into merchants, categories, channels and recurring groups.
- Rows are deduplicated and merged against the existing vault by content hash, encrypted with a password-derived key, and saved to IndexedDB.
- The UI presents categorised spend, subscriptions, UPI rankings and per-bank/month filters, with manual corrections that persist across future imports.
Rebuilding transactions from PDF coordinates
PDF.js returns loosely positioned x/y text fragments, not tables, so the parser has to reconstruct transaction rows and columns from coordinates. A concrete problem solved here was page-break contamination in real statements: reprinted column headers and brought/carried-forward markers were bleeding into narrations, and a footer security disclaimer's phone numbers (“…9191 / 1800 220 229”) were being parsed as a phantom transaction that broke the running-balance chain. The fix detects and skips header/footer/balance-forward rows, strips leaked ledger markers while preserving genuine leading-digit references, and reconciles each row's amount against the running balance to recover debit/credit direction — verified against a real five-page statement where it removed four phantom rows and cleared all balance-mismatch warnings.
Where it stands
Deployed on Vercel (spend-analyser-seven.vercel.app) as a private, single-owner application, in active development. No public user numbers.
The full stack
- Next.js 16 (App Router, Turbopack)
- React 19
- TypeScript
- Tailwind CSS 4
- pdfjs-dist (PDF.js)
- tesseract.js (OCR)
- PapaParse
- Zod
- @node-rs/argon2 (Argon2id)
- jose (JWT sessions)
- Web Crypto (PBKDF2-SHA-256 + AES-GCM)
- IndexedDB
- Vercel
Continue along the river
Want something built like this?
This is one of several products shipped under Astal Creates. Tell me what you're building and I'll write back.