Skip to content

Commit 38e8ccd

Browse files
committed
chore: refactor node runitme
1 parent ec440ba commit 38e8ccd

53 files changed

Lines changed: 2342 additions & 2590 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CLAUDE.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,18 @@
1313

1414
- use `docs-internal/glossary.md` for canonical definitions of isolate, runtime, bridge, and driver
1515

16+
## Node Architecture
17+
18+
- read `docs-internal/arch/overview.md` for the component map (NodeProcess, SandboxDriver, NodeDriver, NodeExecutionDriver, ModuleAccessFileSystem, Permissions)
19+
- keep it up to date when adding, removing, or significantly changing components
20+
1621
## Specs Source of Truth
1722

1823
- bridge/runtime/governance requirements are canonical in `openspec/specs/`
1924
- for secure-exec runtime behavior, target Node.js semantics as close to 1:1 as practical
2025
- any intentional deviation from Node.js behavior must be explicitly documented in OpenSpec deltas and reflected in compatibility/friction docs
2126
- use `openspec/specs/README.md` for how to reference baseline capabilities in new change proposals
22-
- track development friction in `docs-internal/friction/secure-exec.md` (mark resolved items with fix notes)
27+
- track development friction in `docs-internal/friction.md` (mark resolved items with fix notes)
2328
- OpenSpec proposals/design/tasks MUST explicitly list the concrete tests to add or update for the change
2429

2530
## Compatibility Project-Matrix Policy

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Secure Exec SDK
22

3-
Run sandboxed Node.js code in both Node and the browser using a driver-based runtime.
3+
Run sandboxed Node.js code using a driver-based runtime.
44

55
## Features
66

@@ -10,8 +10,8 @@ Run sandboxed Node.js code in both Node and the browser using a driver-based run
1010

1111
TODO:
1212

13-
- **Node + Browser**: Same sandbox API on Node (isolated-vm) and browser (Worker isolate).
13+
- **Node runtime**: isolated-vm backed sandbox execution with driver-owned capability wiring.
14+
- **Browser runtime**: temporarily disabled during the driver-owned runtime refactor.
1415
- **Driver-based**: Provide a driver to map filesystem, network, and child_process.
1516
- **Permissions**: Gate syscalls with custom allow/deny functions.
1617
- **Opt-in system features**: Disable network/child_process/FS by omission.
17-

docs-internal/arch/overview.md

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# Architecture Overview
2+
3+
```
4+
NodeRuntime → RuntimeDriver → NodeExecutionDriver
5+
(API) (config) (isolated-vm engine)
6+
```
7+
8+
## NodeRuntime
9+
10+
`src/index.ts`
11+
12+
Public API. Thin facade — delegates everything to the execution driver.
13+
14+
- `run(code)` — execute as module, get exports back
15+
- `exec(code)` — execute as script, get exit code + stdout/stderr
16+
- `dispose()` / `terminate()`
17+
18+
## RuntimeDriver
19+
20+
`src/runtime-driver.ts` (re-exported from `src/types.ts`)
21+
22+
Config object that bundles what the sandbox can access. Deny-by-default.
23+
24+
- `filesystem` — VFS adapter
25+
- `network` — fetch, DNS, HTTP
26+
- `commandExecutor` — child processes
27+
- `permissions` — per-adapter allow/deny checks
28+
- `runtimeHooks` — factories for isolate + execution driver
29+
30+
### createNodeDriver()
31+
32+
`src/node/driver.ts`
33+
34+
Factory that builds a `RuntimeDriver` with Node-native adapters.
35+
36+
- Wraps filesystem in `ModuleAccessFileSystem` (read-only `node_modules` overlay)
37+
- Optionally wires up network and command executor
38+
39+
## NodeExecutionDriver
40+
41+
`src/node/execution-driver.ts`
42+
43+
The engine. Owns the `isolated-vm` isolate and bridges host capabilities in.
44+
45+
- Creates contexts, compiles ESM/CJS, runs code
46+
- Bridges fs, network, child_process, crypto, timers into the isolate via `ivm.Reference`
47+
- Caches compiled modules and resolved formats per isolate
48+
- Enforces payload size limits on bridge transfers
49+
50+
## ModuleAccessFileSystem
51+
52+
`src/node/module-access.ts`
53+
54+
Filesystem overlay that makes host `node_modules` available read-only at `/root/node_modules`.
55+
56+
- Blocks `.node` native addons
57+
- Prevents symlink escapes (resolves pnpm virtual-store paths)
58+
- Non-module paths fall through to base VFS
59+
60+
## Permissions
61+
62+
`src/shared/permissions.ts`
63+
64+
Wraps each adapter with allow/deny checks before calls reach the host.
65+
66+
- `wrapFileSystem()`, `wrapNetworkAdapter()`, `wrapCommandExecutor()`
67+
- Missing adapters get deny-all stubs
File renamed without changes.
File renamed without changes.
Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,26 @@
22

