Skip to content

[Homebrew/VFS] Verify the lazy shell before boot and activate Homebrew atomically - #1109

Merged
brandonpayton merged 22 commits into
mainfrom
homebrew/sealed-lazy-lifecycle-qk044
Jul 27, 2026
Merged

[Homebrew/VFS] Verify the lazy shell before boot and activate Homebrew atomically#1109
brandonpayton merged 22 commits into
mainfrom
homebrew/sealed-lazy-lifecycle-qk044

Conversation

@brandonpayton

@brandonpayton brandonpayton commented Jul 26, 2026

Copy link
Copy Markdown
Member

Why

Kandelo's main shell is moving from one large virtual filesystem (VFS) image
to a smaller image whose optional programs are downloaded lazily from Homebrew
bottles.

That design is useful only if Kandelo can prove two things:

  1. Every downloaded package tree is complete and authentic before Node.js or
    browser code reads, changes, combines, exports, or boots it.
  2. Homebrew and all files it needs become visible together. A user must never
    see a partly installed brew command or runtime.

Continuous integration (CI) must also test the exact kernel, programs, and
images built from this pull request. It must not silently replace them with
older released files that happen to have the same names.

This PR establishes those guarantees. It also provides a temporary
source-built shell so pull requests and GitHub Pages can test current source
while the final bottles are rebuilt from merged main.

What changed

Safe lazy package activation

  • Kandelo verifies sealed lazy package groups before composition, inspection,
    mutation, export, restore, or boot.
  • Node.js and browser hosts reject failed verification and clean up partially
    started workers.
  • If a newer browser boot replaces an older one while a download or
    verification is in progress, the older boot cannot become active afterward.
  • Stock Homebrew, its Ruby source, and its bottle-backed runtime become visible
    as one transaction on first use.
  • Node.js and Chromium exercise the same first-party and independent
    third-party tap lifecycle: tap, install, reinstall, export, reboot, verify
    persisted state, perform a pinned no-op upgrade, uninstall, revoke trust, and
    untap.

Exact CI inputs

A build output now keeps its immutable local-generation identity while CI
packages and stages a workspace.

This means CI:

  • freezes one complete generation before staging it;
  • preserves both single-file and multi-file package generations;
  • rejects missing identities, stale cache keys, mixed sessions, incomplete
    package closures, unsafe symlink ancestors, and lookalike paths;
  • detects concurrent source mutation; and
  • fails instead of substituting an older released artifact.

The candidate kernel's public path is intentionally a compatibility symlink.
The workflow now validates that link's namespace, cache identity, build
session, publication claim, and exact bytes, then passes the direct immutable
generation member to acceptance tests.

Honest image-owned demo metadata

The final bottled base shell does not include fbDOOM or modeset, so its
acceptance test does not claim or exercise those optional programs.

The temporary source-built shell does include them. It now composes a separate
profile overlay that:

  • owns only the Doom and modeset profiles;
  • launches the exact executables installed in that image;
  • preserves the Doom asset hash and presentation settings; and
  • fails if metadata advertises an unowned program or launches the wrong path.

A future optional bottle layer can carry those programs and their profiles
without making them part of the base shell.

Publication safety

The canonical shell recipe remains bottle-backed and fail-closed. This PR does
not publish a replacement shell, bottle, package archive, VFS release, or
binary index.

Packages can declare a project-owned publication_state. One shared Rust
policy excludes a pending package and everything that depends on it from
publication. Explicitly requesting a blocked package reports its dependency
chain; unrelated parse, graph, tool, and I/O failures remain real failures.

Durable generation validation also distinguishes an immutable source release
from its new content-addressed destination. Re-homing is allowed only when the
index, snapshot, archive inventory, digests, and producer provenance remain
identical.

Temporary source-shell bridge

Until final bottles are rebuilt from merged main, the temporary shell:

  • builds the exact checkout from checksum-pinned sources;
  • starts with an empty package cache and binary index;
  • cannot fall back to an old bottle or ambient local artifact;
  • boots the resulting image in Node.js and Chromium; and
  • restricts the GitHub Pages path to the expected repository, branch, commit,
    runner, workspace, run, temporary directory, cache, and index.

Failure or cancellation prevents later build, seal, freshness, and deployment
steps.

This broad source-build path is temporary. Final cutover will replace it with
narrower bottle-backed validation.

What the previous hosted run found

