docsv0.5.1

Manifest

Every field of the .urna v1 manifest, the Capabilities and CapabilitiesExt flags, validation rules, JSON serialization and the additivity rule for new fields.

The manifest is the JSON object stored between the section table and the first payload. It declares the model, the dimension, the dtype and the search contract of the file. This page lists every field, the rules the reader enforces, and how new fields are added without breaking old files.

A real manifest, from the quickstart corpus:

{
  "format_version": 1,
  "schema_version": 1,
  "embedding_model": "minishlab/potion-base-8M/v1",
  "embedding_dim": 256,
  "n_chunks": 12,
  "dtype": "float32",
  "metric": "ip",
  "score_type": "cosine",
  "normalize": "l2",
  "index_type": "hnsw",
  "rerank_policy": "exact",
  "model_hash": "sha256:8f2eb91a754b4da59cdd8223d0ba196185fed1bb6f092fd2be0ff02b893b1c98",
  "chunker_version": "quickstart/1",
  "capabilities": {
    "supports_exact": true,
    "supports_ann": true,
    "supports_bm25": true,
    "supports_citations": true,
    "supports_reproducible_build": true
  },
  "title": "quickstart",
  "version": "0.1.0",
  "created": "1970-01-01T00:00:00Z",
  "capabilities_ext": {
    "graph_present": true
  }
}

On disk it is compact JSON with no whitespace; urna inspect prints it indented.

Manifest fields

Rust struct urna_format::Manifest. "Default" is the value of Manifest::default().

FieldTypeRequiredDefaultRule
format_versionu32yes11 or lower, else UnsupportedFormatVersion
schema_versionu32yes11 or lower, else UnsupportedSchemaVersion
embedding_modelStringyes""not empty
embedding_dimu32yes0greater than 0; equals the header
n_chunksu64yes0greater than 0; equals the header and the chunk count at build
dtypeStringyes"float32"float32, float16, int8 or int4
metricStringyes"ip"only ip
score_typeStringyes"cosine"cosine or hybrid_rrf
normalizeStringyes"l2"only l2
index_typeStringyes"exact"exact, hnsw or hybrid
rerank_policyStringyes"none"none or exact; hnsw and hybrid require exact
model_hashStringyes""sha256: followed by 64 hex digits
chunker_versionStringyes""not empty
capabilitiesCapabilitiesyessee belowsee below
title, version, created, description, licenseOption<String>nounset, omittednone; created is free text
authorsOption<Vec<String>>nounset, omittednone
mrl_dim, full_dimOption<u32>nounset, omittedsee Matryoshka fields
capabilities_extOption<CapabilitiesExt>nounset, omittednone
any other keyextra mapnoemptykept and written back unchanged

"Required" means the key must be present for the JSON to parse. Unknown top-level keys do not fail: they land in the flattened extra map and round-trip.

index_type routes queries. The CLI verbs ask, retrieve and search-text, and Python UrnaFile.retrieve, pick the search path from the declared index_type, not from which sections are present. urna.build and the forge never write hybrid; only the Rust builder's hybrid() does. See Search paths and the exact rerank.

model_hash

model_hash fingerprints the embedding model. Queries must come from the same model, and the CLI checks this before searching: see The model gate.

Validation checks the format only. The all-zero value sha256: followed by 64 zeros passes.

The zero placeholder model_hash is accepted on write

Neither UrnaFileBuilder, urna.build nor the reader rejects the placeholder sha256:0000...0000: a file with it builds, validates and opens. The refusal happens only at query time. urna ask and urna retrieve refuse to search such a file, and urna search-text refuses unless you pass --skip-model-hash-check. In Python the check is opt-in: only retrieve(..., expected_model_hash=...) and search_space(..., expected_model_hash=...) compare hashes, and none of them treats the placeholder specially. Rust exposes the value as MmapUrnaFile::model_hash() and does no check of its own. See Known limits.

Matryoshka fields

