Skip to content

Binary attestations: phase 2 – reproducible Fastly builds - #1449

Open
prk-Jr wants to merge 124 commits into
mainfrom
feature/binary-attestations-phase2
Open

prk-Jr wants to merge 124 commits into
mainfrom
feature/binary-attestations-phase2

Conversation

@prk-Jr

@prk-Jr prk-Jr commented Oct 26, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Build Fastly twice on every push to main and every v* tag, require equality of the complete wasm and deployable package, and publish the genuine signed rebuild check. A mismatch prevents signing and publication. This remains ordinary equality (release_qualification:false), not controlled-recipe qualification.

  • Publish trusted-server-fastly.tar.gz alongside the unchanged wasm name. A fixed service-neutral manifest and normalized tar/gzip metadata make packaging independent of host modes, ownership, paths and timestamps. Operators verify and deploy the downloaded archive unchanged.
  • Bind each role's package to its measured observation and authenticated artifact transfer. Provenance covers wasm, SPDX, package and rebuild check; the SPDX attestation covers wasm and package. Existing bundle names and binary verification commands remain stable. Publication is 19 immutable files, with 18 checksum entries.
  • Defer unused controlled producer code, tests, schemas, fixtures and tool/policy inventories to their enabling phase. Retain the live ordinary release path, strict JSON/bounded I/O/process helpers, Cargo/path checks and Fastly packaging/inspection. Remove jsonschema; main's existing dependency versions and external dependency edges are preserved. The complete PR now adds about 25.6k lines rather than about 94k.
  • Keep the existing rebuild-check design and permanent companion filename. The optional GitHub custom-predicate alternative is deferred to a separate design/migration decision.

Spec / Plan

Phase 2 spec and reviewed package amendment · Gate 1 record · implementation record · phase/dependency assignment.

Part of #1235. Phase 3 #1463 must restore the deferred foundations it consumes and reconcile package evidence/indexing. Packages PR #1543 must reconcile the additional release asset and subjects. This PR does not edit either dependent branch or open later qualification/index/consumer gates. Closed-path findings in #1517 belong to the deferred implementation.

Verification

Current head: 27361ec58, signed and GitHub Verified.

  • Local (macOS arm64, Rust 1.98.1, Node 24.20.0, Viceroy 0.21.0, Spin 4.1.0): release crate 209 unit + 10 command tests; release Node 45 tests; release and all eight target clippy gates; Fastly, reusable Fastly, Axum, Cloudflare, Spin, CLI and parity suites; native build-digest tests/lint; checked/ordinary JS release fixtures; JS 3361 pass / 3 skip; JS build; required formatters, shell checks, docs-proposal tests, Node pin guard, wasm clock lint and Fastly/Cloudflare/Spin deadline runtime fixtures pass. The full CLI rerun passed after one cleanup-test failure; its focused rerun also passed.
  • Negative controls: removing package equality fails the package-only command regression; removing the signer package guard fails the package-size signing case. Restored source passes. Missing/changed/unsigned packages and package attestation failures stop publication before mutation.
  • Real packaging/runtime checks: varied source path/mode/time/umask produces identical archives; checksum-verified Fastly CLI 16.0.0 accepts the new writer output and the inspector confirms the module/manifest. Rebuilding the new published .40 package on macOS exactly matches its Linux-published bytes. The published wasm serves three publisher pages plus injected JS under pinned Viceroy with full source headers, actual request/response logs and no build-fact lines; listeners stopped and bytes remained unchanged. The downloaded macOS arm64 CLI starts with the exact source/target/version, and both macOS binaries retain the 13.0 metadata floor. This is local execution, not a Fastly deployment or macOS 13 runtime qualification.
  • Current-head hosted CI: 30 of 30 checks pass. The earlier Integration run at e9e46e584 also passed on attempt 3, including both previously failed browser jobs.
  • Fork-only v0.0.0-rehearsal.40 at this exact head: all 12 jobs pass, with 19 immutable prerelease assets, not Latest. Independent download checks passed: 18 checksums, all release memberships, 44 bundled attestation checks (online/offline with workflow and exact-tag certificate identities), 11 GitHub attestation API lookups, all four transfer/job bindings and source-owned recipe/lock hashes. Offline checks ran with network denied; a same-name one-byte-altered package was rejected after its unchanged control passed. Reproduction took 384s and comparison 69s; both finished before the remaining native builds, so neither delayed signing in this run.

