Self-custodied, cross-tool memory that ties every fact to your repo. Memory whose source no longer checks out is blocked before it reaches the model - and what passes is labeled for exactly what it is. One command wires every agent you use.
$ npm install -g memwarden && memwarden up
A stored fact silently goes stale - it points at code you've since changed or deleted - and the agent injects it with full confidence anyway. OWASP added Memory Poisoning (ASI06) to its 2026 Agentic Top 10. Most memory still stores everything and trusts everything. A memory that points at code you deleted is worse than none. memwarden flips the default: memory is untrusted until its source still checks out.
Memory is firewalled before it reaches a model. Stale memory is never injected. memwarden doctor runs the same check as a red/yellow/green audit against any existing store. Point it at your own memory and watch it light up yellow.
Your second brain shouldn't depend on a vendor's roadmap. memwarden is local-first, tamper-evident, and portable: one export produces a Brain Bundle you can move between machines or agents. Zero cloud - the data lives at ~/.memwarden and nowhere else.
Memory whose provenance stopped checking out is dropped before it reaches the model; what passes is labeled verified / sourced / unsourced, never laundered into one pile. The unique lever - possible only for a coding tool because the repo is ground truth - is tying memory validity to source-file content hashes.
Every memory is classified against the live repo before it can reach a model.
memory_resume, the recall prompt, the Claude Code SessionStart hook, and the proxy
all run with the firewall on. It blocks stale before injecting. It does not
block unsourced - unsourced means unverified, not dangerous, so
it stays available for explicit lookups. What passes is firewall-passing, trust-labeled memory - never laundered into one "verified" pile.
A captured source-file hash still matches the file on disk - code-backed and current. This is the only label reserved for content-hash-confirmed memory. Auto-injected.
Has a source (a command, or files present but not hashable) but no content hash to re-check. Allowed, labeled, not content-verified.
A referenced file was deleted, or its content changed since capture. Never injected into the model.
No evidence trail. Kept for explicit lookups, labeled so the agent knows it is unverified - never silently promoted.
Two policies: balanced (default) blocks stale and keeps the rest - sourced and unsourced - each labeled; verified-only raises the floor so only hash-verified memory is ever auto-injected. memwarden why <id> explains one memory; doctor --fix-stale clears the stale inbox. No LLM, no fuzzy black box.
$ memwarden doctor . VERIFIED: 8 memories (code-backed, current) SOURCED: 3 memories (sourced, not content-verified) STALE: 2 memories reference files that changed/deleted UNSOURCED: 1 memory has no evidence [stale] Edit (obs_…) - references files that no longer match (changed: src/legacy.ts)
memwarden is the one process that sees every agent's sessions, so it can do
what no per-tool memory can: when any agent resolves an error, it captures
{error signature → root cause + fix} with
provenance file-hashes. When any agent later hits a matching error, the fix is surfaced
automatically — but only if its files still hash-match. A stale fix is never surfaced.
# Codex solved a failing test yesterday. Today Claude Code hits the same failure: Déjà Fix (memwarden): a prior session resolved this error and the fix is verified current against your working tree. solved by: codex · root cause: clock skew · fix: mock NTP in conftest # scriptable too - pipe a failing command's output straight in: npm test 2>&1 | npx memwarden dejafix lookup
Cross-agent (a fix learned in Codex helps Claude Code), project-scoped
(a fix never leaks across repos), and safe by construction - it reuses Verified Recall, so
file drift or deletion auto-suppresses it. Only verified current fixes auto-inject.
A coding agent has something general-purpose memory doesn't: the repository on disk is the source of truth. When memwarden captures a code-backed memory it records a SHA-256 content hash for each referenced file. On recall it re-hashes the live file and compares. If the file moved or vanished, the memory is provably stale - not by heuristic, by hash.
Every write lands in an append-only SHA-256 hash-chained oplog. memory_verify recomputes the chain, so an edit or reorder of any past entry breaks it. Tamper-evident, not tamper-proof: no signing, and the chain does not detect tail-truncation.
forget <id> returns an offline-checkable delete receipt. forget --erase nulls payloads and re-chains; compact reclaims bytes so content leaves the file. A post-erase residual scan refuses to claim contentErased while a copy survives.
The brain lives at ~/.memwarden and nowhere else. On-device embeddings, two runtime dependencies, no cloud, no vendor in the loop. Once it is up you never touch it - self-healing on crash and at login.
memwarden up starts a self-healing daemon (one global brain at
~/.memwarden), installs the on-device embedding runtime so recall is semantic from
day one, and wires hooks + MCP into every installed tool - in that tool's own config schema,
without clobbering anything you already have. No keys to paste, no cloud, no config files to
hand-edit.
$ memwarden up daemon ✓ http://localhost:3111 brain: ~/.memwarden semantic ✓ local embeddings ready (all-MiniLM-L6-v2 - on-device) ✓ Claude Code MCP + hooks - auto inject + auto capture ✓ Codex MCP + hooks (run /hooks once to trust) ✓ Cursor MCP + hooks ✓ Gemini CLI hooks ✓ Kiro MCP + hooks (per custom agent) ✓ OpenCode MCP + plugin Done. Every agent on this machine now shares one verified brain.
Then prove it's flowing, per tool - detected (installed), configured (MCP + hooks on disk), live (a hook from that host actually reached the daemon). Wired-but-never-live is the failure every other memory layer hides; here it's a column in memwarden status.
Cross-tool reach is table stakes - the trust layer is the point. Still, the mechanics matter, so here they are honestly, with no "native hooks everywhere" hand-waving. There are three ways memory reaches a tool, and up wires whichever each supports.
Claude Code, Codex, Cursor, Gemini CLI, Kiro and OpenCode get real hooks in their own config format: session-start injects this project's firewall-passing memory before you type; capture happens as tools run. The agent cannot forget - there's no instruction to follow.
mechanicalPoint a tool's model base URL at the memwarden proxy on :3141 and every turn
is recalled and captured with no agent cooperation. On a secured install the tool's API key
is your memwarden secret, so nothing else can spend your upstream key. Works where you
control the endpoint: Ollama, LM Studio, custom OpenAI base URLs.
Tools without a hook surface (Antigravity, OpenClaw) still get the MCP server - ask the
agent to call memory_resume - plus an AGENTS.md instruction as a soft fallback.
Explicitly the fallback, not the mechanism.
| Tool | Capture / recall | Explicit recall |
|---|---|---|
| Claude Code | automatic (hooks) | /mcp__memwarden__recall |
| Cursor | automatic (hooks) | call memory_resume |
| Gemini CLI | automatic (hooks) | call memory_resume |
| Codex | automatic (after /hooks) | call memory_resume |
| Kiro | best-effort (per agent) | call memory_resume |
| OpenCode | best-effort (plugin) | call memory_resume |
| Antigravity · OpenClaw | manual (MCP only) | call memory_resume |
| Ollama · LM Studio · any OpenAI URL | automatic (proxy) | n/a - automatic |
Capture compresses on-device, redacts, and hashes into the store down the left; recall scopes, searches, classifies, and frames firewall-passing memory back up the right. Scoping is by canonical path, so recall never silently misses.
The launch claim is a memory firewall, so the first number is the firewall
itself - not retrieval. A deterministic corpus of 250 memories (file-backed, command-sourced,
and unsourced) across 5 projects with 50 controlled staleness events, 5 poisoned-handoff traps,
and 5 delimiter forgeries. Reproduce with npm run eval (CI-gated at 100% on all
eight gates).
| Firewall eval | Result |
|---|---|
| Stale retrievable - each stale memory first proven retrievable unfiltered (so "blocked" measures work, not absence) | 100% |
Stale refusal - known-stale memories blocked under safe_only | 100% |
| Fresh retention - verified-current memories not wrongly blocked | 100% |
| Project isolation - out-of-project results under scoping | 0 leaks |
| Label accuracy - labels match ground truth across all three classes | 100% |
Handoff traps - hostile prompts beside fresh file hashes never laundered to verified | 0 laundered |
| Verified-only policy - sourced/unsourced/handoff refused; verified still flows | 100% |
| Injection containment - forged delimiters can't escape the untrusted-data block on any surface | 100% |
Retrieval next. Real on-device model
(all-MiniLM-L6-v2), 30 labelled memories buried in 2,000 plausible distractors, 14 paraphrased
queries, and the compressed index running with no exact rescoring. Reproduce with
npm run benchmark.
| Retrieval - gold answer in top-k | R@1 | R@5 | R@10 |
|---|---|---|---|
| Full-precision vectors | 57% | 79% | 86% |
| TurboQuant (4-bit, no rescore) | 57% | 79% | 86% |
| Keyword search (lexical baseline) | 7% | 57% | 57% |
And the engine: an optional native Rust backend built on
turbovec
(Google's TurboQuant algorithm - real IdMapIndex, O(1) deletion, allowlist filtering
inside the SIMD kernel). Measured at 10,000 × 384-dim vectors.
| Vector backend | recall@10 vs FP32 | search p50 / p95 | bytes / vector |
|---|---|---|---|
| typescript/full (baseline) | 100% | 14.96 / 16.21 ms | 1536 |
| typescript/turboquant-4bit | 100% | 18.90 / 19.53 ms | 260 |
| turbovec/native-4bit | 100% | 0.15 / 0.20 ms | 196 |
~125× faster search, zero recall drop. Honest defaults: the native backend is
quietly selected when its prebuilt binary loads (pin with MEMWARDEN_VECTOR_BACKEND),
and memwarden status always names the backend actually serving - a native backend
that failed to load reports its TypeScript fallback, never a silent claim. 602 tests across the
Verified Recall suite, Déjà Fix, HTTP security, per-host hook dialects, and a cross-tool
reliability harness.
| Tool | What it does |
|---|---|
memory_resume | Verified recall of this project across all past sessions and tools |
memory_search | Hybrid semantic + keyword search (unfiltered, for explicit lookups) |
memory_remember | Save a memory explicitly |
memory_verify | Confirm the oplog hash chain is intact (tamper-evident; not signed) |
memory_stats | Live counts, compression ratio, token reduction, latency |
Plus the recall MCP prompt, surfaced in Claude Code as
/mcp__memwarden__recall <query>. On other MCP hosts, ask the agent to call
memory_resume; where hooks are automatic, recall arrives on its own at session start.
Audit the memory you already have - zero install, read-only - then wire every tool with one command.
$ npx memwarden audit ~/.claude-mem/claude-mem.db --root ~/code/my-repo