Loams v0.1.0 is out: run the whole stack yourself with Docker Compose. See the roadmap

Blog/Research

What agents need from infrastructure: memory as data, durable runs, sandboxes with policy, and a harness that does not trust the model

A survey with primary sources of agent memory (MemGPT and Letta, Mem0, Zep and Graphiti, supermemory, LangGraph), durable execution (Temporal, Restate, Inngest, DBOS, Resonate), sandboxes (Firecracker, gVisor, E2B, Modal, Daytona, Cloudflare), coding-agent harnesses and the security research on agents. Then what most stacks get wrong, and where Loams stands, with each piece labelled by its status.

What agents need from infrastructure: memory as data, durable runs, sandboxes with policy, and a harness that does not trust the model
On this page
  1. Memory: everyone converged on the same three parts
  2. Durable runs: five models of the same promise
  3. Sandboxes: isolation is solved enough, policy is not
  4. Harnesses: permission modes, and reviewers that are not boundaries
  5. What the security research says
  6. What most stacks get wrong
  7. Where Loams stands
  8. Studio: supervised by default
  9. Durable, embedded
  10. Memory as data
  11. One sandbox crate
  12. Places and the Jailer
  13. The trade-offs
  14. What we take from all this

An agent is a loop: call a model, run the tool it asks for, append the result, call the model again. A coding agent may run that loop for an hour. A support agent may pause it for three days while a person approves a refund. Each turn reads things the agent did not write, and some turns change things that matter.

Strip away the frameworks and an agent asks four things of the infrastructure under it:

  1. Memory that outlives a context window and a session, and that it can be told to forget.
  2. Durable runs, so a crash at step seven does not repeat six paid model calls or lose an approval that a person already gave.
  3. A sandbox, so the code and commands it runs cannot reach what they should not.
  4. A harness you can trust: the part that decides which tool calls run, and that keeps deciding correctly when the model has been talked into something.

This post surveys what the field has built for each, with primary sources, and what the security research says about the fourth. Then it argues that most stacks get three things wrong, and describes where Loams stands. Every Loams claim carries its status from the 0.1.0 release notes and the decision log: Available ships in 0.1.0, Experimental ships but may change, Planned is designed and approved but not built, and Proposed is a design the owner has not approved yet. A large part of what follows is Planned or Proposed. We say so each time.

Memory: everyone converged on the same three parts

MemGPT and Letta. The MemGPT paper (Packer et al., 2023) framed the problem as an operating system would: "virtual context management", moving information between a small fast tier (the prompt) and large slow tiers, with the model itself deciding what to page in. Letta, the company that grew out of it, kept the idea as memory blocks: labelled strings pinned to the system prompt, which the agent edits through memory tools and the developer through the API. Every message is stored, so evicted history stays searchable.

Mem0. Mem0 (Apache-2.0) extracts salient facts from conversations, consolidates them against what it already holds, and retrieves them later. Its paper reports, on the LOCOMO benchmark, 91% lower p95 latency and over 90% fewer tokens than putting the whole history in context, and describes a graph variant, Mem0g. The vector store is pluggable, with Qdrant as the default.

Zep and Graphiti. Graphiti (Apache-2.0) builds a bi-temporal knowledge graph: every fact links to the episodes it came from, and a fact that stops being true is invalidated, not deleted, so the graph can answer what was believed at a given time. It runs on a graph database you bring: Neo4j, FalkorDB or Amazon Neptune with OpenSearch. The Zep paper reports 94.8% on the DMR benchmark against MemGPT's 93.4%.

supermemory. supermemory (MIT) is a memory and context engine with user profiles, hybrid search and connectors that pull in documents from elsewhere.

LangGraph. LangGraph separates the two horizons cleanly. A checkpointer persists a thread's graph state, which gives short-term memory, resumption and time travel; Postgres is the recommended production backend. A store holds "long-term, cross-thread memory" as key-value items under namespaces such as (user_id, "memories"), with optional semantic search. LangMem (MIT) adds extraction and background consolidation on top.

