SDK
Scripting
Run untrusted JavaScript in a sandboxed Boa engine with capability-scoped access to the VM. A fresh engine has zero ambient authority — you grant exactly what a script may touch.
One-shot vs long-lived
javascript
// one-shot: create, eval, dispose const r = await nano.script("({ sum: [1,2,3].reduce((a,b)=>a+b,0) })", { expose: {}, }); // long-lived engine you drive and dispose yourself const engine = await nano.scripting({ expose: { fs: "readonly", run: true } });
script() creates an engine, evaluates the source, and disposes it. scripting() returns a long-lived ScriptEngine. Both require scripting.wasm (boa.wasm) in the Nano config.
The capability model
expose is the only security boundary — nothing is granted by default.
Grant
Effect
fs: "none" | "readonly" | "readwrite"
access to nano.fs (default none)
run: boolean
nano.run / exec / sh (default false)
node: boolean
nano.node (default false)
webapis: [...]
"console" | "encoding" | "url" | "timers" (default ["console"])
Host bindings
registerFunction(name, fn) exposes a host function (sync or async) to scripts; defineGlobal(name, value) injects a value; env is mirrored as <global>.env.
javascript
engine.defineGlobal("CONFIG", { timeout: 30 });
engine.registerFunction("fetchRow", async (id) => ({ id, name: `row-${id}` }));
const out = await engine.eval(`(async () => (await fetchRow(7)).name)()`);
engine.dispose();Limits
- limits.loopIterations / limits.recursion — cap runaway scripts.
- timeoutMs — host-side watchdog.
- syncOnly — skip the async job pump.
- onStdout / onStderr — capture script output.
ScriptError is thrown on evaluation errors.