Historical .39 at e9e46e584 passed all 12 jobs and published the earlier 18-file contract. It is not evidence for this new package contract. Earlier source-specific transcripts remain linked in the phase records and PR conversations.

Human gates and limits

Merging Phase 2 activates the second Fastly build plus comparison on every main push and v* tag; there is no separate equality switch. Build/comparison ceilings remain 90/10 minutes. Maintainers still own activation, immutable release/tag rules and publication settings. Native release builds remain single builds. Cloudflare/Spin are outside the published cohort. No IAB release, tag, setting change or deployment is performed by this PR response.

The signing job still trusts the pinned hosted Node installation while holding its signing permissions; it never checks out or executes build inputs. No global host allowlist additions remain after deferring the schemas/policies; existing reviewed per-line API suppressions remain.

Checklist

  • Scope/package amendment reviewed in two independent source rounds.
  • Local verification and negative controls completed.
  • Signed fix published to this source branch and confirmed GitHub Verified.
  • Current-head hosted CI complete.
  • Current-input 19-file signed-release rehearsal and independent verification complete.
  • Four current review conversations answered and resolved after current-head verification; the optional predicate redesign is deferred by the agreed scope.

Closes #1235

prk-Jr added 30 commits October 22, 2026 15:41
Port the remaining slice 2.3 resource-discovery change from #1262 at 122b786. Preserve the build facts already carried in #1435.

Verify the three pure filesystem tests in development and release-build configurations with exact-name selection guards. CLI clippy, Rust formatting, domain lint and independent review pass.
Record Gate 1 for user approval: real A/B comparison and rebuild-check publication preserve Phase 1 identities while qualification remains closed. The narrower ordinary-release assurance claim requires explicit approval before implementation.
Record the shared runner Cargo home as the cargo-home-unvaried gap and
describe it accurately, and document that the gate runs on every push to
main and every v* tag. Let the signer accept a companion sealed in an
earlier attempt, retry transient read-only transfer failures, and report
rebuild mismatches and identity failures as such.

Pin the signing validator, comparison, measurement, npm-lock and post-reap
checks the review found unpinned, forbid continue-on-error on the gate
jobs, and tie the closed cohort to the published binaries. Rename release
environment names to TRUSTED_SERVER__RELEASE_*, reject `..` in both helper
TMPDIR guards, move the index schema into the release crate, and drop
fixture producers no job runs. Refresh stale status across the guide,
CHANGELOG, spec and plans.
@prk-Jr

prk-Jr commented Nov 10, 2026

Copy link
Copy Markdown
Collaborator Author

Replies to the review's body-level findings, all in f243408ce. The inline threads have their own replies. 61fd17831 first merges the Phase 1 branch's fourth review round, which resolved the base conflicts.

"Producer CI" doesn't exist. The ignore reasons now say "frozen #1262 producer test, no workflow runs it". The qualification README's staging note now says its text is the frozen 122b78664 README: the candidate, Docker and Xcode tests are ignored, and src/distribution.rs, the hosted proposal and the dated reports exist only at that commit. All eight broken links in both READMEs are pinned to 122b78664, and each target exists there. cargo-fixture.Dockerfile, js-fixture.Dockerfile and the three prepare-*-fixture.mjs scripts are removed, and their ledger rows say they're deferred until a job consumes them. cli-builder.Dockerfile stays because a unit test reads it. I did not add the esbuild hasInstallScript test.

Stale status. The spec status now keeps only the design contract and points to #1449 comments and #1235 for rehearsal and approval state. The three plans have a dated current-state note: four binaries and 18 files, no post-publish byte comparison, the gate on every main push and v* tag, and .30 at 44557229a as the last rehearsal before this round. The draft/no-reviewers wording, the 29→30 and seven-binary contract lines, spec line 41 (your suggested text), Gate 1 plan line 3 and the stale "carried" ledger rows are corrected. The ledger now has 19 "not carried; frozen at 122b78664" rows; adapter-fastly/build.rs and core/src/build_info.rs are marked removed in #1435. The slice map's axum_amendment is marked moot at 4 executables and 17 files. Earlier rehearsal sections remain as dated history. The PR description refresh is coming separately.

Closed-path defects. Not fixed here, because main can't reach that code. They belong in one tracking issue together with the two 📌 threads; I'll link it once it's opened.