Read side by side, these systems agree more than their marketing suggests. The shape they share has three parts: raw episodes (messages, documents, events) kept as the record, which Graphiti and Letta make explicit; facts and entities derived from them by a model, which is what Mem0, Graphiti and LangMem do; and a few blocks (a profile, a persona, an index) pinned into the prompt, as Letta and supermemory do. They also agree, by omission, on two harder problems. The first is where the state lives. A typical deployment is a vector database, a graph database and Postgres, each with its own access control and its own idea of deletion. The second is that memory is an attack surface. Anything an agent reads can become a "fact" it will act on next week, which turns a one-off prompt injection into a persistent one.

Durable runs: five models of the same promise

Durable execution means a function's progress survives the process running it. The five engines most often used for agents get there in different ways.

ModelWhere state livesLicence
TemporalEvent-history replay: workflow code reruns from the top against recorded events, so it must be deterministic; side effects go in ActivitiesTemporal Server on Cassandra, PostgreSQL or MySQL, plus a visibility store (Elasticsearch recommended)MIT
RestateA journal of each step and its result, replayed on recovery; virtual objects keep keyed state with one writer at a timeThe Restate server's embedded storeBUSL-1.1, Apache-2.0 after four years
InngestStep memoization: the handler re-runs from the top and each completed step.run returns its saved resultInngest's state store and system databaseServer SSPL with delayed Apache-2.0; SDKs Apache-2.0
DBOSA library, not a server: workflows and steps checkpoint to Postgres, one write per stepYour PostgresMIT
ResonateDurable promises ("distributed async await"): each call's result is a promise on the server, and a function replays against themThe Resonate server, on SQLite, Postgres or other pluginsApache-2.0

For agents the differences matter less than what they share. A model call is the expensive, non-deterministic step; all five record its result once, so a retry does not pay for it twice. And a human approval is just a step that waits: a promise that a person resolves tomorrow, which the run picks up from where it stopped. The Resonate deep dive covers why we like promises as the primitive.

What none of them gives you is the rest of the agent's state. The workflow engine knows the run's steps. It does not know what the agent remembered, which files it changed in its sandbox, or which branch it wrote to. Those live elsewhere, under other identities, and replaying a run means stitching them back together by timestamp.

Sandboxes: isolation is solved enough, policy is not

The isolation technology has settled. Firecracker (Apache-2.0) is a minimal KVM monitor that, per its NSDI 2020 paper, boots to application code in under 125 ms with under 5 MB of memory overhead per microVM; its jailer wraps each one in namespaces, a dropped uid and a seccomp filter. gVisor (Apache-2.0) is a user-space kernel in Go that implements syscalls itself, trading compatibility and per-syscall cost for a much smaller host surface.

The products for agents are built on these. E2B runs each sandbox as a Firecracker microVM, resumes from snapshots, and can pause and resume memory and disk. Modal Sandboxes run on gVisor or a VM runtime. Daytona claims sandboxes running code in under 90 ms; its README now says core development moved to a private codebase in June 2026. Cloudflare's Sandbox SDK gives each sandbox a Durable Object and a container, and its security page says plainly that "the sandbox is the smallest unit of trust".

The network is where they differ, and where the hard part is. E2B and Modal both offer domain allowlists, and both document the limits: E2B matches only the HTTP Host and TLS SNI on ports 80 and 443 (docs); Modal says DNS stays open unless all outbound traffic is blocked, and that domain fronting can get past its allowlist (docs). That honesty is to their credit. It also shows that a sandbox answers "can this process escape?" and leaves open the question that matters for agents: "what is this process, holding these credentials, allowed to send where?"

Harnesses: permission modes, and reviewers that are not boundaries

The coding-agent harnesses have converged too. Claude Code has permission modes from manual approval through acceptEdits and a classifier-reviewed auto to bypassPermissions, deny-then-ask-then-allow rules, and hooks that can allow, deny or ask before a tool runs. Its sandbox uses Seatbelt on macOS and bubblewrap on Linux, routes the network through a proxy with an empty allowlist, and covers shell commands only. Codex CLI (Apache-2.0) pairs read-only, workspace-write and danger-full-access sandbox modes with approval policies, on Seatbelt and on bubblewrap plus seccomp. OpenCode (MIT) resolves each tool to allow, ask or deny, without OS containment. T3 Code (MIT) is a control surface over these CLIs, with an event log as its source of truth and checkpoints as hidden git refs; its own docs say full access is the initial default.

