Urna
A vector database in one file, with citations that stay valid.
A .urna file holds the chunks, the embeddings, the source spans, the indices and the search contract. The rust runtime maps it into memory, checks its hashes, and answers with exact cosine scores and a urna://content_hash/chunk_id citation for every hit. It works offline and rebuilds byte for byte. Python builds the file, rust serves it.
Renamed from
nestafter 0.4.0. A.nestfile written by 0.4.0 still opens.
Install
brew install hoffresearch/urna/urna
npm install -g @urna/cli
cargo install urna-cli
curl -sSf https://raw.githubusercontent.com/hoffresearch/urna/main/scripts/install.sh | sh
Then run setup once. It downloads the offline embedder, prepares a python env and checks the install:
urna setup
Python only, no setup step needed:
pip install "urna[embed]"
Windows, docker, cargo binstall and how to verify a download are in the install reference.
In the terminal
urna setup shows the plan before it writes anything and ends on the doctor checks.
urna tui opens a corpus, validates it, and lets you ask it questions. Each hit shows its score, the stored text and its citation.
urna tui my_corpus.urna
Quickstart
examples/quickstart/ has twelve short paragraphs and the spec that builds them. From a checkout:
urna build --spec examples/quickstart/corpus.toml
urna ask examples/quickstart/out/quickstart.urna "can I use this offline" -k 1
urna retrieve examples/quickstart/out/quickstart.urna "how do citations work" -k 2 --format jsonl
urna cite examples/quickstart/out/quickstart.urna 'urna://sha256:1147b256.../sha256:b5dfeb09...'
urna validate examples/quickstart/out/quickstart.urna
ask prints the answer with its citation, retrieve prints json for another program, cite turns a citation back into the stored text, and validate checks every hash. To build from your own rows, see usage section 13.
What the file guarantees
| Property | How |
|---|---|
| Self-contained | The file is the whole database. Copy it like a sqlite file. |
| Verifiable | Sha-256 per section, per file and over the decoded content. urna cite resolves any citation. |
| Reproducible | Same chunks and same model give a byte-identical file on any machine. |
| Offline | The runtime never opens a socket. A query from the wrong model fails at the model_hash check. |
Python
import urna
from urna.embed_potion import potion_embedder
emb = potion_embedder()
db = urna.open("my_corpus.urna")
qvec = emb.embed_texts(["can I use this offline"])[0]
hits = db.retrieve(qvec, 5, expected_model_hash=emb.model_hash())
print(hits[0].citation_id, hits[0].score, hits[0].text)
Search variants, validate, build
db.search(qvec, 5) # exact
db.search_ann(qvec, 5, 100) # hnsw, then exact rerank
db.search_hybrid(qvec, "vacina contra covid", 5, 100) # bm25 + vectors, exact rerank
db.search_graph(qvec, 5, hops=2, ef=100) # chunk graph from the seeds
db.search_space("clip-vit-b32", ivec, 5) # one named multimodal space
assert db.validate() is True
info = db.inspect()
Each chunk is a dict with canonical_text, source_uri, byte_start, byte_end and embedding:
urna.build(
output_path="my_corpus.urna",
embedding_model="sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2",
embedding_dim=384,
chunker_version="fixed-512/1",
model_hash=model_hash,
chunks=chunks,
reproducible=True,
preset="hybrid",
)
python examples/quickstart/quickstart.py runs the whole loop, build to cited hits, with no network.
CLI
The engine verbs take a file and a vector and never run python. The agent verbs (ask, retrieve, build) take text and use the offline embedder. setup and tui are the terminal ui. urna --help lists all three groups.
Agent verbs
urna ask my_corpus.urna "can I use this offline" -k 3
urna retrieve my_corpus.urna "can I use this offline" -k 5 --format jsonl
urna build --spec corpus.toml --dry-run
build reads one toml: the source (sqlite, csv, jsonl, an image dir), the media settings, and one or more embedding models from the registry (potion, clip-vit-b32, siglip2, wemm-2b, ...). Each model becomes a named vector space in the same file. The full spec is in usage section 13.
Search
urna search my_corpus.urna "[0.1, 0.2, ...]" -k 10
urna search-ann my_corpus.urna "[0.1, 0.2, ...]" -k 10 --ef 200
urna search-graph my_corpus.urna "[0.1, 0.2, ...]" -k 10 --hops 2 --ef 100
urna search-space my_corpus.urna "[0.1, ...]" --space "wemm-2b@256" -k 5
urna search-text my_corpus.urna "vacina contra covid funciona" -k 5
Inspect, validate, stats, cite, media, benchmark, doctor
urna inspect my_corpus.urna --json
urna validate my_corpus.urna
urna stats my_corpus.urna
urna cite my_corpus.urna 'urna://sha256:1aa9.../sha256:8f314...'
urna media my_corpus.urna --export DIR
urna benchmark my_corpus.urna -q 100 -k 10 --ann 100 --madvise-cold
urna doctor
Benchmarks
100,000 x 384 rows, k=10, one thread, same machine for every store.
| Store | p50 (ms) | p99 (ms) | Cold open (ms) |
|---|---|---|---|
| hnswlib | 0.32 | 0.53 | 182 |
| usearch | 0.67 | 61.4 | 58 |
| urna hybrid | 0.72 | 1.02 | 356 |
| urna exact | 7.80 | 8.32 | 292 |
| lancedb | 16.7 | 19.4 | 612 |
| sqlite-vec | 19.8 | 24.8 | 50 |
Both urna rows return recall@10 = 1.000. Urna's cold open includes checking every section hash before the first answer. Urna does not do updates, filters or concurrent writers. Method and the full table: docs/benchmarks.md.
Presets: size vs recall
| Preset | Embeddings | Index | Size | Recall@10 |
|---|---|---|---|---|
exact |
float32 | 1.000 | 1.000 | |
compressed |
float16 | 0.339 | 1.000 | |
tiny |
int8 | hnsw | 0.256 | 0.992 |
micro |
mrl256-int8 | hnsw | 0.223 | 0.810 |
nano |
int4 | hnsw | 0.209 | 0.913 |
hybrid |
float32 | hnsw + bm25 | 0.609 | 1.000 |
Measured on a 30,725-chunk pt-br corpus. Recall here is rank stability under quantization, not real-query quality. Details in usage section 6.
Images: 38,627 magic cards in one file
| Profile | Media | File | Vs the jpeg source |
|---|---|---|---|
archive |
JPEG XL, byte-reversible | 3.61 GB | 1.10x |
stills |
AV1 all-intra crf35 | 1.37 GB | 2.89x |
retrieval |
AV1 all-intra crf50 | 533 MB | 7.46x |
Text-to-image hit@1 over every card: siglip2 0.750, wemm-2b 0.744, jina 0.336, clip 0.098. Code and data: mtg-urna-benchmark.
Reference
- docs/usage.md: every verb, presets, models, builds, install channels
- docs/benchmarks.md: how the numbers were measured
- docs/SECURITY.md: reporting, hardening, data governance
- docs/CHANGELOG: releases with measured numbers
- docs/arc/arc.toml: the architecture map
- AGENTS.md: notes for contributors and agents
The crates are urna-format (the container), urna-runtime (search), urna-cli (the binary) and urna-python (the bridge).
License
MIT, see docs/LICENSE. Hoff Research
Made it simple, but significant (∂μfμν = jν)
Author: brenner cruvinel
Release files for urna 0.5.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Built distributions (wheels)
| File | Reset | |||
|---|---|---|---|---|
| urna-0.5.0-cp312-abi3-win_amd64.whl | CPython 3.12 | abi3 | Windows x86-64 | Details |
| urna-0.5.0-cp312-abi3-manylinux_2_34_x86_64.whl | CPython 3.12 | abi3 | Linux glibc 2.34+ x86-64 | Details |
| urna-0.5.0-cp312-abi3-manylinux_2_34_aarch64.whl | CPython 3.12 | abi3 | Linux glibc 2.34+ ARM64 | Details |
| urna-0.5.0-cp312-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl | CPython 3.12 | abi3 | macOS 10.12+ universal2 (ARM64, x86-64), macOS 10.12+ x86-64, macOS 11.0+ ARM64 | Details |
Total release size: 117.8 MB
Release files / urna-0.5.0-cp312-abi3-win_amd64.whl
| Download URL | urna-0.5.0-cp312-abi3-win_amd64.whl |
|---|---|
| Size | 29.1 MB |
| Tags | CPython 3.12 Windows x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
0574f0b64edd4b847579e2342cbdea813fb6386863fc623367dcce51377d8c35
|
|
BLAKE2b-256 checksum How to use checksums |
919f83d261a33438513c6a06904c6bc43ea5182d848e0cb36491f212daeca586
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Release files / urna-0.5.0-cp312-abi3-manylinux_2_34_x86_64.whl
| Download URL | urna-0.5.0-cp312-abi3-manylinux_2_34_x86_64.whl |
|---|---|
| Size | 29.4 MB |
| Tags | CPython 3.12 Linux glibc 2.34+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
42d19f5e82d4caacf1f0c4615cda1141f0653bea1a6b179b3ab9599723f81390
|
|
BLAKE2b-256 checksum How to use checksums |
bc0d8cc38b4ece6a410b1916762d8f2d328762e25831cc1b57975d7581ec136f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Release files / urna-0.5.0-cp312-abi3-manylinux_2_34_aarch64.whl
| Download URL | urna-0.5.0-cp312-abi3-manylinux_2_34_aarch64.whl |
|---|---|
| Size | 29.3 MB |
| Tags | CPython 3.12 Linux glibc 2.34+ ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
cf2cb2312ec0e75a2b51384e4a6f0983082348b997de92941c0878b1bb059155
|
|
BLAKE2b-256 checksum How to use checksums |
4c9837c2be999e82730ce24205e4222d2759bd31db50687b044b49355be3857c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Release files / urna-0.5.0-cp312-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl
| Download URL | urna-0.5.0-cp312-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl |
|---|---|
| Size | 30.0 MB |
| Tags | CPython 3.12 abi3 macOS 10.12+ universal2 (ARM64, x86-64) macOS 10.12+ x86-64 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
229ce9c9458397ed055fe2884fc6aa0436b2ce2a524dffc279922b2f474a0b38
|
|
BLAKE2b-256 checksum How to use checksums |
f8c236730b121f32869dedcd7f8e1607932f42a6dd3fba6aff20899594f99f3d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|