Repository navigation
Conversation
aram356
left a comment
There was a problem hiding this comment.
Summary
This spec answers #1235 with attested release artifacts, verified deployment and byte-for-byte reproducible builds, then defines Phase 2 contracts for Cloudflare, Spin and Axum. Its trust boundaries are careful: build provenance, release membership, publisher identity and runtime claims are kept distinct, and the no-configuration rule is stated well. The main problems are scope and fit. It makes reproducibility of every artifact an all-or-nothing release gate and ties #1235's closure to Phase 2. It designs past two in-flight PRs (#1214 and #1213) that already change the same mechanisms. It doesn't account for how releases are branched or what the gh verifier needs. Several tool facts (Fastly packing and deploy, --signer-workflow matching, the Spin ABI) also need correcting.
The architecture I recommend preserves four small ownership boundaries:
flowchart TD
A[Controlled build and composition evidence] --> B[Signing and immutable publication]
B --> C[Trusted Server verifier library]
P[Trusted local policy and source approval] --> C
C --> D[Opaque VerifiedArtifact with retained bytes]
D --> E[EdgeZero prepared payload and resolved context]
E --> F[Observed operation state and recovery record]
Internally, the verifier should separately establish build evidence, official publication and consumer/action authorization before constructing VerifiedArtifact. EdgeZero should preserve payload integrity and own concrete lifecycle operations. It should not implement IAB release policy.
6 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. The rest describe the change in prose because it spans several sections or lines outside a single contiguous range.
Blocking
🔧 wrench
- One reproducibility gate makes every artifact wait for the hardest one: see inline at line 63
- Strict JS mode should build on #1214, not beside it: see inline at line 265
- The verifier's
ghprerequisites and trust chain aren't specified: see inline at line 357 - Build facts should be derived where possible, follow the env naming convention, and reconcile with #1213: see inline at line 559
- Rollout steps 2 and 3 are in an order that can't be executed: see inline at line 787
- Phase 2 in the acceptance criteria keeps #1235 open until Spin migrates: see inline at line 835
❓ question
- Which branch are release tags cut from?: see inline at line 320
Non-blocking
♻️ refactor
- Put the release-index v1 schema in the spec: see inline at line 255
- Build tsjs once per release and require identical embedded JS across artifacts: see inline at line 278
- Cargo's own output already separates host and target units; the wrapper and Syft add a component without adding coverage: see inline at line 294
- Drop
RELEASE_SETTINGS_READ_TOKENand check the published release instead: see inline at line 347 --signer-workflowis a prefix match; pin the exact certificate identity: see inline at line 380- The pack output doesn't match "exactly one
bin/main.wasmmember" (suggestion): see inline at lines 528-529 - State what
compute deploydoes on the native route (suggestion): see inline at line 553
🤔 thinking
reusable-sandboxis runtime-gated, so it may belong in the canonical artifact: see inline at line 61- The A/B gate measures nondeterminism, not environment sensitivity: see inline at line 208
- Separate assurance dimensions from release-cohort selection: see inline at line 208
- The CLI platform baselines exclude common operator hosts: see inline at line 234
- Define SBOM coverage before settling the recorder mechanism: see inline at line 294
- A local reusable workflow ties the signer identity to the source commit: see inline at line 326
- Make the evidence graph and verifier policy normative: see inline at line 357
- The release record gives the tag object for annotated tags, not the commit: see inline at line 361
- The publication record carries the same trust as pinning the artifact digest: see inline at line 473
- The EdgeZero seam is new surface, not an addition to an existing typed one: see inline at line 514
- Validate observed Fastly bindings, not only manifest declarations: see inline at line 514
- Recovery needs retained state and explicit ownership: see inline at line 537
- Specify Cloudflare's actual configuration handoff: see inline at line 650
- Spin's locked SDK targets a WASI 0.3 release candidate, not plain WASIp2: see inline at line 658
🏕 camp site
- Bound verification processes and evidence (suggestion): see inline at line 412
- The "configuration baked into the wasm" claims go beyond CI comments (suggestion): see inline at line 788
⛏ nitpick
- The scaffold is the repository-root file (suggestion): see inline at line 163
artifact-metadata: writeisn't needed for file attestations (suggestion): see inline at line 335
Cross-cutting / body-level findings
- ♻️ Add an explicit threat model. §1 lists each claim and what it doesn't establish, but there is no adversary table. Add one row per adversary (asset or mirror tampering, a malicious evidence-transfer path, a compromised dependency or build script, a rogue tag pusher, a compromised runner image, a compromised operator host, GitHub itself) with its capability, the check that defends against it, and what remains. Each check in the spec can then cite the threat it defends against, which makes it easier to see which checks earn their cost, for example the settings token at line 347 and the publication record at line 473.
- ♻️ Write it as a contract. The spec is about 16,000 words with no BCP 14 keywords (42 lowercase "must", no MUST or SHOULD), and paragraphs such as lines 176, 202 and 208 each carry six to ten requirements without IDs. The test plan and acceptance criteria paraphrase each other without traceability. Suggest requirement IDs with BCP 14 keywords, one table mapping requirement → test → acceptance item → #1235 "Done when", and rationale moved to an appendix. Also give the "release manifest" (line 60) and "release-tools manifest" (lines 288 and 412) names and paths; "manifest" already means
edgezero.tomlandfastly.tomlelsewhere in the spec. - 🌱 Cloudflare notes for the follow-up spec (§12, lines 642-654):
- worker-build 0.8.5 downloads the wasm-bindgen CLI (0.2.126), binaryen
version_130and@esbuild/linux-x640.28.1 (separate from the JS lockfile's esbuild 0.27.3) with no checksum check. A network-denied recipe needs them pre-seeded with digest checks, throughWASM_BINDGEN_BIN,WASM_OPT_BINandESBUILD_BINor its cache layout. - Output-changing switches to control include
CUSTOM_SHIM,RUN_TO_COMPLETION,COREDUMP/COREDUMP_FLAGS,NO_MINIFY,--no-optand--panic-unwind. worker-build also adds ansdk workers-rsentry to the wasm producers section. build.shinstalls worker-build without--lockedand derives its version fromCargo.lockat run time.- The
.tar.zstcanonical archive brings back the archive/member identity question that line 242 avoids for Phase 1. Publishing the members as raw assets plus an attestedmodules.jsoninventory keeps every subject a raw file. - The versions API silently drops unresolved
inheritbindings unlessbindings_inherit=strictis set, andinherit/keep_bindingsresolve against the latest uploaded version, which may not be the deployed one. Line 650's "fail when unresolved" needsbindings_inherit=strictand an explicitversion_id.
- worker-build 0.8.5 downloads the wasm-bindgen CLI (0.2.126), binaryen
- 🏕 Mark the January spec as superseded.
docs/superpowers/specs/2026-01-15-attestation-design.mdstill says**Status:** Proposaland doesn't point here. #1235 says this spec replaces its binary-attestation sections, so a one-line "superseded in part by" banner in that file would keep readers from following it.
CI Status
- cargo fmt: PASS (required)
- cargo test: PASS (required)
- format-docs: PASS (required)
- format-typescript: PASS (required)
- Analyze (actions): PASS
- Analyze (javascript-typescript): PASS
- Analyze (javascript-typescript): PASS
- Analyze (python): PASS
- Analyze (rust): PASS
- browser integration tests: PASS
- cargo check (cloudflare native + wasm32-unknown-unknown): PASS
- cargo check/build/test (spin native + wasm32-wasip1): PASS
- cargo test (axum native): PASS
- cargo test (cross-adapter parity): PASS
- cargo test (ts CLI, native) (macos-latest): PASS
- cargo test (ts CLI, native) (ubuntu-latest): PASS
- CLAUDE.md symlink guard: PASS
- CodeQL: PASS
- integration tests: PASS
- integration tests (Fastly EC lifecycle): PASS
- prepare integration artifacts: PASS
- vitest: PASS
Resolve the lockfile while preserving release dependencies and main's upgrades. Adapt the release tool to HMAC 0.13 and Rust 1.98 lint requirements without changing candidate guards or qualification policy.
Add a pure collection::workflow_jobs adapter that turns one strict, bounded GitHub Actions "jobs for a run attempt" REST body, the upload step outputs and the collector's own wait clock into the existing Upload and OptionalBuild inputs. Expected job names, assets, roles and recipes come only from an explicit ExpectedTopology that must name every fixed canonical and reproduction job exactly once. The adapter derives started from executed steps rather than started_at, maps success/failure/cancelled/skipped/timed_out and rejects every other job or step conclusion, accepts a conclusion-less job only at the deadline, binds each expected name to exactly one job of the exact run, attempt and head, requires mandatory jobs to start, succeed and upload, and passes upload identity through unchanged. The trusted dependency constructor stays closed; no HTTP, workflow or command wiring is added.
An optional native reproduction whose job did not succeed now has any upload output dropped unused and is recorded as NotUploaded with its derived outcome, so it can neither block collection nor lend its upload identity. Mandatory behaviour is unchanged. Document and test the single-fresh-attempt rule: run_attempt is required on every listed job and a carried-over job from an older attempt rejects the response. Document that the fetch must request per_page=100 and test a truncated page. Identify the mandatory reproduction by its Wasm target instead of its fixed-artifact position. Cover waiting and requested mandatory jobs and completed jobs without a conclusion, and split the fixtures and transfer integration out of the adapter test module.
…nto claude/workflow-adapter
Reuse the reviewed ZIP32 primitive and negative fixtures from #1262 at 122b786 without moving collection authority into Phase 2. Enforce exact build/observation member sets and source-owned bounds before the original full integrity checks. Verify red-green profile admission, 71 audited pure tests, strict clippy and an exact-ID read-only probe of the current Fastly archive against its recorded GitHub digest. Independent/security review approves this structural unit; transfer authentication remains pending.
Summary
Publishers and vendors need verifiable release bytes, and operators need to distinguish those bytes from ordinary source builds. This PR contains the binary-attestation contract, implementation plans, release build guards, JS composition capture, and the first private Rust producer utilities. It is an implementation PR with explicitly pending release gates.
tsexecutables, with provenance, artifact-specific SPDX, immutable membership, mandatory Fastly/shared-JS byte equality, and bounded native rebuild outcomes.The producer bootstrap remains closed. Source/preflight, authenticated acquisition, actual Rust preparation/boundaries/recipes, hosted qualification, signing/publication, and the consumer verifier are not complete. This checkpoint enables no official release or deployment and does not close #1235.
Changes
docs/superpowers/specs/2026-10-09-1235-release-attestation-design.md, feasibility audit, migration addendum and three implementation planscrates/trusted-server-release/**, workspace manifest/lockfilebuild_info.rs; CLI/Fastly build scripts and owning readersbuild.rs, inventory/copy/bundle helpers and native fixturesscripts/release/collect-js.mjsscripts/release/**.github/workflows/test.yml,format.yml; crate READMEs and operator guides.mjsformatting, and current build-fact/control documentation.The governing index-v1 schema, cohort and consumer-policy bytes were preserved during this review correction. Structural schema acceptance does not authorize staging or another artifact; the selected cohort and planned semantic policy enforcement remain narrower.
The initial deployment choice is the validated manual Fastly production route (AC-9). Integrated EdgeZero context checks, staging and recovery are the later AC-8 milestone. Publisher/configuration authority and actual remote execution remain separate #161 claims.
Linked work
Refs #1235, #1213, #1214, #1292 and #161. The packaging and EdgeZero plans describe later reviewed milestones; they are not implemented by this checkpoint.
Test plan
Local platform: macOS 26.6.2 ARM64, Rust 1.95.0 and Node 24.12.0. These results do not qualify macOS 15, native Intel, the GNU compatibility floor, signing/publication or provider submission.
.mjsformatting, workflow actionlint, whitespace checks, staged and committed PR domain lint.Checklist