Skip to content

fix(subos): decouple storage from sandbox + transparent bwrap probe errors#293

Merged
Sunrisepeak merged 2 commits into
mainfrom
fix/subos-sandbox-ux-hotfix
May 15, 2026
Merged

fix(subos): decouple storage from sandbox + transparent bwrap probe errors#293
Sunrisepeak merged 2 commits into
mainfrom
fix/subos-sandbox-ux-hotfix

Conversation

@Sunrisepeak
Copy link
Copy Markdown
Member

Summary

V6 storage isolation auto-forced --sandbox entry when storage=image|tmpfs, fusing two axes that V4 design kept orthogonal. Result: xlings subos use <image-subos> silently required root + bwrap + working mount namespace just to switch shells, and any failure cascaded into a run: xlings install bwrap loop with no real diagnostic.

This PR is a UX hotfix covering four items from .agents/docs/sandbox-v6-followup-fixes-2026-05-15.md:

  • P0-1 — Decouple storage from sandbox: subos use <name> without --sandbox always enters shell-level regardless of storage. Image / tmpfs only take effect on the explicit --sandbox path. New warn_storage_dormant_on_shell_ helper emits a one-line stderr hint (eval-safe for --shell <kind> output mode).

  • P0-3 — probe_bwrap_ transparency: replaced system() + 2>/dev/null + bool with platform::run_command_capture + platform::shell_quote, returning (ok, output). New classify_bwrap_probe_error_ matches three known failure modes (setuid build-mode mismatch, AppArmor uid_map block, kernel-disabled userns) and returns actionable hints; unmatched output falls through to raw stderr so users always see the truth.

  • P0-3 (continued) — Re-probe on image-rejects-proot: when image/tmpfs hits the proot fallback rejection, re-probe bwrap and surface the classified error instead of the stale xlings install bwrap suggestion.

  • P0-4 — subos remove umounts image first: detect a live <subos>/.mountpoint and unmount_image_ before rm -rf. Without this, rm -rf either hit EBUSY at the mountpoint or recursed into the live filesystem and erased image contents (home.img marked (deleted)). Linux-guarded.

Cross-platform impact

  • 🐧 Linux: image/tmpfs storage no longer touches sandbox code path on shell-level entry; bwrap probe failures now show real causes; remove safely umounts before rm.
  • 🍎 🪟 macOS/Windows: shell-level path unchanged (env/PATH only); image/tmpfs storage was already unsupported on these platforms, P1-6 (cross-platform storage validation on subos new) is tracked as a separate followup.

User-facing behavior change

Before:

$ xlings subos use mode-image-test
[xlings] storage=image requires sandbox, entering sandbox mode...
[sudo] password for speak:
[error] image storage requires bwrap (proot does not support mount namespace)
[error]   run: xlings install bwrap     ← misleading, bwrap was already installed

After (with bwrap broken/missing on host, but user wanted shell-level only):

$ xlings subos use mode-image-test
[xlings] storage=image is sandbox-only; entering shell-level (use --sandbox to activate)
<xsubos:mode-image-test>$           ← directly enters, no sudo, no bwrap touch

After (when user explicitly does --sandbox but bwrap build is broken):

$ xlings subos use mode-image-test --sandbox
[error] image storage requires bwrap (proot does not support mount namespace)
        xim:bwrap binary was built without setuid support; rebuild xlings-res/bwrap mirror with `-Dsupport_setuid=true` and bump the version
          binary: /home/speak/.xlings/data/xpkgs/xim-x-bwrap/0.11.2/bin/bwrap

Test plan

  • xmake build xlings — clean compile (subos.cppm rebuilt, no errors/warnings on changed code)
  • bash tests/e2e/subos_sandbox_test.sh — passes (S1-S2 verified; S3+ skipped pending live backend, which is the existing behavior)
  • bash tests/e2e/subos_install_remove_isolation_test.sh — passes (remove path unchanged for shared-storage subos)
  • bash tests/e2e/subos_workspace_c2_schema_test.sh — passes (workspace schema unaffected)
  • Manual: image-storage subos + subos use --shell sh — stdout is eval-safe (exports only), stderr has the storage hint, eval "$out" then echo $XLINGS_ACTIVE_SUBOS returns the subos name
  • Manual: image-storage subos + subos remove (no live mount) — succeeds cleanly, directory removed
  • Manual: shared-storage subos + subos use --shell sh — no storage hint emitted (correct)
  • CI: full Linux/macOS/Windows/ArchLinux matrix

Followups (separate PRs)

Tracked in .agents/docs/sandbox-v6-followup-fixes-2026-05-15.md:

  • P1-5: scan stale .mountpoint on sandbox entry (kill -9 / force-close recovery)
  • P1-6: subos new --storage image|tmpfs cross-platform validation (reject at create time on macOS/Windows)
  • P2-7: xim:bwrap install hook sudo prompt UX

…rrors

V6 storage isolation (image/tmpfs) auto-forced `--sandbox` entry,
fusing two axes that V4 design explicitly kept orthogonal. A user with
`storage=image` would silently require root, bwrap, and a working mount
namespace just to switch shells — and any failure cascaded into a
`run: xlings install bwrap` loop with no real diagnostic.

Four changes, all in subos.cppm:

- `use_spawn_shell` / `use_emit_shell`: removed the auto-upgrade to
  sandbox when storage != shared. Storage now only takes effect on the
  explicit `--sandbox` path. New `warn_storage_dormant_on_shell_`
  helper writes a one-line hint to stderr (eval-safe for the
  `--shell <kind>` output mode) so the user knows the storage
  attribute is dormant in this entry.

- `probe_bwrap_`: switched from `system()` + 2>/dev/null + bool to
  `platform::run_command_capture` + `platform::shell_quote`, returning
  `(ok, captured_output)`. New `classify_bwrap_probe_error_` matches
  three known failure modes (setuid build-mode mismatch, AppArmor
  uid_map block, kernel-disabled userns) and returns actionable hints;
  unmatched output falls through to "raw stderr" so users always see
  the truth.

- `use_sandbox_mode_`: when image/tmpfs storage is rejected because
  backend fell back to proot, re-probe bwrap and surface the
  classified error instead of the stale `xlings install bwrap`
  suggestion.

- `remove`: detect a live `<subos>/.mountpoint` (image storage) and
  `unmount_image_` first. Without this, `rm -rf` either hit EBUSY on
  the mountpoint or recursed into the live filesystem and erased
  image contents before EBUSY surfaced (home.img marked `(deleted)`).
  Linux-only branch (`#if defined(__linux__)`).

Docs: changelog entry, V6 design doc updated to reflect orthogonality,
followup-fixes plan added (.agents/docs/sandbox-v6-followup-fixes-
2026-05-15.md) tracking remaining items (stale-mount scan on sandbox
entry, `subos new` cross-platform storage validation).
@Sunrisepeak Sunrisepeak merged commit 76c2523 into main May 15, 2026
3 checks passed
Sunrisepeak added a commit that referenced this pull request May 15, 2026
Includes:
- fix(subos): decouple storage from sandbox + transparent bwrap probe errors (#293)
- fix(subos): avoid bwrap prompts for piped sandbox commands
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.

1 participant