Memory

Semantic vector memory that persists across channels and conversations. Encrypted at rest, searchable by meaning, and always improving.

7 min read

Memory

Chvor’s memory system gives your AI persistent, semantic recall. Every conversation, fact, and preference is embedded as a vector and stored locally in SQLite. When the AI processes a new message, it retrieves the most relevant memories — not by keyword match, but by meaning. Memory is shared across all channels, encrypted at rest, and gets better the more you use it.

Memory Simulation
waiting

Try a query:

prefUser prefers TypeScript over JavaScript2 weeks ago
ctxProject uses pnpm, not npm1 week ago
ruleAlways cite sources with links5 days ago
ctxUser is building a React + Hono app3 days ago
prefPrefers concise answers with code examples2 days ago
ctxWorking on MCP tool integration1 day ago
ruleUse Tailwind CSS for all stylingtoday

How it works

Vector embeddings

When a conversation turn is worth remembering, Chvor converts the text into a high-dimensional vector using an embedding model. These vectors capture semantic meaning — similar concepts end up close together in vector space, even if they use completely different words.

"Deploy the app to production"  →  [0.023, -0.156, 0.891, ...]
"Ship the code to prod"        →  [0.019, -0.148, 0.887, ...]  ← very similar
"What's the weather today?"    →  [0.742, 0.331, -0.205, ...]  ← very different

Storage

Vectors are stored in SQLite alongside their metadata:

-- Simplified schema
CREATE TABLE memories (
  id TEXT PRIMARY KEY,
  content TEXT NOT NULL,          -- The original text
  embedding BLOB NOT NULL,        -- Vector (float32 array)
  channel_type TEXT,              -- Where this memory was created
  channel_id TEXT,
  user_id TEXT,
  created_at INTEGER,
  accessed_at INTEGER,            -- Last time this memory was recalled
  access_count INTEGER DEFAULT 0  -- How often this memory is recalled
);

Retrieval

When a new message arrives, Chvor:

  1. Embeds the incoming message into a vector
  2. Performs a cosine similarity search against all stored memories
  3. Returns the top-k most relevant results above a relevance threshold
  4. Injects these memories into the prompt as additional context
// Conceptual retrieval flow
const queryVector = await embed(userMessage);
const memories = await vectorSearch(queryVector, {
  topK: 10,
  threshold: 0.7,    // Minimum similarity score
});

// memories = [
//   { content: "User prefers TypeScript over JavaScript", score: 0.94 },
//   { content: "Last deployment was to AWS us-east-1", score: 0.87 },
//   { content: "Project uses pnpm, not npm", score: 0.82 },
// ]

Conversation memory

Chvor automatically manages what gets stored in memory. Not every message is worth remembering — the system applies heuristics to decide what to keep:

  • Facts and preferences stated by the user are always stored
  • Important outcomes (successful deployments, resolved bugs) are stored
  • Routine exchanges (“thanks”, “ok”, “got it”) are not stored
  • Sensitive content flagged by the user is excluded

You can also explicitly tell the AI to remember or forget something:

User: Remember that our staging server is at staging.example.com
AI: Got it — I'll remember that your staging server is at staging.example.com.

User: Forget what I told you about the staging server
AI: Done — I've removed that memory.

Memory across channels

Memory is not siloed by channel. A fact learned on Telegram is available when chatting on Discord or Web Chat:

[Telegram]
User: We just migrated the database to PostgreSQL 16
AI: Noted — I'll remember the database migration to PostgreSQL 16.

[Discord, 3 days later]
User: Can you write a query to check the migration status?
AI: Sure. Since you're running PostgreSQL 16, I'll use the
    pg_stat_progress_create_index view which was improved in v16...

This cross-channel recall is what makes Chvor feel like a single, consistent assistant rather than separate bots on different platforms.


Directives as persistent memory

Directives are a special type of memory — persistent rules that are always injected into the prompt, regardless of relevance scoring. See the Directives page for full details.

The difference between regular memory and directives:

Regular MemoryDirectives
InjectionOnly when semantically relevantAlways injected
ScoringRanked by cosine similarityNo scoring, always included
PurposeFacts, context, preferencesRules, constraints, behavior
PersistenceCan be forgottenPersist until explicitly removed

Encryption

All memory data is encrypted at rest using AES-256-GCM. This includes the original text content and the vector embeddings.

