Storage formats
A LeanCorpus commit is a manifest plus a set of immutable segment files. Format changes must preserve the ability to identify the version being read, reject malformed lengths before allocating, and either remain readable or have an explicit migration path.
Segment inventory
The exact inventory depends on the fields and features used by a segment.
| Extension | Contents |
|---|---|
.dic |
Term dictionary backed by an FST |
.pos |
Postings, frequencies, positions, offsets, and optional payloads |
.fdt, .fdx |
Stored-field data and block index |
.dvd, .dvm |
DocValues data and metadata |
.nrm |
Field-length norms |
.bkd |
Numeric point index |
.vec, .vem, .hnsw |
Vector values, vector metadata, and optional HNSW graph |
.tvd, .tvx |
Term-vector data and index |
.del |
Deletion bitmap |
.sdel |
Soft-deletion metadata |
.pbs |
Parent markers for block joins |
.srt |
Index-sort metadata |
segments_N identifies the files belonging to a commit. Collection statistics may be stored separately as stats_N.json.
CodecKit envelope
Most CodecKit-framed files begin with a one-byte format version followed by a variable-length body size and the body. The size is validated before the body is allocated or decoded.
The diagram is generated from docs/diagrams/codeckit-envelope.edn using Bytefield-SVG. The SVG is committed so DocFX builds do not require Node.js. Run npm ci and npm run diagrams in docs/diagrams after changing its source.
Some streaming formats use a trailer form instead:
[version: byte] [body: bodyLength bytes] [bodyLength: int64 little-endian]
The fixed-width trailer lets a reader seek backwards from the end. Stored fields have their own streaming header because buffering a complete stored-field file would defeat block streaming.
Checksums
Framing and checksumming are separate concerns:
CodecFileHeaderidentifies the version and body boundary. It does not add a checksum by itself.WithChecksumCodecwraps a codec when a checksummed payload is required.segments_Nuses its commit checksum representation rather than the generic CodecKit envelope.- file-level validation also checks declared lengths, expected versions, referenced files, and format-specific invariants.
Do not document or implement a universal magic + body + CRC32 shape. The repository contains several deliberately different framing strategies.
Version registration
CodecFormat identifies a logical format and its ordered CodecVersionStep readers. A writer uses the current step. A reader dispatches according to the version stored in the file.
flowchart LR
V[Version byte] --> R[CodecFormat registry]
R --> S1[Version step 1]
R --> S2[Version step 2]
R --> SN[Current version step]
S1 --> D[Decoded model]
S2 --> D
SN --> D
A format change requires:
- a new version constant and
CodecVersionStep; - the previous reader retained when backward reading is supported;
- malformed and boundary-case tests;
- compatibility inventory and migration coverage;
- documentation of the on-disk difference.
See Adding formats and Codec migrations.
Commit format
The commit manifest is published after all referenced files. Its checksum protects the serialised manifest. Readers reject an invalid or incomplete generation and recovery can select the latest valid predecessor.
packet-beta
0-7: "Manifest JSON (variable length)"
8-11: "Line break & #crc32="
12-15: "CRC32 & line break"
The packet is schematic because the JSON and hexadecimal checksum text have variable lengths. Use it to explain ordering, not fixed byte offsets.
Compatibility inspection and migration
IndexFormatInspector inventories codec versions without opening the index for normal search. IndexCompatibility evaluates that inventory against the current reader. IndexOpenGuard enforces the configured compatibility mode.
IndexCodecMigrator performs user-facing staged migration. This is distinct from adding a new CodecVersionStep: contributors define readable formats, while operators use migration APIs to rewrite an existing index safely.
See validation, recovery, and compatibility for operational examples.
Store abstraction
MMapDirectory is the normal filesystem-backed directory. IndexInput and IndexOutput provide bounded reads, writes, seeking, slicing, and lifetime ownership. IndexAtomicFileWriter, DirectoryFsync, and FileOpenRetry centralise platform-sensitive filesystem behaviour.
Keep raw filesystem access behind the Store boundary. This preserves retry policy, atomic publication, Windows sharing behaviour, metrics, and testability.