Lemmary turns scans, PDFs, and office files into an organized library you can search, review, and research in plain language.
See the codeDocument storage built with Go + PocketBase and a React + TanStack Router frontend. Upload documents, run OCR, extract metadata with an OpenAI-compatible AI provider, and review results in the UI.
Lemmary implements a paperless-ngx-compatible REST API under /api/, so you can use third-party clients instead of (or alongside) the built-in web UI. Coverage is partial — document list/upload/download, tags, metadata, and the document list's search and filters generally work, but not every paperless-ngx endpoint or feature is implemented.
The API has been tested with the swift-paperless iOS app and mostly works for browsing and uploading documents. See docs/paperless_ngx.md for connecting external clients.
/rag/search) finds documents and lists them as cards; Deep Research (/rag/research) searches, reads the documents it finds, surveys hundreds at once on the General AI model, counts and totals, and answers with links to its sources — streaming each step as it works; a run that outgrows the model's context window fails with the provider's error; chats are saved, listed in a sidebar, and resumable by URLEvery screen has one in docs/screenshots.md. The library in all of them is a demo archive of invented documents, so nothing in these images belongs to anybody. A few to start with:
Documents list with AI-extracted titles, summaries, and tags, and a timeline that counts the archive by month:

Document detail: the file itself beside editable metadata, summary, and OCR text. Fields the model wrote in the document's own language keep the original under the translation:

AI assisted search — a natural-language query, and the documents it matched:

Deep Research — the same archive read rather than listed, answered with links to the documents each figure came from:

Split a scanner's multi-document PDF back into one document per part, by hand or with the cuts the model proposes:

Admin Settings: providers, models and worker timeouts as runtime configuration:

mistral), Google Cloud Vision (google_vision), a file-capable OpenAI/OpenRouter/Anthropic model, or Docling (docling) — a keyless sidecar on your own host running PaddleOCR's PP-OCR models, so scans never leave the machine; see docs/local_ocr.md. Configured in Settingsopencode), Mistral, OpenAI, Anthropic (anthropic), OpenRouter, or a ChatGPT subscription (chatgpt), via the official OpenAI and Anthropic Go SDKs — Claude is reached on its own Messages API, and Opencode serves a third of its catalogue there too — see docs/ai_providers.mdbackend/ PocketBase app, migrations, OCR/AI worker
frontend/ React UI
docs/ Setup and operation guides
cp .env.example .env
# Optional: seed OCR/AI keys in .env for first boot (skips those wizard steps)
docker compose up -d
Open http://127.0.0.1:8090. On first launch, the in-app setup wizard creates your admin account and collects OCR + LLM API keys (hard gate until both are set). A single Mistral key covers OCR, extraction and embeddings. Data is stored in a Docker volume (app_data).
Volumes, reverse proxies, backups and upgrades: docs/self_hosting.md. To run without Docker, see docs/development.md.
docs/setup.md is the configuration reference; docs/ai_providers.md covers the AI-specific options.
WORKER_CRON_EXPR, the LIMIT_* family, VAULT_* and the frontend's VITE_* stay in .envapp_settings). AI_API_KEY plus SETUP_ADMIN_EMAIL/SETUP_ADMIN_PASSWORD in .env bring a fresh instance up with nothing to answer; otherwise the first-launch wizard collects them. Either way Settings is authoritative afterwardsAI_MANAGED=1 inverts that for a hosted fleet: the environment is re-applied on every boot and the tenant's Settings page has no Providers, Models or Duplicates sectionsUnit tests live beside the code they cover:
cd backend && go test -tags vectors ./... -count=1
cd frontend && pnpm test
The vectors tag is not optional: bleve's vector search is a cgo binding to
blevesearch's FAISS fork, and the backend does not build without it. FAISS is
needed on this machine only for Go commands run here, since the verification
image the overlay builds carries its own. Installing it is a one-off, and the
shortest route needs no compiler at all:
docker buildx build --target faiss --output type=local,dest=./.faiss .
mkdir -p "$HOME/.local/faiss" && cp -a .faiss/lib .faiss/include "$HOME/.local/faiss/"
With direnv, direnv allow then points the toolchain at
it and sets the tag for you. The other two routes — building it into
/usr/local or into your home directory with scripts/faiss-build.sh — are in
docs/development.md, along with
the packages each one needs.
The end-to-end suites, the dev runner and the full verification stack live in a
separate private repository and are not part of this one. ./scripts/test-all.sh
requires that overlay; without it the command fails.
Lemmary is source-available, not open source. It is licensed under the PolyForm Noncommercial License 1.0.0 (see LICENSE).
| ✅ Allowed | Self-hosting for personal or household use; hobby projects, research, study; use by charities, schools, public research, public safety/health, environmental, and government organizations; reading, forking, modifying, and redistributing the source |
| ❌ Not allowed without a commercial license | Use by or on behalf of a business; offering Lemmary to third parties as a hosted or paid service; bundling it into a commercial product |
For commercial licensing, contact Alexander Arutyunov licensing@lemmary.app.
Opencode Go is a perfect plan to use with this project as AI provider: one subscription covers a catalogue of models, and AI_SDK=opencode is the whole configuration — it knows which of Opencode's three endpoints each model is served on, so nothing else needs binding. See the docs.
261 commits
Go
78.5%
TypeScript
20.7%
Lemmary turns scans, PDFs, and office files into an organized library you can search, review, and research in plain language.
See the codeDocument storage built with Go + PocketBase and a React + TanStack Router frontend. Upload documents, run OCR, extract metadata with an OpenAI-compatible AI provider, and review results in the UI.
Lemmary implements a paperless-ngx-compatible REST API under /api/, so you can use third-party clients instead of (or alongside) the built-in web UI. Coverage is partial — document list/upload/download, tags, metadata, and the document list's search and filters generally work, but not every paperless-ngx endpoint or feature is implemented.
The API has been tested with the swift-paperless iOS app and mostly works for browsing and uploading documents. See docs/paperless_ngx.md for connecting external clients.
/rag/search) finds documents and lists them as cards; Deep Research (/rag/research) searches, reads the documents it finds, surveys hundreds at once on the General AI model, counts and totals, and answers with links to its sources — streaming each step as it works; a run that outgrows the model's context window fails with the provider's error; chats are saved, listed in a sidebar, and resumable by URLEvery screen has one in docs/screenshots.md. The library in all of them is a demo archive of invented documents, so nothing in these images belongs to anybody. A few to start with:
Documents list with AI-extracted titles, summaries, and tags, and a timeline that counts the archive by month:

