Privacy-first knowledge graph engine for AI agents.
Extracts entities and relations from any text. Builds a temporal knowledge graph. Works locally with zero API keys — and when it does call an LLM, it makes one call per episode instead of Graphiti's six.
brew install rohansx/tap/ctxgraph
ctxgraph init && ctxgraph models download
ctxgraph log "Migrated auth from Redis sessions to JWT. Chose JWT for stateless scaling."
ctxgraph query "Why did we move away from Redis?"Every knowledge graph engine requires an LLM for every operation. Graphiti makes 6 API calls per episode. Mem0 calls GPT-4 on every add/search. Microsoft GraphRAG is so expensive they put a cost warning in their README.
ctxgraph runs a tiered extraction pipeline: local ONNX models handle most episodes at zero cost, and only escalates to an LLM when local confidence is low. One call, not six. PII stripped before it leaves your machine.
Validated on 20 unseen episodes across 12 domains:
| ctxgraph | Graphiti | Mem0 | LightRAG | |
|---|---|---|---|---|
| Combined F1 | 0.678 | 0.287 | N/A (no KG eval) | N/A (RAG, no KG) |
| Entity F1 | 0.854 | 0.468 | — | — |
| Relation F1 | 0.502 | 0.106 | — | — |
| LLM calls per episode | 1 | 6 | 1-2 | 1+ |
| Works without LLM? | Yes | No | No | No |
| Works offline? | Yes | No | No | Partial |
| Query latency | <15ms | ~300ms | ~100ms | ~200ms |
| Infrastructure | SQLite | Neo4j+Docker | Vector DB | Varies |
| Language | Rust | Python | Python | Python |
| Privacy (PII protection) | CloakPipe (feature flag) | None | None | None |
Text comes in
|
v
[Tier 1] GLiNER entities + GLiREL relations (local ONNX, FREE, ~10ms)
|
v
Confidence gate: good enough?
|
+-- YES (tech text, ~70%) --> Graph. Done. $0.
|
+-- NO (cross-domain) --> [CloakPipe strips PII] --> LLM (1 call) --> Graph.
| Tier | What | Cost | Latency |
|---|---|---|---|
| Local ONNX | GLiNER (entities) + GLiREL (relations) | $0 | ~10ms |
| LLM fallback | One call for entities + relations when local isn't confident | ~$0.0003/ep | ~3-5s |
| Dedup | Jaro-Winkler similarity + alias table (local) | $0 | <1ms |
| Search | FTS5 + semantic + graph walk, fused via RRF (local) | $0 | <15ms |
Graphiti does ALL of these via LLM: entity extraction, deduplication, relation extraction, contradiction detection, summarization, community detection. Six calls. Every episode.
| ctxgraph | Graphiti | Cognee | WhyHow.AI | |
|---|---|---|---|---|
| Extraction | Local ONNX + LLM fallback | LLM only (6 calls/ep) | LLM only | LLM only (OpenAI) |
| Graph DB | SQLite (embedded) | Neo4j/FalkorDB | Neo4j/Kuzu | MongoDB Atlas |
| Works offline? | Yes | No | No | No |
| Temporal queries | Bi-temporal | Bi-temporal | No | No |
| MCP support | Yes | Yes | Yes | No |
| Language | Rust (single binary) | Python | Python | Python |
| Schema-driven | Yes (ctxgraph.toml) | Yes (prescribed ontology) | Yes | Yes |
| Cost/1000 eps | $0.30 | $1.80 | ~$1.50 | ~$2+ |
| Stars | Early | 24K | 15K | 900 |
| ctxgraph | Mem0 | Basic Memory | mcp-memory-service | |
|---|---|---|---|---|
| Entity extraction | Automated (ONNX+LLM) | LLM-only | None (manual) | None (manual) |
| Relation extraction | Automated (GLiREL+LLM) | Limited | None | Manual typed edges |
| Knowledge graph | Yes (temporal) | Optional (Neptune) | Semantic links | Basic typed edges |
| Works without LLM? | Yes | No | Yes | Yes |
| Query latency | <15ms | ~100ms | ~10ms | ~5ms |
| Dedup | Jaro-Winkler + aliases | LLM-based | None | None |
| Cost | $0 (local) / $0.30 (hybrid) | LLM cost per op | $0 | $0 |
| Stars | Early | 51K | 2.7K | 1.6K |
| ctxgraph | LightRAG | Microsoft GraphRAG | nano-graphrag | |
|---|---|---|---|---|
| Purpose | Knowledge graph engine | RAG retrieval | Document summarization | Lightweight GraphRAG |
| Incremental updates | Yes | Yes | No (batch only) | No |
| Temporal awareness | Yes (bi-temporal) | No | No | No |
| LLM per query | No | Yes | Yes | Yes |
| Offline capable | Yes | Partial (Ollama) | No | Partial |
| Cost/1000 docs | $0.30 | ~$1-5 | ~$10-50 | ~$1-5 |
| Stars | Early | 31K | 32K | 3.8K |
No other tool has all of these:
- Automated extraction that works offline — GLiNER + GLiREL via ONNX. Mem0 and Basic Memory have no extraction. Graphiti/Cognee/LightRAG always need an LLM.
- Typed relations, not free-form — Graphiti produces
SWITCHED_TO,CAUSED_OOM_KILLS. ctxgraph producesreplaced,caused— queryable by type. - Bi-temporal history — Only ctxgraph and Graphiti have this. Every other tool is current-state only.
- PII protection — CloakPipe strips PII before LLM calls (
--features cloakpipe). No competitor offers this. - Single Rust binary — Every competitor is Python with pip/Docker/Neo4j. ctxgraph is
cargo install. - 6x fewer LLM calls — 1 call vs Graphiti's 6. Same model, better results.
Tested on 20 completely new episodes across 12 domains. Both systems use GPT-4o-mini. Neither system has seen this data before.
| Graphiti | ctxgraph | |
|---|---|---|
| Entity F1 | 0.468 | 0.854 |
| Relation F1 | 0.106 | 0.502 |
| Combined F1 | 0.287 | 0.678 |
| Time/episode | 18.2s | 5.1s |
| LLM calls (20 eps) | ~120 | 20 |
| Domain | Entity F1 | Relation F1 | Combined |
|---|---|---|---|
| Tech (Slack, PRs, ADRs, incidents) | 0.877 | 0.530 | 0.703 |
| Finance | 0.769 | 0.600 | 0.685 |
| Healthcare | 0.909 | 0.286 | 0.598 |
| Legal | 0.857 | 0.500 | 0.679 |
| Manufacturing | 0.667 | 0.333 | 0.500 |
| Education | 0.667 | 0.286 | 0.477 |
| Real Estate | 1.000 | 0.667 | 0.834 |
| E-commerce | 0.800 | 0.500 | 0.650 |
| Logistics | 0.889 | 0.667 | 0.778 |
| Gaming | 1.000 | 1.000 | 1.000 |
| Government | 0.909 | 0.000 | 0.455 |
| LLM | Hostable locally? | Cross-domain F1 | Cost/1000 eps |
|---|---|---|---|
| None (local only) | N/A | 0.325 | $0 |
| Llama 3.2 3B (Ollama) | Yes (8GB) | 0.472 | $0 |
| Qwen 2.5 7B (Ollama) | Yes (16GB) | 0.508 | $0 |
| GPT-4o-mini | Cloud | 0.650 | $0.30 |
| Claude 3.5 Haiku | Cloud | 0.611 | $0.09 |
| Gemini 2.0 Flash | Cloud | 0.552 | $0.05 |
| ctxgraph | Graphiti | |
|---|---|---|
| Full-text search | <1ms | ~50ms |
| Semantic search | 3-5ms | ~100ms |
| Graph traversal (2-3 hops) | <5ms | 5-50ms |
| Fused search (RRF) | <15ms | ~300ms |
- Tiered extraction — Local ONNX first, LLM only when needed
- Privacy — CloakPipe strips PII before cloud LLM calls (enable with
--features cloakpipe) - Zero infrastructure — One binary, one SQLite file
- Any LLM — Ollama, NVIDIA NIM (free), OpenRouter, OpenAI, Anthropic
- Bi-temporal — Time-travel queries, fact invalidation
- Schema-driven — Entity/relation types via
ctxgraph.toml - MCP server — Claude Code, Cursor, Cline, any MCP client
- Embeddable — Rust library, CLI, or MCP server
- Entity dedup — Jaro-Winkler + alias table across episodes
# Homebrew
brew install rohansx/tap/ctxgraph
# Or build from source (Rust 1.85+)
cargo install ctxgraph-clictxgraph models download # one-time ONNX model download
ctxgraph init # initialize graph in current dir
ctxgraph log "Alice chose PostgreSQL" # extract + store
ctxgraph query "why PostgreSQL?" # search the graph# ctxgraph.toml
[llm]
provider = "openrouter"
[llm.openrouter]
model = "openai/gpt-4o-mini"
api_key_env = "OPENROUTER_API_KEY"
[privacy]
# Enable PII stripping: cargo install ctxgraph-cli --features cloakpipeWithout this, everything runs locally at 0.846 F1 on tech text.
{
"mcpServers": {
"ctxgraph": { "command": "ctxgraph-mcp" }
}
}| Tool | Description |
|---|---|
ctxgraph_add_episode |
Record a decision or event |
ctxgraph_search |
Fused FTS5 + semantic + graph search |
ctxgraph_traverse |
Walk the graph from an entity |
ctxgraph_find_precedents |
Find similar past events |
ctxgraph_list_entities |
List entities with filters |
ctxgraph_export_graph |
Export entities and edges |
let graph = ctxgraph::Graph::init(".ctxgraph")?;
graph.add_episode(Episode::builder("Chose Postgres for billing").build())?;
let results = graph.search("why Postgres?", 10)?;crates/
+-- ctxgraph-core/ Types, storage, query, temporal
+-- ctxgraph-extract/ Tiered extraction (ONNX + LLM)
+-- ctxgraph-embed/ Local embeddings
+-- ctxgraph-cli/ CLI binary
+-- ctxgraph-mcp/ MCP server
+-- ctxgraph-sdk/ Rust SDK
See CONTRIBUTING.md.
MIT