Smaller items.

  • Done: guide 290-298 ordering, the line 496 wording, the 361-363 contradiction and the error-reference.md:680 link.
  • Moot: lines 487-489. The base merge replaced that recovery block with "run the download-and-verify block", which already verifies the rebuild check.
  • normalization.rs keeps the specific check_cargo_config cause underneath the generic rejection.
  • The negative fixture is renamed ordinary_compare_mismatch_writes_no_companion, including in test.yml and the Node contract.

This round's verification (macOS arm64, Rust 1.98.1, Node 24.20.0):

  • Tests: release crate 979 + 4 + 19 and CLI suite pass; release Node suite 42/42; vitest 1901 passing.
  • Lint and format: cargo fmt, release-crate and CLI clippy -D warnings, shellcheck, docs and markdown prettier, and the domain linter against the base all pass.
  • Linux-only branches are covered by CI, not locally.

Fork rehearsal v0.0.0-rehearsal.32 at f243408ce: all 13 jobs passed, including the two Fastly builds, the comparison, the four signing legs and the release. The release is immutable, a prerelease and has 18 assets. Both companion digests are 74bc5f7e…f696 and equal the published wasm. The companion lists the five gaps including cargo-home-unvaried, SHA256SUMS checks, and the companion verifies against the Fastly provenance bundle and as a release member.

Dependency sources under the shared runner Cargo home stayed unmapped in
the final wasm, so A/B equality depended on that shared path. Each role
now builds with a fresh Cargo home beside its target root, both mapped
to /cargo-home. Observations record the Cargo home, the recorder
requires every resolved package to come from the checkout or that home,
and the binary scan covers all six physical roots. The
cargo-home-unvaried gap is removed.
Pin the job interval, compare-time target, variant, locale, timezone and
acquisition label, and the signing job's asset allowlist, identity
formats and size bounds; each new case asserts its own guard. Remove
release-crate items no caller or test uses: the DenialReceipt accessors,
index_identity, read_final_bytes, the startup diagnostic_for and the
composition projection fixtures.
Pin the current lock's unreviewed install scripts so the next
hook-carrying bump fails visibly, check the pinned Node before the
signing job validates its inputs, and remove release-crate items that
no caller or test references.
@prk-Jr

prk-Jr commented Nov 10, 2026

Copy link
Copy Markdown
Collaborator Author

The optional and "consider" items from the review body are in 0b1e350e8.

  • esbuild install-hook gap: materials::manifest::tests::current_lock_lists_only_known_unreviewed_install_scripts lists the lock's hasInstallScript entries that have no reviewed hook, and pins that set. It is not esbuild@0.28.2 alone: the lock also flags fsevents@2.3.3, an optional macOS-only package, so both are listed by name. A hook-carrying bump fails it; I checked with a simulated core-js bump.
  • Signing job Node check: added with a contract test (see the release-build.yml:491 thread).
  • Further unused-code deletions: see the main.rs thread.

Two items stay as they are. Isolating the euid check needs a fixture owned by another user, which needs root chown; it remains covered together with the owner check by the claimed-uid + 1 case. The SIGKILL mutant for confirmation_never_kills_a_live_group was not re-run here; your run against it is the evidence.

Fork rehearsal v0.0.0-rehearsal.34 at 0b1e350e8 passed all 13 jobs: the Fastly A/B comparison matched and 18 immutable assets were published. Local: release crate 985 + 4 + 19, release Node suite 43/43, fmt, clippy -D warnings and the domain linter passed.

@prk-Jr
prk-Jr requested a review from aram356 November 10, 2026 10:34

@ChristianPavilonis ChristianPavilonis left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requesting changes for the two reachable issues below. The scanner-budget comment is future-facing and is not, by itself, an ordinary-release blocker.

Reviewed commit: 0b1e350e8cbcb0df8b3ab2addfa8c3aa6ac2d47d.

Coverage: an incremental review of the updates and live release paths, not an exhaustive reading of the full 306-file PR (approximately 94,000 added lines).

Validation at the reviewed commit:

  • All 30 remote CI checks passed.
  • The locked/offline native release-producer build passed.
  • 127 focused Rust tests and 43 release-script Node tests passed.
  • The pinned-range whitespace check passed.
  • The package allocation failure and the live ordinary-measurement signal-handling issue were reproduced.

All three comments are prose-only, with no GitHub suggestions. Proposed fixes were not applied or verified.

Check the manual package module against its independently approved
SHA-256 before the Wasm parser runs, so an unapproved module cannot drive
parser allocations.

Restore the default SIGCHLD action at startup and refuse every spawn while
an ignored SIGCHLD or SA_NOCLDWAIT would let the kernel reap the leader.
On Linux an inherited ignored SIGCHLD made ordinary-measure's tool queries
fail with ECHILD and signal a process group the leader no longer pinned.

@aram356 aram356 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

Round 3 at 37dfc6d28. Thanks for working through the whole round-2 list. We re-checked every claimed fix against the code, re-ran the round-2 mutants, and checked the private Cargo homes in the published .33 and .34 wasm: no /home/runner path is left, and every dependency path reads /cargo-home (3,417 registry and 119 git lines). ChristianPavilonis's two fixes hold too: on Linux the inherited-SIG_IGN command test fails with the fix removed, and a mismatched package is rejected before parsing. What's left is small: a few new checks that tests still don't pin (most importantly the recorder's package-location check), a tagging checklist that the new "every main push" sentence relies on but that doesn't name the comparison runs, a description that calls an unrehearsed live-path change rehearsed, and doc leftovers.

10 of the inline comments below carry a one-click GitHub suggestion — use Commit suggestion (or Add suggestion to batch for several at once) to apply them as commits on the PR branch. Each was applied alone to 37dfc6d28 and checked with the matching gate (Rust: cargo fmt --check, release-crate clippy with -D warnings and the full release-crate suite; Node and workflows: prettier, the full release Node suite and actionlint; docs: prettier), and all ten were checked together, with no other file changes. The test suggestions were also run against the mutants they target and fail on each. The remaining comments describe the fix in prose because it spans files or lines outside the diff.

Blocking

🔧 wrench

  • The new "every package from the checkout or the private Cargo home" check can be deleted with the suite green — see inline at crates/trusted-server-release/src/ordinary/inputs/record/tests.rs:458
  • Two signer checks are still unpinned, one of them added by the bounded-attempt change — see inline at .github/workflows/release-build.yml:534
  • This sentence relies on a tagging checklist that doesn't name the comparison check runs — see inline at docs/guide/release-verification.md:128
  • The description says the current head is 0b1e350e8 and exact-head rehearsed — below

Non-blocking

🤔 thinking / ♻️ refactor / ⛏ nitpick / 📝 note

  • 🤔 The Cargo-home proof reads a separate cargo metadata run, not the build — see inline at crates/trusted-server-release/src/ordinary/inputs/record.rs:244
  • ♻️ The archive retry policy and the real backoff/deadline path aren't pinned — see inline at scripts/release/phase1.test.mjs:242
  • ♻️ The fail-closed contract still allows continue-on-error on checks and on the CLI build step — see inline at scripts/release/phase1.test.mjs:2482
  • ⛏ This step goes green with zero tests if the test is renamed — see inline at .github/workflows/test.yml:398
  • ⛏ The Fastly jobs still pass the runner's Cargo home, which the script then discards — see inline at .github/workflows/release-build.yml:110
  • ⛏ The Node floor is 22.18, not 24.2; my round-2 comment overstated it — see inline at CHANGELOG.md:17
  • ⛏ SA_NOCLDWAIT doesn't survive exec, and its branch is unpinned — see inline at crates/trusted-server-release/src/process/supervise.rs:132
  • ⛏ CARGO_HOME is matched by path components, not by its exact spelling — see inline at crates/trusted-server-release/src/ordinary/normalization.rs:140
  • ⛏ The recipe policy still describes four maps — see inline at crates/trusted-server-release/src/ordinary/normalization.rs:307
  • ⛏ No test pins the new error variants — see inline at crates/trusted-server-release/tests/commands.rs:1196
  • 📝 The three owner checks aren't pinned individually — see inline at crates/trusted-server-release/src/ordinary/inputs/record.rs:217
  • ⛏ Doc leftovers after the round-2 cleanup — below
  • 📝 Phase 3 still uses helpers this PR deleted — below
  • 📝 Smaller items — below

Cross-cutting / body-level findings

  • 🔧 The description says the current head is 0b1e350e8 and exact-head rehearsed. The head is 37dfc6d28, which changes live release code: main now resets SIGCHLD before any child exists, spawn_group refuses unwaitable children for every BoundedCommand (so for ordinary-measure, inspect-fastly-package and preflight), and package.rs checks the digest before parsing. Nothing has been rehearsed at that head, and the checklist still ticks "Current 0b1e350e8 … exact-head rehearsal .34". Please rehearse 37dfc6d28 (or say plainly that it isn't rehearsed) before merge. Also in the description: every rehearsal is said to pass "all 13 jobs", but those runs have 12 (checks, four builds, reproduction, comparison, four signing legs, release; for example run 42789411060 reports total_count: 12, and the Gate 1 plan line 128 says the same for .27), and the local count is now 987 + 4 + 20.

  • ⛏ Doc leftovers after the round-2 cleanup.

    • "Keep #1449 draft, without reviewers." is still at binary-attestations-phase2-fastly-integration.md:220, and "Keep #1449 draft and request no reviewers." at binary-attestations-phase2-fastly-integration-implementation.md:177.
    • binary-attestations-phases-2-4.md: line 39 still says the Fastly build script and the build-info module are "already landed in #1435" and that the CLI lib.rs/run.rs "carry the build-info re-export", while ledger rows 245 and 252 say both were removed; line 65 counts "22 carried documentation/evidence paths" (the ledger has 2 carried, 1 moved, 19 not carried); rows 136-156 keep "1 — carried" in the Landing column although their State says "Not carried"; line 32 says fixture_for_projection remains, but 092de447b deleted it.
    • "The frozen lifecycle observer remains byte-identical" (binary-attestations-phases-2-4.md:582) and "The observer is ported unchanged" (binary-attestations-phase2-fastly-integration.md:162) are no longer true: f243408ce rewrote scripts/release/observe-npm.mjs's deletion regex at our request. Reword both, or restore the frozen bytes.
    • binary-attestations-slice-map.json:615 still ends "Phase 1 publishes only 4 per executable plus SHA256SUMS: 29." two lines below phase1_release_files: 17.
    • binary-attestations-phase2-fastly-integration-implementation.md:50 says a partial rerun carrying old successful jobs fails closed and to rerun all jobs; ordinary/run.rs:50-57 and the guide (lines 207-211) now say carried-over binding is intended.
    • docs/guide/release-verification.md:419 (outside the diff) says "The eight pre-creation gh attestation verify runs"; publish.mjs now runs nine (two per binary plus the rebuild check).
    • Four #[ignore] reasons don't use the new wording: recipe/startup.rs:626 still says "requires explicit ordinary CI CLI fixture", which implies CI provides it, and materials/npm_bundle.rs:261, materials/git/tests.rs:748 and recipe/apple/sdk.rs:545 don't say no workflow runs them.
  • 📝 Phase 3 still uses helpers this PR deleted. At the #1463 head (origin/feature/binary-attestations-phase3, d885a965a), distribution.rs:244 calls index_identity(), and composition/native/tests.rs:656-792 plus the frozen spdx/native/tests.rs use fixture_for_projection and fixture_native_registry, all removed in 092de447b at our round-2 suggestion. The next merge of Phase 2 into Phase 3 won't compile unless they're restored there; worth a line in the phases plan so it isn't a surprise.

  • 📝 Smaller items.

    • The shell-side Cargo-home wiring (phase1-build.sh:33-37, and the capture's realpathSync) has no PR-CI coverage: deleting the export, the refusal or the per-role name passes the Node suite. Each fails closed on the next main push through normalization.rs:136-142, so this is optional; one assert.match on the export, ordered before ordinary-rustflags, would catch it in PRs.
    • tool() maps every process failure, including the new UnwaitableChildren, to "ordinary measurement inputs or tool pins are invalid" (record.rs:303), which is what the pre-fix Linux run printed. ProcessFailure kinds carry no child output, so attaching the kind would be safe and would make that failure self-explaining.
    • Module-level cfg_attr(not(test), expect(dead_code)) flags a module that becomes fully live, but process, documents, source and inspect are partly live, so their remaining dead items stay hidden. Item-level attributes would be needed to see them; not asking for that here.

Round-2 follow-up

Verified fixed at this head: the private remapped Cargo home (no runner path in .33/.34), the every-main-push wording, the signer's receipt, attempt, allowlist, format, size and Node checks (except the two in the comment on release-build.yml:534), the canonical-side, start-time, Node-pin, name-uniqueness, compare-scan, rustc-pin, tool, euid, identity and npm-lock tests, the post-reap no-SIGKILL test (it fails on the restored SIGKILL under Linux), the module doc, the error variants and Usage text, the carried-over binding doc, the TRUSTED_SERVER__RELEASE_* renames (no setter/reader mismatch, no gate silently skipped), the dead-code attributes and deletions (clean on macOS and Linux), the schema move, the retries, the continue-on-error jobs we listed, the cohort tie, the .. guard, the CLI release-cfg step, the npm notifier, the signing-job Node threat note, the CHANGELOG, the offline block, the spec anchor, the producer-CI wording and links (all 13 pinned links resolve at 122b78664), and the esbuild/fsevents install-hook pin. Closed-path items are tracked in #1517.

CI Status

At 37dfc6d28: 30 of 30 checks pass.

  • integration tests: PASS
  • browser integration tests: PASS
  • integration tests (Fastly EC lifecycle): PASS
  • trace runtime and browser acceptance: PASS
  • CodeQL: PASS
  • prepare integration artifacts: PASS
  • cargo test (ts CLI, native) (macos-latest): PASS
  • cargo fmt: PASS (required)
  • cargo test (cross-adapter parity): PASS
  • Analyze (rust): PASS
  • Analyze (javascript-typescript): PASS
  • vitest: PASS
  • HTML EOF runtime (Fastly): PASS
  • ts dev lint domains (changed lines): PASS
  • CLAUDE.md symlink guard: PASS
  • cargo test (axum native): PASS
  • worker-deadlines (cloudflare): PASS
  • format-typescript: PASS (required)
  • cargo check (cloudflare native + wasm32-unknown-unknown): PASS
  • cargo test: PASS (required)
  • cargo test (ts CLI, native) (ubuntu-latest): PASS
  • cargo check/build/test (spin native + wasm32-wasip1): PASS
  • docs-proposal scripts: PASS
  • worker-deadlines (spin): PASS
  • release scripts: PASS
  • format-docs: PASS (required)
  • CI Node pin guard: PASS
  • Analyze (actions): PASS
  • Analyze (javascript-typescript): PASS
  • Analyze (python): PASS

Comment thread .github/workflows/release-build.yml Resolved
Comment thread docs/guide/release-verification.md Resolved
Comment thread scripts/release/phase1.test.mjs Resolved
Base automatically changed from feature/binary-attestations-phase1 to main November 11, 2026 10:00
@prk-Jr prk-Jr added this to the 202611 milestone Nov 11, 2026
Tie the Cargo-home proof to the build's own compiler-artifact messages,
match CARGO_HOME by its exact spelling, name the Cargo-home prefix in the
v2 recipe policy and stop passing the runner's Cargo home to the Fastly
jobs.

Pin the recorder's dependency-source check, the fractional signer attempt,
the SPDX size bound, a file-wide continue-on-error ban, the archive retry
policy and deadline, the SA_NOCLDWAIT refusal, the reproducibility error
variant, the private Cargo-home wiring and the release-cfg test count.

Correct the Node floor to 22.18/24.2, the pre-tag checklist, the
verification count and the stale plan, ledger and ignore-reason text.
@prk-Jr

prk-Jr commented Nov 11, 2026

Copy link
Copy Markdown
Collaborator Author

Round-3 body-level items, addressed in ba05cabea:

Description / rehearsal. Agreed. 37dfc6d28 was not rehearsed, and neither is ba05cabea. ba05cabea changes live release code again: the recorder also checks the build's own compiler-artifact paths, prepare matches CARGO_HOME byte for byte, the recipe policy is now -v2, and the Fastly jobs no longer pass CARGO_HOME. The description's head, the "exact-head rehearsal .34" tick, and "13 jobs" (runs have 12) need correcting. A rehearsal at the new head is still owed before merge.

Doc leftovers: all fixed.

  • Removed "Keep Binary attestations: phase 2 – reproducible Fastly builds #1449 draft…" from both plans.
  • phases-2-4.md: build-facts paragraph now says 8 landed and 2 removed (Fastly build.rs, build_info.rs). The count now says 22 Phase 1 doc paths: 2 carried, 1 moved, 1 removed, 18 not carried. The ledger Landing column reads 1 — not carried / 1 — moved. The fixture_for_projection line is corrected.
  • Both "observer unchanged / byte-identical" sentences now name the f243408ce deletion-pattern change.
  • slice-map.json: now ends …plus SHA256SUMS: 17, or 18 with the Phase 2 rebuild check.
  • implementation.md:50 now describes carried-over binding.
  • Guide now says nine pre-creation gh attestation verify runs.
  • The four #[ignore] reasons now say "frozen Add binary attestation contracts and release build guards #1262 producer test, no workflow runs it", and startup.rs no longer says "CI fixture".

Phase 3 helpers: phases-2-4.md now records that 092de447b removed fixture_for_projection, fixture_native_registry and index_identity(). Phase 3 still calls them, so the next Phase 2 → Phase 3 merge must restore them there.

Smaller items

  • Shell Cargo-home wiring: added an assert.match pinning the per-role export, its refusal and mkdir ahead of ordinary-rustflags. Renaming the home or dropping the refusal fails the suite.
  • tool() still maps process failures to the fixed diagnostic; not changed this round.
  • Module-level expect(dead_code): left as is, per your note.

Local checks at ba05cabea (macOS, Rust 1.98.1, Node 24.20.0):

  • release crate: tests 988 + 4 + 20 and clippy -D warnings
  • release Node suite 45/45
  • cargo fmt, prettier (docs, markdown, JS) and actionlint
  • domain lint on the changed lines
  • full /verify: builds, release Fastly wasm, clippy, test-fastly (Viceroy 0.21.0), test-axum, test-cloudflare, vitest and formatters

Linux-only paths are left to CI.

Main now carries the squashed Phase 1 (#1435), whose tree equals the final Phase 1 head. Integrate main's changes since the Phase 1 commit this branch already contains (3e6bb0f): the last Phase 1 review round, #1405, #917 and #1504.

@aram356 aram356 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

Round 4 at e9e46e584, now against main. Every round-3 fix checks out:

  • Fixes: I re-ran each mutant at this head (dropping the recorder's package-location call or its build-message clause, the signer's integer and SPDX bounds, continue-on-error on checks, the exact CARGO_HOME spelling, the SA_NOCLDWAIT arm, the mismatch variant, and the shell's per-role Cargo home), and the suite fails for each.
  • The main merge kept every change from both sides: the PR's patch against main equals its pre-merge patch, apart from context lines.
  • Rehearsal: .39 was rehearsed at this exact head.

What's left is mostly a scope question: about two-thirds of the added code is producer code no release runs, and it brings the dependency that moves the shipped wasm's crates. There's also a design suggestion for a much smaller rebuild check, a CI rerun, and one stale number.

1 of the inline comments below carries a one-click GitHub suggestion — use Commit suggestion to apply it as a commit on the PR branch. It was applied alone to e9e46e584 and checked with prettier --config docs/.prettierrc, with no other file changes. The remaining comments describe the change in prose because it spans many files.

Blocking

🔧 wrench

  • Two Integration checks failed — below

❓ question

  • Can the unrun #1262 producer code, and the jsonschema dependency it brings, land with the phase that turns it on? — see inline at crates/trusted-server-release/src/main.rs:12

Non-blocking

🤔 thinking / ⛏ nitpick

  • 🤔 The rebuild check could be a GitHub-native attestation over in-run artifacts — see inline at .github/workflows/release-build.yml:339
  • ⛏ Run .27 had 12 jobs, not 13 — see inline at docs/superpowers/plans/binary-attestations-phase2-fastly-integration.md:128

Cross-cutting / body-level findings

  • 🔧 Two Integration checks failed: browser integration tests and trace runtime and browser acceptance. Neither is a required check, and both logs show an infrastructure failure rather than a test failure: apt couldn't resolve azure.archive.ubuntu.com ("Temporary failure resolving") while installing packages, on both attempts of run 43038396012. The PR's only changes in that area are doc comments (browser/helpers/infra.ts, tests/parity.rs, adapter-spin/tests/routes.rs), so I don't expect a real failure; please re-run them once the mirror is back, so the head is green before merge.

Round-3 follow-up

Verified fixed at this head:

  • Recorder: the package-location test, the build-message check, and the exact CARGO_HOME match.
  • Signer: the oversized-spdx and fractional-attempt modes.
  • Test and CI guards:
    • the file-wide continue-on-error contract;
    • the retry tests (the archive-status mutants and the backoff mutants now fail);
    • the CLI release-cfg step's 1 passed guard;
    • the shell-side Cargo-home wiring test.
  • Docs and doc comments:
    • the tagging checklist, which now names Reproduce and Compare;
    • the SA_NOCLDWAIT doc and its test;
    • the ordinary-rust-path-remap-v2 policy;
    • the RebuildMismatch assertion;
    • the Node 22.18/24.2 wording;
    • the dropped Fastly CARGO_HOME passthrough;
    • the description, the doc leftovers, the four #[ignore] reasons and the Phase 3 restore note.

Still open by your choice, which is fine: the shared-deadline seam in acquireOrdinaryTransfers, the RebuildIdentity context drops, and the three owner checks that need a root fixture.

CI Status

At e9e46e584: 28 of 30 checks pass.

  • browser integration tests: FAIL
  • trace runtime and browser acceptance: FAIL
  • integration tests: PASS
  • integration tests (Fastly EC lifecycle): PASS
  • CodeQL: PASS
  • HTML EOF runtime (Fastly): PASS
  • cargo fmt: PASS (required)
  • format-typescript: PASS (required)
  • worker-deadlines (spin): PASS
  • Analyze (rust): PASS
  • Analyze (actions): PASS
  • vitest: PASS
  • worker-deadlines (cloudflare): PASS
  • cargo test: PASS (required)
  • ts dev lint domains (changed lines): PASS
  • cargo test (axum native): PASS
  • CLAUDE.md symlink guard: PASS
  • Analyze (javascript-typescript): PASS
  • cargo check (cloudflare native + wasm32-unknown-unknown): PASS
  • prepare integration artifacts: PASS
  • format-docs: PASS (required)
  • cargo check/build/test (spin native + wasm32-wasip1): PASS
  • docs-proposal scripts: PASS
  • cargo test (cross-adapter parity): PASS
  • release scripts: PASS
  • cargo test (ts CLI, native) (ubuntu-latest): PASS
  • cargo test (ts CLI, native) (macos-latest): PASS
  • CI Node pin guard: PASS
  • Analyze (javascript-typescript): PASS
  • Analyze (python): PASS

Comment thread .github/workflows/release-build.yml Resolved

@aram356 aram356 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Follow-up to the round-4 review: one more blocking item, inline on .github/workflows/release-build.yml:149. The release should publish and attest the Fastly package that fastly compute deploy uploads, as a downloadable .tar.gz built reproducibly, alongside the wasm.

Comment thread .github/workflows/release-build.yml Resolved
Defer the unused controlled producer graph, fixtures and schemas to its
own enabling phase. Preserve main's production dependency resolution and
retain the live ordinary comparison and process safety helpers.

Build deterministic Fastly packages from a fixed service-neutral manifest,
bind both packages to their observations, compare every archive byte, and
include the package in provenance, SPDX signing and exact release sets.
Preserve wasm verification and document deployment of the unchanged package.
@prk-Jr
prk-Jr marked this pull request as draft November 12, 2026 04:19
@prk-Jr

prk-Jr commented Nov 12, 2026

Copy link
Copy Markdown
Collaborator Author

For the round-4 CI blocker: reran the failed Integration jobs at e9e46e584; attempt 3 passed. All 30 checks also pass at the new signed head 27361ec58.

The new package contract was rehearsed at that exact head in fork .40: all 12 jobs passed and 19 immutable prerelease assets were published, not Latest. Independent download/membership/checksum checks, both attestation identities online/offline, GitHub attestation API lookup and all four source/job/archive joins passed. The actual published wasm also passed local Viceroy publisher/JS/source-header/log checks. No IAB release/settings change or deployment occurred. Earlier rehearsal results remain attributed to their original sources; the PR description now reflects this head.

@prk-Jr
prk-Jr marked this pull request as ready for review November 12, 2026 05:14
@prk-Jr
prk-Jr requested a review from aram356 November 12, 2026 05:19
The release-verification heading became "Deploy the verified Fastly
package", so links to #deploy-the-verified-fastly-wasm from the getting
started and EdgeZero guides no longer resolved.
Keep both Unreleased breaking-change entries in CHANGELOG.md: main's
integration configuration validation (#1455) and Phase 2's tsjs build
requirements.
aram356 added a commit that referenced this pull request Nov 12, 2026
Per the user, binary attestation and package publishing (#1449, #1463,
#1543) are not part of this work and carry lower priority (DEC-19). AX-5
keeps only the production image built from source, A6 shrinks to building
it and no longer waits on #1449, and the image and Terraform units restack
on #1531, where they apply cleanly, instead of #1543.

This branch has not been deployed

No deployments
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.

Publish attested release binaries with build provenance and SBOMs

5 participants