docsv0.5.1

urna ask

Ask a .urna file a question in plain text and get the stored text of each hit with its urna:// citation, embedded offline and model-gated.

urna ask takes a text query, embeds it offline with the embedder the corpus manifest calls for, checks that embedder against the corpus model_hash, and prints the stored canonical text of each hit with its urna:// citation. It is the verb for a person at a terminal; urna retrieve returns the same hits as JSON for a program.

Usage

urna ask [OPTIONS] <FILE> <QUERY>

Arguments

ArgumentDescription
<FILE>Path to the .urna file.
<QUERY>The question, as one argument. Quote it in the shell.

Options

OptionDefaultDescription
-k, --k <K>10Number of hits to print.
--disclose <DISCLOSE>answeranswer: the cited text and its urna:// citation only. explain: also the route, the candidate counts per path, the rerank source and the recall line.
--embedder <EMBEDDER>routed by the manifest modelPath to a query-embedder script to run instead of the routed one. It must speak the query embedder protocol.
--candidates <CANDIDATES>4*k, at least 64The HNSW beam on an hnsw file, the candidates per path on a hybrid file. Ignored on an exact file.
--model-path <MODEL_PATH>noneLocal model directory, passed to the embedder as --model-path. For a potion corpus it is the potion table directory; for a registry model, the model snapshot directory.
-h, --helpPrint help (-h prints the summary).

Behavior

  1. Opens the file and validates it completely: header and section checksums, footer hash, manifest contract, the NaN and Inf walk over the embeddings, and the index payloads. A file that fails here stops before any Python runs.
  2. Reads embedding_model, embedding_dim and model_hash from the manifest.
  3. Picks the query embedder. A model name that starts with minishlab/potion runs embed_query_potion.py; any other name runs embed_query_model.py, the registry embedder. --embedder replaces the choice. When the manifest records full_dim (a corpus built with mrl_dim), the CLI also passes --mrl-dim <embedding_dim>.
  4. Runs the script under the resolved Python interpreter and prints the choice on stderr as [urna] embedder interpreter: <path>. Both lookups are described in Query embedder protocol.
  5. Applies the model gate in order: the reported model name equals the manifest name, the reported dim and the vector length equal the manifest dim, the manifest model_hash is not the all-zero placeholder, and the reported model_hash equals the manifest model_hash. Any failure exits 1 before the search runs. ask has no flag to skip the gate.
  6. Routes by the manifest index_type: hnsw runs the HNSW path with --candidates as the beam, hybrid runs the vector and BM25 legs with the query text, exact runs the exact scan. Every path ends in an exact cosine rerank, so every score is a real cosine value. See Search paths and the exact rerank.
  7. Prints the stored canonical text for each hit, highest score first. This is the same text urna cite returns, never a reopen of the original source bytes.

The installed binary answers potion corpora only

The embedder payload that urna setup and the one-line installers lay down carries embed_query_potion.py and the potion table, and not embed_query_model.py. On a corpus built with a registry model (wemm, jina, clip, siglip2), an installed binary stops with embedder script not found: python/forge/embed_query_model.py (override with --embedder). Run ask from a checkout of the repository, with the model's Python dependencies installed, or pass --embedder pointing at that script in a checkout. See Open a corpus you downloaded and Known limits.

Preset-built files take the HNSW route

Files built with the hybrid preset (the default [build] preset of urna build) declare index_type = "hnsw", so ask never runs their BM25 section. The HNSW beam is also never smaller than the ef_construction the file was built with (400 by default in urna.build and urna build), so a --candidates value below that changes nothing. See Known limits.

Output

stdout carries the answer. For each hit, the stored text, then an indented line with the citation and the source_uri in parentheses, then a blank line. With no hits, ask prints no hits..

stderr carries the interpreter line and, on failure, Error: <message>.

With --disclose explain, four lines and a blank line come before the answer:

LineValues
route:the path that ran: exact, hnsw or hybrid
candidates:exact=, ann=, bm25=, graph= counts, and fusion=none or fusion=rrf for hybrid
rerank_source:real cosine when the rerank read float32 vectors, real cosine at stored precision when it read float16, int8 or int4
recall:1 on the exact path; (not computed; rerank guarantees real cosine) on the others

Real output on the quickstart corpus:

[urna] embedder interpreter: /Users/nn/.local/share/urna/venv/bin/python
route:         hnsw
candidates:    exact=0 ann=12 bm25=0 graph=0 fusion=none
rerank_source: real cosine
recall:        (not computed; rerank guarantees real cosine)

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://sha256:1147b2560863331b21bd9d60fe6bdd99507dc34e17108444dc38194f8e6f09df/sha256:eed9a60b68133464e91c831f8af5960491f6c444cf1645fc5e4864435ab4bd44 (demo/04-offline-sovereignty.md)

The corpus has 12 chunks, so the HNSW shortlist holds all 12 (ann=12).

Exit codes

CodeMeaning
0The search ran. This includes a run that printed no hits..
1Any error: the file failed to open or validate, the embedder script was not found, the embedder exited non-zero or printed invalid JSON, the model gate refused, or the search rejected the query. The message is on stderr.
2Usage error: a missing argument, an unknown flag, or a --disclose value other than answer or explain.

Examples

Ask for the single best hit:

urna ask examples/quickstart/out/quickstart.urna "can I use this offline" -k 1

Show how the answer was found:

urna ask examples/quickstart/out/quickstart.urna "can I use this offline" -k 1 --disclose explain

Keep only the answer, without the interpreter line:

urna ask corpus.urna "how do citations work" -k 3 2>/dev/null

Pin the interpreter and the potion table for a sealed run:

URNA_PYTHON=/opt/urna-env/bin/python \
  urna ask corpus.urna "how do citations work" --model-path /opt/urna/potion-base-8M

For a walkthrough of ask, retrieve and cite together, see Ask and retrieve from the terminal.

On this page