Empirical agents with hard constraints, strict isolation and trusty protocols A new essay argues that AI coding agents should run generated code inside WebAssembly to cap the blast radius of untrusted output, citing the React Server Component "Flight" protocol vulnerability CVE-2025-55182, which received a maximum CVSS score of 10.0 for unsafe deserialization enabling remote code execution. The author points to WebAssembly's decade of availability in every browser and runtimes for every popular platform, and to existing users including Figma for its data model and rendering, Zoom for a custom video codec in browsers, and Firefox's RLBox to isolate third-party libraries. The piece also invokes Simon Willison's "lethal trifecta" of untrusted content, private data and external communication as the risk that vibe coding without review creates. Empirical agents with hard constraints, strict isolation & trusty protocols Agentic coding now allows every language to be treated as a scripting language. Whether you write JavaScript, Lua, or Rust, the same prompt can create a working program in any of the popular LLMs. We have been given a gearbox, where first gear writes a little regex or bash script, and top gear thunders at full throttle tens of thousands of lines of assembly code. But what is the blast radius of one of those lines? Assembly can run syscalls, talk to hardware, and hose your system. Is this the right abstraction layer for the coming wave of security exploits that AI also brings? Casey Muratori encourages us to at least be able to read assembly, which I support, but is this what I want to default to for my code review? Will I rely on static analysis tools to detect any “bad” assembly instructions or any paths of logic that are likely problematic? I’m trying to marry a probabilistic generator the coding agent with another probabilistic filter humans, evolving linter rules . We saw this a year ago with React2Shell https://www.cisecurity.org/advisory/a-vulnerability-in-react-server-component-rsc-could-allow-for-remote-code-execution 2025-111 with “unsafe deserialization of RSC “Flight” protocol payloads, enabling an attacker to send a crafted request that triggers execution of code on the server.” “The vulnerability received a maximum CVSS score of 10.0 https://www.offsec.com/blog/cve-2025-55182/ ” To me this underscored a few things. First, we should stick to venerable protocols that have had decades to mature and receive intense security study. RSC was added to one of the most popular software libraries ever made and crossed the stack from client input to network communication to server runtime in ways that made bugs particularly high stakes. Second, we should limit the blast radius of code, especially when handling untrusted input. Our web servers have been dealing with things like SQL injection for years, and iMessage employs a sandboxed parser and validator called BlastDoor. Web browsers run each tab in its own sandbox — why don’t all apps and servers work like this? Third, the issues in the RSC Flight protocol were quickly fixed, but the potential still lay there like Godzilla hibernating waiting for the next sequel. Could we accidentally introduce a similar vulnerability by vibe coding React? What about installing the wrong NPM package? I’m skeptical that delaying updating dependencies is a reliable solution to supply chain attacks. It’s like waiting for the person in front of you to step on the Titanic lifeboat to see if it sinks or not. It’s a strategy for survival forged in dangerous waters and I want solid land. Simon Willison coined the term the lethal trifecta https://simonwillison.net/2025/Jun/16/the-lethal-trifecta/ , where untrusted content mixed with private data and external communication were a poisonous cocktail. By vibe coding and skipping code review are we potentially performing the Lethal Trifecta on ourselves? How can we trust the AI-produced and third-party-made code and weave in private data deploy that securely to the internet to communicate with customers and systems? What if we made something that was guaranteed to have no “bad” instructions? What if our code couldn’t hose the system even if it tried? What if the program always worked the same way no matter whose machine it ran on, whether GitHub was down, or Cargo or NPM were hacked? Fortunately, it already exists and it ships in every browser and has runtimes for every popular platform: it’s WebAssembly. So far in its decade-long life, WebAssembly has had gradual adoption. Figma uses it heavily for its data model and rendering. Zoom uses it for their custom video codec in web browsers https://www.zoom.com/en/blog/how-zooms-video-sdk-stacks-up/ . Firefox uses it to isolate third party libraries https://firefox-source-docs.mozilla.org/rlbox/index.html by compiling C/C++ to WebAssembly and then converting that back to memory-safe C. But most applications don’t primarily use WebAssembly. This article will explore this but by not rewriting absolutely everything in WebAssembly. We’ll strategically identify the deterministic parts of our logic that can be extracted out into wasm modules. I like to embed these into existing hosts in standard vanilla stacks that speak conventional protocols with battle-tested networking and file system code. One mental model I like to use for WebAssembly is as Turing-Complete JSON. Why send inert data when you can send living programs? Take a list of 319 search results for booking a hotel. If you send the entire dataset and need to tweak one sub-filter or one sort parameter, why not send the data as a WebAssembly module that makes levers available to tweak the data? That way you avoid the latency required for minor changes: you’ve transported a mini-computer over the wire that can run in the browser, on a mobile phone, on a server, or in a desktop application. WebAssembly is like a virtual computer with 3 main ingredients: global variables that hold state from run to run, functions that contain instructions that work the same on every CPU including SIMD, and a block of memory that you can access like RAM. From these 3 ingredients you can implement any typical computational problem. I often like to think that for a given input-to-output problem, such as encoding bytes into base 64, there exists an ideal wasm solution that uses the optimal number of bytes. It’s then your task to find that optimal wasm. Or your agent’s. LLMs and WebAssembly are complementary forces. WebAssembly is deterministic while LLMs are non-deterministic. One is completely predictable, the other is probabilistic. One is self-contained and portable to pretty much every platform, the other requires a huge model to function. Here are the steps I’d follow for making a WebAssembly module: 1. Break your problem down into input and output. What format do you want to receive? What format makes sense to produce? 2. Split that into smaller pieces. Are there intermediate formats that make sense as a lingua-franca for your problem space? For example, I’ve been using uncompressed KTX2 for images. It’s like a modern version of BMP. Then the WebP decoder can output KTX2 and the PNG encoder can take KTX2 as its input. You want to identify the reserve currency for your domain, like how the “US dollar” is most widely used for worldwide trade. 3. Test each step. Provide known inputs and outputs that you expect to work. If you are encoding base64, provide values that you are certain are accurate like hello becoming aGVsbG8= . If making a Markdown to HTML converter, write Title becomes