33
## 2026-03-01
44

5-
1. **[resolved]** Default console capture buffered unbounded host memory.
5+
1. **[resolved]** `NodeRuntime` constructor ownership drifted between driver and direct adapter options.
6+
- Symptom: runtime capability and config ownership was split across `NodeRuntime` fallbacks and `createNodeDriver`, which made permission defaults and runtime injection behavior harder to reason about.
7+
- Fix: `NodeRuntime` now requires a `driver`, reads `processConfig`/`osConfig` from `driver.runtime`, and no longer accepts direct constructor adapters/permissions.
8+
- Follow-up: a dedicated pass should continue moving remaining `isolated-vm` execution internals from `NodeRuntime` into the Node driver implementation.
9+
2. **[resolved]** Browser runtime surface needed temporary de-scope during driver boundary refactor.
10+
- Symptom: maintaining Worker/browser runtime paths during Node driver ownership changes increased refactor risk and cross-runtime drift.
11+
- Fix: browser package exports were removed for this phase and browser entrypoints now return deterministic unsupported errors until follow-up restoration.
12+
13+
3. **[resolved]** Default console capture buffered unbounded host memory.
614
- Symptom: runtime execution accumulated console output in host-managed `stdout`/`stderr` arrays by default, enabling memory amplification under high-volume logs.
715
- Fix: runtime now drops console output by default and exposes an explicit streaming hook (`onConsoleLog`) for host-controlled log handling.
816
- Compatibility trade-off: `exec()`/`run()` no longer mirror Node stdout/stderr buffering by default; result payloads no longer expose `stdout`/`stderr` fields, so consumers that need logs must opt into hook-based streaming.
917
- Migration note: switch any `result.stderr` checks to `result.errorMessage` for runtime error assertions.
10-
2. **[resolved]** Node module loading depended on allowlist projection setup and split filesystem paths.
18+
4. **[resolved]** Node module loading depended on allowlist projection setup and split filesystem paths.
1119
- Symptom: sandbox `node_modules` availability varied by `moduleAccess.allowPackages` setup and base filesystem mount location, which added resolver complexity and setup fragility.
1220
- Fix: Node driver now always composes a read-only `/app/node_modules` overlay from `<cwd>/node_modules`, even without a base filesystem adapter. Overlay reads are canonical-path scoped to `<cwd>/node_modules`; writes/mutations remain denied; `.node` native addons are rejected.
1321
- Compatibility trade-off: allowlist-scoped dependency visibility was removed in favor of scoped full-overlay readability under `<cwd>/node_modules`; callers needing stricter package-level exposure must enforce it outside runtime for now.
14-
3. TODO: document extension attack vectors and hardening guidance.
22+
5. TODO: document extension attack vectors and hardening guidance.
1523
- Symptom: extension-oriented threat scenarios are not documented as a consolidated runtime/bridge/driver risk model.
16-
- Next step: add extension-focused vectors and mitigations to `docs-internal/todo/attack-vectors.md`, including memory amplification/buffering abuse, CPU amplification, timer/event-rate amplification, and extension host-hook abuse paths.
24+
- Next step: add extension-focused vectors and mitigations to `docs-internal/attack-vectors.md`, including memory amplification/buffering abuse, CPU amplification, timer/event-rate amplification, and extension host-hook abuse paths.
1725

