Blogagent memory

Why not just files? Memory for more than one agent

A folder of markdown is a good memory for one agent. Here is what changes when several agents, run by several people, share it, and what Willder costs you instead.

Tri Nguyen7 min read2 figures14 references

Sections

The first question on the Willder FAQ is some version of: why not just use files?

It is a fair question, and for a lot of setups the honest answer is that files are fine. This repository has a CLAUDE.md and an AGENTS.md at its root, and I am keeping them. For one agent working in one repo, a markdown file it reads at the start of each session is hard to beat. It lives in Git, you can read it, you can diff it, and it costs nothing.

Things change when the memory is shared: a second agent, a second person, or one piece of information that one agent should see and another should not. Below is what I ran into, one problem at a time, and what Willder does about each. At the end is what Willder costs you, because it is not free.

I.A file does not know who is reading it

A shared folder gives access to whatever can open it. Say your coding agent and a research agent that reads customer call notes share a directory. Both read everything in it. You can split it into subfolders, but nothing stops either agent from opening the other one. The file system checks the user the process runs as, not which agent is asking or for what task.

In Willder, scopes form a tree, like folders. The difference is that each agent holds its own token, and the token names one scope, one permission (read, write or admin) and an expiry. A token on the Coding scope reads Coding and everything under it, and never a sibling or the parent. Minting one is a single call from a signed-in session [1]:

Text
POST /api/access/capabilities
{ "agentId": "research_agent", "scopePath": "<path from Access>", "permission": "read", "ttlHours": 24 }

Ask the same question with a token on a different scope and the answer is No facts found. That is not the model being polite. The check is a database query that runs before any search, and in a guarded read the out-of-scope facts never reach the agent [2]. The product demo below shows it with demo data: one token per agent, a fact saved by one agent and read by another, and the same question on another scope's token answered with nothing.

Video. Silent, with demo data. Each agent gets its own token in Access. Claude Code saves “Launch moves to Friday.”; Codex reads it on its next call, with the agent that wrote it; a Codex session on a Marketing token asks the same question and gets “No facts found.”; Claude Code fixes the fact once to Monday and Codex's next read says Monday; the audit timeline shows who wrote the fact and who read it.

II.You cannot take a file back

Once an agent can read a folder, there is no clean way to say "not anymore" to that one agent. You move files, change permissions for everyone, or rotate a shared secret that every other agent also uses.

A Willder token is revoked on its own. On the hosted endpoint, its next call is refused with HTTP 401 Invalid or unknown capability token., and the audit log gets a capability.refused row for that agent [3]. Nothing waits for the token to expire. The other agents keep working.

III.A file does not record who read it

When something goes wrong with shared notes, the first question is usually "which agent saw this, and when?" A folder of markdown has no answer.

Every guarded memory read and write in Willder goes through one function, check() in services/access/check.ts [5]. It is default deny, it is a database query over grants, token scope, expiry and revocation, and no model takes part in it. It writes one append-only audit row per decision, allow or deny: the actor, the action, the target scope, the decision and the reason.

IV.Two agents, one file, one lost edit

This one is the easiest to miss. Agent A reads a notes file. Agent B reads the same file, changes a line and writes it back. A, still working from what it read earlier, writes its version. B's change is gone, and nobody is told.

In Willder each fact keeps its versions, like commits. The server records what each agent was served, so a write made from an outdated read is refused with stale_version:, naming the current version, and nothing is stored [6]. The agent reads what changed and writes again. If two agents write the same fact within one second, the version stored first stays current, the other is kept as superseded, and its writer is told on its next call [7].

To be clear about the limit: Willder detects and reports the conflict. It does not merge the two versions [8]. Someone, a person or an agent, still decides.

V.Deleting is more than deleting a file

With files, deleting the file feels final. But if you also built a search index over those files, the deleted text is still in the index until you rebuild it. If an agent pasted it into another note, it is there too.

In Willder, deleting a fact in the app removes it from search, from the knowledge graph, and from every agent's task-start brief, and each of those paths has a test. On 29 October 2026, on production, a measure deleted a fact and checked all seven read paths an agent's token can use: all seven stopped presenting it within the 30-second limit, the slowest after 2.3 seconds (Figure 1).