The interesting move is the automated reviewer. Anthropic's write-up of auto mode describes a two-stage classifier that sees user messages and tool calls but not the assistant's text or tool outputs ("reasoning-blind by design"), with 0.4% false positives on real traffic and a 17% miss rate on real overeager actions, which the authors call "the honest number". It escalates to a person after 3 consecutive or 20 total denials. The vendors are candid about what this is: Codex's docs call hooks "a useful guardrail, not a complete enforcement boundary", and Claude Code's say a Bash rule "isn't a security boundary around the program".

What the security research says

Simon Willison named prompt injection in September 2022. Greshake et al. showed the indirect form in 2023: instructions planted in content that the model later retrieves, which an application cannot tell apart from data. Since then the research has mostly mapped where defences fail.

  • The lethal trifecta. Willison, June 2025: an agent with access to private data, exposure to untrusted content, and a way to communicate externally can be made to send the first out through the third. Catching 95% of attacks is, in his words, a failing grade. Meta's Agents Rule of Two restates it as policy: in one session, at most two of untrusted input, sensitive access, and state change or external communication, or a person supervises.
  • MCP made it concrete. Invariant Labs showed tool poisoning, hidden instructions in a tool's description that the model reads and the user does not, and a GitHub MCP attack in which a malicious public issue led an agent to copy private-repository data into a public pull request. They call it architectural, not a server bug. The MCP spec's security best practices now forbid token passthrough and require per-client consent against confused-deputy attacks.
  • Design beats detection. CaMeL separates control flow, taken from the trusted query, from data flow, and attaches capabilities to data; it solves 77% of AgentDojo tasks with provable security against 84% undefended. The design-patterns paper (Action-Selector, Plan-Then-Execute, Dual LLM and others) concludes that a general-purpose agent cannot get reliable guarantees, so you constrain the agent instead.
  • LLM monitors lose to adaptive attackers. The AI-control line of work started with Greenblatt et al.: a weaker trusted model watching a stronger untrusted one. Terekhov et al. then showed that an untrusted model that knows the monitor can inject prompts aimed at it, and that "frontier models consistently evade diverse monitors"; resampling made it worse, acting as best-of-n for the attacker.
  • OWASP agrees. LLM01 says no prevention is foolproof, and LLM06, Excessive Agency, names the root causes as too much functionality, permission and autonomy.

What most stacks get wrong

State scattered across five services. A typical production agent keeps memory in a vector database and a graph database, checkpoints in Postgres, runs in a workflow server with its own database, and execution in a sandbox vendor. Five services means five identity models, five retention rules and five clocks. "Forget this user" becomes five deletes through five APIs, and "what did this run touch?" becomes a join by timestamp across systems that never agreed on time.

Left: five services, five auth models, five retention and deletion rules, and no shared notion of when something happened. Right: the shape Loams is building toward. Graph and Durable ship today; Memory on collections is Proposed, and Durable keeps its state in SQLite until the TiKV store lands.

Sandboxes without policy. A microVM stops an escape. It does not decide that a run which just read an untrusted issue may no longer push to a new remote, and the credentials inside it are only as safe as the rules about where they may be sent. Isolation without a policy that knows about the run's data, branch and taint is a strong wall around an open door.

LLM-only oversight. A classifier or reviewer agent saves people clicks, and the best ones are good. But every vendor says its reviewer is not the boundary, and the research says adaptive attackers beat monitors. A stack where a model is the last thing between a tool call and the world has no boundary at all, only a filter.

Where Loams stands

Loams is not an agent framework. It is the data and execution platform under one, and Studio is its own harness for coding agents. Here is each piece, with its status.

PieceWhat it isStatus
Loams StudioCoding agents in threads, a worktree each, supervised approvals, checkpoints, run groupsExperimental
Loams DurableAn embedded Resonate server: durable promises and tasks on SQLiteAvailable (state on TiKV for clusters is in progress)
Loams GraphGQL on Grafeo, writes through the Loams log, snapshots in the bucketAvailable
Loams MemoryAgent memory on collections plus a linked graph (design §52)Proposed, parked until preview.4
loams-sandboxOne crate, one SandboxSpec, a ladder of adapters (design §56)Planned after preview.3
PlacesScope, tools, roles, approvals and budgets as one record (design §60)Proposed
Loams JailerA deterministic supervisor with an LLM that may only tighten (design §61)Proposed