Document detail: the file itself beside editable metadata, summary, and OCR text. Fields the model wrote in the document's own language keep the original under the translation:

AI assisted search — a natural-language query, and the documents it matched:

Deep Research — the same archive read rather than listed, answered with links to the documents each figure came from:

Split a scanner's multi-document PDF back into one document per part, by hand or with the cuts the model proposes:

Admin Settings: providers, models and worker timeouts as runtime configuration:

mistral), Google Cloud Vision (google_vision), a file-capable OpenAI/OpenRouter/Anthropic model, or Docling (docling) — a keyless sidecar on your own host running PaddleOCR's PP-OCR models, so scans never leave the machine; see docs/local_ocr.md. Configured in Settingsopencode), Mistral, OpenAI, Anthropic (anthropic), OpenRouter, or a ChatGPT subscription (chatgpt), via the official OpenAI and Anthropic Go SDKs — Claude is reached on its own Messages API, and Opencode serves a third of its catalogue there too — see docs/ai_providers.mdbackend/ PocketBase app, migrations, OCR/AI worker
frontend/ React UI
docs/ Setup and operation guides
cp .env.example .env
# Optional: seed OCR/AI keys in .env for first boot (skips those wizard steps)
docker compose up -d
Open http://127.0.0.1:8090. On first launch, the in-app setup wizard creates your admin account and collects OCR + LLM API keys (hard gate until both are set). A single Mistral key covers OCR, extraction and embeddings. Data is stored in a Docker volume (app_data).
Volumes, reverse proxies, backups and upgrades: docs/self_hosting.md. To run without Docker, see docs/development.md.
docs/setup.md is the configuration reference; docs/ai_providers.md covers the AI-specific options.
WORKER_CRON_EXPR, the LIMIT_* family, VAULT_* and the frontend's VITE_* stay in .envapp_settings). AI_API_KEY plus SETUP_ADMIN_EMAIL/SETUP_ADMIN_PASSWORD in .env bring a fresh instance up with nothing to answer; otherwise the first-launch wizard collects them. Either way Settings is authoritative afterwardsAI_MANAGED=1 inverts that for a hosted fleet: the environment is re-applied on every boot and the tenant's Settings page has no Providers, Models or Duplicates sectionsUnit tests live beside the code they cover:
cd backend && go test -tags vectors ./... -count=1
cd frontend && pnpm test
The vectors tag is not optional: bleve's vector search is a cgo binding to
blevesearch's FAISS fork, and the backend does not build without it. FAISS is
needed on this machine only for Go commands run here, since the verification
image the overlay builds carries its own. Installing it is a one-off, and the
shortest route needs no compiler at all:
docker buildx build --target faiss --output type=local,dest=./.faiss .
mkdir -p "$HOME/.local/faiss" && cp -a .faiss/lib .faiss/include "$HOME/.local/faiss/"
With direnv, direnv allow then points the toolchain at
it and sets the tag for you. The other two routes — building it into
/usr/local or into your home directory with scripts/faiss-build.sh — are in
docs/development.md, along with
the packages each one needs.
The end-to-end suites, the dev runner and the full verification stack live in a
separate private repository and are not part of this one. ./scripts/test-all.sh
requires that overlay; without it the command fails.
Lemmary is source-available, not open source. It is licensed under the PolyForm Noncommercial License 1.0.0 (see LICENSE).
| ✅ Allowed | Self-hosting for personal or household use; hobby projects, research, study; use by charities, schools, public research, public safety/health, environmental, and government organizations; reading, forking, modifying, and redistributing the source |
| ❌ Not allowed without a commercial license | Use by or on behalf of a business; offering Lemmary to third parties as a hosted or paid service; bundling it into a commercial product |
For commercial licensing, contact Alexander Arutyunov licensing@lemmary.app.
Opencode Go is a perfect plan to use with this project as AI provider: one subscription covers a catalogue of models, and AI_SDK=opencode is the whole configuration — it knows which of Opencode's three endpoints each model is served on, so nothing else needs binding. See the docs.
261 commits
Go
78.5%
TypeScript
20.7%