test+docs: host-mounted node_modules resolution (#109)#113
Merged
Conversation
railway-app
Bot
temporarily deployed
to
secure-exec / secure-exec-pr-113
June 22, 2026 21:16
Destroyed
railway-app
Bot
temporarily deployed
to
rivet-frontend / secure-exec-pr-113
June 22, 2026 21:17
Destroyed
|
🚅 Deployed to the secure-exec-pr-113 environment in rivet-frontend
🚅 Deployed to the secure-exec-pr-113 environment in secure-exec
|
Add a sidecar integration test that mirrors NodeRuntime.create({ nodeModules }):
project a host node_modules at guest /tmp/node_modules via the host_dir mount
plugin, run a guest program under /tmp, and assert a bare import resolves the
projected package. A missing package reproduces the exact 0.3.0 symptom
('_resolveModule returned non-string'); a present one resolves and runs.
Package resolution through a host-mounted node_modules was broken in 0.3.0 and
is fixed on main by the VFS refactor; this locks the end-to-end behavior in.
Docs: make explicit that 'nodeModules' is a convenience for a read-only
host-directory mount (the same machinery as 'mounts', defaulting to guest
/tmp/node_modules). State it in features/module-loading and add it to the
features/filesystem 'at a glance' list, which previously omitted it.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
NathanFlurry
force-pushed
the
issue-109-nodemodules-regression
branch
from
June 23, 2026 01:19
8ce8bb0 to
ef86eb9
Compare
railway-app
Bot
temporarily deployed
to
secure-exec / secure-exec-pr-113
June 23, 2026 01:19
Destroyed
railway-app
Bot
temporarily deployed
to
rivet-frontend / secure-exec-pr-113
June 23, 2026 01:19
Destroyed
NathanFlurry
added a commit
that referenced
this pull request
Jun 24, 2026
…les symlink coverage
When the host module resolver returns null for a bare specifier, the V8
runtime threw `_resolveModule returned non-string for '<spec>'`. That reads
like an internal type error, but it actually means the module could not be
located, almost always a node_modules layout/discovery problem.
Reword the message to name the importer and state it as a not-found error
with concrete remediation. Also call out the host-mounted node_modules case
(what `NodeRuntime.create({ nodeModules })` projects): a host_dir mount
confines reads to its root, so a package symlinked OUT of the mounted tree
(pnpm/yarn workspace or `file:` deps that link to the workspace root or an
external store) is not followed and surfaces here as not-found. The fix for
that case is to mount a directory containing every symlink target (e.g. the
workspace root), not to widen the mount.
Regression coverage:
- crates/execution/tests/module_resolution.rs: a bare ESM package whose
package.json has `main` but no `exports` map (the `main` fallback is a
separate path from `exports`).
- crates/sidecar/tests/node_modules_symlink_resolution.rs: end-to-end through
a real VM + host_dir mount, projecting a pnpm-style node_modules (top-level
relative symlinks into a `.pnpm` store, plus a scoped package) at guest
/tmp/node_modules and asserting both resolve from guest `import`. The
sibling #113 test only covers a plain layout; this locks in the symlinked
layout that the TypeScript `nodeModules` option actually produces.
Docs: note the escaping-symlink confinement in features/module-loading.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
NathanFlurry
added a commit
that referenced
this pull request
Jun 24, 2026
…les symlink coverage
When the host module resolver returns null for a bare specifier, the V8
runtime threw `_resolveModule returned non-string for '<spec>'`. That reads
like an internal type error, but it actually means the module could not be
located, almost always a node_modules layout/discovery problem.
Reword the message to name the importer and state it as a not-found error
with concrete remediation. Also call out the host-mounted node_modules case
(what `NodeRuntime.create({ nodeModules })` projects): a host_dir mount
confines reads to its root, so a package symlinked OUT of the mounted tree
(pnpm/yarn workspace or `file:` deps that link to the workspace root or an
external store) is not followed and surfaces here as not-found. The fix for
that case is to mount a directory containing every symlink target (e.g. the
workspace root), not to widen the mount.
Regression coverage:
- crates/execution/tests/module_resolution.rs: a bare ESM package whose
package.json has `main` but no `exports` map (the `main` fallback is a
separate path from `exports`).
- crates/sidecar/tests/node_modules_symlink_resolution.rs: end-to-end through
a real VM + host_dir mount, projecting a pnpm-style node_modules (top-level
relative symlinks into a `.pnpm` store, plus a scoped package) at guest
/tmp/node_modules and asserting both resolve from guest `import`. The
sibling #113 test only covers a plain layout; this locks in the symlinked
layout that the TypeScript `nodeModules` option actually produces.
Docs: note the escaping-symlink confinement in features/module-loading.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #109 (NodeRuntime
nodeModulesdoes not resolve external packages in v0.3.0).Diagnosis: the guest error
_resolveModule returned non-string for '<pkg>'is what the V8 side reports whenever the native resolver returns null, i.e. the package was not found through the mountednode_modules. This was a real bug in the published 0.3.0 filesystem/mount-read path. It is already fixed onmainby the VFS refactor (the entirecrates/vfs+crates/secure-exec-vfsand the reworked resolver landed afterv0.3.0; thehost_dirplugin itself is unchanged). Verified against a current-mainbuild: plain packages, CJSrequire,exportsmaps, pnpm symlinks, scoped packages, and subpath exports all resolve.No code fix is needed on main, so this PR is the regression test that would have caught the 0.3.0 break.
What the test does
crates/sidecar/tests/node_modules_host_mount_resolution.rsmirrorsNodeRuntime.create({ nodeModules })end-to-end over the wire:node_modules/mypkg(ESM),/tmp/node_modulesvia thehost_dirmount plugin (read-only),/tmp(so the ancestor-node_moduleswalk reaches the mount),import greet from "mypkg"and asserts it resolves and runs.Lives in its own integration-test binary (one isolate per process), matching the existing guest-executing test convention.
Validation
cargo test -p secure-exec-sidecar --test node_modules_host_mount_resolutionpasses.nodeModulesdoes not resolve external packages in v0.3.0 #109 symptom (_resolveModule returned non-string for 'nonexistent-pkg'), confirming the test fails for the right reason.Docs
The #109 reporter asked whether the behavior was expected/documented. Clarified that
nodeModulesis a convenience for a read-only host-directory mount (same machinery asmounts, defaulting to guest/tmp/node_modules):features/module-loading.mdx: made the mount relationship explicit.features/filesystem.mdx: added an "at a glance" bullet (it previously omittednodeModulesentirely).pnpm --dir website buildpasses.