Skip to content
GitHub Get Started
Reference

Node.js Compatibility

Guest code in Secure Exec runs as Node.js. It never touches the host runtime: every guest import/require of a node: builtin resolves to a kernel-backed bridge or an in-isolate polyfill, never the real host module. This page describes which builtins are available and how each one is implemented.

The guest reports itself as Node v22.0.0 (process.version).

There are three ways a builtin is provided to guest code:

  • Bridge. A kernel-backed implementation. Calls route through the kernel VFS, socket table, process table, DNS resolver, or host entropy. This is how fs, net, http, child_process, dns, os, and crypto reach virtualized resources while staying inside the isolation boundary.
  • Polyfill. A pure-JavaScript implementation from node-stdlib-browser (for example path, events, util, stream, zlib). These need no host access; they run entirely inside the V8 isolate.
  • Denied. The module is intentionally unavailable. Importing it throws an error with code ERR_ACCESS_DENIED.

The canonical inventory lives in crates/execution/assets/polyfill-registry.json.

These route through the kernel and present normal Linux/Node semantics over virtualized resources.

ModuleBacked by
fs, fs/promisesKernel VFS. File and directory operations, fds, createReadStream/createWriteStream, metadata, symlinks. watch/watchFile are guest-side polling wrappers.
child_processKernel process table. spawn, exec, execFile, spawnSync, execSync, execFileSync (and the sync variants) run kernel-managed processes. See Child Processes.
netKernel socket table. TCP client and server sockets, plus Unix sockets.
dnsKernel DNS resolver (lookup, resolve*, and the dns/promises surface).
http, https, http2Built on the kernel socket path. request, get, createServer, agents with connection pooling.
tlsLayered on the kernel net polyfill.
osVM-scoped values (platform, arch, hostname, CPU/memory, user info, os.constants).
cryptoHost entropy and crypto bridges: getRandomValues, randomUUID, randomBytes, createHash, createHmac, cipher/decipher, scrypt, and crypto.subtle (WebCrypto).
processVirtualized process global: env (permission-gated), cwd/chdir, signals, timers, stdio, umask.
modulecreateRequire, Module basics, builtin resolution, Module.builtinModules.
consoleBridge shim with circular-safe formatting. Output is captured into the stdout/stderr result fields and can also be streamed via the onStdout/onStderr hooks on exec/run/spawn.
dgramKernel socket table (UDP).
perf_hooks, diagnostics_channel, async_hooksBridge-backed compatibility surfaces.
worker_threadsCompatibility shim: isMainThread and inert ports for feature detection. Real worker threads are not spawned.
vmCompatibility shim: Script, createContext, isContext, runInNewContext, runInThisContext.
v8Compatibility shim for safe inspection/serialization helpers.
ttyisatty plus ReadStream/WriteStream compatibility constructors.
readline, sqliteBridge-backed compatibility surfaces.
timers, timers/promisessetTimeout, setInterval, setImmediate, and promise variants.
stream/web, stream/consumers, stream/promisesWeb Streams and stream helper subpaths.

Network builtins (net, dgram, dns, http, https, http2, tls) are subject to the per-runtime permission policy, which denies network access by default. Operations fail until you opt in. See Permissions.

Pure-JavaScript implementations from node-stdlib-browser, running inside the isolate. They support default and named ESM imports.

ModulePolyfill
path, path/posix, path/win32path-browserify
bufferbuffer (also re-exports Blob/File)
eventsevents
streamreadable-stream
util, util/typesnode-stdlib-browser
assertnode-stdlib-browser
urlnode-stdlib-browser shims
querystringnode-stdlib-browser
string_decodernode-stdlib-browser
zlibnode-stdlib-browser
punycodenode-stdlib-browser
constantsconstants-browserify (os.constants stays available via os)
console, timersnode-stdlib-browser base, with bridge wiring for stdio and the kernel clock
sysalias of util

Importing any of these throws an error with code ERR_ACCESS_DENIED:

cluster, domain, inspector, repl, trace_events, wasi.

Web platform globals expected by modern npm packages are provided in the isolate, including fetch, Headers, Request, and Response. Guest fetch() runs through undici inside the V8 isolate and then through the kernel socket table, so it obeys the same network permissions as the http/net builtins. TextEncoder/TextDecoder, Buffer, URL/URLSearchParams, Blob/File, FormData, AbortController/AbortSignal, structuredClone, and performance are also available.

WebAssembly is enabled inside the isolate (WebAssembly.Module, WebAssembly.Instance, WebAssembly.instantiate*), so packages that ship .wasm work. Compilation stays inside the isolate and does not cross the isolation boundary.

The builtin surface can be narrowed through the platform / allowedBuiltins config; anything excluded becomes a denied builtin (ERR_ACCESS_DENIED).

  • console.log/warn/error serialize arguments with circular-safe, bounded formatting.
  • exec() and run() buffer guest output and return it as the stdout and stderr strings on the result.
  • To observe output incrementally, pass the onStdout/onStderr hooks, which receive raw Uint8Array chunks in emission order; the full strings are still returned when the run ends.

See Output Capture for details.

The core runtime executes JavaScript. For sandboxed TypeScript usage, see TypeScript.

Import a mix of bridge-backed and polyfilled builtins from guest code:

import { NodeRuntime } from "secure-exec";
const rt = await NodeRuntime.create();
try {
const { stdout } = await rt.exec(`
import path from "node:path";
import { createHash } from "node:crypto";
import { writeFileSync, readFileSync } from "node:fs";
const file = path.join("/tmp", "note.txt");
writeFileSync(file, "hello");
const digest = createHash("sha256")
.update(readFileSync(file))
.digest("hex");
console.log(digest);
`);
console.log(stdout.trim());
} finally {
await rt.dispose();
}
  • Module loading and resolution: how ESM/CJS, the node_modules walk, and unmodified npm packages resolve over the virtual filesystem lives in NPM & Module Loading.
  • Output capture: how stdout/stderr are buffered and streamed is documented in Output Capture.