How encryption works

  1. On first run, Chvor generates a 256-bit encryption key
  2. The key is derived from a passphrase you set (or auto-generated and stored in your OS keychain)
  3. Every memory record is encrypted before being written to SQLite
  4. Decryption happens in-memory during retrieval — plaintext never touches disk
# config.yaml
memory:
  encryption:
    enabled: true              # Enabled by default
    # key_source: keychain     # Default: uses OS keychain
    # key_source: env          # Alternative: use CHVOR_MEMORY_KEY env var
    # key_source: file         # Alternative: read from a key file
    # key_file: ~/.chvor/memory.key

What is encrypted

DataEncrypted
Memory text contentYes
Vector embeddingsYes
Memory metadata (timestamps, channel info)Yes
SQLite database fileNo (individual records are encrypted)

AES-256-GCM provides both confidentiality and integrity — if the database file is tampered with, decryption will fail and Chvor will alert you.

Credential storage

Chvor also uses the memory encryption system to store sensitive credentials like API keys and tokens. When you enter a credential through the UI, it is encrypted with the same AES-256-GCM key before being stored:

User enters API key → AES-256-GCM encrypt → SQLite → disk

Chvor needs API key → SQLite read → AES-256-GCM decrypt → use in-memory → discard

Credentials are never logged, never included in LLM prompts, and never sent to any external service.


Embedding configuration

Chvor supports multiple embedding providers. Configure your preferred model in config.yaml:

memory:
  embedding:
    provider: openai             # openai | ollama | local
    model: text-embedding-3-small
    dimensions: 1536

  # Or use a local model via Ollama
  # embedding:
  #   provider: ollama
  #   model: nomic-embed-text
  #   url: http://localhost:11434
  #   dimensions: 768

  retrieval:
    top_k: 10                    # Max memories to retrieve per query
    threshold: 0.7               # Minimum similarity score (0-1)
    rerank: true                 # Re-rank results with cross-encoder

Local embeddings

For fully offline operation, you can run embeddings locally through Ollama:

# Install and start Ollama
ollama pull nomic-embed-text

# Update config.yaml
memory:
  embedding:
    provider: ollama
    model: nomic-embed-text
    url: http://localhost:11434
    dimensions: 768

With local embeddings, no data ever leaves your machine — not even for generating vectors.


The more you use it, the better it gets

Chvor’s memory system improves over time through three mechanisms:

1. Growing knowledge base

Every meaningful interaction adds to the vector store. After a week of use, your AI has context about your projects, preferences, workflows, and team. After a month, it is deeply personalized.

2. Access-weighted relevance

Memories that are frequently recalled get a boost in relevance scoring. If you keep asking about “the staging server,” that memory naturally rises to the top of retrieval results.

3. Implicit learning

The AI learns your patterns without explicit instruction. If you always ask for TypeScript examples, the AI starts defaulting to TypeScript. If you prefer concise answers, the memory of past interactions shapes future responses — even without a directive.

Week 1:  "Can you write this in TypeScript?"
Week 2:  "Use TypeScript please"
Week 3:  [AI defaults to TypeScript without being asked]

Permanent learning

Unlike chat-based AI that forgets after every session, Chvor learns permanently. Its graph-based memory strengthens important facts with spaced repetition, fades what’s irrelevant through exponential decay, and consolidates related fragments into coherent narratives during idle cycles. The more you use it, the more it understands — not just what you said, but what matters to you.


Managing memory

View stored memories

From the Web Chat UI, open the Memory panel to browse, search, and delete stored memories. You can also use the API:

# Search memories by semantic similarity
curl "http://localhost:3000/api/memory/search?q=database+migration" | jq

# Get memory statistics
curl "http://localhost:3000/api/memory/stats" | jq
# {
#   "total_memories": 1247,
#   "oldest": "2025-01-15T...",
#   "newest": "2025-03-24T...",
#   "storage_size_mb": 42.3,
#   "channels": { "web": 834, "telegram": 289, "discord": 124 }
# }

# Delete a specific memory
curl -X DELETE "http://localhost:3000/api/memory/mem_abc123"

Backup and export

Memory is stored in the SQLite database at ~/.chvor/db/chvor.db. Back it up like any other file:

cp ~/.chvor/db/chvor.db ~/.chvor/backups/chvor-$(date +%Y%m%d).db

Note that backups contain encrypted data. You will need the same encryption key to restore and read the memories.


Next steps

  • Directives — set persistent rules that are always included in context
  • Channels — see how memory is shared across platforms
  • Schedules — automate tasks that build on accumulated memory