Repository navigation
Publish attested release binaries with build provenance and SBOMs #1235
Description
Summary
We need release binaries that publishers and vendors can verify were built from this repo. Today there is nothing to verify. No workflow publishes release artifacts, tags v1.0.0 through v1.1.0 have no GitHub Releases, and every operator builds its own wasm from source. GitHub's built-in supply-chain tooling covers this. Build the binaries in a release workflow, attest SLSA build provenance and an SBOM for each one through Sigstore, and publish them as immutable release assets that anyone can check with gh attestation verify.
Binary attestation takes priority. An attestation only means something if every publisher runs the same bytes, so no configuration may be compiled into the binary. See Constraints.
PR #167 and its proposal, also on main as docs/superpowers/specs/2026-01-15-attestation-design.md, are reference only. #167 will be closed without merging. The proposal predates config separation (#799), the ts CLI and the non-Fastly adapters. This issue covers its build-provenance part. Runtime claims about the running config stay with #161.
Spec first
Write a design spec and get it approved before opening an implementation PR.
- Put it at
docs/superpowers/specs/YYYY-MM-DD-1235-<slug>-design.mdand follow the existing specs there. Include the issue link, date, status, problem, goals, non-goals, design, test plan and rollout. It replaces the binary-attestation sections of2026-01-15-attestation-design.md. - Land the spec in its own PR. The implementation PR links the approved spec and follows it. If the design changes during implementation, update the spec in the same PR.
- The spec states the no-configuration-in-the-binary rule as a requirement and lists every compile-time input of the release binary.
- The spec must settle these open decisions:
- Which artifacts ship first. The Fastly wasm and the
tsCLI for Linux and macOS are a suggestion. Cloudflare, Spin and standalone tsjs bundles can follow. - How operators deploy an attested artifact.
ts deploybuilds from source today. Either it learns to verify and deploy a release artifact, which likely needs an EdgeZero change, or builds become reproducible so a local build matches the attested digest. Pick one, or both in a stated order. - The SBOM format and generator, and whether the SBOM ships as a release asset.
- The release trigger, tag push or manual dispatch, and whether to start with a reusable build workflow for SLSA Build Level 3.
- The exact verification commands for publishers and vendors, the identity they pin (repo, workflow path, ref), and the offline flow.
- Which build facts the runtime can report for Trusted Server attestation #161, such as version and git commit, given that a wasm module can't hash itself.
- Which artifacts ship first. The Fastly wasm and the
Current behavior
Line numbers are at 8048301.
- No workflow publishes or attests anything.
test.ymlbuilds the Fastly wasm (.github/workflows/test.yml:111) and the Spin wasm (test.yml:174) only to run tests. - Operators build from source.
ts buildandts deployhand off toedgezero_cli::run_buildandedgezero_cli::run_deploy(crates/trusted-server-cli/src/run.rs:124,run.rs:148), andts deploybuilds before it uploads. The CLI itself installs from source withcargo install-cli(.cargo/config.toml:59). - The wasm on
mainembeds no operator config. Its compile-time inputs are:- The source tree,
Cargo.lockandrust-toolchain.toml(Rust 1.95.0). - The logical config store ID, which
crates/trusted-server-core/build.rs:37-38reads fromedgezero.tomlandconfig_payload.rs:23compiles in. Operators map their physical stores to that logical name with resource links andEDGEZERO__STORES__...__NAMEoverrides, so the repo'sedgezero.tomlis the same for everyone. - The tsjs bundles, which
crates/trusted-server-js/build.rsbuilds with npm frompackage-lock.jsonand embeds.
- The source tree,
crates/trusted-server-js/build.rsfalls back to whatever is indist/, with only a warning, when npm is missing (lines 41-45), whennpm cifails (lines 56-58), or whenTSJS_SKIP_BUILD=1is set (line 23). Only a failednpm run buildstops the build (lines 81-84).
Proposed change
Add .github/workflows/release.yml.
- Runs on a
v*tag, on GitHub-hosted runners only. Every action is pinned by commit SHA, and job permissions are limited tocontents: write,id-token: write,attestations: writeandartifact-metadata: write. - Builds with
--lockedand npm installed, and fails if the tsjs bundles weren't built in that run. - Generates the SBOM from the release build so it covers the Cargo crates and the npm packages compiled into the wasm. GitHub's dependency-graph export describes the repo, not a build, so it doesn't fit.
- Attests each binary with
actions/attest@v4. It creates SLSA build provenance by default and an SBOM attestation when givensbom-path. The repo is public, so signing goes through Sigstore's public-good instance and the entries are public in Rekor. - Creates a draft release, uploads the binaries, the SBOM and a
SHA256SUMSfile, then publishes it. With immutable releases on, nobody can change the assets or move the tag after that, and GitHub adds its own release attestation.
Attest the .wasm, not a Fastly package. A package also carries fastly.toml, which holds the operator's service ID, so its digest differs per publisher.
Verification for a publisher or vendor then looks like this:
gh attestation verify trusted-server-fastly.wasm -R IABTechLab/trusted-server \
--signer-workflow IABTechLab/trusted-server/.github/workflows/release.yml
gh attestation verify trusted-server-fastly.wasm -R IABTechLab/trusted-server \
--predicate-type https://spdx.dev/Document/v2.3
gh release verify-asset <tag> trusted-server-fastly.wasmConstraints
- No configuration may be compiled into the release binary. Anything a deployment varies goes through
trusted-server.tomlandts config push, or through platform stores. Other features have to fit this rule, not the other way around. - The permission model in Add the permission model with the Privacy Taxonomy vocabulary #1045 currently breaks the rule. It compiles its policy in with
include_str!(crates/trusted-server-core/src/permissions.rs:483at 22262a0), and Add the integration seam design specs #1084 describes the same design. The permission spec has to resolve that. This spec doesn't. edgezero.tomlis a compile-time input, so attested builds use the repo's copy unchanged. Operators who run several services in one account rename physical stores, not logical IDs.- A wasm module can't read its own bytes, so it can't report its own digest at runtime. That belongs to Trusted Server attestation #161. This spec only says which build facts get compiled in for Trusted Server attestation #161 to use.
Out of scope
- Runtime attestation, config hashes and per-request claims (Trusted Server attestation #161).
- How permission policy reaches a deployment. The permission spec owns that.
- Signing or attesting
trusted-server.toml. - Vendor CODEOWNERS review of integration directories, phase 3 of the Separate config and build for attestation #167 proposal.
- Signatures embedded in a wasm custom section.
- The external Prebid bundle from
ts prebid bundle, which operators build and pin by hash in config.
Done when
- The design spec is approved and merged under
docs/superpowers/specs/, and the implementation PR links it. - The spec states the no-configuration-in-the-binary rule and lists every compile-time input of the release binary.
- Pushing a
v*tag publishes an immutable GitHub Release with the chosen binaries, the SBOM andSHA256SUMS. -
gh attestation verifypasses for each binary with--signer-workflowpinned, and for the SBOM with--predicate-type.gh release verify-assetpasses for each asset. - The release build fails, instead of reusing
dist/, when npm is missing,npm cifails, orTSJS_SKIP_BUILDis set. - Either building the tag the documented way gives the same wasm digest as the release asset, or the docs say operators must deploy the release asset, matching the spec's decision.
- Immutable releases are on, and a tag ruleset limits who can push
v*tags. -
docs/guide/cli.md, a new page on verifying a release, andCHANGELOG.mdare updated.
Affected area
CI / Tooling
Activity
[-]Write a spec for attested release binaries and SBOMs[/-][+]Publish attested release binaries with build provenance and SBOMs[/+]