Overview
EchoVault is a local-first memory system that uses a hybrid storage architecture combining human-readable Markdown files with a powerful SQLite database for indexing and search.Architecture
The system consists of three main components working together:1. Markdown Vault
All memories are stored as human-readable Markdown files organized by project and date:- One file per session per project
- Valid Markdown with YAML frontmatter
- Fully compatible with Obsidian and other Markdown editors
- Located at
~/.memory/vault/
The vault uses a session-based file structure where each day’s memories for a project are grouped into a single file like
2026-03-03-session.md.2. SQLite Database
The database (index.db) provides fast search and retrieval using two specialized technologies:
FTS5 (Full-Text Search)
- Built-in SQLite extension for keyword search
- Uses Porter stemming and Unicode normalization
- Provides BM25 ranking scores
- Works immediately with zero configuration
- Vector similarity search for semantic queries
- Dynamically sized based on embedding provider
- Deferred creation until first embedding is generated
- Supports dimension mismatch detection
From
~/workspace/source/src/memory/db.py:120-133, the vec table is created dynamically:3. Configuration System
The config file (config.yaml) controls:
- Embedding provider:
ollamaoropenai - Enrichment: Optional LLM enhancement of memories
- Context behavior: When to use semantic vs. keyword search
Memory Save Pipeline
When you save a memory, EchoVault executes a complete processing pipeline:Redaction
All text fields pass through the 3-layer redaction system to remove secrets before anything hits disk.
Deduplication Check
FTS search looks for similar existing memories in the same project. If a match is found (normalized score greater than or equal to 0.7 and title match), the existing memory is updated instead of creating a duplicate.
Markdown File Write
Memory is appended to the session file in the vault directory with proper YAML frontmatter.
Database Insert
Memory metadata is inserted into the
memories table. Details (if present) are stored separately in the memory_details table.Embedding Generation
The embedding provider generates a vector from the memory’s text. This step is non-fatal - if it fails, the memory is still saved without a vector.
Compact Pointers
EchoVault uses a “pointer” pattern to minimize token usage in context injection:- Search results return ~50 tokens: ID, title, category, tags, creation date
- Full details (potentially thousands of tokens) are only fetched on demand
- Agents can scan many memories efficiently, then request full details only for relevant ones
Database Schema
The SQLite database has a carefully designed schema:Core Tables
memories - Main memory metadata- Stores: id, title, what, why, impact, tags, category, project, source, related_files, file_path, section_anchor, created_at, updated_at, updated_count
- Auto-incrementing rowid used as primary key for joins
- Separated to keep main table compact
- Only loaded when explicitly requested
- Stores embedding dimension and other config
Virtual Tables
memories_fts - FTS5 full-text index- Automatically synced with memories table via triggers
- Tokenizes with Porter stemming and Unicode61
- Content-less table (references memories via rowid)
- Created dynamically when first embedding is generated
- Dimension stored in meta table for validation
From
~/workspace/source/src/memory/db.py:81-95, FTS triggers automatically keep the index in sync:Zero Idle Cost
EchoVault has no background processes:- No daemon running in the background
- No RAM overhead when not in use
- MCP server only runs when an agent starts it
- Database connection opened on-demand
Cross-Agent Memory Sharing
All agents (Claude Code, Cursor, Codex, OpenCode) share the same memory vault:- Memories saved by one agent are immediately searchable by others
- The
sourcefield tracks which agent created each memory - Filtering by source is optional - by default all memories are searchable
The memory home location is controlled by:
MEMORY_HOMEenvironment variable (highest priority)- Persistent config via
memory config set-home - Default:
~/.memory