Architecture¶
KGLite is an embedded Rust graph engine with several thin delivery surfaces. This page describes the current source layout and runtime boundaries. The generated project facts page records facts that come directly from Cargo metadata, packaging metadata, active workflows, and source constants.
Layer diagram¶
Python Rust applications Non-Rust applications
kglite-py (PyO3) kglite-mcp-server kglite-c (C ABI)
kglite-bolt-server │
└──────────────────┬────────────────────────────┘
▼
kglite::api boundary
lifecycle · query execution · errors · storage
│
┌──────────────┴──────────────┐
▼ ▼
Cypher parser/planner/executor Shared graph primitives
└──────────────┬──────────────┘
▼
DirGraph metadata and indexes
│
GraphBackend
┌──────────────┼──────────────┐
▼ ▼ ▼
MemoryGraph MappedGraph DiskGraph
StableDiGraph graph + mmap CSR + mmap
The core crate stays synchronous. Python owns GIL handling and Python object
conversion; protocol servers own their async runtimes, wire formats, logging,
and connection lifecycle. Rust-side wrappers call kglite::api directly.
Other languages use the C ABI rather than reaching into graph internals. See
the boundary principle for the full rule.
Workspace¶
The six crates have distinct responsibilities:
kglite— engine, storage, Cypher, and shared API. (Code-graph parsing moved to the standalone codingest project.)kglite-py— PyO3 classes and Python-specific conversion.kglite-c— stable C ABI for non-Rust bindings.kglite-mcp-server— MCP protocol adapter over the core.kglite-bolt-server— Bolt protocol adapter over the core.kglite-cli— command-line client.
The exact member paths and shared version are generated on the project-facts page.
Graph container and storage backends¶
DirGraph owns graph-wide metadata: schemas, string interning, index
definitions, secondary labels, embeddings, temporal/spatial configuration,
transaction versioning, and caches. Its topology is behind GraphBackend,
which implements the GraphRead and GraphWrite traits.
The three user-selectable modes are:
Mode |
Topology and properties |
Persistence role |
|---|---|---|
|
Heap-resident |
Saves to a |
|
Petgraph topology with property columns forced to mmap spill during build |
Saves to a |
|
CSR topology and mmap-backed stores in a directory |
The directory is the graph |
RecordingGraph is an internal test wrapper, not a fourth user mode. It logs
trait-path operations to verify that new backends can stay behind the storage
interface.
In-memory performance is the product gate. Disk-specific safety or scale work must remain gated to disk mode or large graphs rather than slowing ordinary in-memory queries.
Indexes¶
DirGraph coordinates several index families. Their physical representation
can differ by backend:
Index |
Purpose |
|---|---|
Type and secondary-label indexes |
Candidate discovery by label |
ID index |
Lookup by |
Property index |
Equality and |
Range index |
Ordered comparisons and ranges |
Composite index |
Multi-property lookup |
Disk prefix/property bundles |
Persistent disk-mode candidate routing |
Index definitions are explicit. The planner estimates and chooses among available routes; a missing optional index falls back to scanning candidates.
Query execution¶
The Cypher path is:
query → tokenizer → parser/AST → ordered planner passes → executor → CypherResult
The ordered optimizer registry in
crates/kglite/src/graph/languages/cypher/planner/mod.rsis the source of truth for pass order and stable pass names.Shared matching, filtering, and traversal primitives live under
crates/kglite/src/graph/core/so Cypher and the fluent API do not grow separate engines.The executor runs synchronously against
GraphRead/GraphWriteand enforces query deadline, cancellation, and work/row budgets.
ResultView is lazy at the Python boundary for eligible projections: the
executor can retain row bindings and materialize cells on demand. Other query
shapes hold already-computed Rust values. Either way, conversion into Python
objects is deferred until rows are accessed; this is not a promise that every
query operator streams without intermediate Rust rows.
Concurrency and snapshots¶
Graph state is shared through Arc<DirGraph>:
KnowledgeGraphis a single-owner Python handle. Concurrent reads are safe; overlapping mutation on the same Python object is rejected.FrozenGraphis an immutable O(1) snapshot for parallel readers.Sessionis the shareable server handle. Reads take momentary snapshots and run without holding the session lock; writes serialize and atomically swap the committedArc.Mutating a graph while another snapshot exists can trigger an
Arc::make_mutcopy. This is the cost of snapshot isolation.
Disk readers resolve CURRENT once and retain the selected immutable mmap
generation. A retained cross-process writer lease prevents two writers from
publishing concurrently. Readers do not take that writer lock. The detailed
contract and verification matrix live in Concurrency.
Spatial execution¶
Spatial values are represented with the geo/WKT stack. The fused spatial
join builds an rstar::RTree for one side of the current query, queries its
envelopes for candidates, and then applies the precise geometry predicate.
The tree is a per-query acceleration structure, not a persistent graph index;
other spatial expressions can still use bounding-box rejection and cached WKT
parsing.
Persistence¶
.kgl snapshots use the RGF v4 container:
magic RGF\x04
core-data version (u32 LE)
JSON metadata length + metadata
zstd-compressed topology section
zstd-compressed column sections by node type
optional embeddings, timeseries, and secondary-label sections
Container and core-data versions are separate. RGF v3 is detected and refused with a rebuild message; it is not silently interpreted as v4. Within v4, metadata additions use serde defaults where compatible, while incompatible embedded cache layouts are detected explicitly. Index definitions are stored so non-persistent index structures can be rebuilt on load.
Disk mode uses a different lifecycle: writers build a staged generation,
write completion metadata, rename it into generations/, then atomically
replace CURRENT. A failed or incomplete stage is never selected by a new
reader. Existing readers keep their old immutable generation alive.
Code-graph ingestion¶
Code-graph parsing and construction live in the standalone codingest project
(a sibling workspace that links this engine as a crate and emits ordinary
kglite graphs). kglite itself carries no tree-sitter grammars; it serves,
queries, and reads code graphs — api::code_entities, read_code_source,
explore, and the rev procedures all operate on any code-schema graph.