ERINYS
Mentions Bootstrap in example use cases for applying optimization techniques to Bootstrap-related scripts.
Exports memory data to markdown format for compatibility with markdown-based tools like Obsidian.
Exports memory data to Obsidian-compatible markdown files with wikilinks for instant knowledge graph creation.
Supports PostgreSQL as a database option for production reliability, with conflict detection against other database choices.
Planned PyPI package distribution for easy installation of the memory server as a Python package.
Includes testing framework integration for running tests on the memory server implementation.
Built as a Python-based MCP server with Python environment setup and package management.
Uses SQLite as the local-first database storage with FTS5 and vector extensions for hybrid search capabilities.
ERINYS — Verifiable local memory for AI agents
~10 ms local retrieval. No API key. No token cost.
A local trust layer for agent memory: one SQLite file, zero LLM calls in the retrieval path. Retrieval recall 99.4% R@5 / 100% R@10 on LongMemEval-S, 96.8% R@5 on the harder _m split — see Benchmarks.
🇯🇵 日本語 · Limitations · Comparison · Tool reference · Changelog
AI agent memory systems have always mimicked human memory. Short-term, long-term, episodic, semantic — textbook categories bolted straight onto implementations.
Something always felt off.
Humans forget. But existing memory systems don't — they grow endlessly, serving stale facts with the same weight as fresh ones. Humans notice "wait, didn't you say something different before?" But memory systems silently overwrite. Humans connect two unrelated experiences and think "oh, I can use that here." Memory systems just store and retrieve.
What needed to be mimicked wasn't the taxonomy of memory. It was the behavior. That discomfort is what summoned ERINYS.
ERINYS is a guard dog: it stores facts, preserves history, catches contradictions, and proves deletion.
Origin: ERINYS was built as the retrieval layer for HyperAION, an AI agent self-improvement framework. It is released as a standalone MCP server so any agent stack can use it independently.
Quickstart (30 seconds)
1. Install.
pip install erinys-memory2. Verify your environment. One command checks Python, SQLite extension support, sqlite-vec, embeddings, dependencies, and the DB — each failing check prints a fix.
erinys doctor3. Register the MCP server with your client (Claude Desktop / Claude Code / any stdio MCP host):
{
"mcpServers": {
"erinys": {
"command": "erinys-memory",
"env": {
"ERINYS_DB_PATH": "~/.erinys/memory.db"
}
}
}
}4. Save and search from the JSON CLI (no LLM, no network):
erinys save --title "JWT httpOnly flag was missing" \
--content "Cookie was JS-accessible; added httpOnly, secure, sameSite=strict." \
--type bugfix --project demo
erinys search "auth cookie security" --project demoRetrieval runs in ~7–10 ms against a single local SQLite file, with zero LLM calls.
Related MCP server: cell-mem
New in v0.6.0
Historical backfill that preserves time. erinys backfill imports reviewed
Slack, email, chat, Web, or archive records with their original timestamps.
Recorded time, valid time, and historical use events stay separate, so decay
and reinforcement begin from what actually happened rather than the import
date. Plan/apply digests, event-ledger idempotency, pre-write embedding,
automatic backups, and a guarded rollback path make direct SQLite injection
unnecessary. Apply is disabled by default. See
Timestamp-aware backfill.
Admission / Activation Sentry. Provenance says where memory came from;
Sentry decides what it may be used for. Every write receives a server-controlled
trust classification, while reads declare an intended_use. Recalled text does
not become an instruction merely because it was retrieved, and risky directives
inside untrusted records are blocked before content reaches the caller. See
Memory safety.
Current Valid Claim Evidence Path v0 + GraphRAG read-only shadow. A strict claim contract separates valid time, recorded time, scope, status, provenance, and edge verification. An opt-in shadow evaluates the existing RRF candidates over bounded verified paths without changing the production response. It is off by default and remains a measurement path, not the answer path. See the rollout contract.
Benchmarks
These are retrieval recall numbers ("is the correct session in the top-K?"), not end-to-end QA accuracy — QA and answerability harnesses shipped in v0.5.1, real runs pending (LIMITATIONS.md). All results use the same mode (enhanced_v2_boost) with zero LLM calls in the retrieval pipeline, reproduced on current dependencies (2026-07).
Benchmark | N | R@5 | R@10 |
LongMemEval-S | 500 | 99.4% | 100.0% |
LongMemEval-M (~476-session haystack) | 500 | 96.8% | 98.0% |
LoCoMo | 1,982 | 92.7% (fair ≈ 95.7%¹) | 97.2% |
ConvoMem | 250 | 97.6%² | — |
¹ A miss audit found 42% of LoCoMo R@5 misses are benchmark-label defects (single-gold labels for multi-session answers, unanswerable adversarial questions), not retrieval failures. ² April 2026 configuration; re-run on current dependencies pending.
Why this matters: no API keys, no network, no tokens burned for retrieval. ERINYS reaches these numbers with FTS5 + sqlite-vec + algorithmic boosting alone — your agent's memory searches at the speed of SQLite. Full methodology, per-category breakdowns, miss analyses, and reproduction commands → benchmarks/BENCHMARKS.md
The story of how we got here → 🇺🇸 English / 🇯🇵 日本語
What makes ERINYS different
Forgetting. Most memory systems only accumulate. ERINYS decays memories over time following the Ebbinghaus forgetting curve: old noise sinks, frequently accessed knowledge floats. Search results stay relevant without manual curation — decay runs automatically, no LLM needed.
Verified forgetting. erinys_forget deletes a memory and its derived closure (distilled descendants) in a single transaction, then runs a membership test proving zero residue across every DB substrate (observations / vectors / FTS / edges / collisions). Deletion you can prove, not just request.
Provenance. Every observation carries a server-stamped provenance block — who wrote it, via which write path, derived from which parents. erinys_lineage walks any memory back to its origins. Callers cannot spoof it.
Distillation. A specific bugfix ("JWT httpOnly flag was missing") automatically generates three layers: the concrete fact → a reusable pattern ("new endpoints need a security checklist") → a universal principle ("security defaults should be safe without opt-in"). ⚠️ Requires an LLM call (local Ollama by default).
Dream Cycle. Two memories are fed to an LLM: "is there a connection?" Candidate pairs are selected by semantic similarity — close enough to be related (cosine > 0.65), far enough to not be redundant (< 0.90). ⚠️ Requires LLM calls; not part of the zero-LLM retrieval path.
Distillation and the Dream Cycle are the generative edge of ERINYS: from memories that existed, it even synthesizes memories that never did. Both are LLM-backed and live outside the zero-LLM retrieval path.
Adaptive search. Query complexity is classified automatically (L1/L2/L3): simple keyword lookups stay FTS-heavy, complex multi-hop questions shift to vector-heavy retrieval. WHY/WHO/WHEN intents adjust boost parameters and graph edge types, and graph-reachable neighbors are reranked upward. CJK queries route to vector search by default, where embedding models outperform FTS5's porter tokenizer.
Design philosophy
Memory has layers
Not all memory is equal. ERINYS organizes knowledge by abstraction level:
Concrete — what happened. "The JWT httpOnly flag was missing on
/api/auth."Abstract — patterns from facts. "New API endpoints need a security header checklist."
Meta — principles from patterns. "Security defaults should be safe without manual opt-in."
A single bugfix generates all three through distillation. The meta layer accumulates principles that transfer across projects and tech stacks.
Forgetting is a feature
Every memory has a strength score that decays over time. A memory saved 6 months ago ranks lower than one saved yesterday. Memories accessed frequently resist decay — repeated retrieval reinforces them.
When strength drops below a threshold, the memory becomes a pruning candidate. The database stays lean. Search stays relevant.
Facts change. History shouldn't disappear
When information updates — "we moved from AWS to GCP" — ERINYS doesn't overwrite. It creates a supersede chain: the old fact is marked as replaced but preserved. You can ask "what did we believe in March?" and get the answer that was true then.
Contradictions should be caught
If memory contains both "use PostgreSQL" and "use SQLite", ERINYS detects the conflict. Instead of silently switching, the agent asks: "you previously chose PostgreSQL — has the requirement changed?"
Search finds meaning, not just keywords
Two searches run simultaneously and fuse results:
Keyword search (FTS5) — exact term matching with NEAR phrase expansion.
Vector search (sqlite-vec) — semantic similarity via local embeddings.
RRF fusion — Reciprocal Rank Fusion combines both rankings with adaptive weights.
Intent routing — WHY/WHEN/WHO queries adjust boost parameters and edge types.
Graph reranking — knowledge-graph neighbors boost fusion scores.
No LLM in the loop. Retrieval latency stays under 15 ms.
Everything stays local
One SQLite file. No cloud API, no subscription, works offline. Your agent's memory never leaves the machine.
Use cases
Memory across sessions. An agent saves what it learned; next week's agent finds it.
erinys_save(title="Fixed JWT httpOnly flag",
content="Cookie was JS-accessible; added httpOnly, secure, sameSite=strict.",
type="bugfix", project="my-app")
erinys_search(query="auth cookie security", project="my-app")
# → the JWT fix comes back, scored. The same mistake is not repeated.Contradiction detection.
erinys_save(title="DB choice", content="Use SQLite for simplicity", project="my-app")
erinys_conflict_check(observation_id=42)
# → "⚠️ Conflicts with observation #18: 'Use PostgreSQL for production reliability'"Time-travel queries.
erinys_timeline(query="deployment target", as_of="2026-03-01") # → "AWS EC2 (decided 2026-02-15)"
erinys_timeline(query="deployment target", as_of="2026-04-01") # → "GCP Cloud Run (switched 2026-03-20)"Verified forgetting.
erinys_forget(id=42) # dry run: shows the derived closure that would be deleted
erinys_forget(id=42, dry_run=False) # deletes it, then proves zero residue across all DB substratesCLI
The MCP server is the agent-facing adapter; the same operations are available from a JSON CLI for scheduled jobs, CI, recovery, and manual verification — stable exit codes, machine-readable output.
erinys health --project my-app --deep --json # authoritative: server import + search smoke test
erinys search "Buffer DNS" --project my-app --limit 5 --readonly --json
erinys context --project my-app --limit 10 --readonly --json
erinys save --title "Decision" --content "What: ..." --type decision --project my-app --json
erinys undistilled --project my-app --limit 10 --json
erinys distill 123 --level meta --json--readonlyreads via SQLitemode=ro— keyword search only, no migration or audit-log writes. Drop it when semantic search is needed.dream/pruneoperate on the whole database across all projects;prune --executeadditionally requires--confirm-global.Usage errors also emit JSON (
error.code: "USAGE", exit code 2).Module form:
python -m erinys_memory.cli <command>.
MCP tool surface (29 tools)
Tier | Tools | What | LLM |
Basic | 17 | save / search / recall / sessions — the stable core | ❌ none |
Governance | 8 | lineage, supersede, timeline, backfill, conflict check, verified forgetting | ❌ none |
Experimental | 4 | distill, dream, collide, eval — research features | ⚠️ distill / dream / collide call an LLM |
Every tool returns the same {ok, data, error} envelope. Full per-tool reference → docs/TOOLS.md
Configuration
Variable | Default | Description |
|
| SQLite database path |
|
| fastembed embedding model |
|
| Auto-distill on save (set |
|
| Local Ollama model for distillation |
|
| Ollama generate endpoint |
|
| Permit a reviewed backfill plan to be applied |
|
| Local CLI item cap per request; MCP remains capped at 100 |
|
| Maximum observations per apply transaction |
| empty | Comma-separated namespaces allowed to act as first-party evidence |
|
| Set |
Architecture
┌─────────────────────────────┐
│ FastMCP Server │ 29 tools, uniform {ok, data, error} envelope
├─────────────────────────────┤
│ search.py │ graph.py │ RRF hybrid search │ typed edges
│ decay.py │ session.py │ Ebbinghaus decay │ lifecycle
│ temporal.py │ collider.py │ versioning │ dream cycle
│ distill.py │ policy.py │ 3-level distill │ access policy
│ backfill.py │ sentry.py │ event ledger │ use control
│ claim_shadow │ policy.py │ read-only graph │ access policy
│ provenance.py │ db.py │ lineage stamps │ SQLite + vec
├─────────────────────────────┤
│ embedding.py │ fastembed (multilingual-MiniLM, 384d)
├─────────────────────────────┤
│ SQLite + FTS5 + sqlite-vec │ fully local, no network at runtime
└─────────────────────────────┘Development
git clone https://github.com/GhostyAI-HA/ERINYS-mem && cd ERINYS-mem
python3 -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
PYTHONPATH=src pytest tests/ -v # run tests
python -m erinys_memory.server # run the MCP server from source (stdio)
ollama pull gemma4:e4b # only for distill / dream (LLM features)Release highlights
v0.5 — benchmark truth (reproduced numbers, first
_msplit evaluation), temporal grounding, answerability, opt-in memory access policy, QA / answerability eval harnessesv0.4 — VMG: server-stamped provenance,
erinys_lineage, verified forgetting (erinys_forget); MAGMA retrieval (adaptive weights, intent routing, graph reranking)v0.2 — adaptive retrieval, intent-aware router, distillation quality scoring, dream outcome scoring
Full details → CHANGELOG.md
Roadmap
Dream Daemon — run the Dream Cycle in the background (currently manual)
Auto-prune when the DB crosses a size threshold
Multi-agent scoping — per-agent memory isolation
ConvoMem re-run + real end-to-end QA evaluation on current dependencies
License
MIT © 2026 SHUN FUJIYOSHI (GhostyAI-HA) — see LICENSE
Available Tools
29 toolserinys_backfillErinys BackfillC
Plan or apply bounded historical records with event-ledger idempotency.
Inputs: items (required), run_key (required), mode, expected_plan_digest, +1 more. Returns: object
erinys_backupErinys BackupC
Create a consistent SQLite backup and return backup metadata.
Inputs: path. Returns: object
erinys_batch_saveErinys Batch SaveC
Bulk-add observations with automatic similarity linking.
Inputs: items (required), auto_link. Returns: object
erinys_collideErinys CollideC
Manually collide two observations to generate insight.
Inputs: obs_a_id (required), obs_b_id (required). Returns: object
erinys_conflict_checkErinys Conflict CheckC
Detect contradicting observations.
Inputs: observation_id (required). Returns: object
erinys_contextErinys ContextC
Get recent session context for a project.
Inputs: project (required), limit, intended_use. Returns: object
erinys_deleteErinys DeleteB
Delete an observation and cascade dependent rows via FK rules.
Inputs: id (required). Returns: object
erinys_distillErinys DistillC
Distill observation to higher abstraction level.
Inputs: observation_id (required), level. Returns: object
erinys_dreamErinys DreamC
Run Dream Cycle: batch collision of candidate memory pairs.
Inputs: max_collisions. Returns: object
erinys_evalErinys EvalC
Self-evaluate memory quality (LOCOMO-inspired metrics).
Inputs: project. Returns: object
erinys_exportErinys ExportC
Export observations as markdown payload (Obsidian-compatible [[wikilinks]]).
Inputs: format, project. Returns: object
erinys_forgetErinys ForgetA
VMG Verified Forgetting: 観測とその distilled 派生物を全 DB substrate から 削除し、不在を membership test で実証する。
forgetting は派生物に伝播する: distilled 子孫を含む closure ごと忘れる (子に親の情報が残るのを防ぐ)。closure を leaf-first で単一Txで削除し、 observations/vec/FTS(トリガ)/edges/collisions(CASCADE) を消す。 erinys_delete と違い、子を持つ親も忘れられる(NO ACTION FK を closure で解消)。 dry_run(既定)では削除せず closure と現在の在席のみ返す。
範囲外(正直な明示): Obsidian 書庫は次回 export の orphan 掃除で遅延除去、 .bak バックアップは復元用に設計上保持するため、この DB 検証の対象外。
Inputs: id (required), dry_run. Returns: object
erinys_getErinys GetB
Get a single observation by ID (full content, untruncated).
Inputs: id (required), intended_use. Returns: object
erinys_lineageErinys LineageB
VMG Provenance Visibility: 記憶の出自系譜を祖先方向に辿って返す。
各ノードの provenance.parents(無ければ distilled_from)を辿り、 どの記憶からどう派生したかの lineage-complete な鎖を構成する。
Inputs: id (required), max_depth. Returns: object
erinys_linkErinys LinkC
Create typed edge between observations.
Inputs: relation (required), source_id (required), target_id (required), weight. Returns: object
erinys_pruneErinys PruneC
Prune decayed observations below threshold.
Inputs: dry_run, threshold. Returns: object
erinys_recallErinys RecallC
Recall recent observations for context.
Inputs: limit, project, intended_use. Returns: object
erinys_reinforceErinys ReinforceC
Reinforce an observation by updating access_count and last_accessed.
Inputs: observation_id (required). Returns: object
erinys_saveErinys SaveB
Save a structured observation to ERINYS memory.
principal: この記憶を書いた主体(例 claude-opus-4-8 / codex)。省略時は env ERINYS_PRINCIPAL → "unknown"。VMG provenance に記録される。
P0-7 (opt-in): allowed_projects / allowed_scopes は書き込み許可の access gate。明示引数 > env(ERINYS_ALLOWED_PROJECTS / ERINYS_ALLOWED_SCOPES)> 無効(None)。どれも設定されなければ従来どおり無条件に保存する。許可外の project/scope への書き込みは POLICY_DENIED で拒否する。
Inputs: title (required), content (required), type, scope, +8 more. Returns: object
erinys_save_promptErinys Save PromptC
Save a user prompt to track intent and goals.
Inputs: content (required), project, session_id. Returns: object
erinys_searchErinys SearchC
RRF hybrid search (FTS5 keyword + vector similarity).
P0-7 (opt-in): allowed_projects / allowed_scopes は取得許可の access gate。 明示引数 > env(ERINYS_ALLOWED_PROJECTS / ERINYS_ALLOWED_SCOPES)> 無効 (None)。設定時は、許可外の project/scope の行を結果から除外する(global scope は常に可視)。どれも設定されなければ従来どおり全件返す。
Inputs: query (required), limit, project, principal, +6 more. Returns: object
erinys_session_endErinys Session EndC
End a session with optional summary.
Inputs: id (required), summary. Returns: object
erinys_session_startErinys Session StartC
Start a new session.
Inputs: id (required), project (required), directory. Returns: object
erinys_session_summaryErinys Session SummaryC
Save comprehensive end-of-session summary (Goal/Discoveries/Accomplished).
Inputs: content (required), project (required), session_id. Returns: object
erinys_statsErinys StatsC
Database statistics: observation count, project breakdown, health.
Inputs: project. Returns: object
erinys_supersedeErinys SupersedeC
Supersede an old observation with updated fact.
Inputs: old_id (required), reason (required), new_content (required). Returns: object
erinys_timelineErinys TimelineC
Query facts valid at a specific point in time.
Inputs: query (required), as_of. Returns: object
erinys_traverseErinys TraverseC
Traverse graph from a starting observation.
Inputs: start_id (required), max_depth, relation_filter. Returns: object
erinys_updateErinys UpdateB
Update an existing observation. Only provided fields are changed.
Inputs: id (required), type, scope, title, +3 more. Returns: object
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 29 tools
Several tools have overlapping memory-access purposes: erinys_recall vs erinys_context vs erinys_search, erinys_save vs erinys_batch_save, erinys_delete vs erinys_forget, and erinys_session_summary vs erinys_session_end. The descriptions clarify nuances, but an agent may still hesitate between near-duplicate operations.
All tools consistently use snake_case with the erinys_ prefix. However, the naming mixes verb-only forms (recall, export, link, prune), noun forms (context, stats, timeline, lineage), and compound forms (batch_save, conflict_check), so the pattern is mostly but not fully predictable.
With 29 tools, the surface is heavy for a memory server and exceeds the typical well-scoped range. Some operations could likely be consolidated or made optional groups, especially the near-duplicate save/read/delete/session variants.
The server covers observation CRUD, search, sessions, graph linking/traversal, temporal/provenance operations, export, backup, stats, and advanced maintenance such as dreaming and forgetting. Minor gaps exist, such as backup without restore/import and link creation without an explicit unlink operation.
Maintenance
Related MCP Connectors
Cloud-hosted MCP server for durable AI memory
Cloudflare Workers MCP server: agent-memory
Correction-first memory for AI agents. Run it locally or connect any MCP agent to the hosted server.
Persistent memory, knowledge graph, task management and more for AI agents via MCP.
Related MCP Servers
AlicenseNot gradedqualityBmaintenanceMCP server providing cognitive memory tools (remember, recall, think, etc.) for AI agents, enabling forgetting, consolidation, and contradiction detection.179Apache 2.0- AlicenseNot gradedqualityDmaintenanceAn MCP server that provides AI agents with persistent, multi-layered memory inspired by the human brain, including consolidation, self-reflection, and generative replay.1MIT
- AlicenseNot gradedqualityDmaintenanceAn MCP server that gives AI agents persistent, forgetting memory with layered decay, semantic search via token overlap, and zero external dependencies.MIT
- AlicenseAqualityBmaintenanceAn MCP server providing long-term memory for AI agents with forgetting curves, consolidation, and graph-based retrieval.1014 npmMIT