smriti
by Smriti-AA
README.md
<p align="center">
<a href="https://github.com/smriti-AA/smriti/actions"><img src="https://img.shields.io/github/actions/workflow/status/smriti-AA/smriti/ci.yml?branch=main&label=build" alt="Build"></a>
<a href="https://crates.io/crates/smriti"><img src="https://img.shields.io/crates/v/smriti.svg" alt="crates.io"></a>
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="MIT"></a>
</p>
# Smriti
**A self-hosted knowledge graph and AI agent memory layer — one binary, one SQLite file, zero cloud dependencies.**
**Git for LLM wikis.** Atomic multi-write transactions, enforced provenance on every claim, append-only event log with a hash chain, and an integrity verifier — so agent-authored knowledge is auditable by construction.
`2.5µs` KV retrieval · `235ns` graph traversal · `0` cloud dependencies
[screenshot: web-ui-dashboard.gif]
```bash
cargo install smriti
```
## Why Smriti (vs. Obsidian / Zep / Mem0 / Letta / Neo4j)
| Property | Obsidian | Zep | Mem0 / Letta | Neo4j / Graphiti | **Smriti** |
|-----------------------------------------------|:--------:|:---:|:------------:|:----------------:|:----------:|
| Single binary, single file, no server | ✓* | ✗ | ✗ | ✗ | ✓ |
| Fully local / offline | ✓ | ✗ | ✗ | partial | ✓ |
| Bi-temporal edges (valid_from / valid_until) | ✗ | ✓ | ✗ | ✓ | ✓ |
| **Atomic multi-write transactions (SAVEPOINT)** | ✗ | ✗ | ✗ | ✓ (server) | **✓** |
| **Enforced provenance on every claim** | ✗ | ✗ | ✗ | ✗ | **✓** |
| **Append-only event log + hash chain** | ✗ | ✓ | ✗ | ✗ | **✓** |
| **`smriti verify` integrity sweep** | ✗ | ✗ | ✗ | ✗ | **✓** |
| Contradiction inbox (never auto-resolves) | ✗ | partial | ✗ | ✗ | ✓ |
| MCP-native for agents | plugin | ✗ | ✗ | ✗ | ✓ |
\* Obsidian is a filesystem with no transactional guarantees. Smriti's moat isn't novelty — it's **write-time discipline in a local-first Rust runtime**.
## Research foundation
Every integrity feature cites an arXiv paper so you can trace the design back to the literature:
- **Bi-temporal edges & event log T / T′** — Zep / Graphiti, arXiv:2501.13956
- **Structural overlap verification (claim ↔ source)** — FACTUM, arXiv:2601.05866 and Citation-Grounded Code Comprehension, arXiv:2512.12117
- **Contradiction confidence scoring** — MemoTime, arXiv:2510.13614 and EvoReasoner / EvoKG, arXiv:2509.15464
- **Belief revision & conflict policy on memory_store** — AGM postulates, arXiv:2603.17244
- **Graph + BM25 hybrid retrieval** — Graph-Based Memory Survey, arXiv:2602.05665
- **Typed graph layers (semantic/temporal/causal)** — MAGMA, arXiv:2601.03236
- **Zettelkasten-style agent memory** — A-MEM, arXiv:2502.12110 (NeurIPS 2025)
- **Hallucination grounding requirements** — arXiv:2510.24476
## Integrity layer (v0.2)
Four MCP tools turn Smriti from a CRUD store into a wiki with invariants:
- `wiki_transaction_submit` — batch of create/update/link/source ops applied atomically inside a SQLite `SAVEPOINT`. Every content write must carry a `claim_spans` array or be rejected (provenance enforced by default).
- `wiki_verify` — runs referential integrity + re-verifies every stored claim's overlap score + walks the event-log hash chain. Returns pass/fail. Never mutates.
- `contradictions_detect` — pairwise scan over recent notes using *w1·semantic + w2·recency + w3·authority* weighted scoring. Candidates land in a review inbox — Smriti never auto-resolves.
- `contradictions_list` — the review inbox.
CLI mirrors all of it:
```bash
smriti verify # integrity sweep
smriti pending-tx # list transactions awaiting review
smriti commit-tx <id> # commit a pending transaction
smriti reject-tx <id> -r "..." # reject with reason
smriti detect-contradictions # scan for candidates
smriti contradictions # show review inbox
```
---
## What is Smriti?
Smriti (Sanskrit: स्मृति, *memory*) is a single Rust binary that runs a knowledge graph, a full-text + semantic search index, and an MCP server on top of one SQLite file. It is designed for two users: a knowledge worker who wants a private second brain their AI assistant can read, and a developer who needs a persistent, structured memory layer for AI agents.
Notes connect to each other through typed wiki-links — write `[[rel:causal|Decision X]]` in a note and Smriti records a directed `causal` edge in the knowledge graph automatically. Agents can then traverse that graph to answer questions like "what led to this decision?" without re-reading every note.
---
## Quick start
```bash
# Install
cargo install smriti
# Create your first notes — links and tags are extracted automatically
smriti new # interactive guided prompt
smriti create "Acme Corp" --content "Key client. Met via [[Sarah Chen]]." --tags client
# Search
smriti search "Acme"
# Open the web dashboard
smriti serve
# → http://localhost:3000
```
Your notes, graph, and search index live in `~/.local/share/smriti/smriti.db`. Back up with `cp`.
---
## Use cases
### Client knowledge graph
Track every client, contact, and engagement as linked notes. When you brief Claude before a call, it reads the full context — history, decisions, open items — without you re-explaining anything.
```bash
smriti create "Acme Corp Q2 Review" \
--content "Next steps: [[rel:temporal|Budget approval]] by June. Owner: [[Sarah Chen]]." \
--tags client decision
```
### Decision log
Record decisions with context and consequences. The `rel:causal` link type lets agents trace why something was decided.
```bash
smriti create "Switched to Rust" \
--content "Replaced Python service. Reason: [[rel:causal|Memory leak in prod]]." \
--tags decision
```
### Daily AI context
Store your current focus in the KV store. Claude reads it at the start of every session through MCP.
```bash
smriti serve # then ask Claude: "what's my current focus?" — Smriti answers via MCP
```
### SOPs and playbooks
Document repeatable processes as linked notes. Import existing markdown files in one command.
```bash
smriti import ./playbooks --recursive
```
---
## MCP integration
Smriti runs as an MCP server over stdio. Add it to Claude Desktop in `claude_desktop_config.json`:
```json
{
"mcpServers": {
"smriti": {
"command": "smriti",
"args": ["mcp", "--db", "/path/to/smriti.db"]
}
}
}
```
For claude.ai remote MCP, start `smriti serve` and point the MCP client at `http://localhost:3000/mcp`.
### MCP tools
| Tool | What it does |
|------|-------------|
| `notes_create` | Create a note; `[[wiki-links]]` and `#tags` are auto-extracted |
| `notes_read` | Read a note by ID or title |
| `notes_search` | Full-text BM25 search across all notes |
| `notes_list` | List recent notes, filter by tag |
| `notes_graph` | Return a subgraph (BFS, typed edge filter) around a note |
| `notes_search_semantic` | Vector + FTS5 hybrid search with reciprocal rank fusion |
| `memory_store` | Store a key-value pair; supports TTL and conflict policy |
| `memory_retrieve` | Retrieve a stored value by agent ID + key |
| `memory_list` | List all memory entries for an agent |
| `memory_history` | Retrieve superseded values for a key (versioned memory) |
Full MCP reference: [`docs/mcp.md`](docs/mcp.md)
---
## Performance
Measured on Apple Silicon, in-memory SQLite, using [Criterion](https://github.com/bheisler/criterion.rs). Run: `cargo bench`
| Operation | p50 |
|-----------|-----|
| Insert 1 note | 32.5 µs |
| Insert 100 notes | 2.0 ms |
| Insert 1,000 notes | 23.1 ms |
| FTS5 search — 1k notes | 331 µs |
| FTS5 search — 10k notes | 2.86 ms |
| Graph build — 1k nodes | 216 µs |
| BFS depth-2 (cached) | 235 ns |
| BFS depth-3 (cached) | 410 ns |
| Memory KV store — 100 keys | 513 µs |
| Memory KV retrieve (hit) | 2.48 µs |
| Memory KV retrieve (miss) | 2.25 µs |
### Smriti vs alternatives
| | Smriti | Mem0 | Letta | Zep |
|---|---|---|---|---|
| Self-hosted | Yes | No | Yes | Partial |
| Knowledge graph | Yes (petgraph) | No | No | Yes (Neo4j) |
| Typed edges | Yes | No | No | Yes |
| Bi-temporal edges | Yes | No | No | Yes |
| Belief revision | Yes (AGM) | No | No | No |
| MCP native | Yes | No | No | No |
| Full-text search | FTS5 (BM25) | Vector only | Vector only | Vector + keyword |
| Hybrid search | Yes (RRF) | No | No | No |
| KV memory + TTL | Yes | No | Yes | Yes |
| Language | Rust | Python | Python | Python/Go |
| Deployment | Single binary | SaaS | Docker + Postgres | Docker + Neo4j + Redis |
| KV retrieval latency | ~2.5 µs | ~50–200 ms | ~10–50 ms | ~5–20 ms |
---
## Architecture
```
src/
├── models/ Note, Link, AgentMemory, ToolLog — Serde on every type
├── storage/ SQLite + FTS5 + sqlite-vec; WAL mode; single connection pool
├── parser/ [[wiki-link]] and #tag extraction via regex; no runtime deps
├── graph/ petgraph DiGraph; lazy GraphCache (Arc<RwLock>); typed BFS
├── mcp/ JSON-RPC 2.0 over stdio; dispatches to same handlers as REST
├── web/ Axum router; localhost-only CORS; embedded React SPA
├── cli/ clap v4 derive; 11 commands; shell completions; interactive new
├── sync/ WebDAV + filesystem sync with per-device conflict tracking
└── features/ Smart link suggestions; daily digest
```
### Design decisions
**Why SQLite, not Postgres.** A knowledge base for one person or a small team should not require a running database server. SQLite in WAL mode handles hundreds of concurrent reads per second — more than enough for any personal knowledge graph. The entire database is one file: backup is `cp`, migration is `mv`.
**Why Rust, not Python.** Agent memory sits in the critical path of every tool call. Python MCP servers typically add 50–200 ms per round-trip from serialization overhead and GIL contention. Smriti's Rust implementation retrieves a KV entry in 2.5 µs, keeping memory operations invisible to the agent's response latency.
**Why FTS5 + vector, not one or the other.** Keyword search (BM25) is precise for known terms; vector search recalls semantically related content the user didn't think to search for. Neither is sufficient alone. Smriti combines both with reciprocal rank fusion, weighted at query time — matching the finding in [arXiv:2602.05665](https://arxiv.org/abs/2602.05665) that hybrid retrieval outperforms pure vector on multi-hop reasoning tasks.
**Belief revision on `memory_store`.** When an agent stores a key that already exists, naive overwrite discards history. Smriti implements four AGM conflict resolution policies ([arXiv:2603.17244](https://arxiv.org/abs/2603.17244)): `overwrite` (default), `reject` (fail if exists), `version_and_keep` (archive old value), and `invalidate` (mark old as superseded). Superseded values are queryable via `memory_history`.
### Research basis
| Paper | arXiv | What it grounds in Smriti |
|-------|-------|--------------------------|
| Zep / Graphiti | [2501.13956](https://arxiv.org/abs/2501.13956) | Bi-temporal edges on `links` table; 18.5% LongMemEval improvement |
| MAGMA | [2601.03236](https://arxiv.org/abs/2601.03236) | Typed graph layers; BFS filtered by `link_type`; 95% token reduction |
| Graph-Native Belief Revision | [2603.17244](https://arxiv.org/abs/2603.17244) | `ConflictPolicy` enum on `memory_store` |
| Graph-Based Memory Survey | [2602.05665](https://arxiv.org/abs/2602.05665) | FTS5 + sqlite-vec hybrid with reciprocal rank fusion |
---
## Contributing
```bash
git clone https://github.com/smriti-AA/smriti.git
cd smriti
cargo test --all-features # should be green
cargo bench # performance baseline
```
See [`CONTRIBUTING.md`](CONTRIBUTING.md) for the development workflow. Issues labelled [`good first issue`](https://github.com/smriti-AA/smriti/labels/good%20first%20issue) are self-contained storage or CLI changes that don't require understanding the full codebase.
Before opening a PR: `cargo clippy --all-features -- -D warnings` and `cargo fmt --check`.
---
## License
[MIT](LICENSE)