steinborn.dev
← all projects

LibrisVault

A self-hosted intake pipeline for everything you read - PDFs, articles, videos, screenshots and notes turned into linked, cited wiki pages that stay plain files on disk.

TypeScript · React · Node · Fastify · SQLite · Claude Agent SDK
View source
PolyForm Noncommercial · free to self-host, commercial use by arrangement
1,027
automated tests
52
API endpoints
10
maintenance run types
five months of one vault, April to September · dots are pages, lines are wikilinks · colour by domain, size by link count · 20 domains · 1,088 entries · labels hidden
01 · The problem

A folder full of PDFs is a graveyard.

Knowledge arrives as PDFs, office documents, web articles, images, and videos. Saving them somewhere is easy; what has value is the connected form - pages that link related ideas, cite their sources, and stay findable years later. Doing that by hand does not scale past the first busy week.

LibrisVault automates the whole path, across nearly any input channel: a watched folder, drag-and-drop, or anything sent to its Telegram bot from your phone - PDFs, office documents, blog posts and web links, YouTube videos, screenshots, plain notes. Everything is preprocessed and handed to sandboxed Claude Agent SDK sessions that write linked, cited wiki pages into an Obsidian vault. It all runs on your own machine - the vault stays a plain git repository on disk.

The vault format and its ingest skill are claude-obsidian, an MIT-licensed project by AgriciDaniel, used unmodified. LibrisVault is the service layer around it - automated intake, sandboxed agent runs, a job queue, the dashboard - and it extends what the vault holds through documented extension points rather than by editing it: a registry that binds every page to one field of knowledge from a closed list, and a set of rules carried into every run, from English-only content to page hygiene and entity notability.

02 · The system
intake folder · drag & drop · Telegram preprocessing poppler/OCR · pandoc · yt-dlp agent sessions Claude Agent SDK · sandboxed bubblewrap · no network during ingest · every run a git commit Obsidian vault linked, cited pages · git dashboard graph · library · research
binds 127.0.0.1 by default - the only traffic leaving the box is the agent's to Anthropic

A Fastify backend over SQLite with a React dashboard, 81 source files on the server side and 74 on the web side. An ingest is a job in a queue. A job is an agent session. An agent session is a git commit. Nothing else in the design matters as much as that last equivalence: it is what makes every automated change revertible with a tool the user already trusts.

03 · Why it holds up

OS-level sandboxing around every agent run

Every agent run happens inside a bubblewrap sandbox: write access to exactly one directory, no network during ingest, and every run is a git commit - fully revertible.

Live probes re-verify every guard

Measured that the SDK's permission callback effectively never fires for the writes that matter - so the security boundary sits at OS level, runs fail loudly without a sandbox, and live probes re-verify the guards after every SDK upgrade.

Domain decisions belong to the operator

Every page is filed under exactly one domain from a closed registry the agent reads as data. Inventing a key is not an option, because that page is edited by a person and never by an ingest run. What fits nothing goes to an unassigned sentinel. That pile is where proposals come from: a deterministic pass surfaces any tag recurring across five or more of them. An optional agent review only judges what it is handed. The operator decides, then a backfill re-files the backlog.

A read-only demo that cannot be talked out of it

The hosted demo refuses every request that is not a read, before any route handler runs. The first version of that guard also checked the URL prefix. A percent-encoded path slipped past it, because the router decodes a path before matching while the hook saw it raw. The fix removed the path condition rather than adding a second one. The tests now carry the spellings that broke it.

Retrieval that was measured before it was believed

Finding the right page in a vault of several hundred is a search problem. The honest way to tune it is a fixed question set. The instrument is 35 questions with a known answer page. At 947 pages the lexical arm alone puts the right page in the top five for 33 of them and first for 23. The one regression against the earlier run turned out to be structural rather than lexical. Naming it that way is what led to the fix.

Ten maintenance jobs, all of them explicit

Nothing about the vault is maintained by hope. Linting, repair, deduplication, tag fixes, domain backfill and review, the hot-cache rewrite and the retrieval index are each a named run with its own prompt, its own outcome and its own entry in the history.

A dashboard for the whole machine room

React frontend with SSE live updates, an interactive wikilink graph rendered on canvas with a web worker, a browsable library of every page, and web plus vault research with citations.

04 · What it produces

A vault that stays a vault. Plain markdown in a git repository on disk, readable in Obsidian with the service switched off. There is no proprietary store and nothing to migrate out of.

A dashboard for the machine room. Intake, job status with live updates over server-sent events, a browsable library of every page, and research over the web and the vault, each answer citing the pages it came from.

A shape you can see. The whole vault renders as one interactive wikilink graph, laid out by a web worker and drawn on canvas: communities detected and tinted, cross-community bridges drawn with their direction, six lenses over one layout - field of knowledge, page type, authority by inbound links, recency, orphans, stubs. Sources are not a pile. Each one sits beside the concepts and entities it supports, so what a cluster of knowledge actually rests on is visible rather than asserted.

An order that survives growth. Every page carries exactly one field of knowledge from a closed, human-governed set. The library browses along that field, along page type, recency and backlink count. Orphans and stubs are filters rather than surprises. The gaps - links pointing at pages nobody has written yet - are listed rather than quietly dropped. A vault of a thousand pages stays navigable in the same shape as one of fifty.

One command to stand it all up. A setup script installs Node, the sandbox and preprocessing toolchain, the vault and the service, and leaves exactly one step for the browser: connecting an Anthropic account. Windows without WSL gets a PowerShell path that installs it first.

05 · Scope

It is a service layer, not a vault format. The vault and its ingest skill are claude-obsidian's, used unmodified. What this project owns is everything that makes them run unattended: intake from three channels, the job queue, the sandboxed runner, the domain governance, ten maintenance jobs, the demo guard, the dashboard.

The sandbox is the boundary, not the callback. The Agent SDK's permission callback was measured to fire zero times, so the guarantee cannot sit there. It sits in the sandbox the runner configures: writes confined to the vault root, no web access in an ingest run, with the web reserved for the research flow. If the sandbox cannot be created, the run fails rather than proceeding. Unsandboxed commands are switched off as well, because the shell tool ships a parameter that would otherwise let a command opt out. Without that one setting the sandbox would be decorative.

What is on this page is what ships today. The next line of work is private for now. In it the library becomes a place with rooms and shelves rather than a filter bar. The agents become named research assistants that work a shift overnight, plan before they act, and leave proposals to be accepted or vetoed in the morning. None of that is described above, because none of it ships yet.

The licence is deliberately not open source. PolyForm Noncommercial: free to self-host, commercial use by arrangement.

06 · Notes from this project
2026-09-10 ai-engineering

One guard beats fifty route checks

How LibrisVault's read-only demo mode is a single request hook instead of per-route logic. The tests target the boundary itself rather than a list of routes.

2026-08-31 ai-engineering

The deploy that only half happened

A frontend rebuild under a running service left the dashboard full of 404s. A small debugging story about boot-time snapshots.

2026-08-28 ai-engineering

The permission callback that never fired

Why LibrisVault trusts the OS sandbox instead of the SDK permission layer. The measurement that settled it.

2026-08-07 ai-engineering

How a page becomes a vector

Two encodings of the same text, sparse and dense, because each is blind where the other sees. Which one a corpus needs is a measurement, not a principle.

2026-07-24 ai-engineering

The agent doesn't get to invent categories

How LibrisVault prevents ontology drift in an agent-maintained knowledge base with closed sets, explicit proposals, and a human veto.

Browse the vault.

Open demo
read-only demo · a curated slice of my real vault