Agent memory should be a file. We built it.
HN spent 94 comments agreeing agent memory should be a plain file. Uteke is that file with meaning-based search inside: one SQLite file, ~45ms recall, zero cloud.
Uteke is a semantic memory engine for AI agents. One Rust binary, one SQLite file, recall in about 45 milliseconds, zero cloud. We started building it because our agents kept forgetting things, and every memory system we tried demanded an API key, a database cluster, or an LLM subscription just to remember our name.
Last Sunday a post by Cal Paterson called "Agent memory as a file format" took off on Hacker News. It collected 190 points and 94 comments in two days, which for a topic as unglamorous as "remember things to a text file" tells you how much this problem hurts. We read it with the particular joy of people who recognized their own product thesis in someone else's words. Then we noticed the part where plain files stop working, and that part is where Uteke lives. So in this post: what the thread got right, what files alone can't do, and what we built to close the gap without giving up a single thing the thread asked for.
What the thread got right
Paterson's argument is that most agent memory systems are broken in one of three ways. Some lock you into a vendor's harness and mostly mine your conversations for facts about you. Some are absurdly heavy, and he names the shape: pgvector, a Neo4j graph database, and a separate LLM whose only job is deciding what deserves to be remembered. Others chase an idealized "rationalist" memory of graphs and distilled facts that strips away the context a model needs.
His fix: treat memory as data, not as a pipeline. A zip of markdown pages with YAML frontmatter, plus an optional SQLite file for semantic search. Agents are good at writing prose, so let them write memories as prose. Keep pages short enough to embed, about 8KB, and add more pages when you need more detail.
We agree with every line of that. Files are inspectable. You can cat a memory, diff two memories, put the whole directory in git, copy it to a new machine with scp, and read every byte with your own eyes. No vendor can deprecate your zip file. The 8KB cap is a feature too, since it forces memories to stay readable instead of rotting into a junk drawer.
That's the demand signal in those 94 comments. Developers want memory they can see and hold. Not a subscription. Not a graph database spinning up on their laptop.
Where plain files stop working
You store this memory on a Tuesday: "We agreed to deploy v2.1 to staging after the payment provider freeze ends." Three weeks later you ask your agent: "When are we shipping the next release?" Grep finds nothing. The words "ship" and "release" never appear in the file. The meaning does, but grep doesn't do meaning.
At five memories this is a curiosity. At five hundred, which is about two weeks of real agent work, it's the whole ballgame. Retrieval by meaning is the difference between memory and an archive, and you can't grep your way there. You need embeddings, which means you need an index, which is exactly the machinery the thread (rightly) distrusts when it arrives bundled with lock-in and a cloud bill.
So the question we built Uteke to answer: can you keep every property that makes file-based memory attractive, and still get search by meaning?
What Uteke is
Uteke stores memories in a single SQLite file on your machine. That file is yours: back it up, copy it, open it with the sqlite3 CLI and read the rows. The engine around it is one static Rust binary with no runtime dependencies. First run downloads a local embedding model, about 188MB, once. After that nothing leaves your machine.
Using it looks like this:
# Install (macOS, Linux, Windows)
curl -sSL codecora.dev/uteke/install | sh
# Store a memory
uteke remember "Deploy v2.1 to staging at 3pm"
# Search it back
uteke recall "when do we deploy?"
That third command is the one files can't do. Recall runs a hybrid search: full-text (SQLite FTS5) and vector similarity, fused with reciprocal rank fusion so you get keyword precision and semantic reach in one ranked list. There's a comparison table in our README where the competitors in this space are mostly one or the other. Uteke does both by default, with zero configuration.
Beyond the core loop, three things we kept building toward because multi-agent work needs them:
Rooms. Multiple agents share one memory store with author attribution. Your coding agent and your research assistant read the same memories and you can see who wrote what.
Time-travel queries. Ask what the memory store knew as of any past date. When an agent acts on stale facts, you can reconstruct exactly what it saw.
Decay. Memories fade on a policy you control instead of piling up forever, which is what happens to every folder of markdown notes by month three.
If you genuinely want the server, Docker mode is a one-liner. But the default is local-first, and it stays that way.
The numbers we publish
Two claims, each with its conditions attached:
| Claim | Condition |
|---|---|
| 98.2% recall_any@5 | LongMemEval-S, 500 questions, zero-config fusion default, public harness in the repo |
| 42ms P50 / 50ms P95 recall | 10K memories, local, and latency stays flat from 100 to 10K memories (HNSW, O(log N)) |
The full methodology and benchmark tables live in the repo, and the harness is public, so you can run it against your own stack and argue with us. Uteke is Apache-2.0, and as of this writing the repo sits at 237 stars, built almost entirely from developers who found it by way of exactly these kinds of memory debates.
Try it
curl -sSL codecora.dev/uteke/install | sh
uteke remember "the CI pipeline breaks if you skip the migration step"
uteke recall "why does CI fail sometimes?"
If you read Paterson's post and nodded along, those three commands are the whole evaluation. Your agent's memory stays a file you own. The difference is the file can answer questions.
Repository and docs: github.com/codecoradev/uteke. And if your memory store grows until the embeddings themselves get heavy, that's a storage problem rather than a retrieval one. We built a tool for that too: vecq compresses vectors about 4.8x into one dependency-free file. We wrote about that side of the stack separately.