Packages: Boot login from package-backed images - #1307
Merged
Conversation
The root browser application can contain the account database and session supervision while still lacking the privileged login and sudo program product. In that state it falls back to an ordinary shell and cannot show the authenticated login lifecycle implemented by the platform. Compile one exact closed local product into the root-only browser build, validate every asset and projection, and republish the privileged programs from the selected virtual file system (VFS) image before boot. Explicit demos and custom VFS URLs retain their ordinary product-free behavior. This prototype still obtains its build input from the Homebrew lifecycle harness through KANDELO_LOCAL_LOGIN_PRODUCT_ROOT. It must be reworked to consume the package-backed root product before this change is merge-ready. Source-commit: bb6fc6896e0f35533b72f0f4e24c59b1a5d7fc45
Make ordinary VFS metadata and mount flags the sole set-ID authority. Build login, sudo-lite, sudo, the root filesystem, and browser products through the normal package resolver without a Homebrew runtime path. Share one bounded experimental terminal-session contract across Node and browser hosts, preserve maker login and privilege behavior, and keep Homebrew workflows and package metadata explicitly dormant. Seal local SourceOnly outputs coherently, require current fork metadata at artifact boundaries, and isolate local-build machine JSON from dev-shell output. Launch POSIX conformance programs from real guest paths so tests that exec themselves exercise the same platform path as user software.
brandonpayton
force-pushed
the
draft/package-backed-root-login
branch
from
August 25, 2026 18:41
5306f08 to
0c26196
Compare
brandonpayton
changed the base branch from
fix/package-backed-main-vfs
to
main
August 25, 2026 18:42
brandonpayton
marked this pull request as ready for review
August 25, 2026 18:42
brandonpayton
added a commit
that referenced
this pull request
Aug 26, 2026
## Why Kandelo carried two packaging systems. PR #1307 turned Homebrew off for active image products, builds, tests, and CI, but deliberately left the whole Homebrew implementation dormant in the tree -- a second, unused packaging path that still had to be reasoned about, kept compiling, and maintained. That is exactly the kind of parallel system the platform-values contract warns against: it invites drift and hides which path is real. At the same time, the source-only build path (`./run.sh local-build`) is meant to be the single way user software is built, but it did not actually complete from a clean checkout. An OpenSSL build race made openssl fail to compile, which blocked every downstream product (php, curl, node, nginx, mariadb, wordpress, lamp) -- so the "one true build path" the Homebrew removal depends on was itself broken. This PR removes the dormant Homebrew system entirely so there is one packaging path, decouples the browser build from the removed machinery, and fixes OpenSSL so that single source-only path builds end to end. ## What changed **Remove Homebrew and its staging pipeline** - Delete every Homebrew-named file repo-wide: host producers, the `homebrew/` tap tree, `packages/registry/homebrew-bootstrap`, the homebrew build/publish scripts, apps demos/tests, the `tools/xtask` Homebrew modules, and the disabled Homebrew CI workflows. - Remove the ABI bottle-staging pipeline that existed only to produce and publish Homebrew bottles: fourteen `abi_staging` modules and their subcommands, plus the disabled GitHub Pages deploy workflow. - Result: a case-insensitive sweep for "homebrew"/"brewfile" returns zero matches. **Keep and rename the one non-Homebrew survivor** - The VFS product-catalog code (`product_manifest.rs` + `canonical_json.rs`) is the only part `./run.sh local-build` consumes. Move it out of the `abi_staging` shell: `tools/xtask/src/abi_staging` -> `tools/xtask/src/vfs_products`, and `xtask abi-staging products` -> `xtask vfs products`. **Treat the product catalog as a build output** - `images/vfs/products/generated/catalog.json` is derived from the product manifests, so it is now gitignored and regenerated at the start of every `xtask local-build`, instead of being a committed file that could drift. **Rebuild the browser demo product serving on the catalog** - The Homebrew removal deleted `abi-staging-pages-site-builder.ts` and `abi/staging/legacy-vfs-adapters.toml` but left `vite.config.ts` importing them, which broke the browser build. Replace that with `scripts/vfs-product-deployment.ts`, which serves the same directory-scoped VFS product deployments but derives each product's artifact and build target straight from `catalog.json` instead of the deleted legacy-adapter layer. Directory-scoped deployment is preserved; the legacy indirection is gone. **Converge VFS image naming** - VFS images whose base name collides with a same-named program carry a `-vfs` suffix (`node-vfs`, `mariadb-vfs`, ...). nginx already installed and imported as `nginx-vfs.vfs.zst` but its catalog/package/build still said `nginx.vfs.zst`. Rename the canonical output to `nginx-vfs.vfs.zst` / `nginx-php-vfs.vfs.zst` so the catalog matches the on-disk and browser-import names with no adapter. **Fix the OpenSSL wasm build** - OpenSSL's Makefile does not fully order its generated headers ahead of object compilation under `-j`, so folding `build_generated` into the parallel `libssl.a`/`libcrypto.a` targets let a `.c` compile before its headers existed. Run `build_generated` as its own barrier first (matching the reference formula), then build the libraries in parallel. ## Validation Run in `scripts/dev-shell.sh`: - `cargo test -p xtask --target aarch64-apple-darwin`: 738 unit tests + integration binaries, 0 failed. - Host Vitest passes for the affected VFS/product-builder suites; `check-pages-vfs-product-registry` node tests pass (9/9). - The browser Vite config loads with zero abi-staging/legacy-pages plugins. - `./run.sh local-build` completes source-only: 89/89 nodes, 7/7 products. - The browser demos serve from those artifacts (kernel boots the shell, nginx, wordpress, etc. images). - Case-insensitive repository sweep for "homebrew"/"brewfile": zero matches. ## Known follow-up `scoped-deployments.spec` (an @slow test not run in CI) fails on pre-existing stale hardcoded closure constants (lazy-body and snapshot-member counts) that `origin/main` also carries. My changes do not touch that counting logic, so this is a pre-existing staleness, not a regression here; correcting those constants and their unit fixtures is left to a follow-up. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
brandonpayton
added a commit
that referenced
this pull request
Aug 26, 2026
) ## Why Interactive tab auto-completion in the browser demo shell stopped working. Bash provides that completion through readline; dash does not. PR #1307 ("Boot login from package-backed images") changed the main-shell demo boot from launching bash directly to booting through `login`: before: argv = ["bash", "-l", "-i"] # interactive bash after: argv = ["/usr/bin/login", "-p", "-f", "maker"] `login -f maker` execs the maker account's login shell from images/rootfs/etc/passwd, which was `/bin/sh` -- and `/bin/sh` is a dash alias (see images/rootfs/PACKAGES.toml). So the demo silently went from an interactive bash shell (with completion) to dash (with none). ## What changed Set the maker account's login shell to `/bin/bash` in images/rootfs/etc/passwd. Bash already ships in the image (images/rootfs/PACKAGES.toml maps /usr/bin/bash with a /bin/bash alias, and build-source-rootfs-shell-image.ts requires those aliases), so `login -f maker` now execs an interactive login bash and completion returns. Root keeps `/bin/sh`. Update the two host tests that assert the maker record against the real images/rootfs/etc/passwd -- getpwent.test.ts (the getpwent probe) and demo-login-image.test.ts (the "canonical rootfs data is truthful" guard) -- to expect `/bin/bash` for maker. The demo-login fixture cases that pass an explicit `/bin/sh` to configureDemoLogin are unchanged; they exercise that helper's shell parameter, not the shipped passwd. ## Validation The changed assertions are string-consistent with the edited passwd by construction (getpwent deterministically echoes the passwd shell field). Full browser verification of interactive tab-completion requires booting the rebuilt shell image; the host tests above are the automated guard and run in CI. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
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.
Why
Kandelo's default browser machine needs to reach an authenticated user
shell through the same package, root-filesystem, virtual file system
(VFS), syscall, and host-runtime path used by other software. The
previous prototype depended on a separate Homebrew fixture and could
leave the image with account state but without the matching
loginandsudoprograms.That split made local builds vulnerable to stale or mixed package
generations and made the browser appear more complete than the
underlying product image. Login, privilege changes, and terminal
relaunch now come from ordinary sealed package artifacts and
authoritative kernel/VFS state.
What changed
login,sudo-lite, andsudo, and composethem into package-backed root and browser product images.
nosuidmountstate the privilege authority used by Node.js and browser hosts.
hosts so login exit and relaunch behavior is image-owned.
SourceOnly) builds publish one coherentsealed generation, keep build scripts inside declared
dependency/output roots, and isolate machine JSON from
development-shell diagnostics.
programs that execute themselves.
dormant; it is no longer a runtime or build authority for the default
product.
main's local-gallery and authoritative dinitservice-startup behavior while rebasing this branch.
Validation
after the rebase (86 cache hits, three image nodes rebuilt).
projection.
tests passed (2/2). Dinit status tests passed (3/3).
password, exposed
sudoandsudo-lite, and returned to a freshmaker login after logout. The shell reported UID 1000,
/home/maker, and the expected working directory before and afterrelogin.
and WebKit. Shell, Node.js, nginx, nginx/PHP, WordPress
SQLite/MariaDB, Doom, and OPFS product gates passed.
and no unresolved tests or timeouts. Guest-path launch added about
4.7 ms (0.7%) in the focused runner comparison.
declared flaky case passed.
xtask's 859 unit tests and integration tests passed.skips and expected failures only. Fresh kernel-entry retry/parity
tests passed (13/13).
launcher-wrapper tests (8/8), shell syntax checks, and
git diff --checkpassed.Known gaps
delivery. The defect is documented and the browser case is marked
fixme; its fix is deferred. The Node virtual-network suite passed8/8 and all 212 Sortix UDP cases passed.
23 expected failures, 53 skipped, and five timed out. The three
failures are
nl_typescatalog cases returningENOENT. Threepoll/select timeout cases pass sequentially and appear to be parallel
contention; the remaining two
ppollSA_RESTARTtimeouts areunchanged and outside this branch.