Horizontal bar chart of how many milliseconds after a delete each of the seven agent read paths stopped presenting the deleted fact: memory_query 0, memory_entity 557, memory_version 900, memory_changes 1,049, change notice 1,274, GET /api/changes 1,870, task-start brief 2,311.Horizontal bar chart of how many milliseconds after a delete each of the seven agent read paths stopped presenting the deleted fact: memory_query 0, memory_entity 557, memory_version 900, memory_changes 1,049, change notice 1,274, GET /api/changes 1,870, task-start brief 2,311.Horizontal bar chart of how many milliseconds after a delete each of the seven agent read paths stopped presenting the deleted fact: memory_query 0, memory_entity 557, memory_version 900, memory_changes 1,049, change notice 1,274, GET /api/changes 1,870, task-start brief 2,311.Horizontal bar chart of how many milliseconds after a delete each of the seven agent read paths stopped presenting the deleted fact: memory_query 0, memory_entity 557, memory_version 900, memory_changes 1,049, change notice 1,274, GET /api/changes 1,870, task-start brief 2,311.
Figure 1. Time from a delete until each of the seven agent read paths stopped presenting the deleted fact, on production on 29 October 2026, one agent per path; the signed-in web routes were not measured. The limit was 30 seconds; the slowest path took 2.3.

What is not covered yet: copies in backups, uploaded source files, and answers already delivered to an agent. MCP also has no delete tool, so deleting goes through the app [8].

VI.Reading the whole file stops working

An agent can read a 2 KB notes file in full on every session. It cannot do that with a year of decisions across ten projects. At some point you need search, and once you have search you have an index to keep in step with the files, which takes you back to the deletion problem above.

Willder searches two stores: Postgres with pgvector (384-dimension BGE-small embeddings behind an IVFFlat cosine index) for long text, and a knowledge graph (Graphiti on Neo4j) for facts and the links between them. Retrieval is hybrid: every query asks all the stores its account tier allows, in parallel, and fuses the results [9] [10]. The stack is on the architecture page [11]. The leak tests check that scoping still holds with thousands of facts and concurrent callers [12]. Retrieval can still miss things. I would rather say that than promise recall I have not measured.

VII.What Willder costs you

Files cost nothing. Willder does not, and here is the honest list.

Infrastructure. Hosted, you do not run it. Self-hosted, you run Postgres with pgvector, Neo4j and a Python memory service, through Docker Compose. That needs Node, Docker and a few GB of disk [13]. The memory service loads a local embedding model of about 130 MB. Extracting facts into the graph on a write needs a model endpoint (a local one works); without it, a write is still stored and readable, and only the graph extraction is skipped.

Latency. Reading a local file takes almost no time. On production on 29 October 2026, a fact one agent wrote was readable by another at a median of 538 ms (p95 881 ms) with one writer at a time, and 688 ms (p95 1,292 ms) with six writers at once (Figure 2). The task-start brief added a p95 of 31.8 ms to an agent's first call on a demo scope, and 88.0 ms on a scope holding a team's real work. Every check is a SQL query plus an audit write.

Grouped bar chart of write-to-read latency on production, median and p95, by load: one writer at a time, 538 and 881 ms; six writers at once, 688 and 1,292 ms; every write retried with one writer revoked halfway, 834 and 1,588 ms.Grouped bar chart of write-to-read latency on production, median and p95, by load: one writer at a time, 538 and 881 ms; six writers at once, 688 and 1,292 ms; every write retried with one writer revoked halfway, 834 and 1,588 ms.Grouped bar chart of write-to-read latency on production, median and p95, by load: one writer at a time, 538 and 881 ms; six writers at once, 688 and 1,292 ms; every write retried with one writer revoked halfway, 834 and 1,588 ms.Grouped bar chart of write-to-read latency on production, median and p95, by load: one writer at a time, 538 and 881 ms; six writers at once, 688 and 1,292 ms; every write retried with one writer revoked halfway, 834 and 1,588 ms.
Figure 2. Write-to-read latency on production on 29 October 2026: from sending a write to another agent reading it, median and p95, by load. 60, 60 and 66 samples.

Moving parts. Two stores mean two ingest paths and two ways to fail. The memory service is Python and the app is Node, so there are two services to deploy.

Trust in one function. The access check runs inside the application. Anyone holding the database URL, the Neo4j password or the memory service key reaches memory without it. The README lists every such credential [14]. With files, the equivalent is anyone with shell access, so this is not new, but it is worth knowing.

VIII.When I would still use files

One agent, one person, nothing in it that any agent should not see: use a file. It is simpler and faster, and you lose nothing.

Willder is for the point where you are about to write the second AGENTS.md, then a folder per agent, then a script to sync them, then a note to yourself about which agent saw which file. That layer is what Willder is for.

References

  1. [1]Quickstart. Willder documentation. https://willder.ai/docs/quickstart. Accessed 29 Oct 2026. ↩
  2. [2]SECURITY.md, The access model. willderai/willder-core on GitHub. https://github.com/willderai/willder-core/blob/72d62a85d2e2231733913c64f71c2c2efaed1211/SECURITY.md#the-access-model. Accessed 29 Oct 2026. ↩
  3. [3]SECURITY.md, What is audited. willderai/willder-core on GitHub. https://github.com/willderai/willder-core/blob/72d62a85d2e2231733913c64f71c2c2efaed1211/SECURITY.md#what-is-audited. Accessed 29 Oct 2026. ↩
  4. [4]SECURITY.md, What a report cannot change. willderai/willder-core on GitHub. https://github.com/willderai/willder-core/blob/72d62a85d2e2231733913c64f71c2c2efaed1211/SECURITY.md#what-a-report-cannot-change. Accessed 29 Oct 2026. ↩
  5. [5]services/access/check.ts, the access check. willderai/willder-core on GitHub. https://github.com/willderai/willder-core/blob/72d62a85d2e2231733913c64f71c2c2efaed1211/services/access/check.ts. Accessed 29 Oct 2026. ↩
  6. [6]mcp/README.md, Writing without baseVersion. willderai/willder-core on GitHub. https://github.com/willderai/willder-core/blob/72d62a85d2e2231733913c64f71c2c2efaed1211/mcp/README.md#writing-without-baseversion. Accessed 29 Oct 2026. ↩
  7. [7]services/memory/conflict.ts, the one-second contention window. willderai/willder-core on GitHub. https://github.com/willderai/willder-core/blob/72d62a85d2e2231733913c64f71c2c2efaed1211/services/memory/conflict.ts. Accessed 29 Oct 2026. ↩
  8. [8]README.md, Limits. willderai/willder-core on GitHub. https://github.com/willderai/willder-core/blob/72d62a85d2e2231733913c64f71c2c2efaed1211/README.md#limits. Accessed 29 Oct 2026. ↩
  9. [9]services/memory/hybrid-store.ts, hybrid retrieval. willderai/willder-core on GitHub. https://github.com/willderai/willder-core/blob/72d62a85d2e2231733913c64f71c2c2efaed1211/services/memory/hybrid-store.ts. Accessed 29 Oct 2026. ↩
  10. [10]services/memory/routing-policy.ts, readStores: the same stores for every query shape. willderai/willder-core on GitHub. https://github.com/willderai/willder-core/blob/72d62a85d2e2231733913c64f71c2c2efaed1211/services/memory/routing-policy.ts. Accessed 29 Oct 2026. ↩
  11. [11]Architecture: the stack and design decisions. Willder. https://willder.ai/architecture. Accessed 29 Oct 2026. ↩
  12. [12]README.md, Prove it does not leak. willderai/willder-core on GitHub. https://github.com/willderai/willder-core/blob/72d62a85d2e2231733913c64f71c2c2efaed1211/README.md#prove-it-does-not-leak. Accessed 29 Oct 2026. ↩
  13. [13]README.md, Run it on your machine. willderai/willder-core on GitHub. https://github.com/willderai/willder-core/blob/72d62a85d2e2231733913c64f71c2c2efaed1211/README.md#run-it-on-your-machine. Accessed 29 Oct 2026. ↩
  14. [14]README.md, Credentials that bypass the authorizer. willderai/willder-core on GitHub. https://github.com/willderai/willder-core/blob/72d62a85d2e2231733913c64f71c2c2efaed1211/README.md#credentials-that-bypass-the-authorizer. Accessed 29 Oct 2026. ↩