orbok
Local-first AI document search — private and storage-aware.
Overview
orbok searches your local files by combining exact keyword retrieval and dense vector (semantic) search, fused with Reciprocal Rank Fusion. Everything runs on your computer. Document contents are never sent to an external server.
A cross-encoder reranking stage is designed (RFC-010) and not implemented; there is a trait seam and no production reranker.
Supported document types (current): plain text, Markdown, HTML, PDF, DOCX, CSV, and common source-code files.
Why orbok
| Need | orbok |
|---|---|
| Search by exact identifier or error code | Keyword index (FTS5) |
| Search by meaning or concept | Local embedding model |
| Privacy — no cloud upload | All processing is local by default |
| Mixed Japanese and English documents | Unicode tokenization (RFC-014 refines) |
| Understand what is stored | Storage dashboard with cleanup controls |
Quick Start
# Install (requires Rust 1.91+)
# Launch the GUI
# Validate backend without a display (CI / headless)
ORBOK_DATA_DIR=/tmp/orbok-test
On first launch, orbok asks you to add at least one source folder. It will scan that folder and build a local search index.
Semantic search requires a local embedding model; keyword search works with no models installed at all.
Design Notes
Local-first by design
orbok does not copy your source files. It stores derived indexes (chunk offsets, FTS5 tokens, embeddings) and metadata.
It also caches the extracted text of each indexed document in
orbok-cache.sqlite3, so that changing the embedding model does not require
re-parsing every PDF. That cache has a 90-day freshness limit — an entry older
than that is never served, forcing a fresh extraction — and both
"Reset saved app data..." and the Storage view's "Clear extracted text" erase it
outright, on demand, regardless of age (RFC-059).
Data is classified into three lifecycle layers (RFC-001):
- Persistent catalog — source registrations, settings, file metadata. Never deleted by routine cleanup.
- Rebuildable indexes — keyword index, embedding vectors. Deletable and rebuildable from source files at any time.
- Ephemeral cache — extracted text, chunk bundles, recent snippets, search result cache. Rebuildable from source files. The extracted-text namespace — the largest of these — has the 90-day freshness limit above, an on-demand full erase, and a 20,000-entry bound applied automatically when indexing goes idle, least recently used first (RFC-059). Chunk bundles and previews have neither a limit nor a bound.
Security boundary
The Rust backend enforces a strict source allowlist (RFC-003): the frontend requests data through typed service calls and never reads arbitrary filesystem paths.
One backend path is currently outside that guard: snippet loading opens the file recorded in the catalog directly, so a file whose source was later paused or removed can still be read to render an excerpt. RFC-060 routes it through the allowlist.
Keyword search
SQLite FTS5 with unicode61 tokenization. The index is contentless: tokens are indexed, but source text is not retained. Display snippets are loaded dynamically from source files via stored byte offsets.
Japanese segmentation is in the roadmap (RFC-014).
Semantic search (optional)
Local dense embedding via a pluggable Rust inference backend (ONNX Runtime via tract). Model files stay on your machine.
Switching models does not currently invalidate or rebuild existing embeddings — vectors indexed under the previous model remain and are not used by the new one. Re-index manually after a model change. RFC-060 tracks the fix.
Disk use
orbok keeps separate databases: orbok-catalog.sqlite3 for the
authoritative catalog and orbok-cache.sqlite3 for the localcache
payload store (per Appendix A of the RFC set).
More Detail
Full documentation is in docs/ (mdbook):