Skip to content

[Homebrew/Packaging] Make guest brew a reproducible product package#1074

Closed
brandonpayton wants to merge 25 commits into
mainfrom
integration/homebrew-bootstrap-after-atomic-qk044
Closed

[Homebrew/Packaging] Make guest brew a reproducible product package#1074
brandonpayton wants to merge 25 commits into
mainfrom
integration/homebrew-bootstrap-after-atomic-qk044

Conversation

@brandonpayton

@brandonpayton brandonpayton commented Jul 23, 2026

Copy link
Copy Markdown
Member

Why

Kandelo needs a reproducible copy of upstream Homebrew in order to run the normal brew command inside a guest. That copy was previously produced only as a side effect of a larger image build, so it had no independent package identity or durable publication path.

The publisher also has tools that are needed only on the native CI host. Homebrew calls these tools Requirements. They must be available while a Formula is built without becoming dependencies that a Kandelo guest tries to install.

Finally, a VFS image is a product artifact. Missing pinned source files, dropped symlinks, failed writes, or exhausted image capacity must stop publication instead of producing a partial image.

What changes

Package guest Homebrew

  • Add homebrew-bootstrap as a normal Kandelo program package.
  • Pin one exact upstream Homebrew commit, the reviewed Kandelo platform patch, licenses, prepared source tree, portable Ruby, Git version, ZIP digest, and byte count.
  • Build from the resolver-provided sealed Git checkout without a second source download or ambient credentials.
  • Reject dirty repositories, hooks, executable Git configuration, credential helpers, alternate object stores, wrong source identities, and output drift.

Model publisher-only tools correctly

  • Represent supported Homebrew Requirement classes as native publisher inputs instead of guest runtime dependencies.
  • Generate and validate one bounded lifecycle plan through the real Homebrew parser.
  • Prove the complete publisher lifecycle offline so undeclared network or host inputs cannot slip into publication.
  • Keep unsupported Requirements visible as explicit failures.

Refuse incomplete VFS products

  • Propagate file-copy, symlink, capacity, serialization, and required-fixture failures before an image can be published.
  • Give shell-derived images one reviewed capacity contract.
  • Require the complete curated MariaDB test inventory and fixture trees.
  • Make SHA-pinned WordPress and SQLite archives authoritative for product builds.
  • Keep the unverified setup.sh alias available only for local demos, exclude that exact alias from product source copying, and reject every unrelated symlink.
  • Materialize the pinned SQLite plugin separately for WordPress; keep LAMP intentionally plugin-free.

Preserve the execution plan

  • Record the remaining migration as three accelerated landing batches: packaging, generic VFS, and product shell cutover. This changes landing mechanics, not product scope or validation requirements.

Validation

Validated on exact #1073 head 665ac0b0fec7ddd3fffe5f951c5abed50d6432b3 and current combined head 2e52d12520699230dd479bbc7b73ede0eee76f92:

  • All 14 component commits replay patch-identically from their reviewed branches.
  • Program-package projection generation, freshness, and complete source-context checks passed.
  • The full Homebrew publisher workflow passed, including the real pinned offline lifecycle.
  • All 61 candidate tap Formulae generated and validated schema-4 plans containing 113 native Requirements.
  • 28 focused WordPress, VFS, and build-input tests passed.
  • Host TypeScript typechecking and the documentation build passed.
  • A forced WordPress source build produced a 45,022,674-byte archive after materializing 3,951 pinned core files and 19 pinned plugin files.
  • A forced LAMP source build produced a 47,998,216-byte archive after materializing the same 3,951 pinned core files with no plugin.
  • Shell and Ruby syntax checks and git diff --check passed.

Fresh combined CI is now the integration proof. After #1073 merges, this branch will be rebased onto its exact merge commit and revalidated before merge.

Stack and boundaries

This PR is stacked on #1073 and must land after it. It combines and supersedes the implementation in draft PR #1068 while preserving its commits and review history.

It changes packaging, publication, product-image build validation, tests, and documentation. It does not change the kernel, libc, Kandelo ABI, or generic VFS runtime behavior, and it does not yet switch the main shell image.

A package can contain several executable outputs, runtime data files, or both. Publishing those paths one at a time can expose a mixture of builds, and a direct local copy can accidentally follow a fetched mirror symlink into the shared cache.

Validate the complete declared closure before changing its live package directory, stage fetched mirrors beside the destination, and replace the directory as one transaction. Collect direct local builds into create-once session generations with one-shot publication claims, while retaining safe atomic replacement for one-member packages and legacy aliases. Treat outputs plus runtime files as one package identity so executable-plus-runtime packages such as CPython and Erlang use the same contract.
A complete package path could previously mix files from different builds or retarget after validation. Malformed and legacy spellings could bypass the package closure, while the browser development server exposed the entire program cache instead of only the files selected by the resolver.

Project the registry into one closed, cached package model; resolve outputs and runtime files as one verified generation; return canonical member paths; and preserve truthful not-found versus invalid-state errors. Narrow Vite serving to exact resolver-approved regular files so Node.js and browser hosts consume the same package generation without exposing neighboring cache content.
Install the reviewed shell VFS with install-local-artifact under a run-unique generation session, then resolve and compare the canonical installed bytes before browser validation. Strengthen the shell workflow contract so direct local-binaries writes cannot return.
@brandonpayton
brandonpayton marked this pull request as draft July 23, 2026 13:16
@github-actions

