VaultLine
A replacement for the document management product we were paying for — four ways to get a document in, one path they all go through, and a record of everything that happens to it.
- My role
- Wrote the spec, built it, deployed it, run it
- Built with
- AI assistance (Claude, ChatGPT) against a written spec
- Timeline
- About two and a half weeks
- Where it runs
- An internal Linux server
The problem
Invoices and supporting paperwork were arriving every way a business receives things — emailed to a shared mailbox, scanned to a folder, handed over on paper, dropped onto a shared drive. Filing was manual, finding something later depended on knowing where someone put it, and the commercial product we'd otherwise buy costs real money per person for what's essentially a filing cabinet with permissions.
The part that actually needed thinking about wasn't storage. It was that four completely different ways in had to end up in one place with the same handling — same checks, same duplicate detection, same record of what happened — without turning into four separate systems that gradually drift apart.
One way in
Whichever route a document arrives by — dragged into the browser, dropped in a watched folder on someone's desktop, sent from Outlook, or pulled from a mailbox on a schedule — it goes through the same single point of entry. Nothing gets into the archive any other way.
That entry point checks the file is really the type it claims to be rather than trusting the file extension, scans it, checks whether we already have an identical copy, and only then files it. If the same document gets sent twice, the second one returns the original result instead of creating a duplicate.
What's the record and what isn't
The filed document is the permanent record. The search index is built from it separately and is explicitly not the record — it can be deleted and rebuilt from the documents at any time.
That's worth the extra work for two reasons. Search can be slow, or briefly out of date, or replaced with something else entirely, without ever putting the actual documents at risk. And nobody has to work out which one is right when the two disagree — the documents are always right.
The history of who did what is protected by the database itself, not just by the code. Even if something in the application went badly wrong, or someone connected to the database directly, those records can't be edited or deleted.
The desktop piece
There's a small program people can install that watches a folder on their machine. Drop a file in and it uploads — and it only deletes the local copy once the server has confirmed it has it. Anything the server rejects stays put locally rather than quietly disappearing. If the machine is offline it waits and retries. It installs without needing admin rights.
One detail there took some working out. To find someone's Desktop folder, the obvious path is wrong on most of our machines — with OneDrive backing up folders, which is the default on company Windows setups, the obvious location points at an old empty folder the user never sees. Their real desktop is somewhere else. It has to be looked up properly rather than assumed.
An early version quietly used a placeholder instead of the real text-reading engine, and it swallowed a scan somebody actually needed. Now the real one is always the default and the placeholder is only ever used deliberately, for testing.
Where the AI is allowed to decide
Working out what a document is happens in order of how much it can be trusted: first a layout we've confirmed before, then simple keyword rules, and only then the AI. Anything the AI classified goes to a person to confirm regardless of how sure it says it is.
For a document to get filed without review, both the document type and every extracted detail have to clear a confidence threshold. Invoices over five thousand dollars go to someone for approval no matter what. And when the system learns from a correction, it only learns from ones a person confirmed — so it can't teach itself its own mistakes.
The details it pulls out get highlighted directly on the picture of the page, so checking it is a glance rather than comparing a form against a document in two windows.
How it got built
This is the biggest thing here and it came together in about two and a half weeks, which is a fair thing to raise an eyebrow at.
The way it worked: I wrote a detailed spec first — how documents come in, what counts as the record versus what's rebuildable, when the AI is allowed to decide and when a person has to, what the history needs to guarantee. Then I worked through it section by section with AI tools, testing and correcting as I went. The volume of code isn't a measure of how much I typed, and I wouldn't present it that way.
What it does show is the thinking about what to specify before building anything, and the work of getting it onto a server and keeping it there. The decisions I'd point at — one entry point rather than four, the record-versus-index split, the history the database won't let you change, the OCR incident above — are judgement calls, and they're the reason it works.
I keep documentation with it that grades each feature as verified, partial, or not done, including the honest negatives — the Outlook piece has never been tested in a real tenant, and there are known gaps I've written down rather than papered over.
What it still needs
No limits on how often something can hit it, and the tests only run when I run them by hand. The desktop program isn't signed, so it throws a warning on any machine outside our environment.