Skip to content

Repository files navigation

PaperlessBrain logo

PaperlessBrain

The brain for your Paperless-ngx archive.
Chat with your documents, let a vision LLM read every page, never miss a deadline.
Installable PWA — built for mobile and desktop.

License: MIT Python 3.12+ Status: alpha Built with NiceGUI

Chatting with the archive — the assistant searches, reads and cites documents through an agentic tool loop


Status and expectations. This is a personal homelab project, shared as-is because it might be useful to someone else. It's alpha, it has rough edges, and I'm not a full-time maintainer — issues and PRs get answered when I have time.

I'd rather this grew into one good app than five half-finished ones, so if something is missing, broken or awkward, please open an issue or a PR here. Good work gets merged, and if you want a larger role than that, just ask. It's MIT — you're free to fork, but I'd much rather build it with you.

What it does

Paperless-ngx stores and organizes your documents. PaperlessBrain reads them — every page, through a vision LLM — and turns the archive into something you can talk to.

  • Chat with your archive — an agentic tool loop that searches, reads, cross-references and cites your documents. Any Anthropic-compatible endpoint (Claude, MiniMax …) or OpenAI-compatible endpoint (local Ollama, OpenAI, OpenRouter, vLLM …) — you add models per user in Settings.
  • Vision-LLM ingestion — each page is rendered to an image and read by a vision model: full-text summary, tables, actions and deadlines, extracted per document type. Runs entirely locally on Ollama if you want it to, or on any cloud vision model — same registry as the chat.
  • Deadlines & actions — extracted obligations ("cancel by …", "pay until …") surfaced on the dashboard.
  • Brain memory — the assistant remembers facts about you as plain Markdown files, embedded for recall in every conversation.
  • Vault notes — your own Markdown knowledge base, searchable in chat alongside the archive. Works with any editor; Obsidian optional.
  • Deep research — autonomous multi-step research, but the kicker is the data source: the agent researches across your own documents alongside the web. A deterministic orchestrator splits a job into sub-tasks, runs scoped agents, and synthesizes a reviewed result.
  • Document generation — DIN-5008 letters (German market), email drafts, chat-to-PDF saved back into Paperless.
  • Notes back into Paperless — the assistant can file a note on a document under your own Paperless account ("paid on 12.05., bank transfer"), so the record lives where the document lives.
  • OCR write-back (opt-in) — after each sync, documents whose vision-read text beats what Paperless-ngx has get their text replaced there, which fixes the Paperless full-text search too. Off by default; see Text write-back.
  • Email & calendar tools — per-user IMAP and CalDAV credentials, encrypted; the assistant can check mail and appointments when you ask.
  • Web search — via your self-hosted SearXNG instance, with full-page reading (trafilatura, optional headless Chromium for JS-heavy pages).
  • PWA — installable on phone and desktop, per-user language (English/German), dark/light theme.

Why I built this

This is a small project I built for my own document system. In early 2026 I tried several existing tools and none fit my needs — for what I wanted they were missing the advanced parts: using high-end consumer hardware effectively, a more detailed LLM ingestion, an information-rich vector database, and genuinely useful document detail views. It grew feature by feature and is now feature-complete enough that it feels worth sharing with the community.

My best personal results have been with Qwen3.6-35B-A3B (MTP, Q4) as the chat model — Multi-Token Prediction makes it faster than any cloud model I've tried, while staying really strong at tool use. Your mileage will vary with your own hardware and models; nothing here is tied to a specific one.

Screenshots

Dashboard showing extracted deadlines and actions
Dashboard — deadlines & actions extracted from every document
Document detail dialog with vision-read pages, tables and actions
Document detail — vision-read pages, tables, actions, cross-references
Deep research — autonomous multi-step research module in action
Deep research — autonomous multi-step research, reviewed before persist

Architecture