github-actions Bot commented Jul 23, 2026

Copy link
Copy Markdown

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

ABI v41. 12 built, 0 failed, 12 total.

Package Arch Status Sha
homebrew-bootstrap wasm32 built 5abead30
kandelo-sdk wasm32 built 2f07a46b
mariadb-test wasm32 built 1ec82027
mariadb-vfs wasm32 built 6de85a49
mariadb-vfs wasm64 built b3dc3d08
redis-vfs wasm32 built bca44df6
shell wasm32 built feffb1d8
lamp wasm32 built b1b73fc8
nginx-php-vfs wasm32 built 7a0a085c
nginx-vfs wasm32 built 952fec76
node-vfs wasm32 built e12fabca
wordpress wasm32 built b02121f9

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

Require shell-derived images to match the reviewed capacity profile as well as its data and inode reserves. Keep a deliberate expected-capacity override for a future larger product profile.

Make host-tree composition fail on every read, unsupported-entry, and VFS-write error; intentional omissions remain explicit excludes. Cover the complete copy-option surface, ENOSPC propagation, capacity drift, and larger-profile escape path.
Validate the serialized image capacity before compression or output writes, require intentional symlink handling, and propagate MariaDB test source failures. Add contract coverage for masked capacity, failed host reads, and shell profile constraints.
Bind the Homebrew main-shell serializer to its encoded capacity contract before output writes. Require every declared MariaDB test and fixture tree instead of preserving best-effort omissions, and remove the stale simple_select entry that the pinned source archive does not contain. Add executable failure-path, selection-parity, and package-input coverage.
Regenerate the atomic program projection after replaying the VFS integrity series. This binds MariaDB's new source-copy input and every VFS package that consumes the hardened shared image helpers to their current cache keys.
Allow the static Formula planner to recognize three canonical native-tool Requirement classes without evaluating Formula source. Preserve the existing sealed homebrew/core identity plan, and reject unknown, dynamic, forged, unloaded, or test-only Requirement declarations.
Bind allowlisted Requirement classes, Formula identities, sentinel executables, and tags into a closed schema-4 host dependency plan. Validate that plan in every producer and consumer, reconstruct only matched build-only Homebrew dependencies for Superenv, and expose sealed test tools through the normal Formula test lifecycle. Cover malformed plans and evaluated-object drift, plus an actual install/test against the exact pinned Homebrew source.
Provision an isolated Bundler copy before the publisher boundary, then seal it and run the real pinned Homebrew install and test commands behind an OS-enforced network sandbox. Probe that boundary with a reachable control socket and fail if the gem tree changes or Bundler activity appears.
Carry the dev-shell PATH explicitly through the passwordless-sudo network namespace used by GitHub runners. This keeps sealed native Requirement sentinels visible after sudo applies secure_path, while retaining the OS-enforced offline boundary.
Document the closed schema-4 native Requirement contract, the offline pinned publisher lifecycle, and the remaining canonical-package activation gate. Keep the trusted publisher Homebrew revision distinct from the dedicated guest homebrew-bootstrap package so staging URLs are not mistaken for durable consumer inputs.
Generate the real pinned Homebrew lifecycle plan through the same static Formula parser and closed-schema validator used by publication before staging it for Build and test. Bound every host dependency list to the platform's 128-entry limit in both validation layers, and cover oversized plans so protected control data cannot grow beyond the reviewed graph contract.
Make product WordPress and LAMP builds resolve only their SHA-pinned source archives instead of invoking the unpacked local-demo setup path. Centralize the reviewed WordPress core copy policy, materialize the SQLite plugin from its separate pinned source, and keep every unrelated source symlink fail-closed.

Cover the package entrypoint boundary, local-demo setup ownership, exact exclusions, plugin guest placement, unexpected symlinks, and refreshed package identities.
@brandonpayton
brandonpayton force-pushed the integration/homebrew-bootstrap-after-atomic-qk044 branch from 714e74b to 2e52d12 Compare July 23, 2026 14:30
@brandonpayton brandonpayton changed the title [Packaging/Homebrew] Distribute guest brew as its own package [Homebrew/Packaging] Make guest brew a reproducible product package Jul 23, 2026
@brandonpayton
brandonpayton marked this pull request as ready for review July 23, 2026 14:31
Declare nginx, nginx+PHP, and Redis composite images as complete package generations with exact dependencies and cache inputs. This prevents their browser mirrors from bypassing the strict package-generation contract.

Add a source-level ownership audit for every composite VFS wrapper so a future image cannot call the package installer without a manifest, build sidecar, exact source artifact, canonical mirror path, and explicit non-Wasm policy.
Declare the shell-owned inputs used by nginx-derived images and narrow the nginx-only and Redis host inputs to the resolver/VFS boundaries they actually consume. Keep nginx+PHP broad because opcache prewarming boots the complete host runtime.

Extend the cache-domain test with an inline explanation of that distinction so future maintainers cannot silently over- or under-invalidate these images.
@brandonpayton

Copy link
Copy Markdown
Member Author

Closing as decomposed into the merged #1097 publication/Requirement foundation plus #1109 product commits ee45cb47e, d61a83b69, and 43eabacc7. The older aggregate branch is no longer the merge source.

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