Johannes Kaindl377 downloadsLocal, offline retrieval over your vault: related notes, semantic search, and grounded chat with a local LLM — nothing leaves your machine.
🇬🇧 English · 🇩🇪 Deutsch
Retrieval over your own vault — related notes and semantic search, always on-device — plus grounded chat with the LLM endpoint you configure.
Vault Retrieval turns your notes into a searchable knowledge base. Retrieval — related notes, semantic search — always runs on your device. It keeps a small embedding index inside your vault, synced along with it and readable on every device, and answers three questions: What else have I written about this? Where did I say something like that? What does my vault know about X? Embedding and chat talk to whatever LLM endpoint you configure — one under your control, local or on your own network, by default. Add a provider's API key, and that traffic goes to them instead.
Interface language: the UI follows Obsidian's own language setting — English by default, German when Obsidian is set to German. A handful of diagnostic strings (endpoint tooltips, Smart Apply guard details) are still German; they are being cleaned up.
Everything lives in one sidebar view with tabs: related notes, search, chat, reformat, plus Smart Apply once you enable it. Panels stay mounted, so a running chat stream or a pending Smart Apply survives switching tabs.
%% guidance comments, and a relevance-ranked template list (cosine over the same index — reusing the stored vectors, no re-embedding) preselects the best fit and updates live as you switch notes. Enable it under Settings → Smart Apply.<vault>/_vaultrag/ itself; from then on notes are re-embedded on save. Alternatively, drop in an index produced by an external backend and synced with the vault — the format is the same.In Obsidian, open Settings → Community plugins → Browse, search for Vault Retrieval, then install and enable it.
Download main.js, manifest.json and styles.css from the latest release, drop them into <vault>/.obsidian/plugins/vault-retrieval/, then enable Settings → Community plugins → Vault Retrieval.
Add the GitHub mirror johannes-kaindl/vault-rag in the BRAT plugin to track pre-release builds.
git clone https://git.jkaindl.de/jkaindl/vault-rag
cd vault-rag
npm install
npm run build # → main.js
# copy main.js, manifest.json, styles.css into <vault>/.obsidian/plugins/vault-retrieval/
_vaultrag/.)The middle column is what you type in the command palette. On a German Obsidian, the commands appear under their German names instead.
| Command | In the command palette | What it does |
|---|---|---|
| Open sidebar (per tab) | Open related notes · Open semantic search · Open vault chat · Open reformat panel |
Opens the sidebar on that tab |
| Reformat selection | Reformat section |
Reshapes the current selection (see step 6) |
| Smart Apply on active note | Smart Apply on active note |
Restructures the active note into a template |
| Reindex vault | Reindex vault |
Rebuilds the whole index from the vault |
| Complete the index | Complete index (missing notes) |
Embeds only what the index is missing |
| Restore index backup | Restore index from backup |
Restores a device-local index backup |
| Setting | What it does | Default |
|---|---|---|
| Embedding endpoint / model | Re-embeds notes on save; a fallback list, each row optionally with its own API key and model | http://localhost:11434 · qwen3-embedding:8b |
| Chat endpoint / model | LLM for RAG chat, Smart Apply and reformatting; same fallback-list shape as embedding | http://localhost:1234 · qwen3 |
| Index folder | Where the synced index lives. Cross-device sync (including iPhone) requires the Obsidian Sync option "Sync all other file types" | _vaultrag |
| Hide index folder in file explorer | Hides the index folder from the file explorer for a cleaner workspace (cosmetic; data and sync are unaffected) | on |
| Similarity / top-k | Retrieval thresholds | 0.3 · 20 |
| Excluded folders | Paths skipped by indexing (dot-folders are always skipped) | Templates/, Archive/ |
| Status bar | Shows embedding progress (● indexed | ⏳ pending); revealed automatically during a reindex |
off |
| Re-embed delay | How long after a save the note is re-embedded | 3000 ms |
| Smart Apply | Off by default; enabling it adds the tab, the command and the template settings | off |
| Context budget | Max characters fed as context (ceiling follows the model window) | 12000 |
| Suppress thinking | Default for new chats; also a per-chat toggle in the panel | off |
| Enter sends | On: Enter sends, Shift+Enter newlines · Off: reversed | on |
Endpoint tip: enter the base URL without a trailing
/v1— the plugin appends it. Both forms are accepted.
External providers: any endpoint row may carry an API key and a model name, turning it into a hosted OpenAI-compatible provider (OpenRouter, Groq, Together, Mistral, OpenAI, …) that sits in the same fallback list as local servers. Keys are stored unencrypted in the plugin's
data.json— like every other setting — and travel with settings sync.
The index in <vault>/_vaultrag/ is a portable note-level Matryoshka-256 int8 mini-index — one 256-dimensional int8 vector per note, roughly 1.4 MB for a few thousand notes. Small enough to sync with the vault, which is the whole point: the plugin loads it and runs brute-force cosine locally, so retrieval works identically on every synced device — no daemon, no VPN, no on-device LLM. What does leave the device is text: each note you save goes to the embedding endpoint to be re-embedded, and chat, Smart Apply and LLM reformatting send their prompt and context to the chat endpoint. Both are endpoints you configure — by default ones under your control — and a third party only receives any of it once you add that provider's API key.
The plugin writes that index itself, as a single container file (_vaultrag/index.bin, CRC-checked on every load — one file instead of several means a sync service can never deliver a mixed generation), and reads any index in the same format — including one exported by an external backend such as HyperForge.
Architecture, module layout and contributor conventions live in AGENTS.md.
Full guides live in docs/, organised along Diátaxis:
| Tutorial | From zero to your first related notes — start here |
| How-to guides | Chat setup, reformatting, Smart Apply, repairing an index, MCP, cross-device sync |
| Reference | Every command, setting, default, MCP tool and the index format |
| Explanation | Why the index looks the way it does, and where its guarantees end |
The plugin's embedding index doubles as a retrieval backend for MCP clients (Claude Code, OpenClaw, …). An in-plugin HTTP server (Streamable HTTP, loopback-only) exposes three read-only tools:
| Tool | What it does | Needs endpoint? |
|---|---|---|
search |
Semantic search over the vault (query → {path, score} hits) |
yes (embeds the query) |
related |
Notes related to a given note (straight from the index) | no — works offline |
read_note |
Full markdown text of a note (.md only, excludes respected) |
no — works offline |
Enable it in the plugin settings under "MCP-Server" (desktop only — the toggle and server
are disabled on mobile). The server binds to 127.0.0.1 on a configurable port (default
8123) and requires a Bearer token on every request. The settings section has a "copy
command" button that generates the registration command for you, e.g.:
claude mcp add --transport http vault-retrieval http://127.0.0.1:8123/mcp \
--header "Authorization: Bearer <token>"
Configuration (endpoints, index folder, excludes) is read from the plugin's own settings — no separate config file. The server only runs while Obsidian is open and picks up index changes live (reloads whenever the plugin rewrites the index). One server instance per vault. The server never writes to your vault.
Note: read_note enforces exclude prefixes case-insensitively (safe on case-insensitive filesystems like APFS/NTFS), while search/related filter result paths case-sensitively — they expose only paths and scores, never content.
Retrieval is also available inside Obsidian, without the MCP server and without a network round trip. Any plugin can ask this one for semantic hits instead of building its own embedding index:
const api = app.plugins.plugins["vault-retrieval"]?.api; // undefined if not installed/enabled
if (api?.apiVersion === 1 && api.status().indexed) {
const r = await api.search("what did I decide about the index format?");
if (r.ok) for (const hit of r.hits) console.log(hit.path, hit.score);
else console.log("unavailable:", r.reason); // "no-index" | "offline"
}
| Member | Signature | Notes |
|---|---|---|
apiVersion |
number |
1. Check it before relying on the shape of anything below. |
status() |
{ apiVersion, indexed, noteCount } |
Synchronous and network-free — meant for "can I offer retrieval at all?". Says nothing about endpoint reachability; that would need a request. |
search(query, opts?) |
Promise<Result> |
Text → semantically similar notes. Needs a reachable embedding endpoint. |
related(path, opts?) |
Promise<Result> |
Note → related notes. Straight from the index: no network, works offline and on mobile. |
Result is either { ok: true, hits: [{ path, score }] } or { ok: false, reason } with
reason one of "no-index", "offline" or "not-indexed" (the latter carries the path).
These calls never throw — expected states are values, and reason is a machine-readable
code, never translated prose, so the caller phrases its own message.
opts accepts k (number of hits) and minSim (similarity floor); both default to your
settings. The exclude list is not overridable — it is a boundary you set, not a tuning knob
for a third-party plugin. Scores are returned raw and unrounded; presentation is the caller's
call.
This deliberately does not expose note reading or raw embedding vectors: a plugin can read the vault through Obsidian's own API, and vectors would tie callers to the index dimension, model and quantisation — internals this plugin needs to stay free to change.
Status: the contract is versioned but young. Treat version 1 as experimental until a second consumer has confirmed its shape.
Image transcription (handwriting/screenshots → Markdown) lives in the sibling plugin image-to-markdown.
Issues and pull requests are welcome on Forgejo (canonical; GitHub is a mirror). The project is test-driven — every change ships with tests (npm test), and larger features go through a brainstorm → spec → plan → TDD flow (docs/superpowers/). See AGENTS.md for architecture and conventions.
LICENSE). A commercial dual-license is available on request if the AGPL's copyleft doesn't fit your use case.LICENSE-DOCS).Copyright © 2026 Johannes Kaindl.