flowchart LR
    P[Paperless-ngx] <-->|REST API| B(PaperlessBrain)
    B <--> C[(ChromaDB<br/>+ JSON sidecars)]
    B <-->|chat + vision ingest<br/>local| O[Ollama]
    B <-->|chat + vision ingest<br/>optional| A["Anthropic-compatible<br/>(Claude, MiniMax …)"]
    B <-->|chat + vision ingest<br/>optional| X["OpenAI-compatible<br/>(OpenAI, OpenRouter, vLLM …)"]
    B <--> V[/"Vault (Markdown + git)"/]
    B -->|web search| S[SearXNG]
    B <-->|mail| M[IMAP]
    B <-->|calendar| D[CalDAV / iCal]
Loading

Sync compares Paperless against the index, new documents are rendered page by page and read by the vision model; results live in JSON sidecars plus ChromaDB embeddings (multilingual-e5 — multilingual retrieval). Each run ends with removal of deleted documents, an LLM review of the extracted deadlines, and — if you enabled it — the text write-back into Paperless. There are two LLM backends — anthropic and openai_compatible — and both take a custom base_url, so any API-compatible provider works, local or cloud. They share one tool set and one streaming event protocol, and the same model registry serves chat, deep research and vision ingestion.

Quick start (Docker)

mkdir paperless-brain && cd paperless-brain
BASE=https://raw.githubusercontent.com/Vailsen/paperless-brain/main
curl -O  $BASE/docker-compose.yml
curl -o .env $BASE/.env.example   # fill in: PAPERLESS_URL, PAPERLESS_SUPERUSER_TOKEN, STORAGE_SECRET
docker compose up -d              # pulls the prebuilt image from GHCR (no local build)

Two files, ~6 KB — the app itself is the prebuilt image, so there is nothing to clone. Prefer a fixed version over main: swap main for a tag such as v0.2.0 in $BASE.

Before the first start, edit the vault mount in docker-compose.yml so it points at the directory holding your existing Markdown notes — one subfolder per Paperless-ngx user (/srv/obsidian/alice for user alice → mount - /srv/obsidian:/mnt/vaults). Left unchanged, the app creates an empty vault under ./vaults and your real notes are never indexed.

.env.example is the short version; grab .env.example.full the same way for the annotated reference with every key.

To build from source instead, clone the repository and swap image: for build: . in docker-compose.yml.

Open http://localhost:8080 and log in with your Paperless-ngx username and password — users, permissions and sessions come from Paperless itself.

First boot downloads the embedding model (~2.2 GB) into a Docker volume; the container needs internet access once.

Image variants: the default tag (:latest) includes headless Chromium for JS-heavy web pages; the :lean tag is a ~1 GB smaller image where web reading falls back to trafilatura only. Pin a release with :1.2.3 (or :1.2.3-lean) for reproducible deploys.

Prerequisites

You need Notes
Paperless-ngx any recent version + a superuser API token
A vision-capable model for document ingestion — local via Ollama (e.g. a Qwen-VL-class model, can run on another machine) or any cloud vision model (Claude, GPT, MiniMax …). Picked in Settings > Processing
An LLM endpoint for chat Anthropic-compatible (Claude, MiniMax …) or OpenAI-compatible (Ollama, OpenAI, OpenRouter, vLLM …) — added per user with URL + key in Settings > AI Models
SearXNG optional — enables the web-search tool
IMAP mailbox / CalDAV calendar optional — enables the mail and appointment tools (per-user credentials, encrypted)
Wake-on-LAN capable GPU server optional — see Power management

Configuration (.env reference)

Copy .env.example to .env (the short list of keys most installs need); .env.example.full is the annotated reference with every key and its default. Keys without a default are required. Keys marked UI can also be changed in the app's Settings page (the app value wins; the env value is only the initial fallback).