mrl_dim and full_dim record a prefix truncation of the source vectors. When set:

  • both are present, or neither;
  • mrl_dim is greater than 0 and at most full_dim;
  • mrl_dim equals embedding_dim: the runtime strides by embedding_dim, so the stored dimension is the prefix dimension;
  • with dtype = "int4", mrl_dim is a multiple of 64.

Queries must be at the truncated dimension. The Rust builder has no truncation method: truncate and renormalize the vectors yourself and set the three fields. Python urna.build(mrl_dim=K) does it for you.

Capabilities

Five plain booleans, all required when parsing. Unknown keys inside capabilities are dropped.

FlagDefaultRule
supports_exacttruemust be true
supports_annfalsemust be true when index_type = "hnsw"; set by hnsw_index()
supports_bm25falsemust be true when index_type = "hybrid"; set by bm25_index() and hybrid()
supports_citationstruenot checked
supports_reproducible_buildtruemust be true; not tied to the builder's reproducible flag

The runtime does not read supports_ann or supports_bm25: it opens HNSW and BM25 when their sections are present.

CapabilitiesExt

Rust path urna_format::manifest::CapabilitiesExt (not re-exported at the crate root). Every flag is Option<bool> and is omitted when unset; the whole object is omitted when no flag is set.

FlagSet byRead by
supports_multimodalspace_table(), space_band()the runtime, to open 0x15 and the bands
graph_presentgraph_adjacency()the runtime, to open 0x0C
blobs_presentblob_refs(), blob_data(), blob_span_overlay()the runtime, to open 0x14, 0x16 and 0x17
graph_entities_presentnothingnothing
supports_catalognothingnothing

Without its flag, the runtime ignores the section: a 0x0C graph without graph_present is not loaded and search_graph falls back to exact search.

Validation

Manifest::validate() checks in this order and returns the first failure: format_version, schema_version, embedding_model not empty, embedding_dim greater than 0, n_chunks greater than 0, chunker_version not empty, model_hash format, dtype, metric, score_type, normalize, index_type, rerank_policy, hnsw or hybrid with rerank_policy = "exact", supports_exact, supports_reproducible_build, hnsw with supports_ann, hybrid with supports_bm25, then the Matryoshka rules.

After the manifest parses and validates, the reader also requires embedding_dim and n_chunks to equal the header, and the search_contract section to agree with metric, score_type, normalize, index_type and rerank_policy. The error variants are on Typed errors.

Serialization

Manifest::to_canonical_json() writes compact JSON: known fields in declaration order (the order of the table above), then the extra keys sorted, with no whitespace. Nested objects keep insertion order. The output is deterministic, but it is not RFC 8785 (JCS), which sorts every key; comments in the crate that say "JCS" mean this declaration-order form.

The manifest has no checksum of its own and is not part of content_hash. Editing a manifest field changes file_hash and leaves every citation valid. See Hashes and citations.

Additivity rule

Format v1 grows without breaking old files:

  • A new manifest field is an Option skipped when unset, so a file that does not use it serializes to the same bytes as before.
  • A new capability flag goes in CapabilitiesExt or in the extra map, never as a new required boolean in Capabilities. A new required boolean would make every old manifest fail to parse and would change every file_hash.
  • Keys a reader does not know survive in extra.

The test crates/urna-format/tests/manifest_additivity.rs guards these rules. See Compatibility.

Provenance and search_contract

Two related values live in sections, not in the manifest:

  • provenance (0x05) is any JSON value. The Rust builder defaults it to {}; set it with with_provenance(value). It is canonical, so its bytes, key order included, move content_hash. reproducible(true) does not rewrite it.
  • search_contract (0x06) is {metric, score_type, normalize, index_type, rerank_policy}, copied from the manifest at build time and cross-checked on read.

Media and spaces are not manifest fields

The .urna manifest has no media or spaces key. Inside the file, named spaces are the 0x15 table plus their bands, and media is 0x14, 0x16 and 0x17 (see Sections). The spaces and media keys you may have seen belong to the forge's sidecar <name>.manifest.json, a separate JSON file written next to the .urna by urna build; see Build artifacts. Keep "the file's manifest" and "the forge build manifest" apart.

On this page