Introduction
urna is a vector database in one memory-mapped file that checks its own hashes, scores hits by exact cosine and cites each one with a stable urna:// id.
urna is a vector database in a single .urna file. The file holds the text chunks, their embeddings, the byte spans back to each source, the optional indices and the search contract. The Rust runtime memory-maps the file, verifies its hashes, and answers every query with an exact cosine score and a urna://content_hash/chunk_id citation you can resolve back to the stored text, with no network access.
Python builds the file (the urna wheel, or the build tooling in a checkout of the repository). Rust serves it: the urna command line tool, the urna-runtime crate and the Python bindings all read the same file.
What the file guarantees
| Property | What it means |
|---|---|
| Self-contained | The file is the whole database: chunks, embeddings, spans, indices, search contract and, optionally, media. Copy it like a SQLite file. Asking it questions in plain text also needs a local query embedder that matches the file's model. |
| Verifiable | The header and every section carry a checksum, the file has a SHA-256 file_hash, and content_hash covers the decoded content. urna validate checks them all and urna cite resolves any citation. These prove the bytes are intact, not who made the file: the checksums are unkeyed, so anyone who edits a file can recompute them. |
| Reproducible | The same rows, the same model and the same build settings produce a byte-identical file. |
| Offline | The Rust runtime never opens a socket, and the default query embedder is a static table that runs locally with no download. Network access happens at install time (package managers, the installers and urna setup), and the Python embedders download a model only when you set URNA_ALLOW_DOWNLOAD=1. The CLI refuses a query embedded by a different model than the file's model_hash records; in Python that check is opt-in. |
Each property has a page: the .urna file, citations and hashes, reproducible builds and offline by construction.
A first look
Install the binary and run setup once. urna setup lays down whatever the install is missing of the offline embedder and a Python env with numpy and tokenizers, then runs the health checks. Other channels (Homebrew, npm, cargo, pip, Windows) are on Installation.
curl -sSf https://raw.githubusercontent.com/hoffresearch/urna/main/scripts/install.sh | sh
urna setupAsk a corpus a question. This is the example corpus the Quickstart builds:
urna ask examples/quickstart/out/quickstart.urna "can I use this offline" -k 1to keep that promise for a brand-new user, the default embedder is a static, offline embedder that ships with the tool. it needs no model download and no network round-trip on first use, and it is deterministic, so a build is byte-identical and reproducible. a power user can bring a stronger embedding model instead, and the model's fingerprint is recorded so the corpus and the query embedder must agree or the search fails loudly.
-- urna://sha256:1147b2560863331b21bd9d60fe6bdd99507dc34e17108444dc38194f8e6f09df/sha256:eed9a60b68133464e91c831f8af5960491f6c444cf1645fc5e4864435ab4bd44 (demo/04-offline-sovereignty.md)Resolve that citation back to the stored text, with the hashes that identify the file and the chunk:
urna cite examples/quickstart/out/quickstart.urna urna://sha256:1147b2560863331b21bd9d60fe6bdd99507dc34e17108444dc38194f8e6f09df/sha256:eed9a60b68133464e91c831f8af5960491f6c444cf1645fc5e4864435ab4bd44citation_id: urna://sha256:1147b2560863331b21bd9d60fe6bdd99507dc34e17108444dc38194f8e6f09df/sha256:eed9a60b68133464e91c831f8af5960491f6c444cf1645fc5e4864435ab4bd44
file: examples/quickstart/out/quickstart.urna
file_hash: sha256:e4d5f8907faad38c192dc6929e36dbf16db558410b2dae5abb4d65f10f508832
content_hash: sha256:1147b2560863331b21bd9d60fe6bdd99507dc34e17108444dc38194f8e6f09df
chunk_id: sha256:eed9a60b68133464e91c831f8af5960491f6c444cf1645fc5e4864435ab4bd44
source_uri: demo/04-offline-sovereignty.md
byte_start: 10
byte_end: 11
text:
to keep that promise for a brand-new user, the default embedder is a static, offline embedder that ships with the tool. it needs no model download and no network round-trip on first use, and it is deterministic, so a build is byte-identical and reproducible. a power user can bring a stronger embedding model instead, and the model's fingerprint is recorded so the corpus and the query embedder must agree or the search fails loudly.urna retrieve returns the same hits as JSON lines for another program, and urna tui opens a corpus in a terminal explorer.
Installed binaries answer potion corpora only
The installed binary asks corpora built with the default potion model. Corpora built with a registry model (clip-vit-b32, siglip2, jina-v5-omni-*, wemm-*) need a checkout of the repository and that model's Python dependencies, and building any corpus with urna build needs a checkout too. See Known limits.
Who it is for
- Developers who want local search with citations inside an application or an agent, through the CLI, the Python module or the Rust crates.
- Teams that ship a curated, read-mostly knowledge base as a versioned file, including to machines with no network.
- Data and research teams that need reproducible corpora: a build spec, a lock file of the build environment, and hashes that tell two builds apart.
What it does not do
- No updates in place. A
.urnafile is built once and read many times: to change a chunk, rebuild and ship a new file. Changing any chunk changescontent_hash, and with it every citation issued against the old file. - No metadata filtering, no query language and no concurrent writers.
- No answer generation.
askandretrievereturn stored text with citations. Summarizing or chatting over it belongs to your application. - No encryption. Chunk text and source URIs are stored in cleartext, and
zstdis compression, not confidentiality. See Data governance. citereturns the stored canonical text and its span. It does not reopen or verify the original source document.- The default embedder,
potion-base-8M, is distilled from an English model. A corpus in another language needs a multilingual model. See Choose and bring embedding models.
Installation
Every channel, and the one setup step after it.
Quickstart
Build the example corpus, ask it, cite it, validate it.
Your first corpus
Turn your own JSONL or CSV rows into a cited corpus.
Concepts
What is inside a .urna file and why each part is there.
CLI reference
All 17 verbs, their flags, output and exit codes.
Python
Open, search and build files from Python.