Key Required Default Description
APP_PATH Absolute path to the app root, with trailing slash. In Docker: /app/
PAPERLESS_URL Base URL of your Paperless-ngx instance
PAPERLESS_SUPERUSER_TOKEN API token of a Paperless superuser (used for sync; chat requests use each user's own session token)
IGNORE_INBOX_TAG_AT_SYNC Inbox UI — documents carrying this tag are skipped during sync
EMBEDDING_MODEL Sentence-transformers model id; ships tuned for intfloat/multilingual-e5-large-instruct
CHROMA_PATH ChromaDB directory, relative to APP_PATH (keep under data/)
CHROMA_COLLECTION Collection name for document embeddings
EXTRACTION_SIDECAR_PATH Directory for per-document JSON extraction sidecars
THUMB_PATH Directory for thumbnails
CHROMA_MAX_RESULTS 20 UI — max search results per query
BRAIN_HINT_SIMILARITY_THRESHOLD 0.70 UI — min similarity for memory hints in search
BRAIN_HINT_WINDOW_FACTOR 1.5 UI — window factor for memory hints
OLLAMA_SERVER empty UI — Ollama base URL for vision ingestion; also names the host the WoL/shutdown buttons control (otherwise inferred from your first local-lane model)
OLLAMA_INGEST_MODEL empty UI — vision model used to read documents
EXTRACTION_PROFILE en UI — extraction-rule profile: en or de (see Extraction rules)
ARCHIVE_LANGUAGE en UI — language of AI-generated summaries (archive-level — sidecars are shared by all users)
TZ system IANA timezone for timestamps on generated documents
ANTHROPIC_API_KEY empty UI — global fallback key for Anthropic-compatible models; every other model (and each user's own key + base URL) is configured in Settings > AI Models
OLLAMA_HOST_LAN_MAC_ADDRESS_WOL empty MAC for Wake-on-LAN (empty = feature hidden)
OLLAMA_SSH_USER empty SSH user for remote shutdown of the Ollama host (empty = feature hidden). Needs passwordless sudo for /usr/bin/shutdown; in Docker, mount an SSH key into the container
OLLAMA_IDLE_SHUTDOWN_MINUTES 30 Idle minutes before the Ollama host is shut down
AI_GENERATED_TAG_NAME AI-generated UI — tag applied to documents the app creates in Paperless
AI_GENERATED_CORRESPONDENT PaperlessBrain AI UI — correspondent for AI-generated documents
AI_GENERATED_DOC_TYPE Information UI — document type for AI-generated documents
SEARXNG_HOST http://localhost:8888 SearXNG base URL for the web-search tool
VAULT_ROOT /mnt/vaults Root directory holding one vault subfolder per user. In Docker this is the path inside the container — leave it at /mnt/vaults and set the host location on the left side of the volume mapping.
BRAIN_SUBFOLDER PaperlessBrain Memory Vault subfolder reserved for agent-curated memory (names a real folder — change only on a fresh install)
VAULT_SYNC_COOLDOWN_S 3 Min seconds between vault sync runs per user
STORAGE_SECRET Secret encrypting server-side sessions — generate with python -c "import secrets; print(secrets.token_hex(32))"
SHUTDOWN_PASSWORD empty Confirmation prompt before the shutdown button acts. Empty = no prompt, one click powers the machine off. What hides the buttons is an empty OLLAMA_HOST_LAN_MAC_ADDRESS_WOL / OLLAMA_SSH_USER
HOST / PORT 0.0.0.0 / 8080 Bind address and port

Vault & memory

The assistant's long-term memory and your personal notes live in a plain folder of Markdown files — one subfolder per user under VAULT_ROOT:

vaults/
└── alice/
    ├── PaperlessBrain Memory/  ← agent-curated facts (one fact = one file)
    └── ... your notes ...      ← searchable knowledge base
  • The folder is git-tracked by the app itself — change detection, sync bookmark and audit trail in one. You don't have to touch git.
  • Obsidian is NOT required. Any editor works; the files are ordinary Markdown with a small YAML frontmatter.
  • Optional topology for editing on other devices: point a WebDAV server at the same directory and sync it with Obsidian + Remotely Save. The WebDAV server must serve the same directory the app mounts — the app keeps the single authoritative copy.

Markdown on disk is the source of truth; ChromaDB is only the index and can always be rebuilt from the files.

Text write-back (opt-in)

Paperless-ngx stores whatever its OCR engine produced. The vision model usually reads the same pages better — scans, tables, poor originals — but that text only lives in the sidecars here, so Paperless' own full-text search keeps hitting the weaker OCR.

Enable Settings > Paperless-ngx write-back > "Push AI extracted text to Paperless-ngx" and every sync ends with a comparison pass: for each document whose sidecar text differs from the Paperless content, the sidecar text is PATCHed in. The comparison ignores whitespace differences, and texts under 40 characters are treated as failed extractions and never pushed.

The setting is per user and off by default, and it travels with the settings export/import.

Three things to know before switching it on:

  • It overwrites. Paperless-ngx keeps no history of document text — the previous OCR result is gone. Everything else about the document (title, tags, correspondent, the file itself) is untouched.
  • Paperless can overwrite it back. Reprocess, rotate, split, merge or edit a document there and its own OCR replaces the text again. The next sync pushes yours back.
  • It runs with your permissions. The PATCH uses the signed-in user's own Paperless token, not the superuser token the sync otherwise uses, so it can only touch documents you may edit. Failures are counted and logged in the sync log rather than aborting the run.

It is idempotent: after a push both sides hold the same string, so the next sync finds nothing to do.

Extraction rules

Ingestion prompts are keyed to your Paperless document type names. Because those names are whatever you called them, the rules ship as selectable profiles in config/extraction_rules/:

EXTRACTION_PROFILE Contents
en (default) ~13 common international types — Invoice, Receipt, Contract, Bank Statement, Payslip, Insurance Policy, Tax Assessment, Notice, Certificate, Letter, Report, Rental Agreement
de ~46 types for the German legal/administrative domain

Any document type without its own entry falls through to _default, which still produces usable extraction — it just lacks type-specific guidance. Nothing breaks if your types don't match; the results are simply more generic.

To tailor it, edit the profile module and add entries keyed by your exact Paperless document-type name:

# config/extraction_rules/en.py
RULES["Warranty"] = {
    "prompt": BASE_INSTRUCTIONS + """
Document type: Warranty certificate.
Pay particular attention to:
- Product, serial number and purchase date
- Warranty period and expiry date
- What is covered and what voids the warranty
""",
}

To add a whole profile, drop <code>.py next to the others exporting a RULES dict and add the code to AVAILABLE_PROFILES in __init__.py. It then appears in the profile selector automatically.

The profile follows the names of your Paperless document types, not the language of the documents. An English invoice filed as Rechnung still matches the de rules — so a mixed-language archive needs no special handling.

ARCHIVE_LANGUAGE separately controls the language of generated summaries; extracted page text always keeps the document's original language, whatever it is. Both are set in Settings > Processing, with the .env values as fallback.

Languages (i18n)

UI ships in English and German; each user picks their language in Settings (chat answers follow it automatically). Adding a language:

  1. Add the code to SUPPORTED_LANGUAGES in i18n.py (e.g. "fr": "Français").
  2. pybabel init -i locales/messages.pot -d locales -l fr
  3. Translate locales/fr/LC_MESSAGES/messages.po.
  4. pybabel compile -d locales

Power management (optional)

For homelab GPU servers: the app can wake the Ollama host via Wake-on-LAN on first use and shut it down over SSH after idle. Set OLLAMA_HOST_LAN_MAC_ADDRESS_WOL and OLLAMA_SSH_USER to enable — with Docker this needs network_mode: host (magic packets don't cross the bridge network).

The shutdown half (dashboard button and the idle watchdog) runs ssh <OLLAMA_SSH_USER>@<ollama host> "sudo shutdown -h now" non-interactively — no password prompt is possible, so both steps below are mandatory:

  1. Key-based SSH login from the app to the Ollama host. On the app host: ssh-copy-id <OLLAMA_SSH_USER>@<ollama host>. In Docker the container has no identity of its own — mount the key read-only (see the commented line in docker-compose.yml): - /root/.ssh/id_ed25519:/root/.ssh/id_ed25519:ro
  2. Passwordless sudo for shutdown on the Ollama host. visudo and add:
    <OLLAMA_SSH_USER> ALL=(ALL) NOPASSWD: /usr/bin/shutdown
    
    Without it the SSH call fails with sudo: a password is required.

Verify without powering anything off:

ssh -o BatchMode=yes <OLLAMA_SSH_USER>@<ollama host> "sudo -n /usr/bin/shutdown --help >/dev/null && echo READY"

(Inside Docker: prefix with docker exec <container> .) READY means both the button and the idle watchdog will work.

Security notes

  • The superuser token is used only for sync/ingestion; every chat request runs with the logged-in user's own Paperless session token, so Paperless object permissions apply.
  • Sessions are encrypted server-side with STORAGE_SECRET.
  • Per-user IMAP/CalDAV credentials and API keys are stored encrypted with a key derived from the user's session — never in plaintext.
  • The app is designed for LAN / reverse-proxy deployment; it does not implement rate limiting or public-internet hardening. Put it behind your proxy + SSO if you expose it.

Bare-metal install

On Windows, use Docker. The bare-metal path needs the GTK3 runtime for PDF generation, which has no pip-installable equivalent — the container ships it for you and behaves identically on every host OS.

WeasyPrint (PDF generation) loads Pango at render time, so a missing library shows up as a failed PDF export rather than a startup error. Install it and a font family up front:

# Debian/Ubuntu
sudo apt install libpango-1.0-0 libpangoft2-1.0-0 fonts-dejavu-core
# Fedora
sudo dnf install pango dejavu-sans-fonts
# Arch
sudo pacman -S pango ttf-dejavu
# macOS
brew install pango

Verify before you rely on it:

python -c "from weasyprint import HTML; HTML(string='<p>ok</p>').write_pdf(); print('PDF OK')"
python3.12 -m venv .venv && source .venv/bin/activate   # 3.12–3.14
pip install --extra-index-url https://download.pytorch.org/whl/cpu -e ".[crawl]"
playwright install chromium        # only for the [crawl] extra
cp .env.example .env               # edit values, APP_PATH = repo root
python main.py

On GPU machines drop the --extra-index-url to get CUDA torch. A systemd unit is the recommended way to run it as a service.

Contributing

Issues and PRs welcome — this is an early alpha extracted from a personal homelab project, so expect rough edges. Please open an issue before large changes. CI runs the test suite on every PR.

Acknowledgements

NiceGUI (zauberzeug/nicegui) is the entire frontend. Every page, dialog, the streaming chat view and the kanban board are plain Python — no JavaScript build step, no separate frontend service, no API layer to keep in sync. A one-person project keeps a UI this large maintainable only because NiceGUI removed that whole category of work. The reactive @ui.refreshable model and the async event loop shared with FastAPI are what make token-by-token streaming into the browser a few lines instead of a websocket protocol. Thank you, Zauberzeug.

Paperless-ngx (paperless-ngx/paperless-ngx) is the archive this is built on — it stores the documents, owns the users and permissions, and is the reason this project can concentrate on reading and reasoning instead of document management. PaperlessBrain adds to it; it does not replace it.

Also standing on: FastAPI, ChromaDB, sentence-transformers with intfloat/multilingual-e5-large-instruct, Ollama, pypdfium2, WeasyPrint and SearXNG.

License

MIT — use it, fork it, ship it, sell it. Attribution is the only condition.

All runtime dependencies are permissively licensed (MIT / BSD / Apache-2.0). PDF rendering uses pypdfium2 (BSD-3/Apache-2.0) and PDF generation uses WeasyPrint (BSD-3).

About

AI brain for your Paperless-ngx archive — chat with your documents via local (Ollama) or cloud LLMs, with agentic tool use, semantic search, and long-term memory.

Topics

Resources

Contributing

Security policy

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages