Skip to main content
Get Started
Reference

Node.js Compatibility

Which node builtins guest code can import in Secure Exec, and how each one is backed.

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).

How builtins are backed

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.

A guest never falls through to a real host builtin. Anything not bridged or polyfilled is denied, so there is no path where guest code reaches the host runtime.

Bridge-backed builtins

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.

Polyfilled builtins

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

Denied builtins

Importing any of these throws an error with code ERR_ACCESS_DENIED:

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

Global APIs

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.

Restricting the builtin surface

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

This config (CreateVmConfig.jsRuntime) rides the wire CreateVmConfig and is not currently exposed through NodeRuntime.create(). See Runtime Platform for the platform ladder and TypeScript SDK for the config shape.

Logging behavior

  • 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.

TypeScript

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

Example

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();
}

See also

  • 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.