DEV Community

Ayush Kulshreshtha
Ayush Kulshreshtha

Posted on

LedgerLocal: sort a freelancer's expenses with an open model that never sees the cloud

Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle Submission 💸

This is a submission for the Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle.

What I Built

LedgerLocal is a small Node.js CLI for freelancers in India. You give it the CSV export from your bank or UPI app, and it sorts every debit into expense categories (software, internet and phone, hardware, travel, meals, learning, coworking, and personal) and tells you roughly how much of your spending was business.

Every freelancer I know does this the same way at tax time: scroll through months of transactions in a spreadsheet and guess. The people who could afford a bookkeeper hand their whole bank history to a stranger or an online app. LedgerLocal does the boring first pass on your own laptop, and nothing about your spending leaves it.

Who would pay for it: freelance developers, designers, tutors, and creators who file their own returns. A tool like this could sell as a one-time purchase (no subscription, because there's no server to pay for), or come bundled with a CA's service as a "send me a clean ledger" step.

Demo

Real output from a live run on the included sample file (14 transactions, model running locally):

GITHUB COPILOT MONTHLY             ₹   830.00  → Software & subscriptions (0.38)
JIO FIBER BROADBAND BILL           ₹   999.00  → Internet & phone (0.62)
SWIGGY ORDER 4412                  ₹   342.00  → Needs review (0.24)
UBER TRIP CLIENT MEETING           ₹   418.00  → Travel (0.30)
UDEMY COURSE REACT ADVANCED        ₹   449.00  → Learning (0.44)
AMAZON LOGITECH MOUSE M331         ₹  1299.00  → Hardware & equipment (0.49)
BIGBASKET GROCERIES                ₹  1870.00  → Personal (not deductible) (0.48)
FIGMA PROFESSIONAL PLAN            ₹  1250.00  → Software & subscriptions (0.35)
WEWORK DAY PASS                    ₹   600.00  → Needs review (0.19)
NETFLIX SUBSCRIPTION               ₹   649.00  → Personal (not deductible) (0.46)
IRCTC TRAIN TICKET JAIPUR          ₹   385.00  → Travel (0.33)
AIRTEL PREPAID RECHARGE            ₹   299.00  → Internet & phone (0.62)
STARBUCKS COFFEE                   ₹   310.00  → Meals & coffee (0.44)
GODADDY DOMAIN RENEWAL             ₹   799.00  → Software & subscriptions (0.28)

Totals by category:
  Software & subscriptions     ₹   2879.00
  Personal (not deductible)    ₹   2519.00
  Hardware & equipment         ₹   1299.00
  Internet & phone             ₹   1298.00
  Needs review                 ₹    942.00
  Travel                       ₹    803.00
  Learning                     ₹    449.00
  Meals & coffee               ₹    310.00

Likely business expenses: ₹7038.00  (check with your accountant)
Enter fullscreen mode Exit fullscreen mode

12 of 14 lines landed in a sensible category. The two it wasn't sure about (SWIGGY ORDER 4412 and WEWORK DAY PASS) went to Needs review instead of being guessed. I'd rather it say "I don't know" than quietly put a food order under business expenses.

Code

LedgerLocal

Sort a freelancer's bank or UPI export into expense categories with an open-weight model that runs on your own laptop. Your transactions never leave your machine.

Built for the Hacktoberfest 2026 Open-Source AI Challenge, Week 2: Side Hustle.

Run it

Requires Node.js 18+.

npm install
node ledgerlocal.mjs sample_transactions.csv
Enter fullscreen mode Exit fullscreen mode

The first run downloads the Xenova/all-MiniLM-L6-v2 model (~23 MB) into ~/.cache/ledgerlocal. After that you can run fully offline:

node ledgerlocal.mjs my_export.csv --offline --out report.csv
Enter fullscreen mode Exit fullscreen mode

Your CSV needs date, description, and amount columns (any order). Amounts can be negative (debits); they are treated as positive spend.

How it works

  1. Each expense category is described in a few plain words.
  2. The model turns every category description and every transaction description into a sentence embedding.
  3. Each transaction is assigned the closest category by cosine similarity. Anything below 0.25 goes to Needs review instead of being guessed.
  4. You get a per-line CSV…




Run it:

npm install
node ledgerlocal.mjs sample_transactions.csv
# after the first run, fully offline:
node ledgerlocal.mjs my_export.csv --offline
Enter fullscreen mode Exit fullscreen mode

How I Built It

  • Model: all-MiniLM-L6-v2, an Apache-2.0 sentence-embedding model, in the quantized ONNX build from Xenova (~23 MB).
  • Runtime: Transformers.js running the model in Node.js on the CPU. No Python, no GPU, no API key.
  • Method: each category is a short plain-language description. The model embeds the categories and every transaction description, and each transaction gets the closest category by cosine similarity. Below a 0.25 similarity it goes to Needs review.

There's no generative model here on purpose. Sorting is a matching problem, and a small embedding model is fast, cheap, and predictable: same input, same answer, every time. The whole run on 14 rows takes a few seconds on a regular laptop.

Changing the categories is just editing words in one object, so a photographer can add "camera gear" and a tutor can add "teaching materials" without retraining anything.

Why Does Open Innovation Matter?

For this project, open isn't a nice-to-have. It's the reason the product can exist.

  • Privacy is the product. A bank statement shows where you live, who you pay, and how much you earn. With an open model running locally, the only network request is the one-time model download. After that, --offline blocks remote loading entirely, and the tool still works.
  • The business math works. With a hosted API, every user costs money every time they run it, which pushes you toward a subscription. Running locally, the marginal cost per user is zero, so it can be a one-time purchase that freelancers actually like.
  • No lock-in. If a better small open model comes out, I change one line. No pricing changes or deprecations decided by someone else.

The trade-off: a 23 MB embedding model doesn't understand context the way a large hosted model does. It can't know that one particular Uber ride was a client meeting unless the description says so. That's why the Needs review bucket exists and why the output says "check with your accountant."

What's Next

  • Learn from corrections: when you move a Needs review line into a category, remember it for next time.
  • Read PDF bank statements directly, not just CSV.
  • Get it in front of a real freelancer. I haven't done that yet, so I can't claim a first user this week.

AI Disclosure

I built this with an AI coding agent (Claude Code) at my direction. The agent wrote the code and drafted this post. The demo output above is from a real run of the code on my machine, including the offline run.

Top comments (0)