# 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).