Studio: supervised by default

Experimental Studio runs Claude Code, Codex, opencode, Pi, ACP agents and Loams' own agent through loams-agentd, a per-user daemon, in the desktop app or against a remote daemon from the browser. Each thread gets its own git worktree on its own branch. Its UI components are vendored from T3 Code under MIT and rebuilt on our stack (design §53, D912–D913).

The part that matters here is approvals. A thread is supervised or full access, and since 0.1.0 a thread nobody chose a mode for is supervised, on your own machine too. In a supervised thread each driver stops before a tool call: Claude Code runs with --permission-mode default, Codex with the approval policy on-request and the thread's own sandbox level, opencode and ACP agents forward their permission events. A few rules carry the weight:

  • Only the owner answers. An agent's MCP token cannot watch, answer or switch a thread to full access. A side chat inherits its parent's mode, so an agent cannot escape supervision by spawning one.
  • "Always" is remembered by the daemon, not the agent, and only for that thread.
  • No answer is a deny. Unanswered approvals are denied after 30 minutes by default, and pending approvals survive a daemon restart.
  • Destructive git actions always ask, whatever the mode, and are never offered to the agent.
  • Every turn is checkpointed as a hidden git commit, so a turn can be undone; a restore checks the diff checksum first and refuses if anything moved.
  • Run groups send one prompt to two to four agents in separate worktrees, compare them, and merge only on a person's click.

What it does not do yet, and the guide says so plainly: the daemon does no OS-level confinement of its own. A worktree separates an agent's edits, not its processes, so supervision is the guard and full access is for work you would leave unattended.

Durable, embedded

Available Loams links the Resonate server into its own binary, so the official Resonate SDKs talk to it unchanged and durable state sits with the rest of the namespace (how it works). In 0.1.0 that state is SQLite on the loams service's volume; a TiKV store for clusters is in progress. A human approval is a durable promise, which is the primitive the Studio and Jailer designs lean on. Durable agent workflows built from agent(), parallel() and phase() steps, with Studio's harnesses as executors, are Proposed (§60 D1090) and need their own design.

Memory as data

Available Graph. Proposed Memory. Loams Graph ships: GQL on our Grafeo fork, writes that reach the Loams log before they are acknowledged, a fenced single owner per graph and snapshots in the bucket. Design §48 §26 records what we took, as ideas only, from HydraDB (AGPL-3.0, so no code): an approved adjacency index built from the graph's own change stream and published to the bucket, with readers overlaying the log tail so an answer is never stale (D1063, D1064).

Loams Memory is the product on top, and it is Proposed and parked until preview.4. The design takes the convergent shape from the survey and puts it on primitives Loams already has:

  • Episodes are the ground truth, as in Graphiti, and every fact names its episodes (D875).
  • Facts are bi-temporal properties, invalidated rather than deleted (D874).
  • Letta-style blocks with compare-and-swap by version, and a supermemory-shaped profile (D876).
  • Truth in collections, traversal in a linked graph, so there is no second database to keep in step (D870, D873).
  • Forget means gone: it cascades to derived data and is purged physically (D879).
  • Memories are data, never instructions: every episode carries a trust level, facts from external content cannot become blocks without review, and returned memory is framed as untrusted (D888).

The last rule is the one most memory layers leave out.

One sandbox crate

Planned Loams has one sandbox built and tested against escapes today: the House worker's, which uses Landlock and seccomp, and refuses to boot when it cannot be put in force. Design §56 generalises it. Its direction is approved by the owner; the plan, SB1, starts after preview.3.

  • One declarative SandboxSpec: filesystem, network, syscalls, resources and identity. It states what is allowed, never how.
  • A ladder of adapters: linux (namespaces, cgroup v2, Landlock, seccomp, unprivileged) by default, then wasm, gvisor and microvm, plus seatbelt on macOS and appcontainer on Windows for the desktop daemon. Multi-tenant hostile code runs the linux spec inside gVisor or a microVM, never on bare linux.
  • Fail closed: an adapter that cannot enforce a field refuses the spawn, unless the spec marks the field best-effort, and every sandboxed process reports what was actually enforced (D990, D1007).
  • The network is policy: none, or a host-side forwarder or allowlisting proxy that resolves names itself and refuses private, link-local and metadata addresses unless listed (D998).
  • agentd enforces the sandbox level itself around the whole harness process tree, instead of trusting each harness's own sandbox (D1004, Proposed). That is the answer to the gap in the previous section.

