Skip to content

Packages: Boot login from package-backed images - #1307

Merged
brandonpayton merged 3 commits into
mainfrom
draft/package-backed-root-login
Aug 25, 2026
Merged

Packages: Boot login from package-backed images#1307
brandonpayton merged 3 commits into
mainfrom
draft/package-backed-root-login

Conversation

@brandonpayton

@brandonpayton brandonpayton commented Aug 19, 2026

Copy link
Copy Markdown
Member

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 login and
sudo programs.

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

  • Add package recipes for login, sudo-lite, and sudo, and compose
    them into package-backed root and browser product images.
  • Make ordinary VFS set-user-ID/set-group-ID metadata and nosuid mount
    state the privilege authority used by Node.js and browser hosts.
  • Share one bounded experimental terminal-session contract between both
    hosts so login exit and relaunch behavior is image-owned.
  • Make local source-only (SourceOnly) builds publish one coherent
    sealed generation, keep build scripts inside declared
    dependency/output roots, and isolate machine JSON from
    development-shell diagnostics.
  • Launch POSIX conformance programs from real guest paths, including
    programs that execute themselves.
  • Keep the Homebrew package and workflow implementation explicitly
    dormant; it is no longer a runtime or build authority for the default
    product.
  • Preserve main's local-gallery and authoritative dinit
    service-startup behavior while rebasing this branch.

Validation

  • Local SourceOnly build: 89/89 graph nodes and 7/7 products succeeded
    after the rebase (86 cache hits, three image nodes rebuilt).
  • Production browser Vite build passed against that sealed SourceOnly
    projection.
  • Rebased live Chromium gate: shell Bash/Vim/NetHack and Node.js product
    tests passed (2/2). Dinit status tests passed (3/3).
  • Manual browser lifecycle verified maker login, rejected a bad
    password, exposed sudo and sudo-lite, and returned to a fresh
    maker login after logout. The shell reported UID 1000,
    /home/maker, and the expected working directory before and after
    relogin.
  • Cross-browser SourceOnly suite: 84/84 passed across Chromium, Firefox,
    and WebKit. Shell, Node.js, nginx, nginx/PHP, WordPress
    SQLite/MariaDB, Doom, and OPFS product gates passed.
  • POSIX: 174 passed, zero failed, three expected failures, two skipped,
    and no unresolved tests or timeouts. Guest-path launch added about
    4.7 ms (0.7%) in the focused runner comparison.
  • libc: 303 passed, zero failed, 20 documented expected failures; the
    declared flaky case passed.
  • Kernel workspace: 1,585 tests plus documentation tests passed.
    xtask's 859 unit tests and integration tests passed.
  • Host Vitest shards: 4,323 tests passed in aggregate, with expected
    skips and expected failures only. Fresh kernel-entry retry/parity
    tests passed (13/13).
  • ABI snapshot check passed with ABI version 43 unchanged.
  • Package-index regeneration/check, product-catalog tests,
    launcher-wrapper tests (8/8), shell syntax checks, and
    git diff --check passed.

Known gaps

  • The browser network-lab UDP test can hang after successful packet
    delivery. The defect is documented and the browser case is marked
    fixme; its fix is deferred. The Node virtual-network suite passed
    8/8 and all 212 Sortix UDP cases passed.
  • The full Sortix run is not claimed green: 5,029 passed, three failed,
    23 expected failures, 53 skipped, and five timed out. The three
    failures are nl_types catalog cases returning ENOENT. Three
    poll/select timeout cases pass sequentially and appear to be parallel
    contention; the remaining two ppoll SA_RESTART timeouts are
    unchanged and outside this branch.

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
brandonpayton force-pushed the draft/package-backed-root-login branch from 5306f08 to 0c26196 Compare August 25, 2026 18:41
@brandonpayton brandonpayton changed the title Browser: Prototype login and sudo for the root product Packages: Boot login from package-backed images Aug 25, 2026
@brandonpayton
brandonpayton changed the base branch from fix/package-backed-main-vfs to main August 25, 2026 18:42
@brandonpayton
brandonpayton marked this pull request as ready for review August 25, 2026 18:42
@brandonpayton
brandonpayton merged commit 48b6692 into main Aug 25, 2026
@brandonpayton
brandonpayton deleted the draft/package-backed-root-login branch August 25, 2026 19:41
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)
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