supermemory research
Every agent gets its own database: one SQLite file with vector search, full-text, a fact graph, and time decay built into the index. Writes land in 0.6 ms. Idle agents cost nothing.
Agent memory is a weird workload. Millions of tenants, 95% of them idle at any moment, write-hot when active, small per tenant — and ranked by time as much as by similarity. Nothing prices that shape correctly: RAM-based vector databases charge for idleness, Postgres runs out of connections around 104 tenants, object-storage engines pay a ~900 ms cold tax on the long tail and disclaim write-heavy workloads in their own docs, and the memory pipelines put an LLM in the write path and bill per byte written.
So I'm building the database I think this workload deserves. Each namespace — a user, an agent, a project — is its own embedded SQLite database inside its own Cloudflare Durable Object: a SPANN vector index, BM25, metadata filters, and a typed fact graph in one file, one process, one transaction.
An insert is 2–3 row writes into a durable buffer, then it returns. A background flush every 30 seconds does the actual indexing — assigns vectors to clusters, packs postings, splits and merges. It's an LSM. Fresh writes are searchable immediately, exactly, before they're ever indexed.
Filters run before scoring, never after — a selective filter skips the ANN index entirely and scores exact, so filtered recall is 100% by construction. Everything else: scan compact codes across the probed clusters, rerank the survivors in full precision.
Every index entry carries its own timestamp. Decay is applied during the scan, so the candidate set itself is time-aware — not a rerank over whatever an oblivious index returned. Change the decay policy and the next query behaves as if the index was always built that way: zero reindexing. Reinforcing a memory is a 4-byte patch. And decay never deletes — forgetting is always explicit.
One database per agent. Isolation is physical, not a WHERE clause. Deleting a user means deleting a file. An idle agent is kilobytes on disk and zero compute.
Records, not rows. The API speaks memory: document | chunk | memory | page, upsert by your id, forget, forgetAfter TTL, reinforce. Replacing a record resets its decay clock — a rewritten memory is fresh.
A fact graph in the same file. Typed edges — updates, contradicts, merge, derives — next to the vectors, so "why does the agent believe this" is a query, not a join across two systems. No LLM in the write path: writes are deterministic sub-millisecond SQL.
Designed around the billing quantum. Cloudflare bills SQLite by rows read, so postings pack 32 entries per row: ~270 billed rows per query instead of ~25,000. Same idea as designing around the object-store GET — just a different quantum.
I red-teamed every claim on this page. Blended, this is a ~1.3–2× system against the best general substrate for this workload — not 10×. The ~10× is real on exactly two axes, and both are architecture, not tuning: write acknowledgment (turbopuffer publishes 165 ms p50 for their object-storage WAL and their docs disclaim chatty writes; a local SQLite insert is three orders quieter) and long-tail wake-up (no networked cold path to fall out of — their published cold p50 is 400–900 ms).
What I haven't done yet, so you don't have to find out later: my benchmarks are self-run at 10K–100K scale on one dataset — only the 10K number is end-to-end. There's no backpressure story if a tenant writes faster than the flush can drain. The cold-tiering economics are designed, not wired. And a single namespace tops out around 1M vectors — if you need one big shared index, use turbopuffer; it's excellent at that.
The full argument, including the number that kills the thesis →