Node.js Sandbox
Run untrusted JavaScript/TypeScript in an isolated sandbox, evaluate model-generated code, run a build, or execute a user script under a file system and network policy you control. The node image ships Node.js ready to run.
Run a snippet
import { getOrCreateSandbox } from "@hiver.sh/client";
const sandbox = await getOrCreateSandbox("node", { image: "node" });
const result = await sandbox.exec(["node", "-e", "console.log(6 * 7)"]);
console.log(result.stdout.trim()); // "42"Run a script
Write the code into the sandbox, then run it:
await sandbox.writeFile("/workspace/main.js", `
const data = Array.from({ length: 5 }, (_, i) => ({ n: i, sq: i * i }));
console.log(JSON.stringify(data));
`);
const { stdout } = await sandbox.exec(["node", "/workspace/main.js"], { cwd: "/workspace" });
console.log(JSON.parse(stdout));Install only the packages you allow
By default the sandbox can't reach the network. allowedNpmPackages generates the exact egress rules npm needs for the packages you name, any other install is blocked.
import { getOrCreateSandbox, allowedNpmPackages } from "@hiver.sh/client";
const sandbox = await getOrCreateSandbox("node", {
image: "node",
egress: [...allowedNpmPackages("lodash")],
});
await sandbox.exec(["npm", "install", "lodash"], { cwd: "/workspace" });
const { stdout } = await sandbox.exec(["node", "-e", "console.log(require('lodash').chunk([1,2,3,4], 2))"], {
cwd: "/workspace",
});
console.log(stdout.trim());The
allowed*package helpers ship in the TypeScript and Python clients. In Go, add the equivalentnpmegress rules toSandboxConfig.Egressdirectly.
Keep a Node session warm
For repeated evaluations, keep one process alive with execStream and stream code into its stdin, module state persists across calls.
End-to-end: an agent that runs JavaScript
Wire the sandbox up as an agent's code interpreter. Give the model a run_node tool whose handler executes the code in the sandbox and feeds the output back, the model writes code, sees the result, and iterates until it has an answer. Everything the model runs stays isolated in the sandbox.
import Anthropic from "@anthropic-ai/sdk";
import { getOrCreateSandbox } from "@hiver.sh/client";
const sandbox = await getOrCreateSandbox("node-agent", { image: "node" });
const anthropic = new Anthropic(); // reads ANTHROPIC_API_KEY
const runNode = {
name: "run_node",
description: "Execute JavaScript with Node.js in a sandbox and return its stdout/stderr.",
input_schema: {
type: "object",
properties: { code: { type: "string" } },
required: ["code"],
},
} as const;
const messages: Anthropic.MessageParam[] = [
{ role: "user", content: "Sum the numbers 1 through 100 and print just the total." },
];
while (true) {
const res = await anthropic.messages.create({
model: "claude-opus-4-8",
max_tokens: 1024,
tools: [runNode],
messages,
});
messages.push({ role: "assistant", content: res.content });
const toolUse = res.content.find((b) => b.type === "tool_use");
if (res.stop_reason !== "tool_use" || !toolUse) {
const text = res.content.find((b) => b.type === "text");
console.log("answer:", text?.text); // "answer: 5050"
break;
}
// Run the model's code inside the sandbox and return the result.
const { code } = toolUse.input as { code: string };
const { stdout, stderr, exit_code } = await sandbox.exec(["node", "-e", code]);
messages.push({
role: "user",
content: [{
type: "tool_result",
tool_use_id: toolUse.id,
content: stdout || stderr,
is_error: exit_code !== 0,
}],
});
}import OpenAI from "openai";
import { getOrCreateSandbox } from "@hiver.sh/client";
const sandbox = await getOrCreateSandbox("node-agent", { image: "node" });
const openai = new OpenAI(); // reads OPENAI_API_KEY
const tools = [{
type: "function",
name: "run_node",
description: "Execute JavaScript with Node.js in a sandbox and return its stdout/stderr.",
parameters: {
type: "object",
properties: { code: { type: "string" } },
required: ["code"],
additionalProperties: false,
},
strict: true,
}];
const input: any[] = [
{ role: "user", content: "Sum the numbers 1 through 100 and print just the total." },
];
while (true) {
const res = await openai.responses.create({ model: "gpt-5", tools, input });
input.push(...res.output);
const calls = res.output.filter((o) => o.type === "function_call");
if (calls.length === 0) {
console.log("answer:", res.output_text); // "answer: 5050"
break;
}
// Run the model's code inside the sandbox and return the result.
for (const call of calls) {
const { code } = JSON.parse(call.arguments);
const { stdout, stderr } = await sandbox.exec(["node", "-e", code]);
input.push({ type: "function_call_output", call_id: call.call_id, output: stdout || stderr });
}
}The loop is the whole pattern: model → run_node → sandbox → result → model, repeating until the model stops calling the tool. Swap node -e for writeFile + node file.js to run longer programs, or add allowedNpmPackages so the code can npm install a fixed set of libraries.
Next: Bash Sandbox