Documentation

Architecture

Understand how KuraDB separates lifecycle management, read-only queries, indexing writes, durable storage, and rebuildable caches.

System overview

graph TB
    CLI["kura CLI"] --> Registry["Registry<br/>db.json"]
    CLI --> Daemon["Background daemon"]
    Daemon --> API["Gin read-only API"]
    Daemon --> Watcher["Filesystem watcher"]
    Watcher --> Parser["Document parsers"]
    Parser --> PerDB["Per-database SQLite<br/>file_data"]
    PerDB --> Embedder["Embedding scheduler"]
    Embedder --> OpenAI["OpenAI embeddings"]
    Embedder --> Vector["In-memory vector cache"]
    API --> Keyword["Keyword search"]
    API --> Semantic["Semantic search"]
    Keyword --> PerDB
    Semantic --> QueryCache["Memory + global SQLite<br/>query_cache"]
    Semantic --> Vector
    Vector --> PerDB
    Registry --> Daemon

Runtime composition

Layer Primary code Responsibility
Entry and lifecycle cmd/app Dispatch CLI commands, daemonize, initialize subsystems, handle shutdown
HTTP API internal/api Expose local read-only routes and validate database selection
Persistence internal/database Open SQLite stores, maintain registry, and execute all content writes
File ingestion internal/filesystem Poll inboxes, detect changes, parse supported files, dismiss removals
Embeddings internal/openai Read credentials, call OpenAI, encode vectors, cache query embeddings
Vector retrieval internal/vector Maintain rebuildable buckets and perform two-stage cosine search
Tokenization internal/utils/segmenter Tokenize and deduplicate keyword queries with gse
Process state internal/runtime Write PID metadata, detect liveness, and stop the daemon

Startup sequence

runServer launches the current executable with --daemon, redirects output to daemon.log, and waits for the endpoint file. runServerDaemon then performs the authoritative initialization sequence:

  1. Create or refresh runtime.uid.
  2. Initialize the KuraDB keychain context.
  3. Load the database registry.
  4. Construct the OpenAI embedder.
  5. Open global.db and preload query embeddings of the expected dimension.
  6. Initialize the tokenizer and process-wide vector cache.
  7. Open every registered per-database SQLite file.
  8. Rebuild each vector bucket from embedded, non-dismissed rows.
  9. Start one watcher and one embedding scheduler per loaded database.
  10. Start the local HTTP server and wait for SIGINT or SIGTERM.

Storage topology

KuraDB uses several local state files under ~/.config/kuradb/:

Store Authority Contents
db.json Durable registry Database names and creation timestamps
config.json Durable configuration Optional pinned HTTP port
global.db Durable cache store Query text to embedding blobs
{db}/data.db Source of truth Parsed chunks, soft-delete state, and embeddings
{db}/record.json Watcher snapshot File size, mtime, type, and child snapshots
runtime.uid Ephemeral process state UID, PID, and start time
endpoint Ephemeral discovery Current local HTTP base URL
daemon.log Operational log Daemon stdout and stderr

vector.Cache and openai.Cache are accelerators, not authoritative stores. They must remain reconstructible from SQLite.

Concurrency model

Each loaded database owns independent watcher and embedder goroutines. HTTP keyword and semantic branches run concurrently when /api/search has no target. SQLite provides a read pool of eight connections and a separate write connection through go-sqlkit. Vector cache maps use process, database bucket, and query-cache mutexes to isolate concurrent access.

Trust boundaries and invariants

Shutdown

A termination signal cancels the shared context. The HTTP server removes endpoint and performs a five-second graceful shutdown. Watchers and embedders leave their select loops, runtime.uid is cleared, and all per-database plus global SQLite connectors are closed.

中文