A local-first vector + graph database that records who vouched for every fact, and when it stopped being true.
Quatzal stores rows with scalar fields, embedding vectors, and graph edges in one engine. A single query can find nearest neighbours and expand the relationships around them — no second database, no sync job, no application-layer glue between a vector store and a graph store.
It is MCP-native: the server speaks JSON-RPC over stdio or Streamable-HTTP, so an AI assistant can query it directly.
- Website and documentation: https://www.quatzal.com/
- Articles: https://www.quatzal.com/articles/index.html
Status: pre-release (0.1.0). Data formats may change between versions with no migration path, indexes are RAM-resident, and recall is measured honestly at every scale — including the scale where it currently falls short. Read Limitations before drawing conclusions.
Most RAG and agent-context pipelines bolt a vector database onto a separate graph store. Every hop across that seam costs latency, consistency, and context. Quatzal's bet is that retrieval for AI workloads wants one engine where similarity search and multi-hop graph expansion compose in a single query — built thread-per-core on io_uring rather than adapted from a general-purpose store.
The second bet is provenance. Retrieval that hands a model bare text snippets gives it no way to tell a reviewed fact from a guess, or a current one from a stale one. Quatzal carries trust tiers, freshness windows, and a claim-verification primitive as first-class parts of a query.
Everything here is Apache-2.0.
- Storage engine — sharded, thread-per-core LSM with write-ahead-log durability, PAX-layout compaction (scalars decoupled from vectors), crash-safe recovery, versioned snapshots, and a documented backup/restore procedure.
- Vector search — a Vamana (DiskANN-family) index plus an
AsyncShardedHybridIndexserving path for concurrent ingest-and-query. Scalar filters, BM25 hybrid retrieval with reciprocal-rank fusion, document grouping, MMR diversity, and trust/staleness filtering compose in one query. - Graph engine — GraphBLAS-style semiring multi-hop expansion with temporal validity windows, composable with vector results.
- Trust and time —
generated_by/verified_by/status/stale_afterscalars, derived trust tiers (human-reviewed > machine-confirmed > unverified),as_oftime-travel over graph edges, and supersession. - Verification —
verify_claim(weighted reachability) and deterministic computation attestation:register_computation+attestcheck a receipt with no model and no network. - MCP server (
quatzal-mcp) — 14 tools over stdio or Streamable-HTTP with TLS and bearer-token auth. - The Atlas —
GET /atlasrenders the live store as one self-contained interactive graph page. - Replication protocol — WAL shipping over the authenticated listener, with content digests that surface divergence.
- Typed clients — zero-dependency Python, Go, and C# clients in
clients/. - Benchmark harnesses — head-to-head runs against live Qdrant, LanceDB, and usearch with a shared recall oracle.
Quatzal is open core. These capabilities are separately licensed and their source is not part of this repo:
SSO (OAuth resource-server validation) · row-level security · audit logging · multi-project serving · HA cluster operations.
Nothing in the Apache core is crippled to sell them. The core is a complete single-node database: it runs forever with no licence file, and no feature here degrades without one. Enquiries: see https://www.quatzal.com/.
Linux / WSL2 only. The storage runtime depends on io_uring through glommio, which does not build on Windows or macOS. Development happens on Ubuntu 24.04.
cargo build --release
cargo testSeveral crates are portable and can be embedded on any platform without the storage engine: quatzal-vector, quatzal-graph, quatzal-schema, quatzal-project, and quatzal-gpu.
# scaffold a .quatzal/ project directory (config.toml, data/, secrets/, exports/)
./target/release/quatzal-mcp --init
# serve over stdio (the local subprocess model an MCP client launches)
./target/release/quatzal-mcp
# or over HTTP on loopback, for a browser or a networked client
openssl rand -base64 32 > tokens.txt && chmod 600 tokens.txt
./target/release/quatzal-mcp DATA_DIR --listen 127.0.0.1:8443 \
--allow-insecure-plaintext --token-file tokens.txtTLS is required off loopback, and the server refuses to listen with no token configured — those are non-zero exits, not warnings. See Documents/OPERATING-HTTP.md for certificates, token scopes, and the Prometheus endpoint.
Then, from Python:
from quatzal import UaceClient
client = UaceClient("http://127.0.0.1:8443", token="...")
client.insert_rows([{"key": "doc-1", "scalars": {"title": "hello", "text": "hello world"}}])
resp = client.search_vectors(5, query_text="hello")
for hit in resp.results:
print(hit.key, hit.trust_tier)The Atlas is at http://127.0.0.1:8443/atlas?token=... — uniquely for that endpoint, the token may ride the URL so it opens in a plain browser tab.
From the comparison harness (cargo run -p quatzal-compare --release, WSL2, dim=256, k=10, Qdrant v1.18.3 as a real subprocess):
| Metric | Quatzal (AsyncHybrid) | Qdrant | LanceDB |
|---|---|---|---|
| Recall@10, n=50,000 | 0.909 | 0.876 | — |
| Recall@10, n=10,000 | 0.997 | 1.000 | 0.124 |
| Ingest accept, n=10,000 | 47,236 vec/s | 12,519 vec/s | — |
| Query p50, n=10,000 | 3.06 ms | 935 µs | — |
Read the caveats. Recall figures are apples-to-apples (same dataset, same oracle, Qdrant's HNSW indexing correctly enabled). Throughput and latency are not a controlled microbenchmark — Quatzal runs in-process while Qdrant is measured over HTTP and LanceDB via async/Arrow. Qdrant remains 2–4× ahead on fully-indexed query throughput, and that gap is understated for GPU deployments: Qdrant v1.13+ can build HNSW indexes on GPU, which this environment cannot run, so all Qdrant numbers here are CPU-indexed. Quatzal's own quatzal-gpu is a different operation — batch exact k-NN for the recall oracle and re-ranking, not index construction.
During harness development we found our early Qdrant numbers were invalid: a units mismatch in indexing_threshold (kilobytes, not points) meant Qdrant was silently doing exact brute-force search instead of HNSW. We fixed the configuration, discarded the earlier results, and re-ran everything.
Stated plainly, because a database that hides them isn't one you should trust:
- Pre-release data formats. Old snapshot files are deliberately unreadable rather than silently misread; there are no compatibility shims yet.
- In-memory indexes — the real ceiling. Vector and graph indexes have no SSD persistence. At 256 dimensions one vector costs ~1,388 bytes of RSS, putting a billion vectors near 1.4 TB of RAM. That does not fit one node and no tuning changes it. The SSD-resident design that fixes it is specified in
Documents/decisions/DR-W5-ssd-resident-index.mdand not built. - Recall at 1M is 0.507. At n=1,000,000 against a GPU-exact oracle, the shipped configuration's recall@10 drops to 0.507 and ingest acceptance to ~920 vec/s. The headline numbers hold at 10k–50k and demonstrably not yet at 1M. The suspects are parameter scale-dependence (per-shard budgets, merge
l_build), not a mystery. Quote the 10k–50k numbers only at their scales. - Plain Vamana recall degrades at high dimension — a documented scaling limitation the hybrid index path addresses.
- Vector compression is opt-in. RaBitQ quantization (~25× smaller than
f32) is wired through the cold tier behind--quantize-cold-tier. Off by default deliberately: it trades recall for memory, and a default that silently trades away recall is the wrong default for a database. - Failover is manual. Replication keeps the data; it does not elect a new primary. Two nodes accepting writes is worse than a pager.
- Single-row writes still fsync. Batched ingest reaches 14,544–21,607 rows/sec end-to-end over TLS, but
insert_rowone at a time pays a synchronous fsync. Useinsert_rows.
Development is red-first TDD against a requirements traceability matrix: every functional and non-functional requirement carries a UACE-FR-* / UACE-NFR-* ID traced from Documents/SRS.md through the tests that cover it, gated in CI. Benchmark claims are only made from the corrected, reproducible harness, and results that were refuted by measurement are recorded as such.
(The UACE- prefix is the project's original internal name, kept because those IDs are referenced by thousands of lines of specification, tests, and decision records. Renaming them would break that spine for no benefit.)
Documents/SRS.md— software requirements specification, including the deferred listDocuments/TDDD.md— test-driven design and the traceability matrixDocuments/OPERATING-HTTP.md— TLS, tokens, scopes, and the metrics endpointDocuments/OPERATING-REPLICATION.md— replication, write concern, failover procedureDocuments/OPERATING-BACKUP.md— backup and restoreDocuments/RECOVERY.md— recovery designDocuments/decisions/— decision records, including what was refuted by measurement and what is deliberately deferred- Full developer documentation: https://www.quatzal.com/docs/index.html
Quatzal is open core, which means contributions to the Apache-2.0 core need a contributor licence agreement before they can be merged — without one the project cannot relicense the work into the commercial build. Please open an issue before starting substantial work.
Apache-2.0 — see LICENSE and LICENSE-APACHE.
Every source file carries an SPDX-License-Identifier header, and the build enforces it: cargo run -p xtask -- license fails if any file's header disagrees with its location. Apache grants are irrevocable for the version they were published under, so that check is a release gate rather than a lint.
"Quatzal" is a trademark of the copyright holder; trademark rights are not granted by this license.