# Feather DB File Format v8: How One File Stores a Knowledge Graph > A single .feather file holds an HNSW index, typed graph edges, metadata, int8 quantized vectors, and namespace tables. Here's how v8 packs all of that into one binary, and what happens when you call DB.open(). - **Category**: Architecture - **Read time**: 7 min read - **Date**: June 16, 2026 - **Author**: Feather DB (Engineering) - **URL**: https://getfeather.store/theory/feather-db-file-format-v8-explained --- ## Everything in one file Feather DB is an embedded database. No server, no socket, no daemon. The entire persistent state of a database instance lives in a single `.feather` binary file. You copy the file, you copy the database. You delete the file, you delete the database. You can back it up with `cp`. Making this work requires that the file format efficiently encodes several heterogeneous data structures: the HNSW graph, raw float32 vectors, int8 quantized vectors with scale factors, per-node metadata, typed graph edges, and namespace tables. The v8 format, introduced in Feather DB v0.15, packs all of these into a single sequential binary layout with a fixed-width header, a section index, and variable-length sections. ## File structure overview The `.feather` file is organized as follows (described logically; the actual byte layout is fixed-width where possible and length-prefixed where variable): **Magic + version header (32 bytes)** The file opens with a 4-byte magic number (`FTHR`), a 2-byte format version (currently `0x0008` for v8), a 2-byte flags field, and a 24-byte reserved block for future use. A reader that sees a version higher than it supports should refuse to open the file and report a version mismatch, not silently corrupt data. **Section index (variable)** Immediately after the header is a section index: a fixed count followed by a list of (section_type, byte_offset, byte_length) triples. The section index allows `DB.open()` to locate any section without scanning the file. New sections can be added in future versions without invalidating existing readers — unrecognized section types are skipped. **Section: Vector store** The vector store section holds all raw float32 vectors, densely packed in row-major order. Each vector is preceded by its node ID (uint32). This section is memory-mapped on open, meaning vectors are read from disk on demand by the OS's page cache rather than loaded entirely into RAM. For a 500K-vector store at 768 dimensions, the vector section is approximately 1.5 GB and typically requires only a fraction of that in RAM at any given time. **Section: HNSW graph** The HNSW graph section encodes the hierarchical navigable small world graph structure. For each node, it stores: the node's level in the HNSW hierarchy, its neighbor list at each level (up to M=16 neighbors per level, encoded as a variable-length uint32 array), and the node's entry point flag (the global entry point is stored once in the section header). The graph section is the most write-intensive during construction and the most read-intensive during search. **Section: Metadata store** Metadata is stored in a length-prefixed key-value format. Each node's metadata block is located via a metadata offset table: a contiguous array of (node_id → byte_offset) pairs sorted by node_id, enabling binary search for O(log n) metadata lookup. Each metadata block stores: the float32 importance weight, a uint32 recall count (incremented on retrieval), the uint64 creation timestamp, and a variable-length attribute dictionary (key-value pairs, both string-typed). **Section: Context graph (typed edges)** The context graph section stores typed edges separately from the HNSW graph. Each edge is a (source_id, target_id, edge_type_enum, uint64_timestamp) tuple, stored as a flat array sorted by source_id. A per-source adjacency index maps node IDs to their edge list offsets for O(1) edge list lookup. Edge types are encoded as a uint8 enum value. **Section: Int8 quantization factors (v8 addition)** v8 adds a per-vector int8 quantization section. When in-RAM quantization is enabled, each float32 vector is also stored as an int8 vector with a single float32 scale factor. The scale factor section is a compact float32 array indexed by node position. This section is absent in files written before v8; readers should treat its absence as "quantization not available." **Section: Namespace table** A namespace table maps namespace string identifiers to sets of node IDs. This supports future namespace-filtered queries. In v8 this section is present but namespace-filtered search is not yet a first-class API — the table is maintained for forward compatibility. ## Version history: v3 to v8 VersionKey change v3Initial stable format. Float32 vectors, HNSW graph, flat metadata. v4Added typed context graph edges. Breaking change from v3. v5Added recall count tracking in metadata. Non-breaking addition. v6Section index introduced; prior versions were fully sequential. Breaking change. v7Namespace table added. HNSW graph encoding optimized (variable-length neighbor lists). v8Per-vector int8 scale factors added. Metadata offset table for O(log n) lookup. Current version. v8 files are not readable by v6 or v7 readers (due to the new section type). v8 readers can open v6 and v7 files by treating the missing int8 section as absent and falling back to float32-only search. ## What happens on DB.open() When you call `fdb.DB.open("memory.feather", dim=768)`, the following sequence occurs: - **File open + header validation.** The magic bytes and version are checked. A version mismatch raises a version error. The flags field is read for options (currently: whether int8 quantization is present). - **Section index read.** The section index is read into memory. All subsequent section accesses use this index to seek directly to the right byte offset. - **Vector section memory-map.** The vector section is memory-mapped rather than copied into heap memory. The OS page cache handles actual I/O on demand. This is why opening a 1.5 GB vector store takes milliseconds rather than seconds — no data is copied until accessed. - **HNSW graph reconstruction.** The HNSW graph section is read and the in-memory graph structure is built. This is the primary startup cost. In Feather DB v0.15+, this reconstruction uses parallel threads, achieving 4.7× speedup over sequential load for large graphs. A 500K-node HNSW graph with M=16 loads in approximately 180ms on a modern CPU using 8 threads. - **Metadata offset table load.** The metadata offset table (a sorted array of node ID → byte offset pairs) is loaded into memory. Metadata blocks themselves remain on disk and are read on demand. - **Context graph index load.** The per-source adjacency index for the typed edge graph is loaded. The edge data itself is accessed on demand during BFS traversal. - **Int8 scale factors load (if present).** If the int8 quantization section is present and in-RAM quantization is enabled, the float32 scale factor array is loaded. Int8 vectors are retained in memory to enable 1.7× RAM reduction for search. The result: a fully functional DB instance where search operates on the in-memory HNSW graph, metadata is accessible in O(log n), and vectors are paged from disk by the OS as needed. For a 500K-vector store, end-to-end open time is typically under 250ms. ## Backward compatibility guarantee Feather DB maintains the following compatibility contract: any file written by Feather DB v0.10 or later can be opened by the current version. The section index design (unknown sections are skipped) means that future versions can add sections without breaking existing readers. Breaking changes require a version number increment and explicit migration tooling. The migration tool (`feather migrate memory.feather`) reads an old-format file, reconstructs the database in memory, and writes a new-format file. For v7-to-v8 migration, the primary change is computing and writing the int8 scale factor section and rebuilding the metadata offset table. ```python import feather_db as fdb # DB.open() handles all of the above automatically db = fdb.DB.open("memory.feather", dim=768) # Check what's in the store print(f"Vectors: {db.count()}") print(f"Graph edges: {db.edge_count()}") # The file format is transparent — use the same API regardless of version results = db.context_chain(query_vec, k=10, hops=2) ``` The file format's complexity is entirely hidden behind `DB.open()`. From the application's perspective, it's one function call that returns a working database. The format does its work invisibly. **Install:** `pip install feather-db` · **GitHub:** [github.com/feather-store/feather](https://github.com/feather-store/feather) --- *This is the machine-readable mirror of the theory post at [getfeather.store/theory/feather-db-file-format-v8-explained](https://getfeather.store/theory/feather-db-file-format-v8-explained). For the full Feather DB documentation, see [getfeather.store/llms-full.txt](https://getfeather.store/llms-full.txt).*