Skip to content

DocStack

A self-hosted document pipeline: OCR, a self-hosted LLM, and an entity graph that builds and resolves itself. No document goes to a cloud API. The model runs on a borrowed GPU machine, and enrichment is paused for now.

paused2026 – presentPython · Docker · Paperless-ngx · Ollama · Postgres
Entity graph in Obsidian
Entity graph in Obsidian

Runs on my own hardware, plus a borrowed GPU machine for the model6 entity types with deterministic resolutionBilingual OCR, French and EnglishIncludes an MCP server for agent use

The problem

A document management system tells you where a file is. It does not tell you that three of them mention the same company under two spellings and a legal suffix.

Every product that does understand the content wants the content: uploaded, indexed, and stored somewhere else. For personal paperwork that is the wrong direction.

What it does

Documents land in Paperless, get converted to Markdown, enriched by a self-hosted LLM, and written into an Obsidian vault as a note plus an entity graph. Tags and a generated summary are written back to Paperless.

The graph is queryable: a GraphRAG endpoint answers questions with context assembled from the entities, and the code also ships an MCP server so an agent can use it as a tool.

Ingestion
5-minute poll, webhook not configured yet
Model
Qwen3.8-27B, quantised, on a borrowed GPU
Vault
Obsidian, replicated by Syncthing

A model reads the documents, code decides what gets filed

Every document waits in a queue until it can be read and checked. Anything unreadable stops in plain sight, anything unsure comes to me, and a machine that is off only delays the job.

  1. 1Document added
  2. 2Job queue
  3. 3Readable text?(claimed)Other branch: retry: Job queueOther branch: 3 failures: Failed, visibly
  4. 4Model reachable?(readable)Other branch: off, backs off: Job queue
  5. 5Model extracts(up)
  6. 6Valid output?(replies)Other branch: invalid after one retry: Job queue
  7. 7Tags and entities(valid)Other branch: unsure: Review file
  8. 8Vault and graph(matched)Other branch: answers: Agent question
  9. 9PaperlessOther branch: Paperless down: Job queue

5 scenariosPick one, the steps above follow it

A bill signed EDF SA, from a company the graph knows as EDFFiled
Decided by
Code: without the legal suffix the name is the same, so it joins the existing entity.
Model saw
The text, the entities already known, and a shortlist of the tag vocabulary.
You get
One note linked to the existing EDF page, with tags and a summary back in Paperless.
A scan whose text comes out as gibberishStopped
Decided by
Code: under 20% of the words have four letters or more, whichever fallback produced them.
Model saw
Nothing. Gibberish does not make a model fail, it makes it invent a plausible document.
You get
After three attempts, a failed job I can see instead of a fake document in the corpus.
A document arrives while the model's machine is switched offWaits, then filed
Decided by
Code: an unreachable model is a wait, never a failure. The pause grows from 30 seconds to 15 minutes.
Model saw
Nothing until it came back, then the same input as usual.
You get
The same filed document, later, with nothing for me to do.
The model proposes a tag outside the vocabularyHeld for review
Decided by
Code: an unknown tag is not approved, and only one new proposal per document is even considered.
Model saw
The vocabulary shortlist, plus the right to suggest one new tag.
You get
The document is filed without the tag, which waits in a review file for my decision.
An agent asks what EDF billed in 2025Answered
Decided by
Code: it selects the documents and does the sum, and never adds two currencies together.
Model saw
The result and the document IDs, not an addition to work out.
You get
A total the agent can trace to each document behind it.

Invented examples. The real documents stay private.

Architecture

01

Paperless-ngx

Ingestion, OCR in French and English, and the document store. The source of truth for the file itself.

02

Markdown conversion

Docling first, MarkItDown as a fallback, and Paperless' own OCR text as a last resort. Three layers, because conversion is where pipelines die.

03

Self-hosted LLM enrichment

Ollama with a pydantic schema and forced JSON output. Structured extraction, not free text that has to be parsed afterwards.

04

Entity graph

A closed six-type ontology with deterministic resolution, written into an Obsidian vault and fed back to Paperless as tags. My instance runs in replace mode, so rerunning a document overwrites them.

PAPERLESSDOCLINGOLLAMAGRAPHBACK TO PAPERLESS

Every stage runs on hardware I own

Under the hood

01Entity resolution is mostly not machine learning

Matching “M. Jean Dupont”, “Jean DUPONT” and “Dupont Jean” is a problem of accents, titles, and legal suffixes long before it is a problem of embeddings. The resolver normalises deterministically, uses fuzzy matching only as a tiebreak, and injects the entities it already knows into the prompt so the model stops inventing new spellings. Pairs it is unsure about go to a review file rather than being merged silently.

02A model that is switched off must not mean a failed job

The GPU node is not always reachable. Jobs whose model is unreachable wait, with backoff, and resume when it comes back. They never fail. That single decision is the difference between a pipeline that survives a power cut and one that needs a human every time.

03Only one writer may touch the tags

Two components enriching the same document will each add their own tags, and neither will be wrong. The rule is that exactly one system writes. A union mode would keep corrections made by hand, but my instance runs in replace mode, so rerunning a document overwrites them.

Try it

This one cannot be opened up, and that is the point.

The whole premise is that the documents stay on machines I trust. Publishing an instance would contradict the only feature that matters. What is on this page instead is the pipeline, the resolution logic, and the failure modes, which is the part worth reading anyway.

Next projectHealth Docs