{"slug": "ledgerlocal-your-receipts-never-leave-your-laptop", "title": "LedgerLocal - your receipts never leave your laptop", "summary": "A developer built LedgerLocal, an open-source CLI that uses a local vision model to extract vendor, date, tax and total from a freelancer's invoices and receipts, categorise them against user-defined rules, file them into a year/quarter/category tree, and export an accountant-ready CSV without any network access. The tool installs a process-wide socket allowlist so that connections to external APIs raise an OfflineViolation while localhost model servers remain reachable, and it audits each extraction with arithmetic checks — catching a misread $65.09 as $65.00 in the demo. The project is Apache-2.0 licensed, spans roughly 3,600 lines with 89 tests, and requires no GPU.", "body_md": "*This is a submission for the [Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle](https://dev.to/challenges/hacktoberfest-week2-2026-10-12)*\n\n**LedgerLocal** — a CLI that turns a freelancer's folder of invoices and receipts into a sorted, accountant-ready archive, entirely on their own laptop.\n\nPoint it at a folder: it reads each document with a local vision model, extracts vendor / date / tax / currency / total, categorises it against rules you control, files it into a `year/quarter/category` tree, and exports the CSV your accountant asks for.\n\n**Who pays:** freelancers and one-person studios with 300 unsorted receipts and a lost weekend every tax season. Plenty of SaaS does this — all of it wants the documents uploaded. Receipts name your clients, expose your rates, and carry card digits and tax IDs. For consultants under NDA or anyone under data-residency rules, uploading isn't a price question, it's a non-starter. Their real choice is *local tool vs. the weekend*.\n\nThe best part isn't extraction, it's distrust: every document is audited after the model answers. In the demo the model misreads `$65.09` as `$65.00` and the tool catches it from the arithmetic — nobody told it to look.\n\n**Sort a year of invoices and receipts into a clean, accountant-ready archive — on your own laptop, with an open-weight model, with no network access at all.**\n\nPoint it at the folder where your receipts pile up. It reads each one with a local vision model, pulls out vendor, date, tax and total, categorises it against rules you control, files it into a sane folder tree, and exports the CSV your accountant actually wants.\n\n``` bash\n$ ledgerlocal ingest ~/Downloads/receipts\n  read    hetzner-feb.pdf\n  read    coffee-with-northwind.jpg\n  read    adobe-jan.pdf\n  read    legal-invoice.pdf\n\n  2026-02-14  Hetzner Online GmbH             32.73 EUR  hosting\n  2026-04-03  Blue Fox Coffee                 11.77 EUR  meals\n  2026-01-07  Adobe Inc.                      65.00 USD  software      review\n  2026-03-02  Bright Path Legal LLP          990.00 USD  professional_fees\n\n4 ingested, 0 already known, 0 failed, 1 need review\n\n$ ledgerlocal review\n\n1 document(s) the pipeline is not confident about:\n\n  2026-01-07  Adobe Inc.                       65.00 USD\n      · subtotal + tax differs from total\n```\n\n…`\"1.234,56 €\"` is `Decimal(\"1234.56\")`. Small models are good at finding, unreliable at formatting.`urllib` against localhost. When the pitch is \"audit this yourself,\" a 40-package tree undermines it.\nSQLite keyed on content hash (re-running a folder is a no-op). ~3,600 lines, 89 tests, no GPU or network needed. Apache-2.0: **github.com/Pager-dot/ledgerlocal**\n\n**The headline feature is one a closed API cannot ship.** Rather than ask users to trust a privacy policy, I removed the capability. Every command installs a process-wide socket allowlist first:\n\n```\nsocket.create_connection((\"api.openai.com\", 443))  # OfflineViolation\nsocket.create_connection((\"127.0.0.1\", 11434))     # fine — that's your model\n```\n\nA hosted version **can't implement this**, because the egress it blocks is the call the product depends on. Not a cheaper substitute — a product that otherwise can't exist.\n\n**Unmetered inference changed the engineering, not just the bill.** The repair pass runs on every uncertain document; on a metered API you'd ration that. `recategorise` replays three years of documents against new rules in under a second with **zero** model calls — there's a test asserting the provider is never touched. With per-document pricing you simply don't change your mind, and the rule system dies unused.\n\n**Swappability is leverage you keep.** A 3B on battery, a 32B at year-end, a LoRA fine-tuned on one client's bizarre invoice layout. With a closed API that last one is a feature request to a vendor who'll never prioritise it.\n\n**Where closed wins, honestly:** a frontier model reads a crumpled thermal receipt better than a 7B. I closed most of that gap with structure instead of scale — constrained decoding, deterministic parsing, arithmetic audits — and surfaced the rest in a `review` queue. The discipline a small model *forced* made a better product, because a frontier model would have tempted me to skip the checks.", "url": "https://wpnews.pro/news/ledgerlocal-your-receipts-never-leave-your-laptop", "canonical_source": "https://dev.to/kparitosh760/ledgerlocal-your-receipts-never-leave-your-laptop-542k", "published_at": "2026-10-16 16:39:32+00:00", "updated_at": "2026-10-16 16:47:34.287157+00:00", "lang": "en", "topics": ["ai-tools", "generative-ai", "computer-vision", "developer-tools"], "entities": ["LedgerLocal", "Hacktoberfest Open-Source AI Challenge", "Hetzner Online GmbH", "Adobe Inc.", "Bright Path Legal LLP", "Blue Fox Coffee", "GitHub"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/ledgerlocal-your-receipts-never-leave-your-laptop", "markdown": "https://wpnews.pro/news/ledgerlocal-your-receipts-never-leave-your-laptop.md", "text": "https://wpnews.pro/news/ledgerlocal-your-receipts-never-leave-your-laptop.txt", "jsonld": "https://wpnews.pro/news/ledgerlocal-your-receipts-never-leave-your-laptop.jsonld"}}