Skip to content

fix(sandbox): weakerWhenNested reaches the sandbox - #5304

Merged
Exelord merged 7 commits into
mainfrom
claude/project-thread-8qy6yu
Nov 13, 2026
Merged

Exelord merged 7 commits into
mainfrom
claude/project-thread-8qy6yu

Conversation

@Exelord

@Exelord Exelord commented Nov 13, 2026 •

Copy link
Copy Markdown
Member

sandbox.weakerWhenNested takes effect

Before: vx run inside another sandbox (an agent's, or an unprivileged container) that refuses a fresh /proc mount. The task sets weakerWhenNested: true, and nothing changes:

$ vx run build --all
┌─ packages/lib#build > failed (exit 1)
$ mkdir -p dist && cp src/a.txt dist/
├─ OUTPUT ────────────────────────────────────────────────────
[vx] packages/lib#build: sandbox not available: a sandboxed `true` failed (exit 1): bwrap: Can't mount proc on /newroot/proc: Operation not permitted
└─ packages/lib#build ── (0ms) failed (exit 1)

After: the same workspace, same outer sandbox.

$ vx run build --all
            success miss           —    65ms     —      —      2B       0 local          packages/lib#build

Without the flag, the refusal now names it: … — a fresh /proc cannot be mounted here (an unprivileged container, or a sandbox around vx): set sandbox.weakerWhenNested: true on every sandboxed task.

Also:

  • A release publishes @vzn/vx only once npm shows every platform package. npm showed @vzn/vx@0.0.656 at 13:21:50 UTC and @vzn/vx-linux-x64@0.0.656 at 13:26:38 (0.0.654: two minutes apart), and an install in between took no binary. The 0.0.656 publish job then failed its own 10-minute "served" wait; both waits are now 15 minutes.
  • bunx @vzn/vx-migrate pins the plugins it adds to the vx the workspace has installed, not to its own version. A bunx-cached 0.0.655 migrate put @vzn/vx-lockfile@0.0.655 beside @vzn/vx@0.0.656. vx init's next line names bunx @vzn/vx-migrate@<running version>.

How it works

The sandbox runtime reads enableWeakerNestedSandbox from its run-wide config only; vx passed it per call, where it is dropped. The availability probe and each task's wrap now set it on the run's config for their own span, the way unixSockets and gitConfig already are, so a task without the flag in the same run keeps the fresh /proc. The docs said the flag lets a sandboxed task start a sandbox; it does not (the secure wrapper forbids nested user namespaces), and docs/schema.md and docs/modules/sandbox.md now say what it is for.

scripts/release.ts publish linux sends the platform packages, waits until the registry answers 200 for each one's version and tarball (the darwin ones too, up to 15 min), then sends @vzn/vx and the plugins. npm processes a publish after the command returns and not in order, so publishing in order was not enough.

workspaceVersion(root) in vx-migrate reads node_modules/@vzn/vx/package.json and falls back to the package's own version.

Breaking changes

None.

Proof

  • Tests (each fails without this change): tests/sandbox-weaker-nested.unsafe.test.ts (a task that sets it binds /proc; one without it in the same run keeps the fresh mount; the probe wraps the weaker way and restores the config); tests/sandbox-hint.test.ts "names the same field when the container refuses a fresh /proc"; tests/release.test.ts publishSettled (waits between the platform packages and @vzn/vx; publishes no @vzn/vx when one never shows; the darwin job waits for nothing); vx-migrate/tests/adopt.test.ts "pins to the workspace's installed vx, so a plugin beside it is not missing"; tests/init.test.ts "the next line names vx-migrate at the running version". tests/sandbox-lifecycle.unsafe.test.ts pinned the dropped per-call option and now reads the wrapper's answer.
  • Driven with a compiled binary inside an outer bwrap --unshare-user: refused before, runs after.
  • Perf: no change to a run with no weakerWhenNested task (the per-wrap config path is taken only when one sets it).
  • Docs updated: docs/schema.md, docs/modules/sandbox.md, docs/cli.md.
  • No conformance case: the first needs a host that refuses a /proc mount, the others are the release script and the migrate CLI's install step.

🤖 Generated with Claude Code

https://claude.ai/code/session_01W6xuYbJ4nYmSNRHVnNwVx2

The runtime reads the weaker nested profile from its run-wide config,
so the per-call flag did nothing: in a container that refuses a fresh
/proc mount, or under an agent's sandbox, every sandboxed task failed
with the flag set as without it. The probe and each task's wrap now set
it for their own span, and the refusal names the field.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W6xuYbJ4nYmSNRHVnNwVx2
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W6xuYbJ4nYmSNRHVnNwVx2
npm showed @vzn/vx@0.0.656 five minutes before @vzn/vx-linux-x64 at
that version (0.0.654: two minutes), and an install in between took no
binary. The publish now waits for every platform package's version and
tarball before it sends @vzn/vx.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W6xuYbJ4nYmSNRHVnNwVx2
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W6xuYbJ4nYmSNRHVnNwVx2
0.0.656's linux-x64 package took over 8 minutes and the 10-minute wait
failed the publish job, so the server images were skipped.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W6xuYbJ4nYmSNRHVnNwVx2
vx-migrate pinned what it installs to its own version. From bun x's
day-old cache, or beside a vx older than the newest, that put a lockfile
plugin of one release next to a vx of another (0.0.655 next to 0.0.656,
its peer unmet). It now pins to the installed @vzn/vx, and vx init's
next line names vx-migrate at its own version.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W6xuYbJ4nYmSNRHVnNwVx2
@Exelord
Exelord merged commit 4a12e38 into main Nov 13, 2026
11 of 14 checks passed
@Exelord
Exelord deleted the claude/project-thread-8qy6yu branch November 13, 2026 13:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants