Live · In development

Kosh

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

Built withNext.js 16 ·TypeScript ·PDF.js ·Web Crypto
Kosh — product screenshot
Introduction

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.

Context & user problem

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.

What was built

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.

How the system works

End to end

  1. The owner logs in against a single configured user ID and Argon2id password hash, receiving a signed 12-hour session cookie.
  2. 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.
  3. Positioned text fragments are reconstructed into transaction rows, then classified into merchants, categories, channels and recurring groups.
  4. Rows are deduplicated and merged against the existing vault by content hash, encrypted with a password-derived key, and saved to IndexedDB.
  5. The UI presents categorised spend, subscriptions, UPI rankings and per-bank/month filters, with manual corrections that persist across future imports.
01Statement file
02PDF.js / OCR extract
03Row reconstruction
04Classify & detect subscriptions
05Dedupe & encrypt
06IndexedDB vault
07Spend views
Hardest engineering decision

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.

Result & current status

Where it stands

Deployed on Vercel (spend-analyser-seven.vercel.app) as a private, single-owner application, in active development. No public user numbers.

100%
parsed in-browser
0
servers for your data
Technology used

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
Next landmark

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.