You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
docs: expand kernel spec with full WasmVM integration and proofing section
- Detail WasmVM current state (TCP/TLS/DNS/poll exists but bypasses kernel)
- Specify migration path: driver sockets → kernel socket table
- Add new WASI extensions: net_bind, net_listen, net_accept, net_sendto, net_recvfrom
- Specify C sysroot patches for bind/listen/accept/sendto/recvfrom
- Detail kernel-worker.ts and driver.ts changes
- Specify blocking semantics (Atomics.wait for accept/recv)
- Specify cooperative signal delivery at syscall boundaries
- Add WasmVM-specific test files and C test programs
- Add proofing section: implementation review, conformance re-test,
expectations update, and PRD update via Ralph
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@@ -305,23 +305,253 @@ Runtimes call kernel DNS before falling through to host adapter.
305
305
306
306
## Part 4: WasmVM Integration
307
307
308
-
WasmVM already communicates with the kernel via synchronous RPC (`kernel-worker.ts` with Atomics.wait + SharedArrayBuffer). New kernel network APIs are exposed the same way:
308
+
### 4.1 Current State
309
+
310
+
WasmVM ALREADY has TCP/TLS/DNS/poll support, but it **bypasses the kernel entirely** and goes direct to host:
Current `driver.ts` handlers (`netSocket`, `netConnect`, etc.) manage `_sockets` Map with real Node.js `Socket` objects. After migration:
334
+
-`netSocket` → calls `kernel.socketTable.create()` instead of allocating local ID
335
+
-`netConnect` → calls `kernel.socketTable.connect()` which handles loopback vs external routing
336
+
-`netSend` → calls `kernel.socketTable.send()`
337
+
-`netRecv` → calls `kernel.socketTable.recv()`
338
+
-`netClose` → calls `kernel.socketTable.close()`
339
+
-`netPoll` → calls `kernel.socketTable.poll()` (unified with pipe poll via `kernel.fdPoll()`)
340
+
341
+
**Step 2: Unify socket FDs with kernel FD table**
342
+
343
+
Currently WasmVM socket FDs (`_nextSocketId` in driver.ts) and kernel FDs (`localToKernelFd` map in kernel-worker.ts) are separate number spaces. After migration:
344
+
-`kernel.socketTable.create()` returns a kernel FD
345
+
- Kernel worker maps local WASM FD → kernel socket FD (same `localToKernelFd` map used for files/pipes)
346
+
-`poll()` works across file FDs, pipe FDs, and socket FDs in one call
347
+
348
+
**Step 3: TLS stays in host adapter**
349
+
350
+
TLS handshake requires OpenSSL — it can't run in-kernel. The kernel socket table delegates TLS to the host adapter:
351
+
-`kernel.socketTable.upgradeTls(socketId, hostname)` → host adapter wraps the host-side socket in TLS
352
+
- From the kernel's perspective, the socket is still a kernel socket — TLS is transparent
353
+
354
+
### 4.3 New WASI Extensions for Server Sockets
355
+
356
+
Add to `native/wasmvm/crates/wasi-ext/src/lib.rs` under `host_net`:
WasmVM uses `Atomics.wait()` to block the worker thread during syscalls. For blocking socket operations:
503
+
504
+
-**`accept()`**: If no pending connection, the main thread handler waits for a kernel socket event (connection arrival) before responding. The worker thread stays blocked on `Atomics.wait()`. Timeout: 30s (existing `RPC_WAIT_TIMEOUT_MS`).
505
+
-**`recv()`**: If no data in kernel buffer, main thread waits for data or EOF. Same blocking pattern.
506
+
-**`connect()` to external**: Main thread creates host TCP connection, waits for connect event, then responds.
507
+
-**`connect()` to loopback**: Kernel instantly connects via in-kernel routing — no host wait.
508
+
-**Non-blocking mode**: If `O_NONBLOCK` is set on the socket, kernel returns `EAGAIN` immediately instead of blocking. The WASM program uses `poll()` to wait for readiness.
509
+
510
+
### 4.8 Signal Handler Delivery
511
+
512
+
WASM cannot be interrupted mid-execution. Signals must be delivered cooperatively:
513
+
514
+
1.**Registration**: Add `net_sigaction` WASI extension. WASM program calls `sigaction(SIGINT, handler, NULL)`. Kernel worker stores handler function pointer + signal mask in kernel process table entry.
515
+
516
+
2.**Delivery**: When kernel delivers a signal to a WasmVM process:
517
+
- Kernel sets a `pendingSignals` bitmask on the process entry
518
+
- At next syscall boundary (any `rpcCall` from worker), kernel worker checks `pendingSignals`
519
+
- If signal pending and handler registered: worker invokes the WASM handler function via `instance.exports.__wasi_signal_trampoline(signum)` before returning from the syscall
520
+
- If no handler: default behavior (SIGTERM → exit, SIGINT → exit, etc.)
521
+
522
+
3.**Trampoline**: The C sysroot patch adds a `__wasi_signal_trampoline` export that dispatches to the registered `sigaction` handler. This is called from the JS worker side when a signal is pending.
523
+
524
+
4.**Limitations**:
525
+
- Signals only delivered at syscall boundaries — long-running compute without syscalls won't see signals (WasmVM #10, fundamental WASM limitation)
526
+
-`SIGKILL` always terminates immediately (kernel-enforced, no handler invocation)
527
+
-`SIGSTOP`/`SIGCONT` handled by kernel process table, not user handlers
528
+
529
+
### 4.9 WasmVM-Specific Tests
530
+
531
+
Add to existing test files:
532
+
533
+
```
534
+
packages/wasmvm/test/
535
+
net-socket.test.ts # UPDATE: migrate existing tests to use kernel sockets
536
+
net-server.test.ts # NEW: bind/listen/accept, loopback server
WasmVM WASI extensions (`native/wasmvm/crates/wasi-ext/src/lib.rs`) call these via the existing host import mechanism. The C sysroot patches route `socket()`, `bind()`, `listen()`, `accept()`, `connect()`, `send()`, `recv()`, `close()`, `setsockopt()`, `getsockopt()` through these host imports.
554
+
These programs are built via `native/wasmvm/c/Makefile` (add to `PATCHED_PROGRAMS` since they use `host_net` imports) and tested via the WasmVM driver in vitest.
After the kernel networking consolidation is implemented, a full audit must be performed before the work is considered complete.
711
+
712
+
### 7.1 Implementation Review
713
+
714
+
An adversarial review agent must verify:
715
+
716
+
1.**Kernel completeness**: Every socket operation (create, bind, listen, accept, connect, send, recv, sendto, recvfrom, close, poll, setsockopt, getsockopt) works in the kernel standalone tests without any runtime attached.
717
+
718
+
2.**Node.js migration completeness**: No networking code remains in the Node.js bridge that bypasses the kernel. Specifically verify:
719
+
-`packages/nodejs/src/driver.ts` has no `servers` Map, no `ownedServerPorts` Set, no `netSockets` Map, no `upgradeSockets` Map
720
+
-`packages/nodejs/src/bridge/network.ts` has no `serverRequestListeners` Map, no `activeNetSockets` Map
721
+
-`packages/nodejs/src/bridge-handlers.ts` has no socket Maps
722
+
- All `http.createServer()` calls route through `kernel.socketTable.listen()`
723
+
- All `net.connect()` calls route through `kernel.socketTable.connect()`
724
+
- SSRF validation is in the kernel, not the host adapter
725
+
726
+
3.**WasmVM migration completeness**: No networking code remains in the WasmVM driver that bypasses the kernel. Specifically verify:
727
+
-`packages/wasmvm/src/driver.ts` has no `_sockets` Map, no `_nextSocketId` counter
728
+
- All `netSocket`/`netConnect`/`netSend`/`netRecv`/`netClose` handlers delegate to kernel
729
+
- New handlers exist for `kernelSocketBind`, `kernelSocketListen`, `kernelSocketAccept`, `kernelSocketSendTo`, `kernelSocketRecvFrom`
730
+
- Socket FDs are unified with kernel FD table (no separate number space)
731
+
-`net_bind`, `net_listen`, `net_accept`, `net_sendto`, `net_recvfrom` WASI extensions exist in `lib.rs`
732
+
- C sysroot patches exist for `bind()`, `listen()`, `accept()`, `sendto()`, `recvfrom()`
733
+
-`setsockopt()` no longer returns ENOSYS for supported options
734
+
735
+
4.**Loopback routing**: Verify that a server in one runtime can accept connections from another runtime without any real TCP:
736
+
- Node.js `http.createServer()` on port 8080 → WasmVM `curl http://localhost:8080` works
737
+
- WasmVM `tcp_server` on port 9090 → Node.js `net.connect(9090)` works
738
+
- Neither connection touches the host network stack
739
+
740
+
5.**Permission enforcement**: Verify deny-by-default for all socket operations through the kernel, for both runtimes.
741
+
742
+
6.**Signal delivery**: Verify WasmVM signal handlers fire at syscall boundaries for SIGINT, SIGTERM, SIGUSR1.
743
+
744
+
7.**Resource cleanup**: Verify all sockets, timers, and handles are cleaned up when a process exits, for both runtimes.
745
+
746
+
### 7.2 Conformance Re-test
747
+
748
+
After kernel migration:
749
+
750
+
1. Run the full Node.js conformance suite (`packages/secure-exec/tests/node-conformance/runner.test.ts`)
751
+
2. Run the full WasmVM test suite (`packages/wasmvm/test/`)
752
+
3. Run the full POSIX conformance suite if socket-related os-tests exist
753
+
4. Run the project-matrix suite (`packages/secure-exec/tests/projects/`)
754
+
755
+
### 7.3 Expectations Update
756
+
757
+
Tests that were blocked by networking gaps should be re-tested and reclassified:
758
+
759
+
1. Re-run all 492 FIX-01 (HTTP server) tests — remove expectations for tests that now pass
760
+
2. Re-run all 76 dgram tests — remove expectations for tests that now pass
761
+
3. Re-run https/tls/net/http2 glob tests — reclassify from `unsupported-module` to specific failure reasons
762
+
4. Update `docs-internal/nodejs-compat-roadmap.md` with new pass counts
0 commit comments