Run untrusted code
Execute agent- or user-generated code in a disposable box with no container, client-side or server-side.
You have code you didn't write — generated by an AI agent, pasted by a user, or pulled from somewhere — and you want to run it without handing it your machine. Lifo gives you a disposable box with a real filesystem, shell, and Node, but no access to the host: no native binaries, no host filesystem (unless you explicitly mount one), and everything scoped to that box. The same code runs client-side in a browser tab or server-side in Node.
Run a snippet and capture the result
import { Sandbox } from "@lifo-sh/core";
const sandbox = await Sandbox.create();
await sandbox.fs.writeFile("/home/user/snippet.js", untrustedSource);
const { stdout, stderr, exitCode } = await sandbox.commands.run(
"node /home/user/snippet.js",
);The code runs inside the box's Node runtime. It can read
and write the box's virtual filesystem, spawn processes, and
use npm — but it can't touch your host, because those APIs are reimplemented
against the box's in-memory state, not your machine.
Put a leash on it: timeout + cancellation
Untrusted code can loop forever or run away. Bound it with a timeout and an
AbortSignal:
const controller = new AbortController();
setTimeout(() => controller.abort(), 5_000); // hard stop after 5s
const result = await sandbox.commands.run("node /home/user/snippet.js", {
timeout: 5_000,
signal: controller.signal,
onStdout: (chunk) => forwardToUser(chunk), // stream output as it comes
});Commands honor ctx.signal, so aborting stops the run promptly (see
Processes).
One box per task
Isolation comes from using a fresh box per untrusted task and throwing it away after. Boxes don't share filesystems, ports, or processes, so two runs can't see each other:
async function runIsolated(source: string) {
const box = await Sandbox.create();
try {
await box.fs.writeFile("/tmp/run.js", source);
return await box.commands.run("node /tmp/run.js", { timeout: 10_000 });
} finally {
box.kernel /* drop the reference; nothing persists unless you asked it to */;
}
}In Node you can run many boxes in one process; a standalone binary that isolates VMs per request is on the roadmap.
What the sandbox does and doesn't protect
- Contained by default — no host filesystem, no host network beyond the CORS proxy / tunnels you opt into, no native binaries.
- You control the surface — outbound
fetchonly reaches what you allow; the host filesystem is only visible if youmountit. - Not a security boundary against the JS engine itself — untrusted code runs in your JS runtime, so treat it like any in-process sandbox: cap time and memory, and for hard multi-tenant isolation run each box in its own process (server-side) or tab (client-side).
Next
- A box for your agents — wire this into an agent loop.
- The Node runtime — how execution and completion work.