The .indx Archive Format
A .indx file is the single portable artifact for a knowledge space — a self-describing container carrying everything a downstream tool needs: the knowledge graph, agent-readable chunks, and the vector matrix.
Under the hood it is an ordinary ZIP (deflate) with a defined internal layout and a manifest, so you can open it with unzip as readily as with KnowledgeSpace.load.
At a glance
Section titled “At a glance”| Property | Value |
|---|---|
| Container | ZIP, deflate compression |
| Access | Random access to individual members (no full decompress) |
| Default base name | handbook → handbook.indx (set with --name) |
| Produced by | The default indx writer (IndxWriter, stage 06), or space.save(path) |
| Opened by | KnowledgeSpace.load(path), indx inspect, indx query |
| Integrity | Per-member SHA-256 checksums in a dedicated checksums.json member |
| Versioning | schema_version (on-disk layout); indx_version (producing build) |
Internal layout
Section titled “Internal layout”A sealed archive contains the manifest, the serialized graph, and a per-member integrity record. The graph is split into newline-delimited members for documents, chunks, and relations.
handbook.indx (ZIP container)├── manifest.json # archive metadata (Manifest model)├── documents.jsonl # one Document per line├── chunks.jsonl # one Chunk per line (embeddings included)├── relations.jsonl # one Relation per line└── checksums.json # per-member SHA-256 digestsA few details make the format predictable:
documents.jsonl,chunks.jsonl, andrelations.jsonlare the serialized knowledge graph, one JSON object per line. They hold theDocument,Chunk, andRelationmodels respectively. JSON keys are sorted and member order is fixed, so the same space produces a byte-identical archive across runs and machines.chunks.jsonlcarries each chunk’s full record, including its embedding vector. The expanded on-disk layout (see below) instead splits chunks into per-file form underchunks/and stores vectors separately underembeddings/. Inside the sealed archive they stay together inchunks.jsonl.checksums.jsonrecords{ "algo": "sha256", "members": { <member>: <hex digest>, … } }. It is written last and is the one member it does not cover, since it carries no self-digest. A reader can therefore detect corruption or tampering of every other member on load.
manifest.json
Section titled “manifest.json”The top-level manifest.json holds the archive’s metadata, serialized from the Manifest model. Read it first to learn what an archive contains and whether you can open it. Integrity digests live separately in checksums.json.
{ "schema_version": "1", "indx_version": "0.1.0", "source_root": "/abs/path/docs", "components": { "parser": "plaintext", "llm": "none", "embedder": "openai:text-embedding-3-small", "store": "qdrant" }, "embedding_model": "openai:text-embedding-3-small", "embedding_dim": 1536}| Field | Type | Meaning |
|---|---|---|
schema_version | string | On-disk layout version. Bumped only on a breaking layout change; readers check it. |
indx_version | string | The producing indx build (e.g. 0.1.0). Recorded for diagnostics/auditing. |
source_root | string | Path of the walked directory/ZIP. |
components | object | The resolved slot → name map (parser, llm, vlm, embedder, store, …). |
embedding_model | string | null | The embedder name pinned for the run. |
embedding_dim | int | null | Vector dimensionality, so a reader can validate compatibility before querying. |
checksums.json
Section titled “checksums.json”Integrity is a dedicated member, not a manifest field, so the record can name manifest.json itself without a self-referential digest:
{ "algo": "sha256", "members": { "manifest.json": "…", "documents.jsonl": "…", "chunks.jsonl": "…", "relations.jsonl": "…" }}Sealing and loading
Section titled “Sealing and loading”Sealing
Section titled “Sealing”Sealing happens in stage 06 (Embed+Pack) via the indx writer (IndxWriter), or explicitly through space.save(path). Sealing the container:
- Writes
manifest.json— the serializedManifestmodel (withschema_versionset). - Writes
documents.jsonl,chunks.jsonl, andrelations.jsonl— the graph, one JSON object per line, with sorted keys and fixed member order. - Computes a SHA-256 digest of each of those members and writes them into
checksums.json(the last member, with no self-digest). - Deflates everything into the
.indxZIP container with zeroed timestamps, so the same space yields a byte-identical archive.
Loading
Section titled “Loading”KnowledgeSpace.load(archive) reverses the process, with verification gates:
- Verify
schema_versioncompatibility — see Versioning below. An incompatible layout fails fast. - Validate checksums — each member is checked against the SHA-256 digest recorded in
checksums.json; a mismatch is an archive error. - Reconstruct the in-memory models — the
Manifestplus theDocument,Chunk, andRelationPydantic v2 models are rebuilt from the JSONL members (write and read share one schema, so the data validates on load).
Versioning
Section titled “Versioning”.indx archives are versioned through two fields, and only one of them gates compatibility.
schema_version identifies the on-disk layout and controls whether an archive can be loaded. It is bumped deliberately on a breaking change to the member set or format; a reader checks it on load and rejects an unrecognized layout. Within a layout version, fields are only added, never removed or retyped, and consumers ignore unknown values rather than fail.
indx_version records the producing build (e.g. 0.1.0) for diagnostics and auditing. It never affects whether an archive loads.
For an end-to-end view of what makes a rebuild byte-identical, see reproducibility.
The expanded on-disk layout (unsealed)
Section titled “The expanded on-disk layout (unsealed)”Running a build writes the expanded layout alongside the sealed archive, so downstream tools can read either the portable container or the loose files directly — whichever is more convenient.
indx ./docs --out ./ai-readyai-ready/├── handbook.indx # the portable archive (sealed)├── index.json # the knowledge graph (version, root, metadata, stats, documents, chunks, relations)├── chunks/ # one chunk_NNNN.json per chunk (embedding excluded)└── embeddings/ ├── vectors.f32 # contiguous little-endian float32 matrix (count × dim) └── manifest.json # { model, dim, count, backend }The loose files are a convenience projection of the same space; the sealed handbook.indx is the canonical artifact. A few details:
index.jsonis the full serialized graph plus astatsblock; embeddings are never inlined here — they live underembeddings/. Its schema is documented in theindex.jsonreference.chunks/chunk_NNNN.jsonholds one file per chunk, named by its zero-padded index (chunk_0000,chunk_0001, …), with the embedding excluded.embeddings/vectors.f32is a raw, contiguous matrix of little-endianfloat32values laid out row-major ascount × dim— no delimiters or headers; its shape comes from the embeddings manifest.embeddings/manifest.jsonrecords{ model, dim, count, backend }— the embedder name, vector dimensionality, vector count, and the store backend — so a reader can validate and reshapevectors.f32without guessing.
The .indx file is what you ship or version-control as a single unit; the expanded form is handy for grepping, diffing, or wiring into a tool that wants plain files.
Why ZIP (and not tar.gz or SQLite)
Section titled “Why ZIP (and not tar.gz or SQLite)”The container choice is deliberate, and serves the goal of an open artifact with no lock-in:
- Random access to individual members. ZIP lets a consumer read just
manifest.json, or just one chunk, without decompressing the whole file — something a streamedtar.gzcannot do. - Stdlib and cross-platform. ZIP is handled by Python’s stdlib
zipfile, so reading and writing archives adds no dependency to the light core. - Inspectable with ubiquitous tooling. Anyone can
unzip handbook.indxand read the JSON by hand — important for an artifact meant to be a public contract.
SQLite was considered (single-file and queryable) but is opaque to non-SQLite tooling and would couple the artifact to a query engine. So SQLite remains a store option rather than the archive format.
The trade-off accepted for ZIP is slightly less compression efficiency for many tiny files than a solid tar.gz stream — a fair price for random access and tooling ubiquity.
See also
Section titled “See also”- The
index.jsonschema — the full structure of the knowledge graph inside the archive. - Inspect and query an archive —
indx inspectandindx queryover a.indxfile. - Output formats —
.indxvsjsonl,langchain, andllamaindexwriters. - Reproducibility — what makes a rebuilt archive byte-identical.