← All posts

Data engineering, cryptography

I turned my bank alerts into an encrypted pipeline

August 2026 · 6 minute read · Vikrant Sharma

Every bank I use sends an email when money moves. Multiplied across two countries, four banks and a stack of subscriptions, that inbox is the most complete financial record I own, and nobody was doing anything with it. So I built a pipeline that does: it reads those alerts, parses them, encrypts them and lands them in a private dashboard at money.vikrant69g.com. The part I am proudest of: the server that stores my data cannot read it.

Sixteen parsers and counting

Bank emails are a hostile data source. Every sender formats differently, formats change without notice, and marketing mail dresses up to look like alerts. The pipeline runs 16 format parsers, one per sender and message shape, covering everything from credit card alerts and transfer receipts to app store invoices and rent receipts. Each parser is tested against verbatim copies of real emails, and a rejection layer throws out the marketing noise. The suite sits at 65 tests green, and every new bank format starts life as a failing test.

The server writes what it cannot read

The interesting engineering problem: the pipeline has to keep ingesting while I am asleep, but I refuse to let any server hold the key to my finances. The answer is a sealed-box pattern. The server holds only a public key. For every transaction it generates a throwaway keypair, encrypts the row, and discards the throwaway private key. From that moment the only thing on disk is ciphertext that exactly one key on Earth can open, and that key never leaves my browser. It is derived from my passphrase when I unlock the dashboard, and it dies when I lock it.

If you dump the production database, you get base64 noise. No amounts, no merchants, no account names. I have checked, because a security claim you have not tested is a wish, not a claim.

What it runs on

The whole thing is TypeScript on Cloudflare Workers with a D1 database. A scheduled job walks Gmail on a cron, backfills history page by page, then switches to incremental sync. The dashboard is React, decrypts in the browser, and gives me lifetime totals, monthly in and out, top merchants and a filterable ledger across two currencies. Hosting cost: effectively nothing.

The lesson that transfers

The pipeline taught me more about production data engineering than any course: messy multi-format ingestion, idempotent writes, cursor-based incremental sync, schema design for data you cannot inspect, and testing against reality instead of fixtures you invented to pass. The encryption layer just raised the stakes, because with this design there is no support ticket that recovers a lost passphrase. Constraints like that make you honest.

Next: Three models are better than one → Talk to me about this