Skip to content

[Homebrew/Publisher] Secure and unblock tap-owned bottle builds - #1121

Merged
brandonpayton merged 13 commits into
mainfrom
packaging/sealed-tap-recipes-main-qk044
Jul 28, 2026
Merged

[Homebrew/Publisher] Secure and unblock tap-owned bottle builds#1121
brandonpayton merged 13 commits into
mainfrom
packaging/sealed-tap-recipes-main-qk044

Conversation

@brandonpayton

@brandonpayton brandonpayton commented Jul 27, 2026

Copy link
Copy Markdown
Member

Why

The trusted rootfs recovery lane could publish only Bash, Dinit, and M4. That hardcoded allowlist blocked valid tap-owned Formulae, while running tap code with the publisher's normal authority would expose its filesystem and credentials.

This PR keeps recipe execution isolated and adds a bounded authority decision so the remaining wasm32 Formulae can be published without weakening that boundary.

What changes

  • Bind each sealed tap recipe, its source, environment, dependencies, resources, and exact files into a publisher attestation.
  • Run tap-owned recipes as unprivileged, network-isolated services with a read-only filesystem allowlist, then seal and validate their output.
  • Validate declared executable and side-module output roles before publication.
  • Project Formula resources into the attested recipe instead of granting ambient filesystem access.
  • Classify only cache-miss Formulae with the repository-pinned Ruby toolchain before the build matrix starts.
  • Automatically admit direct Formula builds and sealed tap recipes. Keep the two remaining registry bridges explicit: modeset -> modeset and nethack -> nethack.
  • Carry the complete classification record into each build and compare it again immediately before execution. A changed tap checkout therefore cannot switch a direct or sealed recipe into a broader registry bridge after provisioning.
  • Cap the transported record at 65,536 bytes and 256 Formulae, and reject malformed, extra, mixed, or unsupported authority metadata.
  • Reject rootfs-generation dry runs at the trusted caller boundary so a repository token is never exposed to branch-selected source.

Boundaries

This is packaging and publisher infrastructure only. It does not change the kernel, libc, ABI, shell image, demos, or host runtime, and it does not include the shell cutover from #1116.

The exact package generation supplies the fixed wasm32 Formula build/test runtime packages. It does not supply the target Formula's bottle, and the workflow builds the current-main SDK and sysroot separately. A temporary bridge still compiles Homebrew-verified source through an authenticated recipe owned by the exact Kandelo main commit.

Validation

  • Focused Formula-authority selection and mutation tests
  • Complete publisher trust checker
  • Package-publish flow, CI scope, and exact-generation materializer suites
  • Formula runtime-closure, patched-launcher, and sealed recipe-runner suites
  • Complete provisioned publisher workflow suite
  • Repository shell syntax and git diff --check
  • Supplemental Actionlint and ShellCheck comparison
  • Independent final publisher/security review: clean

Rollout after merge

  1. Create and promote one fresh exact rootfs generation from the final merged main commit. Do not reuse the earlier b90eff73 generation or invoke an unmerged workflow SHA.
  2. Rotate the tap caller and trust pins once to that immutable merged SHA.
  3. Publish wave A concurrently: libmagic, make, wget, unzip, nano, NetHack, and Modeset.
  4. After those dependencies are current, publish wave B: file-formula and zip.
  5. Convert the two temporary registry bridges to sealed tap recipes in the tap follow-up; that conversion is not required for the first publication wave.

The existing Bash bottle was produced from already-merged authority and remains valid; this PR does not require rebuilding it.

The exact final head prepared for this PR is 0187ff08addb52c97fdae22d6d7c093645286bae on base b90eff73960207b59b7db55c7fb4ed46a4d075c0.

Let an exact tap commit carry a closed, checksummed build-recipe tree while Homebrew remains authoritative for source and dependency kegs. Preflight and runtime verification reject alternate registry authority, mutable or undeclared inputs, and unsafe outputs; Formula identity now controls cache reuse.
Run Formula-owned schema-3 recipes behind a peer-authenticated root supervisor under a distinct UID. Project only attested inputs and exact Nix closures, seal dependencies and results, and verify teardown and escape resistance.

Keep identity creation transactional and enforce the contract in publication trust checks, focused tests, and the Linux malicious-recipe canary.
Forward the isolated recipe user only at the Formula build and verification commands. Keeping this run-scoped identity out of dev-shell.sh preserves every existing package cache identity while retaining the closed-root execution boundary.
Treat deliberately absent legacy resolver paths as optional systemd masks while retaining fail-closed authority checks. Preserve failed audit units only long enough to expose bounded diagnostics, keep Brew on its protected canonical prefix path, and leave only dependency descendants sealed so Formula installs remain possible.
Refresh the protected build and verification step digests after applying the schema-3 supervisor directly to current main. This keeps the reviewed publisher boundary explicit without including the separate mostly-lazy shell cutover.
Homebrew bottles may contain both process executables and dylink.0 side modules. Validate their distinct loading contracts without treating a filename or mode as an exemption from executable ABI checks.

Resolve bin/sbin entrypoints before inspection, reject side modules exposed as process entrypoints, validate side-module memory/import/fork structure, and keep executable ABI-version validation unchanged. Cover valid and malformed roles, unsupported imports, and uninstrumented side-module fork imports.
@brandonpayton
brandonpayton marked this pull request as ready for review July 27, 2026 23:44
@brandonpayton brandonpayton added the skip-staging-tests Opt into skipping the staging tests. This does not skip tests before merge. label Jul 27, 2026
Classify each cache-miss Formula under a pinned Ruby authority tool, automatically admitting direct and sealed-recipe builds while keeping the remaining registry bridges explicit.

Carry the complete bounded classification record into each build and compare it again at the execution boundary so tap drift cannot escalate authority after provisioning.

Reject rootfs dry-runs at the trusted caller boundary, preserve the runtime-only exact-generation contract, and cover the workflow, trust checks, selector, materializer, scope routing, and operator documentation.
@brandonpayton brandonpayton changed the title [Homebrew/Security] Isolate tap recipes without landing the shell cutover [Homebrew/Publisher] Secure and unblock tap-owned bottle builds Jul 28, 2026
@brandonpayton brandonpayton added batched-changes Use rebase auto-merge in prepare-merge so granular commits land on main. ready-to-ship Maintainer attests the exact tested head and requests merge preparation. labels Jul 28, 2026
@github-actions

Copy link
Copy Markdown
Contributor

prepare-merge: runtime/materialization tests passed against the synthetic PR merge; package staging and durable package publishing were skipped. merge-gate=success posted on PR HEAD. This PR is ready for a reviewer to rebase merge.

@brandonpayton
brandonpayton merged commit c647add into main Jul 28, 2026
89 checks passed
@brandonpayton
brandonpayton deleted the packaging/sealed-tap-recipes-main-qk044 branch July 28, 2026 01:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

batched-changes Use rebase auto-merge in prepare-merge so granular commits land on main. ready-to-ship Maintainer attests the exact tested head and requests merge preparation. skip-staging-tests Opt into skipping the staging tests. This does not skip tests before merge.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant