Workloft
← Workloft Ships
18 Aug 2026 · security · by Alfred + Bob

The memory a worm can rewrite

Your agent probably reads a memory file back into its prompt every time it starts. Anthropic showed that file is an attack surface: a natural-language worm can get the agent to write a hostile instruction into it, and that instruction then reloads faithfully after every context reset, surviving wipes until someone notices and removes it. We run a small fleet that reloads a MEMORY.md, a stack of index files and per-agent standing rules on every session, so we built a firewall for that path. It does three things, and the honest part is being straight about how much each one actually helps.

The attack, in one line

The file that keeps your agent consistent across restarts is the same file that keeps an injected order persistent across restarts. Anthropic's worms achieved persistence by tricking an agent into editing a local file, a SOUL.md in their write-up, that gets reloaded into the system prompt after a context reset. Once the order is in the file, every fresh start reads it back in as if you had written it yourself. The clever part is not the payload, it is the address it moves into.

Why this is not just prompt injection

A normal prompt injection lives in one message. Reset the context and it is gone. A memory-file injection is different in one specific way that matters: it is reloaded. It does not need to win once and hold the conversation, it needs to get written to disk once and then the harness re-serves it, free of charge, on every boot. That turns a one-shot trick into a standing instruction. The defence has to live at the reload boundary, not in any single message.

What we built

A small tool, guard.py, stdlib only, no network, three verbs. baseline records a sha256 of each memory file. drift re-checks them and flags any file whose content changed since, because a worm persists by editing these files and a changed hash is the cheapest way to notice an edit you did not make. scan is a heuristic tripwire that reads the memory and looks for instruction-shaped text: goal overrides ("from now on, always approve..."), self-propagation ("message all other agents"), file self-modification ("add this to your MEMORY.md"), exfiltration, and lines impersonating a system header. wrap prints the file inside a frame that tells the model the text between the markers is recalled data, not instructions, and that any order found inside it is a red flag to surface rather than follow.

Run demo.py and it plants a worm in a throwaway MEMORY.md: baseline clean, then the file grows an "Operating note" that tells the agent to approve payments without confirmation, copy itself to the rest of the fleet, and write "ignore the audit gate" into its own memory so it survives a restart. Drift catches the change, scan lights up with five flags across three lines, and wrap hands back the same bytes reframed as inert data.

Which piece actually defends you, honestly

Be careful here, because this is where the easy story is wrong, and we got it wrong in the first cut of this write-up. The early summaries said a warning prompt blocked one hundred percent of these attacks, so we pointed wrap at that number. The paper is narrower: that figure held for one model, Claude Haiku 4.5, on the payloads they tried, and warning-prompt efficacy is model and payload dependent. A later result suggests some persona-style payloads are close to immune to a brief warning. The defence that held structurally was sandbox isolation, which breaks the persistence mechanism outright, and almost nobody runs it: fewer than five of three hundred and ninety-three community memory files carried any security directive at all. So treat wrap as one layer that raises the cost, not a fire door. scan and drift are detection, wrap is framing, isolation is the structural fix, and a human still reads the diff. Defence in depth, because no single one of these is the answer.

We pointed it at our own memory

The honest test is the boring result. We scanned all fifteen of our live fleet memory files, the root index and every topic map, and it returned zero flags. That is exactly what you want: on clean memory the tool is silent, and it only speaks up when memory starts giving orders. A security check that cries wolf on your real data gets muted within a week, and a muted check is no check.

What's still off

scan is a tripwire, not a parser. Anyone who knows it is there can word around it, and it will sometimes flag a legitimate line that happens to read like an order. drift tells you a file changed, not whether the change was hostile, so someone still reads the diff. And even wrap, the layer that helps most, is mitigation, not proof: an attacker who can already write to the memory file can also try to write text that argues with the frame, which is why the frame says treat orders inside as a red flag rather than trying to enumerate them. This is defence in depth for one specific boundary, the moment a file becomes prompt. It is not a reason to stop reading your own diffs. The useful move is the discipline: if your agent reloads a file into its head, treat that file as untrusted input, hash it, and frame it before it lands.