Places and the Jailer

Proposed. The last two pieces are research notes with proposals, written on 2026-10-17. Nothing in them is built.

Places (§60) take an idea from an essay on harness composition: an agent carries a "suit" (identity, memory, values) and works in a "place" (a scope, its tools, its rules). A Place is a record: a data scope with a branch, a tool allow-list, roles, an approval policy and budgets. Entering one computes the effective capability, the agent's grants intersected with the place's, before the run starts, and shows it. When the two disagree, refusal wins. Every artifact the run leaves behind carries (agent, role, place, run), and because writes land on a branch first, what an agent left behind can be promoted or discarded as a unit. We deliberately do not invent a harness language for this (D1092): it is data, checked by OpenFGA and a policy check.

Designs §60 and §61, Proposed. Tools intersect, so the place sets the limit; identity stays with the agent. Taint is one-way: once a run reads untrusted input, new egress hosts and writes outside its branch need approval for the rest of the session.

The Jailer (§61) is the supervisor that uses all of it. The name comes from Firecracker's jailer, the process that confines each microVM. It has three layers: a deterministic policy decision point that every enforcement point asks (the approval hook, the MCP and LLM gateways, the egress proxy, Data API and Git writes); sensors and actuators across the kernel, the VMM and the network (freeze, throttle, cut egress, revoke a token, kill, roll back); and an LLM overseer that sees only what the policy marks ambiguous. The overseer gets a paraphrased view with no agent-written or tool-written text in it, and it may only make a verdict stricter. Its one allowance is to auto-approve an "ask" that already sits inside the effective capability, and only after a shadow-mode period shows a false-positive rate under 1% and no gain for adaptive attackers.

Design §61, Proposed. The deterministic layer and the sandbox are the boundary. The overseer sees a paraphrased, reasoning-blind view, and may deny or escalate; the only thing it may allow on its own is an 'ask' that already sits inside the effective capability.

The rules that matter most are the deterministic ones (§61 §4.5): a trifecta check when a run enters a Place; one-way taint, so that once a run reads untrusted content (a web page, an issue, rows marked untrusted, another agent's files) any new egress host, write outside its branch or unnamed tool needs approval for the rest of the session; egress as its own risk class; a circuit breaker after 3 consecutive or 20 total denials; and budgets that freeze a run rather than warn. One choice in §61 is approved: eBPF kernel programs, if we write them, live in their own crate under GPL-2.0-only OR MIT, and GPL introspection libraries are never linked (D1102).

The trade-offs

These choices have costs, and some of them are large.

  • One platform is one blast radius. Putting memory, durable state, Git and the graph under one namespace removes five auth models, but a bug in that namespace's authorization reaches all of them. We think one boundary you can audit beats five you cannot, but it is a bet.
  • Embedding Resonate ties us to its releases. We get one binary and in-process calls; we also carry its upgrade schedule, and the TiKV store for clusters is not done.
  • Supervision costs clicks. Supervised by default is the right default and an irritating one. Click fatigue pushes people to full access, which is why the overseer exists at all and why it must earn its place in shadow mode first.
  • Deterministic rules are coarse. Session-level taint will ask for approval in cases a finer data-flow tracker such as CaMeL would allow. We accept lower utility for a guarantee we can state.
  • Linux first. The strongest adapters (gVisor, microVMs) and any eBPF sensor need a Linux server with privileges. The desktop gets Landlock and seccomp, Seatbelt or AppContainer, and no kernel sensor on macOS.
  • Much of this is paper. Studio's approvals and Durable ship; the sandbox crate is approved but unbuilt; Places, the Jailer and Memory are proposals that may change.

What we take from all this

The field has done the hard research. Memory systems agree on episodes, facts and blocks. Durable engines agree that a model call's result is recorded once. Sandboxes have isolation that is good enough. Security researchers agree that the model cannot be the boundary.

What is missing is the joining: one place where an agent's memory, its run, its sandbox and its permissions share an identity, a clock and a policy, so that "what may this run do, and what did it leave behind?" has a single answer. That is the problem Loams is built around. Some of the answer ships today, and the rest is written down where you can read it.

More from the blog