The complete run for head
b9ce9cdd04ceade23772aa297b5f49a1831fab6d found four concrete harness and
image-ownership problems:

  1. test-gate-validation expected only
    staged local resolver link retains an external absolute target.
    GNU/Linux correctly rejected the same bad input later as
    local program resolver link targets a noncanonical generation.
    The test now accepts either safe rejection outcome.
  2. The resolver fixture used an anonymous regular kernel.wasm, while the
    real build exposes kernel bytes through a claimed, read-only immutable
    generation. The fixture now models the real ownership contract.
  3. Chromium's modeset test remained at WAITING FOR PAGE_FLIP. The executable
    bytes were correct, but the temporary source shell had copied metadata from
    the lean bottled base and therefore never launched modeset. The source-only
    profile overlay now owns that launch command.
  4. The Node smoke rejected local-binaries/kernel.wasm because that public
    compatibility path is intentionally a symlink. CI now passes the validated
    direct generation member while retaining the strict non-symlink boundary.

The same run otherwise completed the package builds, candidate kernel,
source-rootfs shell build, direct browser-input preparation, fork-instrument
tests, libc tests, POSIX tests, and all Sortix groups. The separate browser
smoke run was green.

What the exact-head replay found

The complete replay for head
680c25bc0c90e198dde40ff3f9b3ae4454bf45c3 established that the ownership
corrections work end to end:

  • all seven selected package builds passed;
  • kernel, fork-instrument, libc, POSIX, Vitest, browser, and every Sortix
    suite passed;
  • the exact source-rootfs shell built and installed;
  • the sealed product tree booted in both Node.js and Chromium; and
  • the separate browser smoke workflow passed.

Its only direct failure was an obsolete migration assertion. The one-shot
#1097 cache-projection bridge expected historical build_deps.rs bytes, but
current source has changed. A current-authority replay also proved that the
rootfs cache identity now differs because declared VFS and rootfs inputs
legitimately changed. Rotating the old pin would therefore misrepresent old
artifacts as current.

The final commit retires that bridge instead:

  • new promotion exposes only complete-tree validation;
  • an early runtime guard rejects a hand-crafted API dispatch of any other
    method;
  • historical evidence stays readable and non-admitted under its frozen pin;
  • a regression proves current validator bytes cannot revive the bridge; and
  • the documented production path is a fresh exact-main rootfs rebuild after
    merge.

Staging’s second red job was only the aggregate gate reflecting that same
validation result; it found no additional failure.

Validation

Local validation for the correction batch:

  • 77 focused binary-resolver and source-rootfs bridge tests;
  • the complete Homebrew main-shell closure contract;
  • the CI suite-group contract on Darwin and amd64 GNU/Linux;
  • host TypeScript typechecking;
  • browser asset and GitHub Pages contracts;
  • Node-smoke bundling;
  • Bash syntax, JSON, and workflow YAML parsing;
  • actionlint, with only the same two pre-existing informational findings as
    the previous head;
  • git diff --check; and
  • the complete package-publication flow, including historical evidence,
    fail-closed dispatch, durable materialization, and merge-candidate lifecycle.

Final exact integration record:

  • Base: 5b13574395acf9a8e46c47c62dc2c38eda3bd3f8
  • Head: a0e0ce0cd65998483a779bc099a9a653bdac3465
  • Tree: bc5f9c2975ec81a3917f94839bd0e3d858bf0cf0
  • 22 linear commits

Fresh hosted validation for that exact head:

These runs are not claimed as passing until every required job reaches a
terminal green result. A quiet or incomplete log is not treated as a failure
and will not be cancelled merely for taking time.

No kernel ABI structure or semantics change in this PR; ABI remains 42.

Remaining cutover after merge

  1. Rebuild the exact-main rootfs dependency closure in parallel.
  2. Build Bash from that closure, then compose and seal rootfs.
  3. Render and merge the first-party tap cutover and record its immutable
    revision in Kandelo.
  4. Run the closed first-party and independent third-party stock-Homebrew
    lifecycle.
  5. Switch the main demo and GitHub Pages to the mostly-lazy bottled shell.
  6. Retire the temporary source-built bridge.

Dinit is already resident in the shell and is not an additional publication
blocker.

Approval

Please do not merge until all hosted gates are green for exact head
a0e0ce0cd65998483a779bc099a9a653bdac3465 and that head has received
explicit approval.

@github-actions

github-actions Bot commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Phase B-1 matrix build status — pr-1109-staging

ABI v42. 7 built, 0 failed, 7 total.

Package Arch Status Sha
homebrew-bootstrap wasm32 built 1ec4e97d
kandelo-sdk wasm32 built 9155d009
mariadb-test wasm32 built e86311db
mariadb-vfs wasm32 built 30eb94ab
mariadb-vfs wasm64 built 64e1f49e
redis-vfs wasm32 built a9f52855
rootfs wasm32 built 9bbf2712

Auto-generated; replaced on each push. Raw data in the publish-status workflow artifact.