1826
## 2026-02-28
1927

@@ -87,7 +95,7 @@
8795

8896
2. **[resolved]** Needed host-driven verification path for sandbox HTTP servers.
8997
- Symptom: Hono runner self-tested using in-sandbox `fetch`, which did not verify host-to-sandbox request path.
90-
- Fix: added host-side `NodeProcess.network.fetch(...)` facade and updated `examples/hono/loader` to issue requests and terminate from loader.
98+
- Fix: added host-side `NodeRuntime.network.fetch(...)` facade and updated `examples/hono/loader` to issue requests and terminate from loader.
9199

92100
3. **[resolved]** Bridge bundle could go stale when non-entry bridge files changed.
93101
- Symptom: runtime still used old `dist/bridge.js` behavior (for example `http.createServer` still throwing) after editing `src/bridge/network.ts`.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
- Add Node driver implementation backed by `node:http` (`packages/secure-exec/src/node/driver.ts`, `packages/secure-exec/src/types.ts`, `packages/secure-exec/src/shared/permissions.ts`).
2626

2727
- [x] Expose host-side request path to sandbox servers via `sandbox.network.fetch(...)`.
28-
- Provide a NodeProcess-level network facade and document concurrent run/fetch pattern (`packages/secure-exec/src/index.ts`, `README.md`, `examples/hono/README.md`).
28+
- Provide a NodeRuntime-level network facade and document concurrent run/fetch pattern (`packages/secure-exec/src/index.ts`, `README.md`, `examples/hono/README.md`).
2929
- Validate end-to-end from loader to runner (`examples/hono/loader/src/index.ts`, `examples/hono/runner/src/index.ts`).
3030

3131
- [x] Fix `run()` ESM semantics to match docs (return module exports/default instead of evaluation result).

examples/hono/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ This example uses a loader/runner split:
77
- `src/fetch-handler.ts` exports only `fetch`.
88
- `src/server.ts` boots a real HTTP server using `@hono/node-server`.
99
- `loader/` verifies the running sandbox server by calling `sandbox.network.fetch(...)`
10-
(`NodeProcess.network.fetch(...)`) and then terminates the sandbox from the host.
10+
(`NodeRuntime.network.fetch(...)`) and then terminates the sandbox from the host.
1111
- It also executes a probe that imports the exported fetch handler directly.
1212
- shared loader helpers live in `examples/shared/src/sandbox-runner-utils.ts`.
1313

examples/hono/loader/src/index.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
allowAllFs,
77
allowAllNetwork,
88
NodeFileSystem,
9-
NodeProcess,
9+
NodeRuntime,
1010
createNodeDriver,
1111
} from "../../../../packages/secure-exec/src/index.ts";
1212
import {
@@ -16,23 +16,23 @@ import {
1616
waitForServer,
1717
} from "../../../shared/src/sandbox-runner-utils.ts";
1818

19-
function createProcess(runnerRoot: string, runnerEntry: string): NodeProcess {
19+
function createProcess(runnerRoot: string, runnerEntry: string): NodeRuntime {
2020
const driver = createNodeDriver({
2121
filesystem: new NodeFileSystem(),
2222
useDefaultNetwork: true,
23+
processConfig: {
24+
cwd: runnerRoot,
25+
argv: ["node", runnerEntry],
26+
},
2327
permissions: {
2428
...allowAllFs,
2529
...allowAllNetwork,
2630
...allowAllEnv,
2731
},
2832
});
2933

30-
return new NodeProcess({
34+
return new NodeRuntime({
3135
driver,
32-
processConfig: {
33-
cwd: runnerRoot,
34-
argv: ["node", runnerEntry],
35-
},
3636
});
3737
}
3838

0 commit comments

Comments
 (0)