Mixture of Actors
Most agent harnesses are one monolithic loop. peerd is a "mixture of actors": it takes the mixture-of-experts pattern that already won inside the model and moves it up to the harness layer, structured on the isolation the browser already ships.
The monolith
Strip the UI off any agent product shipping today and the coding agents, the browser agents, and the everything-assistants are the same shape underneath: one loop, one context window, everything in it. Every tool the agent might ever need described up front and every byte every tool has ever returned, constantly accumulating. And all the authority the user granted, held in the same place as all of that. Subagents help by splitting work across loops, but each loop is typically a general-purpose copy of the parent, carrying the same tools and the same broad prompt, so it reproduces the dense structure instead of escaping it.
This model has three problems. Outside the frontier data center models it degrades fast. Keeping dozens of tools and a couple hundred thousand tokens of mixed history straight is exactly what smaller models are bad at. It is also expensive, because every decision pays for the whole context. The third problem is a lack of structural isolation: if an agent fetches a page it lands in the same context that holds your credentials and your capabilities, and the only thing between a hostile instruction on that page and your authority is a line of defensive prompt text.
The lesson from inside the model
Model architecture already went through a similar evolution. Mixture-of-experts models stopped activating every weight for every token: a router sends each token to a few dynamically routed "experts" and everything else stays idle. Activating a small specialized slice beats activating everything, per unit of compute, and it has become a dominant way to scale capacity: most large open models are built this way now, and in all likelihood all of the frontier closed ones too.
peerd's bet is that the same economics hold one layer up, at the agent orchestration layer. Instead of one loop holding everything, an orchestrator routes each task to a small actor that holds only the context and tools for its function. The browsing actor knows how to drive a page and only how to drive a page. It has the DOM interaction tools and nothing else. The VM actor knows its Linux machine instance inside and out. The notebook actor knows its files, and what utilities are available to it. Each one starts lean and reports back in a sentence or two. The raw page text or terminal output it worked through never lands in the orchestrator's context. Leaner context per decision, and specialization according to the environment and task, is the harness-layer analog of sparse activation. It is also why the architecture has the potential to work really well with smaller locally hosted models: no single model call has to keep dozens of tools straight, each one just has to do one job with a handful.
Despite the confusing and imo misleading "mixture of agents" term, I found the best description for this architecture is a mixture of actors. In a mixture of experts the routing is learned and per-token. At the harness layer the router is itself a model: the orchestrator delegates each task in plain language, or writes a script when a chain of actors needs coordinating. The analogy is looser than the name implies but still useful. MoE experts are weight partitions a learned router happens to lean on, not the clean hand-built specialists the naming suggests. But the main theme holds: routing and dividing to narrower units beats monolithic bloat.
That bet stopped being just peerd's a day after I first wrote this. Anthropic is now recommending a similar move for their own models: run a cheap model as the executor and call a stronger one only as an on-demand advisor, or flip it and run the strong model as an orchestrator that plans and fans work out to a pool of cheap workers, with most of the tokens billed at the cheaper rate either way. The vocabulary mostly overlaps: orchestrator is a term we already use, just for a role that in this system is itself an actor, and advisor and executor are the two genuinely new ones. The wager underneath resembles the one MoE made inside the model: route the expensive reasoning to where the task actually needs it and let something cheaper handle the rest. When the lab that trains the models converges on a similar layer-up argument, that's a good sign the economics are real.
Actors need walls
Isolation between experts is engineered into a mixture-of-experts model: separate weights, a router between them, no way for an expert to reach into its neighbor. At the harness layer that isolation has to be engineered too, and that is most of the work. A monolithic agent loop has none of it: every tool result, every fetched page, every model-written script shares one context with all the authority. Prompt text is not a boundary.
Subagents give you delegation and a separate context window, which is what the advisor and orchestrator patterns above are built on, and that's real and useful. Context and heap are different things though: nothing about routing a task to a smaller model implies its tool surface got scoped down to that task, or that it's running anywhere the parent can't reach. Cost savings come from picking a cheaper model for the job. The isolation a mixture of actors adds is a separate concern entirely, a real memory boundary and a capability surface trimmed to the actor's function instead of inherited whole from whoever spawned it, that structural wall is the next logical step.
So porting the pattern means the units need real walls: private state, message passing, an address, and something above them deciding what happens when one fails. This is an old recipe. Carl Hewitt named it the actor model in 1973. Erlang made it boring and shipped it into telephone infrastructure that stayed up for decades. The recipe never really changed. Isolate state. Talk only by passing messages. Address things by name. When a part breaks, let it crash and let something else decide what to do about it.
Instead of trying to roll my own isolation I reached for the most attacked, most scrutinized sandbox there is: the browser. peerd is an AI agent harness that runs entirely inside it, with no server. It instantiates agent loops bounded to web workers, tabs, and other functions and capabilities, with strict isolation and coordination done purely via message passing. I call these individual units web actors.
The web actor
An actor needs two things from its substrate: isolated units with private state, and messages as the only way between them. The browser ships both.
- Isolated units with private state. The browser offers a spectrum of different isolation for different use cases, and peerd uses all of them where appropriate. A Web Worker is a thread with its own heap; nothing is shared unless you explicitly send it. An opaque-origin iframe runs at an origin of its own, cut off from the extension's storage, cookies, and DOM. A Linux VM compiled to WebAssembly is a whole machine with another layer of isolation. Share-nothing is the default here, not something you opt into.
- Message passing. postMessage with structured clone moves data between contexts, and there are no shared pointers to corrupt by construction.
None of this was designed to build actor systems. It was built for offline apps, games, and keeping hostile pages away from each other. But it composes into almost exactly the substrate Hewitt described. To be clear, peerd is not an actual actor runtime. It is an agent harness that borrows the actor model where it genuinely fits, and uses plainer patterns where it does not. Two places it fits well:
The sandboxes are share-nothing contexts
Every Notebook (a sealed JS worker), App (an opaque-origin iframe), and WebVM (or any other WASM workload) runs in its own sealed scope and reaches the rest of the system only through messages. Storage is partitioned per instance: peerd pins each Notebook a private OPFS root it can only reach through a host bridge, writes each App's files into their own OPFS subtree that the opaque-origin iframe itself can't read, and gives each WebVM its own IndexedDB disk overlay. The agent never shares memory with the code it runs. For an agent that executes model-generated code, that isolation is the threat model: a prompt-injected script in a sandbox cannot reach your vault or another instance's disk. The browser enforces part of that: an App at an opaque origin has no storage to break out of, and no sandbox shares a heap with the vault. peerd enforces the rest, pinning each storage root to its instance so one sandbox has no handle to another's.
This is also what separates the mixture of agents from just a prompting pattern. Each "expert" is not a persona or a system prompt; it is a separate heap. The actors' own reasoning loops get the same treatment: each runs in its own dedicated worker that holds no vault key and no privileged browser APIs, with exactly two gated relays out. A hostile page can still poison an actor's reasoning, but there is a real memory boundary around it, not just a prompt guardrail.
The agent is the supervisor, and this is the new shape
In OTP, a supervisor is a few lines of declarative config: which children to start, and what to do when one dies. In peerd the supervisor is the reasoning loop. It decides what to spawn (subagents, sandboxes), narrows what each child may do (a child gets a strict subset of tools, and capabilities it was not granted are stripped from its context entirely), and bounds how deep the tree may go.
It is also the router. In a mixture of experts the router is a learned layer that cannot explain itself. Here the router reasons in the open: you can read why a task went to the actor it went to, and so can the model on the next turn.
I have not seen another actor system where the supervisor is a model that reasons about the tree at runtime instead of a static policy. A novel idea is not the same as a good one, but the advantages of this architecture seem worth pursuing.
If we do this right, you never think about any of it. There is no actor vocabulary in the interface. You ask for something and it gets done. When a piece fails you can see what failed and why, in order, in one place. peerd should feel like any other assistant, just one that holds up on smaller and cheaper models, doesn't slow down as much as a long session piles up context, and doesn't hand a hostile webpage the keys. The goal is to get the benefits of the architecture to users who never need to know it exists.
The browser is obviously not the BEAM
Now the limitations and trade-offs.
- No preemptive scheduling. The BEAM preempts every process after a couple thousand reductions, so one greedy actor cannot starve the rest. The browser does not. A tight loop in a worker just blocks that worker. I bound it with timeouts and step caps, which is comparatively crude.
- Agent actors are heavy. Erlang spawns millions of processes because a process costs a few hundred bytes. A Web Worker is a thread with its own heap, costing megabytes; a tab is a renderer process, costing tens of megabytes. You get dozens of live actors, not millions. Fan-out is bounded by RAM.
- Routing costs a hop. Every delegation is an extra model turn: the orchestrator states the goal, the actor reasons, the reply comes back. On a task a monolith could do in one shot with the right tool already in hand, the mixture pays latency for isolation and specialization. We think the trade is right, and we keep making the hop cheaper (batching delegation in code instead of one call at a time), but it does not go to zero.
- No supervision trees, really. peerd has depth caps, output caps, capability narrowing, and disposable sandboxes. It does not have
one_for_one, automatic restart, or DeathWatch. If a child dies, the parent finds out the next time it drains, not through a supervision signal. These are guardrails, not OTP's restart strategies, but we can probably learn from them and evolve in a similar direction. - State is evictable, not durable. OPFS and IndexedDB can be evicted under memory pressure. There is no write-ahead log and no replay. "Durable" means the user exported. Cloudflare's Durable Objects persist and replay; peerd's state is a working set, not a system of record.
- Single-threaded per context, serialized across them. Two tool calls in one context run one after another. Every cross-context message is structured-cloned. There is no handing a pointer to your neighbor.
Past these, calling peerd a full actor system would be overselling it: the sandboxes are real share-nothing contexts, the supervisor is real but ad-hoc, and plainer machinery fills the gaps.
So why the browser
With the trade-offs on the table: why build it this way instead of on a server with a real actor runtime?
In a nutshell: every single agent harness is figuring out the tension between capabilities and security risks. The browser has already been navigating that exact same tension for decades, in the most hostile environment there is.
The memory boundary is not peerd's to get wrong. peerd inherits the browser's isolation model. The same boundary that keeps a malicious ad off your bank tab keeps one sandbox out of another's memory. We get that for "free", along with the browser's full API, web access, and broader capabilities: private disks through OPFS, sandboxed compute in every shape from a worker to a WASM Linux VM, cryptographic identity through WebCrypto, and direct browser-to-browser channels through WebRTC. Those last two are what let the mixture pattern extend across machines, where a peer's agent is just an actor whose heap is in someone else's browser. That layer is the agent web, and it gets its own post next.
peerd is an early design and it is evolving fast. Testing, feedback, benchmarking, and contributions are all welcome on GitHub.