khive-db 0.3.0

SQLite storage backend: entities, edges, notes, events, FTS5, sqlite-vec vectors.
Documentation

khive-db

SQLite storage backend for the khive knowledge graph runtime: entity, note, event, and edge storage, FTS5 text search, and optional sqlite-vec vector storage over a WAL-mode connection pool.

Features

  • WAL-mode connection pool — one writer, N concurrent readers (ConnectionPool)
  • Capability-trait factoriesStorageBackend hands out Arc<dyn EntityStore>, GraphStore, NoteStore, EventStore, VectorStore, SparseStore, TextSearch, and SqlAccess from khive-storage
  • Forward-only versioned migrationsrun_migrations applies MIGRATIONS in order, tracked in _schema_migrations
  • Legacy pack-scoped schema plansServiceSchemaPlan / apply_schema_plan for pack-auxiliary tables tracked in _schema_versions
  • FTS5 trigram search (CJK-safe) via text() / text_with_tokenizer()
  • sqlite-vec vector storage (feature vectors) — per-model vec0 virtual tables with a namespace-scoped embedding-model registry
  • Periodic WAL checkpoint task (checkpoint module)

Usage

use khive_db::{run_migrations, StorageBackend};

// File-backed (WAL mode, 1 writer + N readers) or StorageBackend::memory() for tests.
let backend = StorageBackend::sqlite("/path/to/khive.db")?;

{
    let mut writer = backend.pool().try_writer()?;
    run_migrations(writer.conn_mut())?;
}

let entities = backend.entities()?; // Arc<dyn khive_storage::EntityStore>
let graph = backend.graph()?; // Arc<dyn khive_storage::GraphStore>
let text = backend.text("entities_fts")?; // Arc<dyn khive_storage::TextSearch>
let sql = backend.sql(); // Arc<dyn khive_storage::SqlAccess>, for pack-owned tables

Each capability accessor (entities, graph, notes, events, vectors, sparse, text) applies its own DDL idempotently on first call, so callers never need a separate "create schema" step per store. Namespace-scoped variants (entities_for_namespace, graph_for_namespace, …) validate that the namespace is non-empty; the store itself remains namespace-agnostic — callers pass namespace on each query.

Migrations

Two migration systems coexist, both defined in migrations.rs:

  • Versioned (MIGRATIONS: &[VersionedMigration], applied by run_migrations) — the forward-only pipeline for core substrate tables (entities, notes, edges, events). V1 is the consolidated fresh-start baseline loaded from sql/schema.sql; V2..V5 are incremental .sql files applied in order and tracked in _schema_migrations. A database whose recorded version is ahead of the latest known migration fails loudly rather than silently skipping the baseline.
  • Legacy per-service (ServiceSchemaPlan / apply_schema_plan) — used by packs that declare their own auxiliary DDL, tracked per-service in _schema_versions.

Schema DDL is authored in crates/khive-db/sql/*.sql and pulled in via include_str! — never hand-written as inline Rust string literals. Adding a migration means a new .sql file plus a new VersionedMigration entry; V1 itself is never edited on an existing database.

Vector storage

vectors_for_namespace(model_key, embedding_model, dimensions, namespace) creates a vec_<model_key> virtual table (via sqlite-vec, feature vectors) sized to dimensions, with cosine distance. model_key must be ASCII alphanumeric/underscore. Tables predating the field/embedding_model columns (pre-v0.2.8) are rejected with an explicit error rather than silently dropped — vector data is a cache, so callers re-embed after recreating the table.

Where this sits

khive-db implements the khive-storage capability traits (ADR-005) against SQLite, sitting directly above khive-storage/khive-score/khive-types and below khive-query and khive-runtime in the storage dependency chain:

types -> score -> storage -> db -> query -> runtime -> pack-* -> mcp

khive-runtime's KhiveRuntime wraps StorageBackend and layers namespace authorization and pack dispatch on top. Schema evolution follows ADR-015.

License

Apache-2.0.