brandonpayton and others added 14 commits July 26, 2026 12:27
Make deferred package trees one authenticated atomic unit before Homebrew composition, image export, fetch registration, or consumer callbacks. Carry the complete sealed-tree substrate, cross-host lifecycle contracts, and VFS image tooling onto the admitted-generation base so this first cutover commit is usable and testable on its own.

Keep synchronous assertions synchronous and await authentication only at trust boundaries that can safely propagate failure. Preserve the audited base contract that source fallback, package fetch, Vite build, and sealed browser preview all receive one package-cache trust root.

Keep VFS functional checks here, but leave fetched-catalog, eager-derivative, candidate-output, and browser-step assertions with the later lazy-shell workflow they describe. No ABI version, package archive, bottle, VFS release, or binary index is published by this commit.
Move the remaining shell, rootfs-overlay, mkrootfs, PHP, source-smoke, and Homebrew-base consumers behind imported atomic-seal verification before they inspect, copy, rebase, resolve, mutate, extract, or save derived state.

Recheck browser boot ownership immediately after asynchronous verification so superseded work cannot resume against a stale machine. Cover forged member and cohort images without permitting partial output.
Return the exact filesystem verification promise and recheck ownership in the caller continuation, closing the seal-verification microtask race. Apply the same ownership rule after boot-time fetch, hash, composition, spawn, and closed-asset awaits, and register composed buffers for cleanup before rejecting superseded work.

Exercise both supersession orderings and exact promise identity so a stale browser boot cannot regain authority after an await.
Treat imported lazy-tree seals as trust claims: Node and browser consumers must authenticate them before inspecting, mutating, composing, or booting the image.

Make verified asynchronous restore the product boundary, reject sealed metadata on the synchronous registration path, serialize browser registrations, and tear down half-initialized workers on verification failure. Keep low-level parsing available only with an explicit verify-before-use contract.

Cover shared host behavior, Node worker failure, browser boot rejection, and mkrootfs rejection, and document the same boundary for both hosts.
Register the exact Homebrew source package and its complete bottle-backed runtime support as one first-use transaction, then prove stock brew against first-party and independent third-party taps in shared Node and Chromium lifecycle contracts.

Preserve source-rootfs as the required activation lane until production bottle admission and keep the replacement artifact lock pending, so this preparatory state cannot claim or publish stale local image bytes.

Retain the audited one-cache assertions inherited from the base and adapt them only where this commit changes workflow step boundaries. Fetched-catalog, candidate-output, and browser-step assertions land here with the implementation they describe instead of making a sealed-VFS predecessor demand the future lazy-shell shape.
Preserve the canonical shell bottle provenance instead of resolving and copying Dinit again in every service image. Reject partial, lazy, or non-executable inherited sets, while retaining the legacy registry/source fallback until the shell owns neither binary.
Exercise complete inherited Dinit sets, rejection of partial, lazy, or non-executable sets, legacy registry fallback when the shell owns neither binary, and failure when neither source can provide a coherent service manager.
The lazy-shell projection was generated before the sealed VFS helper changes. Regenerate only the derived program index so each of the six VFS consumers binds the helper bytes it will actually build with.

No source recipe, archive, release, bottle, or binary index is published here; this commit updates cache identity metadata for the combined source tree.
Reconstruct the reviewed live lifecycle harness on the combined atomic-activation product base. Preserve the pinned canary product lock while requiring independently supplied M, TF, and C authority, and bind the exact same candidate inputs across Node and Chromium.

Source-Commit: 4b315aad68a7311f79fc0e06149e32e8d22f43ed

Combined-Base: b0b8e11608b02a0e2ab77af9a4bf44d35a47853f
Bind Kandelo M, first-party tap TF, and independent-canary C to one exact anonymous public main record before any detached candidate work. Keep the reviewed product locks as independent corroboration, reject stale-but-reachable revisions in the closure contract, and document the provisional TF pin.
Keep the canonical shell package bottle-backed and fail-closed while the required PR/main lane stages a distinct, non-published source-rootfs bridge.

Exercise the real browser-worker VFS restore boundary without importing unrelated default artifacts, and pin both explicit-byte and default-artifact BrowserKernel behavior with focused tests.
Run the temporary source product lanes for every relevant repository event so transitive package and shared tool inputs cannot evade validation through a stale path allowlist.

Bind transient fetched-shell cleanup to the resolver canonical immutable generation target, and cover the actual target shape plus Pages trigger mutations.
@brandonpayton
brandonpayton force-pushed the homebrew/sealed-lazy-lifecycle-qk044 branch from b7daa10 to 0104d21 Compare July 26, 2026 16:40
@brandonpayton brandonpayton changed the title [VFS/Homebrew] Authenticate lazy activation and gate the exact lifecycle [VFS/Homebrew] Verify lazy shell images and activate stock brew safely Jul 26, 2026
Make publication readiness a shared dependency-closed authority. Pending packages and their reverse-dependent closures are omitted or rejected consistently before archive or release mutation.

