Running AI-Generated Code Safely: Field Notes on Vercel Sand | Coderz Club

Running AI-Generated Code Safely: Field Notes on Vercel Sandbox Headline: Vercel Sandbox runs untrusted code — including code a model just wrote — inside an isolated, ephemeral microVM instead of ins

Running AI-Generated Code Safely: Field Notes on Vercel Sandbox Headline: Vercel Sandbox runs untrusted code — including code a model just wrote — inside an isolated, ephemeral microVM instead of ins

By Coderz Club · 2026-08-09 · Tags: ai, rust

Running AI-Generated Code Safely: Field Notes on Vercel Sandbox

Headline: Vercel Sandbox runs untrusted code — including code a model just wrote — inside an isolated, ephemeral microVM instead of inside my application's own process. I moved every "let the model write and execute a snippet" feature off ad-hoc child_process calls and onto Sandbox: one sandbox per execution, a hard timeout, an isolated filesystem, and no path back into my app's environment variables. Key takeaways Vercel Sandbox (@vercel/sandbox) runs code inside an isolated Firecracker microVM, not a container in your app's own process — a compromised sandbox can't read your Vercel Function's memory or environment variables. A sandbox is ephemeral: you create one, run commands, read the output, then stop it. There is no persistent state between runs unless you explicitly persist it yourself. sandbox.runCommand() executes a process inside the sandbox and returns stdout, stderr, and an exit code; sandbox.domain(port) exposes a running server on a public URL for a live preview. The two cases I actually reach for it: an LLM-authored script that needs to run and return a result, and a user-facing "run this code" feature like an AI-generated component preview. Sandbox is not the tool for trusted, first-party build or CI logic — that belongs in the deploy pipeline. Sandbox is for code you did not write and do not trust. Why can't I just run AI-generated code inside my own Vercel Function? A Vercel Function shares its process, filesystem, and environment with the rest of my app. Running an untrusted string as code in that same process — through child_process.exec or, worse, eval — puts every secret the function can see, API keys and database URLs included, inside the blast radius of whatever the model wrote. A generated snippet can read environment variables, open an outbound connection to exfiltrate them, or just spin the CPU and starve every other request the function is serving at the same time. I treat any code I did not author myself as untrusted by default, and that includes code a model generates on request. Untrusted code needs its own compute boundary: its own filesystem, its own network context, and resource limits I can enforce and then throw away. What is Vercel Sandbox actually running under the hood? Vercel Sandbox provisions a Firecracker microVM for every sandbox — the same virtualization technology AWS Lambda uses to isolate tenants from each other, not a namespace or cgroup container. The practical difference is the escape hatch: breaking out of a container means crossing a kernel-namespace boundary inside a kernel the workload shares with its neighbors, while breaking out of a microVM means finding a hypervisor-level exploit against a kernel nothing else is using. Creating one is a single call: import { Sandbox } from '@vercel/sandbox'; const sandbox = await Sandbox.create({ runtime: 'node22', timeout: 60_000, // ms — hard ceiling before Vercel force-stops it resources: { vcpus: 2 }, }); runtime picks the base image, timeout is a hard ceiling I set per use case, and resources.vcpus controls how much CPU the microVM gets. I set the shortest timeout a feature can tolerate rather than reusing one default everywhere — a code-eval playground gets seconds, a batch-style job gets minutes. How do I actually execute an LLM-generated snippet inside a sandbox? Write the generated code to a file inside the sandbox, then run it as a subprocess — never pass model output through eval or the Function constructor inside your own function, sandboxed or not. await sandbox.writeFiles([ { path: 'snippet.js', content: Buffer.from(generatedCode) }, ]); const result = await sandbox.runCommand({ cmd: 'node', args: ['snippet.js'], }); const stdout = await result.stdout(); const exitCode = result.exitCode; runCommand() gives me back exactly what a subprocess call would: stdout, stderr, and an exit code. The difference is where that process actually ran — inside a disposable microVM instead of next to my app's live secrets. Does a Vercel Sandbox keep state between runs? No. Sandboxes are ephemeral by design — each Sandbox.create() call provisions a fresh microVM with a clean filesystem, and calling sandbox.stop(), or hitting the timeout, tears it down completely, including anything written to disk. If a feature needs to remember something across runs — a multi-turn code-interpreter chat, for instance — that state has to live outside the sandbox: write results to a database or blob store from inside the sandboxed process, or persist a small manifest the caller rehydrates into a new sandbox next time. I treat each sandbox as disposable compute, never as a place to store anything. Can I stream a sandbox's output back to the browser while it's running? Yes. runCommand() accepts a detached option, which returns a handle you can read from as output is produced instead of waiting for the whole command to finish — the same pattern I use for streaming a model's token

View this page on Coderz Club