Skip to content

ADR-007: Personal Memory — sqlite-vec + SQLCipher

Status: Accepted
Date: 2026-05-18
Deciders: Mohsen Seyedkazemi Ardebili


Context

The personal-memory layer is the foundation for the agent loop's recall capability and v2 RAG-augmented reasoning. Three trade-offs drive the decision: embedded vs. server-based vector store; encryption-at-rest scheme; and which embedding model to bundle. An unencrypted, vendor-controlled personal memory store is a privacy and security failure — the Microsoft Recall 2024 launch is the public example of this failure mode. YazSes' commitment is the opposite: user-controlled, encrypted at rest, fully local.

sqlite-vec is the canonical embedded vector store in 2026: pure C, no external dependencies, KNN and ANN, runs on every platform where SQLite runs. SQLCipher provides AES-256 encryption at rest with a user-derived key. Spike S-4 (2026-05-18) confirmed that sqlite-vec and SQLCipher coexist without issue when compiled with SQLITE_CORE and PRAGMA cache_size tuned appropriately.

Decision

The personal-memory store is SQLite + sqlite-vec + SQLCipher, with a bundled BGE-small-en embedding model (384-dimensional; all-MiniLM-L6-v2 as fallback) for query and commit embeddings.

Key implementation details:

  • Encrypted database at ~/.local/share/yazses/memory.db.
  • Encryption key: PBKDF2-derived (default 256k iterations) from a user passphrase + a machine-bound salt at ~/.local/share/yazses/.salt, protected by OS file ACLs.
  • Passphrase ceremony: first commit_to_memory invocation triggers yazses memory init, which prompts for a passphrase; the derived key is held in daemon RAM for the session lifetime.
  • Schema: a vec0 virtual table with columns for embedding (float[384]), transcript, source, EditorContext JSON blob, tags, creation timestamp, and TTL.
  • TTL enforcement: an in-process sweeper deletes expired records on a configurable cadence (default once per hour).
  • forget_last(minutes) is both a typed LLM tool and a CLI command (yazses memory forget --last 30m).
  • PRAGMA cache_size = -200000 (200 MB) is required for acceptable KNN query performance at 100k records. Without this setting SQLCipher re-decrypts embedding pages on every query; with it, P50 query latency is 4 ms vs 40 ms at 10k records.

Consequences

Positive: - Single-file encrypted storage on every platform SQLite supports. - Fully auditable via standard SQL tooling once unlocked — transparent to the user. - Open-source dependency chain: sqlite-vec (Apache-2.0), SQLCipher (BSD-3-Clause). - ANN index paths (IVF, DiskANN) are available for scale beyond the brute-force regime (~100k records). - The schema is open and documented; community plugins for personal memory analytics are straightforward.

Negative / trade-offs: - Passphrase loss equals data loss. There is no recovery path by design — any recovery mechanism is a privacy backdoor. - BGE-small adds approximately 80 MB to the install footprint. - SQLCipher is open-source but maintained by a commercial entity (Zetetic); sqleet exists as an alternative fallback if the licence situation evolves unfavourably.

Implementation

The PersonalMemory module is in yazses-memory/src/. SQLite FFI uses rusqlite with the sqlcipher feature. sqlite-vec is loaded as a runtime extension. The embedding model runs via ort (ONNX Runtime) in yazses-memory/src/embedder.rs. Memory CLI commands are wired through the IPC layer (see ADR-010).