Drive staging, prepare-merge, force-rebuild, and test materialization from the Rust expected ledger. Recheck exact-main, package-source, and durable publication boundaries, with typed blocker evidence so operational failures cannot be mistaken for policy omissions.

Enforce the same-version revision floor on success and failure writers, retain it across ABI pruning, and advance the shell recipe to revision 22 while its publication authority remains pending.

Validated with the full xtask suite, durable package-generation suites, shell closure contract, workflow/scope contracts, package materialization tests, YAML parsing, Bash syntax checks, and the pinned cache-projection transition.
A durable generation preserves the producer snapshot source release tag while publishing identical verified content under a new content-addressed tag. Comparing those locator strings rejected every valid promotion.

Bind the source locator explicitly, normalize only a cloned locator to the destination tag, and then require full snapshot equality. Thread the source tag through preparation, publication, and materialization; keep the one-shot validator transition pin current; and cover successful re-homing plus source-tag, snapshot, index, and archive tampering.
The hosted #1109 staging artifacts prove that the declared Kandelo SDK, MariaDB test, MariaDB VFS, and Redis VFS outputs changed after the shared VFS implementation updates. Advance their package revisions so the human-visible release lineage matches the new payload bytes, then regenerate the checked program-package projection and contextual cache keys.

Rootfs remains at revision 9 because its declared rootfs.vfs payload is byte-identical even though its conservative input-derived cache key moved.
Carry the typed publication-blocker ledger into prepared browser workspaces and source-build each blocked root into an ephemeral local generation. Pin the separately identified source-rootfs shell through build-deps local-libs before rebuilding reverse-dependent VFS images, so exact PR browser validation cannot substitute stale canonical bytes or mutate release state.
Keep direct test compilation out of package-owned program mirrors, and verify the selected shell's existing link set as a preserved subset while allowing unrelated browser packages to add their own mirrors. This prevents unowned regular files from blocking package materialization without weakening generation identity checks.
Keep candidate kernel, userspace, and complete package closures bound to the immutable local generation that produced them while CI stages and resolves the workspace. Freeze the staging snapshot, reject stale identities and unsafe aliases, and fail closed instead of silently substituting released artifacts.

Cover scalar and multi-member closure ownership, portable staging, concurrent mutation, stale-cache, symlink-ancestor, and identityless-root cases.
The first complete hosted run reached every major validation stage, but exposed four mismatches in the test harness:

- Linux rejected an invalid package-generation link at a different valid check than Darwin.
- A resolver test modeled kernel.wasm as anonymous bytes instead of a claimed immutable generation.
- The temporary source shell installed fbDOOM and modeset but copied metadata from the lean bottled base.
- The Node smoke passed a compatibility symlink to a boundary that intentionally accepts only direct regular files.

Assert the stable rejection result across hosts, model the real kernel generation, and pass the validated immutable kernel member to Node without weakening the input boundary.

Give the source shell an image-owned Doom and modeset profile overlay, bind its commands to installed executables, and run the modeset browser proof only for that image. Keep the bottled base honest by testing only software it owns.

Update the source package revision, workflow contracts, focused tests, and authoritative documentation. ABI remains 42.
@brandonpayton brandonpayton changed the title [VFS/Homebrew] Verify lazy shell images and activate stock brew safely [Homebrew/VFS] Verify the lazy shell before boot and activate Homebrew atomically Jul 27, 2026
@brandonpayton brandonpayton added preserve-head-commit Preserve the exact PR head with a merge commit for publication-pinned artifacts. ready-to-ship Maintainer attests the exact tested head and requests merge preparation. labels Jul 27, 2026
@github-actions

Copy link
Copy Markdown
Contributor

prepare-merge: test-gate passed against the synthetic PR merge and sealed merge-candidate-abi-v42-pr-1109-run-30238460784-attempt-1. The canonical ABI index is unchanged. merge-gate=success was posted on PR HEAD. This PR is ready for a reviewer to merge merge; default-branch reconciliation will verify the exact merged tree before activation.

@brandonpayton
brandonpayton merged commit 8d9e855 into main Jul 27, 2026
102 checks passed
@brandonpayton
brandonpayton deleted the homebrew/sealed-lazy-lifecycle-qk044 branch July 27, 2026 05:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

preserve-head-commit Preserve the exact PR head with a merge commit for publication-pinned artifacts. ready-to-ship Maintainer attests the exact tested head and requests merge preparation.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant