secure-exec runs untrusted code in isolate-based sandboxes, the same core pattern used by modern browsers and Cloudflare Workers. Host access is blocked by default and only available through explicitly configured capabilities.
Runtime Guarantees
When you run untrusted code throughsecure-exec, the runtime enforces:
- Isolate containment: code runs inside an isolate with no direct access to host APIs.
- Capability gating: filesystem, network, process spawn, and environment access are all blocked unless you explicitly allow them.
- Timing hardening: high-resolution timers are frozen by default to mitigate timing side channels.
- Resource limits: CPU time and memory are bounded so user code can’t run forever or exhaust the host.
Trust Boundaries
There are two boundaries you need to think about:Runtime Boundary
The isolate + bridge insidesecure-exec.
Untrusted code is confined to an isolate. It can only reach host capabilities through drivers you configure and permission checks the runtime enforces. This is what secure-exec handles for you.
Host Boundary
Your process, container, or serverless runtime. The host process itself is trusted infrastructure. You’re responsible for hardening it. For internet-facing workloads with untrusted input, deploy in an already-hardened environment like AWS Lambda, Google Cloud Run, or a similar sandboxed platform. Both boundaries matter. The isolate alone isn’t enough without a hardened host, and a hardened host alone doesn’t protect against code running with full API access inside your process.Module Loading Boundary
Node runtime executions always expose a read-only dependency overlay at/app/node_modules sourced from <cwd>/node_modules (default cwd is host process.cwd(), configurable via moduleAccess.cwd).
- Overlay reads are constrained to canonical paths under
<cwd>/node_modules. - Overlay paths under
/app/node_modulesare read-only runtime state. - Native addons (
.node) are rejected in this mode. - Access outside overlay paths remains permission-gated and deny-by-default.
node_modules.
Timing Hardening
By default, high-resolution timers are frozen to make timing side-channel attacks harder. You can turn this off if you need Node-compatible advancing clocks. In default"freeze" mode (timingMitigation: "freeze"):
Date.now()andperformance.now()return frozen values within an execution.process.hrtime(),process.hrtime.bigint(), andprocess.uptime()follow the hardened path.SharedArrayBufferis unavailable. Shared memory between threads can be used to build high-resolution timers that bypass frozen clocks.
timingMitigation: "off" gives you normal advancing clocks but weaker side-channel protection.
Resource Limits
Two controls prevent runaway execution:cpuTimeLimitMs: CPU time budget for the runtime. When exceeded, the process exits with code124anderrorMessageincludesCPU time limit exceeded.memoryLimit: Isolate memory cap in MB (default128).
ERR_SANDBOX_PAYLOAD_TOO_LARGE instead of exhausting host memory. Hosts can tune these limits within bounded safe ranges, but cannot disable enforcement.
Logging Contract
- Console output is not buffered into runtime-managed execution result fields by default (
exec()/run()do not exposestdout/stderrresult fields). - To consume logs, configure the optional
onStdiostreaming hook and forward events to your own sink. - This avoids implicit host-memory growth from untrusted high-volume logging.