Skip to content

Commit ec440ba

Browse files
committed
chore: add driver-owned runtime proposal and refresh polyfills
1 parent 8f85633 commit ec440ba

File tree

5 files changed

+200
-0
lines changed

5 files changed

+200
-0
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
schema: spec-driven
2+
created: 2026-03-01
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
## Context
2+
3+
`NodeProcess` currently combines two responsibilities: runtime orchestration and Node/`isolated-vm` execution internals. The constructor also accepts direct capability adapters and permissions, while `createNodeDriver` separately applies capability/permission defaults. This split causes ownership ambiguity and policy drift.
4+
5+
This change adopts a strict boundary:
6+
- Driver owns capability and execution-heavy behavior.
7+
- `NodeProcess` remains the orchestrator for bridge/loader flow over a generic runtime-driver interface.
8+
- Runtime configuration (`processConfig`, `osConfig`) is always injected by `NodeProcess`, but sourced from the driver contract.
9+
10+
For this phase, browser support is intentionally disabled to reduce cross-runtime coupling while the Node boundary is refactored.
11+
12+
## Goals / Non-Goals
13+
14+
**Goals:**
15+
- Make `driver` required for `NodeProcess` construction.
16+
- Remove direct capability injection from `NodeProcess` options and move ownership into driver construction.
17+
- Normalize permission behavior to deny-by-default at driver level.
18+
- Keep bridge/loader orchestration in `NodeProcess` but drive it through a generic execution interface.
19+
- Preserve current Node runtime behavior (run/exec semantics, active-handle wait, host network facade) after boundary changes.
20+
- Temporarily disable browser runtime exports/paths during refactor.
21+
22+
**Non-Goals:**
23+
- Restoring browser runtime support in this change.
24+
- Introducing new sandbox capabilities beyond the existing fs/network/child_process/process/os/runtime set.
25+
- Reworking bridge module semantics unrelated to ownership boundaries.
26+
27+
## Decisions
28+
29+
### 1. Require driver at construction
30+
31+
**Choice:** `NodeProcessOptions.driver` becomes required, and constructor fallback logic that synthesizes a driver from raw options is removed.
32+
33+
**Rationale:** This creates a single capability source of truth and removes implicit behavior.
34+
35+
**Alternative considered:** Keep optional driver for backward compatibility. Rejected because it preserves split ownership and duplicate policy paths.
36+
37+
### 2. Introduce a generic runtime-driver interface
38+
39+
**Choice:** Define an internal runtime-driver contract that exposes execution lifecycle and capability handles consumed by `NodeProcess`.
40+
41+
**Rationale:** `NodeProcess` can remain bridge/loader orchestrator while runtime-specific heavy lifting lives in Node driver implementation.
42+
43+
**Alternative considered:** Move all runtime orchestration into driver and reduce `NodeProcess` to a shell. Rejected for this phase because bridge/loader sequencing remains shared orchestration logic.
44+
45+
### 3. Move execution-heavy Node/isolated-vm behavior into Node driver
46+
47+
**Choice:** Shift isolate lifecycle, module execution/caching, dynamic import handling, and host-side marshalling internals into Node driver-owned implementation.
48+
49+
**Rationale:** These are runtime-specific concerns and should not live in a generic process facade.
50+
51+
**Alternative considered:** Keep runtime internals in `NodeProcess` and only change constructor options. Rejected because it does not achieve driver ownership goals.
52+
53+
### 4. Keep `processConfig`/`osConfig` injected by `NodeProcess`, sourced from driver
54+
55+
**Choice:** `NodeProcess` continues to perform bridge/bootstrap injection, but config values are read from the required driver.
56+
57+
**Rationale:** Preserves deterministic initialization point while aligning configuration ownership with driver contract.
58+
59+
**Alternative considered:** Driver performs direct bridge/global injection. Rejected because it duplicates orchestration coupling and weakens shared initialization control.
60+
61+
### 5. Deny-by-default capability policy in driver
62+
63+
**Choice:** Driver defaults to reject all operations when permissions are not explicitly granted.
64+
65+
**Rationale:** Security baseline should be explicit and centralized at the driver boundary.
66+
67+
**Alternative considered:** Preserve permissive fallback when adapters are present. Rejected as unsafe and inconsistent with intended sandbox posture.
68+
69+
### 6. Temporarily disable browser runtime paths
70+
71+
**Choice:** Comment out browser-facing exports/integration paths for this phase and treat browser runtime as unsupported until re-enabled by a follow-up change.
72+
73+
**Rationale:** Avoids maintaining parallel runtime semantics while core ownership boundaries are being refactored.
74+
75+
**Alternative considered:** Keep browser runtime compiling with compatibility shims. Rejected to keep scope tight and reduce regression surface.
76+
77+
## Risks / Trade-offs
78+
79+
- **Breaking constructor and options contract** -> Mitigation: explicit migration in proposal/tasks and targeted updates across tests/examples/docs.
80+
- **Behavior drift during runtime logic move** -> Mitigation: preserve existing execution scenarios (CJS/ESM exports, dynamic import timing, active-handle waits, host network path) as acceptance criteria.
81+
- **Permission default regression** -> Mitigation: enforce driver-level deny-default tests and remove remaining permissive fallback paths.
82+
- **Temporary browser disable may affect consumers** -> Mitigation: clearly document unsupported status and require explicit follow-up change to restore.
83+
84+
## Migration Plan
85+
86+
1. Define new runtime-driver interfaces in `types.ts` and update `NodeProcessOptions` to require `driver`.
87+
2. Refactor Node driver implementation to own execution-heavy runtime responsibilities and capability policy defaults.
88+
3. Update `NodeProcess` to consume runtime-driver interface and keep bridge/loader orchestration + config injection flow.
89+
4. Disable/comment browser integration paths and document temporary unsupported state.
90+
5. Update tests and examples to required-driver construction; remove direct constructor adapter usage.
91+
6. Update compatibility/friction documentation and add follow-up for browser restoration.
92+
93+
## Open Questions
94+
95+
- Should browser disable behavior surface as compile-time absence of exports, runtime throw, or both?
96+
- Do we keep any public mutator methods on `NodeProcess` (currently none required), or make process instances fully immutable post-construction?
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
## Why
2+
3+
`NodeProcess` currently mixes orchestration with Node/`isolated-vm` runtime implementation details, which makes driver boundaries unclear and keeps capability policy split across constructor fallbacks and driver construction. We need a strict driver-owned runtime model now to simplify ownership, enforce deny-by-default behavior consistently, and make future runtime backends easier to add.
4+
5+
## What Changes
6+
7+
- **BREAKING**: Require `NodeProcess` to be created with a `driver`; remove fallback construction of a default driver inside `NodeProcess`.
8+
- **BREAKING**: Remove direct capability injection options from `NodeProcess` (`filesystem`, `networkAdapter`, `commandExecutor`, `permissions`) and centralize capability setup in driver construction.
9+
- Move Node/`isolated-vm` execution-heavy responsibilities into `NodeDriver` implementation, while keeping `NodeProcess` as the bridge/loader orchestrator over a generic runtime-driver interface.
10+
- Make permission defaults explicit and secure: driver defaults to reject/deny all capability access unless permission checks are provided.
11+
- Keep `processConfig` and `osConfig` as required runtime inputs on `NodeProcess`, sourced from driver-owned configuration and injected by `NodeProcess` during bridge/bootstrap.
12+
- Temporarily disable browser runtime support for this phase by commenting out browser-facing paths and exports until driver-boundary refactor is stabilized.
13+
14+
## Capabilities
15+
16+
### New Capabilities
17+
- None.
18+
19+
### Modified Capabilities
20+
- `runtime-execution-model`: tighten runtime construction contract (required driver), shift runtime ownership to driver-backed execution interface, and temporarily remove browser execution path requirements.
21+
22+
## Impact
23+
24+
- `packages/sandboxed-node/src/index.ts` (`NodeProcess` options/constructor contract, runtime orchestration boundary, browser exports and references)
25+
- `packages/sandboxed-node/src/node/driver.ts` (driver-owned runtime execution logic, deny-by-default permission defaults)
26+
- `packages/sandboxed-node/src/types.ts` (new generic runtime-driver interfaces and updated public constructor types)
27+
- `packages/sandboxed-node/src/browser/*` (commented-out or temporarily disabled integration surface)
28+
- `packages/sandboxed-node/tests/index.test.ts` and related call sites currently using `new NodeProcess()` or direct constructor adapters
29+
- README/examples/docs referencing constructor patterns and browser runtime availability
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
## MODIFIED Requirements
2+
3+
### Requirement: Unified Sandbox Execution Interface
4+
The project SHALL provide a stable Node sandbox execution interface, with `NodeProcess` exposing an `exec` path for running untrusted code and returning structured execution results, and a `run` path that returns module exports. Browser runtime execution support SHALL be disabled for this change phase.
5+
6+
#### Scenario: Execute code in Node runtime
7+
- **WHEN** a caller creates `NodeProcess` with a valid driver and invokes `exec`
8+
- **THEN** the sandbox MUST run the provided code in an isolated execution context and return structured output for the caller
9+
10+
#### Scenario: Browser runtime is disabled for this phase
11+
- **WHEN** a caller attempts to use browser sandbox runtime entrypoints during this change phase
12+
- **THEN** browser runtime execution MUST be unavailable under the runtime contract until a follow-up change restores support
13+
14+
#### Scenario: Run CJS module and retrieve exports
15+
- **WHEN** a caller invokes `run()` with CommonJS code that assigns to `module.exports`
16+
- **THEN** the result's `exports` field MUST contain the value of `module.exports`
17+
18+
#### Scenario: Run ESM module and retrieve namespace exports
19+
- **WHEN** a caller invokes `run()` with ESM code that uses `export` declarations
20+
- **THEN** the result's `exports` field MUST contain the module namespace object with all named exports and the `default` export (if declared)
21+
22+
#### Scenario: Run ESM module with only a default export
23+
- **WHEN** a caller invokes `run()` with ESM code containing `export default <value>`
24+
- **THEN** the result's `exports` field MUST be an object with a `default` property holding that value
25+
26+
#### Scenario: Run ESM module with named and default exports
27+
- **WHEN** a caller invokes `run()` with ESM code containing both `export default` and named `export` declarations
28+
- **THEN** the result's `exports` field MUST be an object containing both the `default` property and all named export properties
29+
30+
### Requirement: Driver-Based Capability Composition
31+
Runtime capabilities SHALL be composed through host-provided drivers so filesystem, network, and child-process behavior are controlled by configured adapters rather than hardcoded runtime behavior. `NodeProcess` construction SHALL require a driver.
32+
33+
#### Scenario: Node process uses configured adapters
34+
- **WHEN** `NodeProcess` is created with a driver that defines filesystem, network, and command-execution adapters
35+
- **THEN** sandboxed operations MUST route through those adapters for capability access
36+
37+
#### Scenario: Missing permissions deny capability access by default
38+
- **WHEN** a driver is configured without explicit permission allowance for a capability domain
39+
- **THEN** operations in that capability domain MUST be denied by default
40+
41+
#### Scenario: Omitted capability remains unavailable
42+
- **WHEN** a capability adapter is omitted from runtime configuration
43+
- **THEN** corresponding sandbox operations MUST be unavailable or denied by the runtime contract
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
## 1. Driver Contract and Public API
2+
3+
- [ ] 1.1 Add a generic runtime-driver interface in `packages/sandboxed-node/src/types.ts` for execution lifecycle, capability handles, and runtime config access
4+
- [ ] 1.2 Update `NodeProcessOptions` in `packages/sandboxed-node/src/index.ts` to require `driver` and remove direct constructor capability options (`filesystem`, `networkAdapter`, `commandExecutor`, `permissions`)
5+
- [ ] 1.3 Remove optional constructor fallback driver creation in `NodeProcess` and simplify permission precedence to driver-owned policy only
6+
7+
## 2. Move Node Runtime Heavy Lifting into Driver
8+
9+
- [ ] 2.1 Refactor `packages/sandboxed-node/src/node/driver.ts` to own Node/`isolated-vm` execution-heavy logic (isolate lifecycle, module execution, dynamic import internals, host marshalling)
10+
- [ ] 2.2 Keep bridge/loader orchestration in `NodeProcess` over the new generic driver interface, with `processConfig` and `osConfig` injected by `NodeProcess` from driver-provided values
11+
- [ ] 2.3 Remove obsolete runtime-specific methods/fields from `NodeProcess` that become driver-owned
12+
13+
## 3. Enforce Deny-by-Default Driver Policy
14+
15+
- [ ] 3.1 Update Node driver defaults so missing permission checks reject all capability access
16+
- [ ] 3.2 Remove permissive fallback behavior in driver construction paths and align all driver-created adapters with explicit allow semantics
17+
- [ ] 3.3 Add/adjust tests for deny-by-default behavior under required-driver construction
18+
19+
## 4. Temporarily Disable Browser Surface
20+
21+
- [ ] 4.1 Comment out browser-facing exports and integration paths in `packages/sandboxed-node/src/index.ts` and package entrypoints for this phase
22+
- [ ] 4.2 Comment out browser runtime implementation paths under `packages/sandboxed-node/src/browser/` as needed to keep Node runtime refactor isolated
23+
- [ ] 4.3 Add clear temporary unsupported notes in code/docs to track browser restoration as follow-up work
24+
25+
## 5. Migrate Tests, Docs, and Validation
26+
27+
- [ ] 5.1 Update `packages/sandboxed-node/tests/index.test.ts` and related call sites to always construct `NodeProcess` with a driver
28+
- [ ] 5.2 Update README/examples/OpenSpec references that mention old constructor fallback or direct capability options
29+
- [ ] 5.3 Log migration friction and fix notes in `docs-internal/friction/sandboxed-node.md`, including temporary browser disable and restore follow-up
30+
- [ ] 5.4 Run targeted checks in `packages/sandboxed-node`: `pnpm run check-types`, targeted `vitest` coverage for NodeProcess/driver behavior, and any required spec-conformance checks

0 commit comments

Comments
 (0)