Persistent memory for AI agents in a single Rust binary with built-in GraphRAG.
- Portuguese version available at README.pt-BR.md
- Public package and repository are live on GitHub and crates.io
- Install the latest published release with
cargo install sqlite-graphrag --locked - Upgrade an existing install with
cargo install sqlite-graphrag --locked --force - Verify the active binary with
sqlite-graphrag --version - See CHANGELOG.md for the full release history
- Release-grade validation includes the
slow-testscontract suites documented indocs/TESTING.md - Build directly from the local checkout with
cargo install --path .
cargo install sqlite-graphrag --locked --force
sqlite-graphrag --version- Stores memories, entities and relationships inside a single SQLite file under 25 MB
- Embeds content locally via
fastembedwith themultilingual-e5-smallmodel - Combines FTS5 full-text search with
sqlite-vecKNN into a hybrid Reciprocal Rank Fusion ranker - Stores and traverses an explicit entity graph with typed edges for multi-hop recall across memories
- Preserves every edit through an immutable version history table for full audit
- Runs on Linux, macOS and Windows natively with zero external services required
- Offline-first architecture eliminates OpenAI embeddings and Pinecone recurring fees
- Single-file SQLite storage replaces Docker clusters of vector databases entirely
- Graph-native retrieval beats pure vector RAG on multi-hop questions by design
- Deterministic JSON output unlocks clean orchestration by LLM agents in pipelines
- Native cross-platform binary ships without Python, Node or Docker dependencies
- Every subcommand accepts
--jsonproducing deterministic stdout payloads - Every invocation can stay stateless, but heavy commands auto-start a persistent daemon for embedding inference, reusing it across calls (this is daemon autostart, separate from automatic entity extraction)
- After binary upgrades, the CLI auto-detects a version mismatch with the running daemon and restarts it transparently before the first embedding request (since v1.0.50)
sqlite-graphrag daemonstill exists for explicit control, but the common path no longer requires manual startup- Every write is idempotent through
--namekebab-case uniqueness constraints - Stdin is explicit: use
--body-stdinfor body text or--graph-stdinfor one{body?, entities, relationships}object; raw entity and relationship arrays use--entities-fileand--relationships-file rememberaccepts body payloads up to512000bytes and up to512chunks- Relationship payloads use
strengthin[0.0, 1.0], mapped toweightin outputs - Stderr carries tracing output under
SQLITE_GRAPHRAG_LOG_LEVEL=debugonly --helpis English-first by design; use--langfor human-facing runtime messages, not static clap help text- Cross-platform behavior is identical across Linux, macOS and Windows hosts
entity_typeaccepts exactly 13 values:project,tool,person,file,concept,incident,decision,memory,dashboard,issue_tracker,organization,location,daterelation(CLI input) accepts any kebab-case or snake_case string. 12 canonical values are well-known:applies-to,uses,depends-on,causes,fixes,contradicts,supports,follows,related,mentions,replaces,tracked-in. Custom values (e.g.,implements,tested-by,blocks) are accepted with atracing::warn!. JSON output normalizes to underscores (e.g.,applies_to).strengthis a float in[0.0, 1.0]representing edge weight; mapped toweightin all read outputs- Unlisted
entity_typevalues are rejected at write time with exit code 1. Customrelationvalues are accepted since v1.0.49. - Use
sqlite-graphrag graph --format jsonto inspect the full stored graph at any time
| Agent | Vendor | Minimum version | Integration pattern |
|---|---|---|---|
| Claude Code | Anthropic | 1.0 | Subprocess with --json stdout |
| Codex | OpenAI | 1.0 | Tool call wrapping cargo run -- recall |
| Gemini CLI | 1.0 | Function call returning JSON | |
| Opencode | Opencode | 1.0 | Shell tool with hybrid-search --json |
| OpenClaw | Community | 0.1 | Subprocess pipe into jaq filters |
| Paperclip | Community | 0.1 | Direct CLI invocation per message |
| VS Code Copilot | Microsoft | 1.85 | Terminal subprocess via tasks |
| Google Antigravity | 1.0 | Agent tool with structured JSON | |
| Windsurf | Codeium | 1.0 | Custom command registration |
| Cursor | Anysphere | 0.42 | Terminal integration or MCP wrapper |
| Zed | Zed Industries | 0.160 | Extension wrapping subprocess |
| Aider | Paul Gauthier | 0.60 | Shell command hook per turn |
| Jules | Google Labs | 1.0 | Workspace shell integration |
| Kilo Code | Community | 1.0 | Subprocess invocation |
| Roo Code | Community | 1.0 | Custom command via CLI |
| Cline | Saoud Rizwan | 3.0 | Terminal tool registered manually |
| Continue | Continue Dev | 0.9 | Context provider via shell |
| Factory | Factory AI | 1.0 | Tool call with JSON response |
| Augment Code | Augment | 1.0 | Terminal command wrapping |
| JetBrains AI Assistant | JetBrains | 2024.3 | External tool per IDE |
| OpenRouter | OpenRouter | 1.0 | Function routing through shell |
| Minimax | Minimax | 1.0 | Subprocess invocation |
| Z.ai | Z.ai | 1.0 | Subprocess invocation |
| Ollama | Ollama | 0.1 | Subprocess invocation |
| Hermes Agent | Community | 1.0 | Subprocess invocation |
| LangChain | LangChain | 0.3 | Subprocess via tool |
| LangGraph | LangChain | 0.2 | Subprocess via node |
cargo install sqlite-graphrag --locked --force
sqlite-graphrag init
sqlite-graphrag remember --name onboarding-note --type user --description "first memory" --body "hello graphrag"
sqlite-graphrag recall "graphrag" --k 5 --jsonRequired flags for
remember:--name,--type,--description. Body via--body "text",--body-file <path>, or--body-stdin(pipe from stdin). Body limit: 500 KB (512000 bytes). Larger inputs are rejected with exit code 6 (limit exceeded); split into multiple memories or trim before sending.
- GraphRAG is enabled by default and runs automatically. Every subcommand auto-initializes
graphrag.sqlitein the current working directory if it does not exist.rememberandingestcan extract entities and relationships via local GLiNER zero-shot NER when--enable-neris passed.recallandhybrid-searchauto-spawn the embedding daemon on demand.
-
Pass
--enable-neror setSQLITE_GRAPHRAG_ENABLE_NER=1to activate entity extraction onrememberandingest -
Works with
--graph-stdin: pass"entities": []in the JSON payload and GLiNER extracts entities automatically -
Select model variant with
--gliner-variant:fp32(1.1 GB, best quality),fp16(580 MB),int8(349 MB, fastest),q4(894 MB),q4f16(472 MB) -
Override default model via
SQLITE_GRAPHRAG_GLINER_MODEL; tune confidence withSQLITE_GRAPHRAG_GLINER_THRESHOLD(default0.5) -
Response field
extraction_methodreports:gliner-<variant>+regex,regex-only, ornone:extraction-failed -
--skip-extractionis deprecated since v1.0.45; NER is off by default, use--enable-nerto activate -
sqlite-graphrag initis OPTIONAL but recommended on first use because it pre-downloads the embedding model and warms a smoke-test embedding (subsequent commands are faster). Withoutinit, the first command pays the model-download cost. -
graphrag.sqliteis created in the current working directory by default (override with--db <path>orSQLITE_GRAPHRAG_DB_PATH) -
For the local checkout,
cargo install --path .is enough -
Re-run
sqlite-graphrag --versionafter any upgrade to confirm the active binary -
After the public release, prefer
--lockedto preserve the tested MSRV dependency graph
- v1.0.55: Full doc audit — export summary
total→exported, list response fields corrected,--tzexit code 1→2, exit 2 added to exit code table, stats legacy aliases documented - v1.0.54: WAL checkpoint for
prune-relations(last missing command),--graph-stdinempty body validation,memory_typeJSON field inlist/export,Vec::with_capacityin 9 cold paths - v1.0.53: WAL checkpoint TRUNCATE after every write command for Dropbox/cloud-sync safety,
export --jsoncontract fix,Vec::with_capacityin 12 hot paths - v1.0.52: 12 gaps fixed, new
exportsubcommand, exit code Duplicate 2→9 (breaking),forgetnot-found no JSON (breaking) - v1.0.51: Namespace env var fix (8 commands), remember on soft-deleted fix, per-chunk RSS watchdog (
--max-rss-mb), daemon test coverage - v1.0.50:
prune-relationssubcommand, daemon auto-restart on version mismatch, V011 index, 37 doc gaps fixed - v1.0.49: Extensible relation vocabulary, V010 migration, 15 doc updates
- v1.0.48: GLiNER NER functional, 5 bug fixes, full doc audit
- v1.0.47: Replace BERT NER with GLiNER zero-shot, 13 custom entity types,
--gliner-variantflag - v1.0.35: Flag aliases (
--from/--to,--old/--new,--limitas alias of--k)
# 1. Initialize (once per database)
sqlite-graphrag init
# 2. Store a memory
sqlite-graphrag remember --name my-note --type user --description "demo" --body "first entry"
# 3. Retrieve by semantic similarity
sqlite-graphrag recall "first entry" --k 5 --json
# 4. Soft-delete (reversible)
sqlite-graphrag forget my-note
# 5. Permanently remove soft-deleted memories older than 0 days
sqlite-graphrag purge --retention-days 0 --yesAll five commands above are safe to run in sequence on a fresh database.
- Rust 1.88 or newer (
rust-version = "1.88"inCargo.toml); older toolchains will fail with an MSRV error duringcargo install.
- Install the latest published release with
cargo install sqlite-graphrag --locked - Upgrade an existing published binary with
cargo install sqlite-graphrag --locked --force - Pin to a specific version with
cargo install sqlite-graphrag --version <X.Y.Z> --locked - Install from the local checkout with
cargo install --path . - Build from the local checkout with
cargo build --release
sqlite-graphrag init
sqlite-graphrag init --namespace project-foo- Without
--dborSQLITE_GRAPHRAG_DB_PATH, every CRUD command in that directory uses./graphrag.sqlite
- By default,
rememberdoes NOT run automatic entity extraction (GLiNER NER is disabled by default) - Pass
--enable-nerto activate GLiNER zero-shot extraction for that call, or setSQLITE_GRAPHRAG_ENABLE_NER=1
sqlite-graphrag remember \
--name integration-tests-postgres \
--type feedback \
--description "prefer real Postgres over SQLite mocks" \
--body "Integration tests must hit a real database."rememberJSON response includesurls_persisted(URLs routed tomemory_urlstable) andrelationships_truncated(bool, set when relationships were capped)- URLs are stored in
memory_urlsvia schema V007 and never pollute the entity graph - Sample JSON output illustrating extracted entities and relationships:
{
"memory": {"id": 42, "name": "audit-note", "type": "project"},
"extracted_entities": [
{"name": "OpenAI", "kind": "organization", "saliency": 0.92},
{"name": "Rust", "kind": "technology", "saliency": 0.85}
],
"extracted_relationships": [
{"source": "OpenAI", "target": "GPT-4", "relation": "develops"}
],
"urls_persisted": [],
"relationships_truncated": false
}- GLiNER zero-shot NER is disabled by default; pass
--enable-nerto activate automatic entity/relationship extraction - GLiNER replaces the former BERT NER model and resolves 13 domain-specific entity types vs. 4 fixed BERT types
- Use
--gliner-variantto trade quality for download size:fp32(default, 1.1 GB),fp16(580 MB),int8(349 MB),q4(894 MB),q4f16(472 MB) - The
extraction_methodfield is populated in the JSON response when NER runs
| Variant | Size | Notes |
|---|---|---|
fp32 |
1.1 GB | Default; best accuracy |
fp16 |
580 MB | Good accuracy, half the size |
int8 |
349 MB | Smallest; slight accuracy drop |
q4 |
894 MB | 4-bit quantized weights |
q4f16 |
472 MB | 4-bit weights, fp16 activations |
sqlite-graphrag remember \
--name release-notes-v1 \
--type document \
--description "release notes for v1.0.0" \
--enable-ner \
--gliner-variant fp16 \
--body-stdin < notes.mdsqlite-graphrag read integration-tests-postgres --json
sqlite-graphrag forget integration-tests-postgres
sqlite-graphrag history integration-tests-postgres --json
sqlite-graphrag edit integration-tests-postgres --body "Updated body text."
sqlite-graphrag rename integration-tests-postgres --new postgres-tests- Positional name is equivalent to
--name <name>forread,forget,history,editandrename
sqlite-graphrag recall "postgres integration tests" --k 3 --jsonsqlite-graphrag hybrid-search "postgres migration rollback" --k 10 --jsonsqlite-graphrag health --json
sqlite-graphrag stats --jsonsqlite-graphrag purge --retention-days 90 --dry-run --json
sqlite-graphrag purge --retention-days 90 --yesDefault retention: 90 days. To purge ALL forgotten memories regardless of age, pass
--retention-days 0.
sqlite-graphrag daemon --idle-shutdown-secs 600
sqlite-graphrag daemon --ping --json
sqlite-graphrag daemon --stop --jsonrecall, hybrid-search, and other embedding-heavy subcommands automatically spawn a background daemon (sqlite-graphrag daemon) when none is running, to amortise the model warm-up cost across multiple invocations.
Default: auto-spawn enabled (idle timeout 600s).
Disable per-invocation via flag:
sqlite-graphrag recall "query" --autostart-daemon=falseDisable globally via env var:
export SQLITE_GRAPHRAG_DAEMON_DISABLE_AUTOSTART=1The CLI flag takes precedence over the env var.
Explicit lifecycle control (foreground, default 600s idle timeout):
sqlite-graphrag daemon
sqlite-graphrag daemon --idle-shutdown-secs 3600
sqlite-graphrag daemon --ping # health-check
sqlite-graphrag daemon --stop # graceful shutdownDaemon convention: uses
--ping/--stop/--idle-shutdown-secsFLAGS, not subcommands. Mirrors systemd-style flags more than git's verb-noun pattern.
sqlite-graphrag ingest ./docs --type document --pattern '*.md' --recursive# Force single-threaded ingest to reduce RSS pressure (recommended for <4 GB RAM
# environments and container/cgroup constraints). Trade-off: 3-4x longer wall time.
sqlite-graphrag ingest ./docs --type document --pattern '*.md' --low-memory
# Or via env var (CLI flag takes precedence):
SQLITE_GRAPHRAG_LOW_MEMORY=1 sqlite-graphrag ingest ./docs --type document
ingestemits NDJSON on stdout: one JSON line per file, then a summary line. Per-filestatusvalues:indexed(created),skipped(duplicate or invalid name),failed(error). Duplicates emitstatus: "skipped"withaction: "duplicate"and do not count as failures. Pass--dry-runto preview the name mapping (kebab-cased basenames) without writing anything to the database. Schema:docs/schemas/ingest-file-event.schema.json,docs/schemas/ingest-summary.schema.json.
sqlite-graphrag rename old-name --new-name new-name --jsonsqlite-graphrag edit integration-tests-postgres --body "Updated body."
sqlite-graphrag edit integration-tests-postgres --description "Updated description."sqlite-graphrag history integration-tests-postgres --json
sqlite-graphrag restore --name integration-tests-postgres --version 2 --jsonsqlite-graphrag migrate --status --json
sqlite-graphrag migrate --jsonsqlite-graphrag namespace-detect --json
sqlite-graphrag namespace-detect --namespace project-foo --jsonsqlite-graphrag optimize --jsonsqlite-graphrag vacuum --jsonsqlite-graphrag link --from "OpenAI" --to "GPT-4" --relation uses --weight 0.8 --jsonsqlite-graphrag unlink --from "OpenAI" --to "GPT-4" --relation uses --jsonsqlite-graphrag related onboarding-note --max-hops 2 --limit 10 --jsonEmpty results are normal for memories without graph edges yet — extract entities first via
rememberoringest. Edges form when ≥2 entities co-occur in the same memory body.
sqlite-graphrag graph --format json --output graph.json
sqlite-graphrag graph stats --json
sqlite-graphrag graph traverse --from "OpenAI" --depth 2 --json
sqlite-graphrag graph entities --entity-type organization --limit 50 --jsonsqlite-graphrag cleanup-orphans --dry-run --json
sqlite-graphrag cleanup-orphans --yes --jsonsqlite-graphrag prune-relations --relation mentions --dry-run --show-entities --json
sqlite-graphrag prune-relations --relation mentions --yes --jsonsqlite-graphrag cache clear-models --yessqlite-graphrag history integration-tests-postgres --no-body --json| Command | Arguments | Description |
|---|---|---|
init |
--namespace <ns> |
Initialize database and download embedding model |
daemon |
--ping, --stop, --idle-shutdown-secs, --db, --json |
Run or control the persistent embedding daemon |
health |
--json |
Show database integrity and pragma status |
stats |
--json |
Count memories, entities and relationships |
migrate |
--json |
Apply pending schema migrations via refinery |
vacuum |
--json |
Checkpoint WAL and reclaim disk space |
optimize |
--json |
Run PRAGMA optimize to refresh statistics |
sync-safe-copy |
--dest <path> (alias --output) |
Checkpoint then copy a sync-safe snapshot |
| Command | Arguments | Description |
|---|---|---|
remember |
--name, --type, --description, --body (or --body-file/--body-stdin), --entities-file, --relationships-file, --graph-stdin, --enable-ner, --gliner-variant |
Save a memory with optional entity graph |
recall |
<query>, -k/--k (alias --limit), --type, --max-hops, --max-distance, --all-namespaces, --no-graph |
Search memories semantically via KNN + graph traversal |
read |
[name] or --name <name> |
Fetch a memory by exact kebab-case name |
list |
--type, --limit, --offset, --include-deleted |
Paginate memories sorted by updated_at |
forget |
[name] or --name <name> |
Soft-delete a memory preserving history |
rename |
[old], or --name/--old/--from <NAME>, --new-name/--new/--to <NAME> |
Rename a memory while keeping versions |
edit |
[name] or --name, --body, --description |
Edit body or description creating new version |
history |
[name] or --name <name> |
List all versions of a memory |
restore |
--name, --version |
Restore a memory to a previous version |
ingest |
<DIR>, --type, --pattern <GLOB> (default *.md), --recursive, --ingest-parallelism N, --low-memory (env SQLITE_GRAPHRAG_LOW_MEMORY=1), --enable-ner, --gliner-variant, --fail-fast, --dry-run |
Bulk-ingest every matching file as a separate memory (NDJSON output); --dry-run previews name mapping without writing |
export |
--namespace, --type, --include-deleted, --limit, --offset |
Export memories as NDJSON for backup or migration |
cache clear-models |
--yes |
Remove cached embedding/GLiNER model files from the XDG cache directory |
Memory name validation. Names must match
[a-z0-9-]+(kebab-case, ASCII only). Unicode and uppercase are rejected with exit code 1. Names longer than 60 chars emitted byingestare truncated to fit; review the WARN log to spot mangled names.
| Command | Arguments | Description |
|---|---|---|
hybrid-search |
<query>, --k, --rrf-k, --with-graph, --max-hops, --min-weight |
FTS5 plus vector fused via Reciprocal Rank Fusion; --with-graph adds graph traversal matches |
namespace-detect |
--namespace <name> |
Resolve namespace precedence for invocation |
link |
--from, --to, --relation, --weight, --create-missing, --entity-type |
Create an explicit relationship between two entities; --create-missing auto-creates entities that do not exist (default type: concept) |
unlink |
--from, --to, --relation |
Remove a specific relationship between two entities |
related |
--name, --limit, --hops |
Traverse graph-connected memories from a seed memory |
graph |
--format, --output |
Export a graph snapshot in json, dot or mermaid |
Breaking change in v1.0.44.
graph entitiesJSON output renamed top-level array fromitemstoentities. Update jaq/jq filters:.items[]becomes.entities[]. Thelistcommand still usesitems.
| Subcommand | Description | Key flags |
|---|---|---|
graph traverse --from <ENTITY> |
Walk the entity graph from a starting node using BFS | --depth (default 2), --namespace |
graph stats |
Print graph statistics (node count, edge count, degree distribution) | --namespace |
graph entities |
List entities stored in the graph with optional filters | --limit (default 50), --entity-type, --namespace |
| Command | Arguments | Description |
|---|---|---|
purge |
--retention-days <n>, --dry-run, --yes |
Permanently delete soft-deleted memories |
cleanup-orphans |
--namespace, --dry-run, --yes |
Remove entities that have no memories and no relationships |
prune-relations |
--relation <type>, --namespace, --dry-run, --yes, --show-entities |
Bulk-delete all relationships of a given type; --show-entities lists affected entities in the dry-run preview |
| Subcommand | Description |
|---|---|
clear-models |
Remove cached embedding/NER model files (forces re-download on next init) |
| Variable | Description | Default | Example |
|---|---|---|---|
SQLITE_GRAPHRAG_DB_PATH |
Path to the SQLite database file override | ./graphrag.sqlite in the invocation directory |
/data/graphrag.sqlite |
SQLITE_GRAPHRAG_HOME |
Override base directory for graphrag.sqlite (used when --db and SQLITE_GRAPHRAG_DB_PATH are absent) |
unset | /var/lib/sqlite-graphrag |
SQLITE_GRAPHRAG_CACHE_DIR |
Directory override for model cache and lock files | XDG cache dir | ~/.cache/sqlite-graphrag |
SQLITE_GRAPHRAG_LANG |
CLI output language as en or pt (aliases: pt-BR, portuguese) |
en |
pt |
SQLITE_GRAPHRAG_LOG_LEVEL |
Tracing filter level for stderr output | info |
debug |
SQLITE_GRAPHRAG_LOG_FORMAT |
Tracing output format on stderr (pretty or json) |
pretty |
json |
SQLITE_GRAPHRAG_NAMESPACE |
Namespace override bypassing detection | none | project-foo |
SQLITE_GRAPHRAG_DISPLAY_TZ |
IANA timezone for *_iso JSON fields |
UTC |
America/Sao_Paulo |
SQLITE_GRAPHRAG_DAEMON_FORCE_AUTOSTART |
Force daemon autostart even when guards would skip it | unset | 1 |
SQLITE_GRAPHRAG_DAEMON_DISABLE_AUTOSTART |
Disable daemon autostart entirely (useful in tests/CI) | unset | 1 |
SQLITE_GRAPHRAG_DAEMON_CHILD |
INTERNAL flag set automatically when spawning the daemon child; do not set manually | unset | 1 |
SQLITE_GRAPHRAG_ENABLE_NER |
Enable GLiNER NER auto-extraction globally (equivalent to --enable-ner on every call). Accepts 1/true/yes/on (case-insensitive) |
unset (NER off) | 1 |
SQLITE_GRAPHRAG_GLINER_VARIANT |
GLiNER ONNX weight variant: fp32, fp16, int8, q4, q4f16 |
fp32 |
fp16 |
SQLITE_GRAPHRAG_GLINER_THRESHOLD |
Entity confidence threshold for GLiNER predictions (float in [0.0, 1.0]) | 0.5 |
0.3 |
SQLITE_GRAPHRAG_GLINER_MODEL |
Override the GLiNER model repository identifier | onnx-community/gliner_multi-v2.1 |
custom path |
SQLITE_GRAPHRAG_EXTRACTION_MAX_TOKENS |
Token budget for entity/relationship extraction per memory; values outside [512, 100 000] fall back to default | 5000 |
8000 |
SQLITE_GRAPHRAG_MAX_ENTITIES_PER_MEMORY |
Maximum distinct entities persisted per memory; values outside [1, 1 000] fall back to default. Note: the extraction pipeline internally caps candidates at 30 before deduplication, so the persistence cap (default 50) acts as a safety ceiling and is only reached when the extractor is extended or replaced. | 50 |
100 |
SQLITE_GRAPHRAG_MAX_RELATIONS_PER_MEMORY |
Maximum distinct relationships persisted per memory; values outside [1, 10 000] fall back to default | 50 |
200 |
ORT_DYLIB_PATH |
Explicit path to libonnxruntime.so for ARM64 GNU dynamic loading |
auto-discovery | /opt/sqlite-graphrag/libonnxruntime.so |
sqlite-graphrag recall "auth tests" --k 5 --json | jaq -r '.results[].name'sqlite-graphrag hybrid-search "postgres migration" --k 10 --json \
| jaq -c '.results[] | {name, combined_score}' \
| xh POST http://localhost:8080/summarizesqlite-graphrag sync-safe-copy --dest /tmp/ng.sqlite
ouch compress /tmp/ng.sqlite /tmp/ng-$(date +%Y%m%d).tar.zstconst { spawn } = require('child_process');
const proc = spawn('sqlite-graphrag', ['recall', query, '--k', '5', '--json']);FROM rust:1.88-bookworm AS builder
RUN apt-get update && apt-get install -y --no-install-recommends pkg-config libssl-dev ca-certificates && rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY . .
RUN cargo install --path .| Code | Meaning | Possible Cause |
|---|---|---|
0 |
Success | Command completed and JSON payload printed when requested |
1 |
Validation error or runtime failure | Invalid --type, malformed --relation (empty or non-snake_case), kebab-case violation, generic anyhow error |
2 |
CLI usage error | Invalid flag, missing required argument, invalid --tz timezone (Clap FromStr rejects before app code) |
9 |
Duplicate detected | Existing --name without --force-merge; ingest skips the file and emits status: "skipped" with action: "duplicate" instead |
3 |
Conflict during optimistic update | edit or restore raced against another writer |
4 |
Memory or entity not found | read, forget, edit, rename, restore or graph traverse target missing |
5 |
Namespace could not be resolved | No SQLITE_GRAPHRAG_NAMESPACE, no flag, no detected default |
6 |
Payload exceeded configured limits | --name longer than 80 bytes, body over 512000 bytes, more than 512 chunks |
10 |
SQLite database error | Corrupted file, schema mismatch, missing migration |
11 |
Embedding generation failed | Model load error or daemon embedding RPC failure |
12 |
sqlite-vec extension failed to load |
Missing native extension or unsupported SQLite build |
13 |
Batch partial failure | import, reindex or stdin batch with at least one failing record |
14 |
Filesystem I/O error | Cache or database directory not writable, nonexistent ingest target directory |
15 |
Database busy after retries | WAL contention exceeded with_busy_retry budget |
20 |
Internal or JSON serialization error | Unexpected serde failure or invariant violation |
75 |
EX_TEMPFAIL lock timeout or all concurrency slots busy |
Five-plus concurrent invocations or flock waited longer than 300s |
77 |
Available RAM below minimum required | Less than 2 GB free RAM detected before model load |
- In-process warm-model latency remains far lower than one-shot subprocess latency
- Stateless CLI invocations typically spend about one second reloading the embedding model per heavy command
- Warm in-process recall can stay well below the stateless subprocess timing once the model is already resident
- First
initdownloads the quantized model once and caches it locally - Embedding model uses approximately 1100 MB of RAM per process instance after the v1.0.18 daemon-based RSS calibration (52 GiB regression in v1.0.17 reduced to 1.03 GiB peak)
- Minimum 3 GB RAM recommended (4 GB+ for large corpora). Floor sits around 2 GB just to load ONNX runtime + GLiNER NER + fastembed multilingual-e5-small models.
- Default parallelism (
--ingest-parallelism = min(4, cpus/2)) increases RSS roughly linearly per worker. With 4 workers, ingest of 30 files peaks around 4.4 GB. - Low-memory mode: pass
--low-memory(or setSQLITE_GRAPHRAG_LOW_MEMORY=1) to force single-threaded ingest. Equivalent to--ingest-parallelism 1and overrides any explicit value. Reduces peak RSS to about 2.6 GB at the cost of 3-4x wall time. - Container/cgroup users: a cap below 3 GB causes OOM-kill during model load. Use cgroup
MemoryMax=4Gor higher in production. - Upstream tracking: see microsoft/onnxruntime#22271 for ONNX CPU memory growth across many runs.
Expected overhead: roughly 8× the total ingested body size (e.g., 7.6 MB of text → ~62.9 MB DB). Overhead comes from 384-dim float embeddings, FTS5 full-text index, and the entities/relationships graph. Run
sqlite-graphrag vacuum --jsonafter bulkforget+purgecycles to reclaim reclaimed space.
- Each invocation loads
multilingual-e5-smallconsuming roughly 1100 MB of RAM after the v1.0.18 measurement pass MAX_CONCURRENT_CLI_INSTANCESremains the hard ceiling at 4 cooperating subprocesses- Heavy commands
init,remember,recall, andhybrid-searchare clamped lower dynamically when available RAM cannot sustain the requested parallelism safely - Lock files live at
~/.cache/sqlite-graphrag/cli-slot-{1..4}.lockusingflock - A fifth concurrent invocation waits up to 300 seconds then exits with code 75
- Use
--max-concurrency Nto request the slot limit for the current invocation; heavy commands may still be reduced automatically - Memory guard aborts with exit 77 when less than 2 GB of RAM is available
- SIGINT and SIGTERM trigger graceful shutdown via
shutdown_requested()atomic
- sqlite-graphrag uses WAL mode by default for high-concurrency writes
- Since v1.0.54, every write command runs
PRAGMA wal_checkpoint(TRUNCATE)after committing (v1.0.53 covered 11 of 12; v1.0.54 added the missingprune-relations) - This ensures the
.sqlitefile is always self-contained when cloud sync tools read it - If corruption occurs despite the checkpoint, recover with
sqlite3 broken.sqlite ".recover" | sqlite3 repaired.sqlite
- Default behavior always creates or opens
graphrag.sqlitein the current working directory - Database locked after crash requires
sqlite-graphrag vacuumto checkpoint the WAL - First
inittakes roughly one minute whilefastembeddownloads the quantized model - On
aarch64-unknown-linux-gnu, embedding-heavy commands resolvelibonnxruntime.sofromORT_DYLIB_PATH, the executable directory,./lib/, then the model cache directory - If ARM64 GNU embedding commands fail at startup, point
ORT_DYLIB_PATHto the exactlibonnxruntime.soshipped with the binary - Permission denied on Linux means the cache directory lacks write access for your user
- Namespace detection falls back to
globalwhen no explicit override is present - Parallel invocations that exceed the effective safe limit receive exit 75 and SHOULD retry with backoff; during audits start heavy commands with
--max-concurrency 1
- Each crate calls the binary through
std::process::Commandwith--jsonflag - No shared memory or FFI required: the contract is pure stdout JSON
- Pin the binary version in your
Cargo.tomlworkspace for reproducible builds - All 18 crates below work identically on Linux, Apple Silicon macOS and Windows
use std::process::Command;
let out = Command::new("sqlite-graphrag")
.args(["recall", "project goals", "--k", "5", "--json"])
.output().unwrap();use std::process::Command;
let out = Command::new("sqlite-graphrag")
.args(["hybrid-search", "agent memory", "--k", "10", "--json"])
.output().unwrap();use std::process::Command;
let out = Command::new("sqlite-graphrag")
.args(["remember", "--name", "task-context", "--type", "project",
"--description", "current sprint goal", "--body", "finish auth module"])
.output().unwrap();use std::process::Command;
let out = Command::new("sqlite-graphrag")
.args(["recall", "decision log", "--k", "3", "--json"])
.output().unwrap();use std::process::Command;
let out = Command::new("sqlite-graphrag")
.args(["hybrid-search", "previous decisions", "--k", "5", "--json"])
.output().unwrap();use std::process::Command;
let out = Command::new("sqlite-graphrag")
.args(["recall", "user preferences", "--k", "5", "--json"])
.output().unwrap();use std::process::Command;
let out = Command::new("sqlite-graphrag")
.args(["stats", "--json"])
.output().unwrap();use std::process::Command;
let out = Command::new("sqlite-graphrag")
.args(["recall", "tool outputs", "--k", "5", "--json"])
.output().unwrap();use std::process::Command;
let out = Command::new("sqlite-graphrag")
.args(["hybrid-search", "graph relations", "--k", "10", "--json"])
.output().unwrap();use std::process::Command;
let out = Command::new("sqlite-graphrag")
.args(["recall", "model context", "--k", "5", "--json"])
.output().unwrap();use std::process::Command;
let out = Command::new("sqlite-graphrag")
.args(["remember", "--name", "session-notes", "--type", "user",
"--description", "session recap", "--body", "discussed architecture"])
.output().unwrap();use std::process::Command;
let out = Command::new("sqlite-graphrag")
.args(["recall", "fallback context", "--k", "3", "--json"])
.output().unwrap();use std::process::Command;
let out = Command::new("sqlite-graphrag")
.args(["recall", "system prompt history", "--k", "5", "--json"])
.output().unwrap();use std::process::Command;
let out = Command::new("sqlite-graphrag")
.args(["hybrid-search", "chat context", "--k", "5", "--json"])
.output().unwrap();use std::process::Command;
let out = Command::new("sqlite-graphrag")
.args(["recall", "tool use patterns", "--k", "5", "--json"])
.output().unwrap();use std::process::Command;
let out = Command::new("sqlite-graphrag")
.args(["recall", "local model outputs", "--k", "5", "--json"])
.output().unwrap();use std::process::Command;
let out = Command::new("sqlite-graphrag")
.args(["hybrid-search", "inference context", "--k", "10", "--json"])
.output().unwrap();use std::process::Command;
let out = Command::new("sqlite-graphrag")
.args(["recall", "llama session context", "--k", "5", "--json"])
.output().unwrap();- Read the contribution guidelines in CONTRIBUTING.md
- Open issues at the GitHub repository for bugs or feature requests
- Follow the code of conduct described in CODE_OF_CONDUCT.md
- Security reports follow the policy described in SECURITY.md
- Contact the maintainer privately before disclosing vulnerabilities publicly
- Authoritative JSON Schemas for every
--jsonresponse live underdocs/schemas/and are versioned alongside the crate - 35 schemas cover
init,remember,recall,hybrid-search,list,read,forget,purge,rename,edit,history,restore,link,unlink,prune-relations,health,stats,migrate,vacuum,optimize,cleanup-orphans,sync-safe-copy,graph(+ stats/traverse/entities),related,namespace-detect,debug-schema,entities-input,relationships-input,ingest-file-event,ingest-summary,export-memory-line,export-summary - Treat these schemas as the agent contract; SKILL.md documents the same shapes in human-readable form
- Validate downstream consumers with any standard JSON Schema validator (e.g.
ajv,jsonschema)
- PRD — Product Requirements Document (source of truth for the 31 behavioral contracts)
- Read the full release history in CHANGELOG.md
fastembedprovides local quantized embedding models without ONNX hasslesqlite-vecadds vector indexes directly inside SQLite as an extensionrefineryruns schema migrations with transactional safety guaranteesclappowers the CLI argument parsing with derive macrosrusqlitewraps SQLite with safe Rust bindings and bundled build
- Licensed under either of Apache License 2.0 or MIT License at your option
- See
LICENSE-APACHEandLICENSE-MITin the repository root for full text