Repository navigation
Conversation
|
Rehearsal record at Run · immutable release. Release ID 420703797, 17 exact assets, no index/build-evidence/build-recipe/rebuild-check/inventory companions. All four builds and signers passed. Publication, initial release-ID/immutability validation, Independent verification below completed with exit status 0: one release verification, 17 asset verifications, 16 attestation verifications (four binaries × provenance/SPDX × signer-prefix/exact tag-ref identity), 16 checksum entries and four signed-SPDX predicate comparisons. It uses the guide commands with only repository/tag/commit substitutions. Workflow CLI: 2.102.0; independent CLI: 2.97.0. Full independent verification transcript — commands and output |
|
Successful rehearsal at Workflow — all jobs passed · immutable release · publisher log. Release ID 420812096: immutable=true, draft=false, 17 exact nonempty assets (four binaries, four SPDX2.3 documents, eight real action-produced bundles and SHA256SUMS). No index, inventory, build-evidence, build-recipe or rebuild-check companions. All four builds, all four isolated signing jobs, publication, cryptographic release/membership verification and the final release-ID/API read passed. The connection fix is exercised successfully; no mutation retry or tag reuse occurred. Immutable Independent verification completed with exit status 0 using checksum-verified GitHub CLI2.102.0, matching the publisher's recorded version: one release verification, 17 asset verifications, 16 attestation verifications (four binaries × provenance/SPDX × signer-prefix/exact tag-ref identity), all16 checksum entries and four signed-SPDX predicate comparisons. Commands are the guide's verification blocks with only repository/tag/commit substitutions. Full verification transcript — commands and output |
|
Rehearsal GitHub CLI 2.102.0 matches the publisher log. Its official macOS ARM archive was checksum-verified (SHA-256 Offline proofAll four binaries passed provenance and SPDX verification under OS-enforced network denial, with both signer identity forms, exact source/ref/signer digest pins, self-hosted runners denied, and published-SPDX predicate equality. No gh --version
gh attestation trusted-root > trusted_root.jsonl
shasum -a 256 trusted_root.jsonl
/usr/bin/sandbox-exec -p '(version 1)(allow default)(deny network*)' env GH_TOKEN=offline-verification-no-network bash -x offline-verify.shFull offline commands and success outputLocal verified Fastly routeThe downloaded wasm and host CLI first passed provenance and immutable release membership checks. Viceroy 0.21.0, pinned in the source tree, served a real configured page: HTTP 200, fixture origin marker, injected JS and the exact full source SHA in Complete local commands and outputset -euxo pipefail
REPO_ROOT=<source-checkout>
EVIDENCE=<evidence-directory>
WORK="$EVIDENCE/viceroy-smoke-2"
TS="$EVIDENCE/ts-aarch64-apple-darwin"
WASM="$EVIDENCE/trusted-server-fastly.wasm"
VICEROY=<pinned-runtime-tools>/viceroy
mkdir "$WORK"
for port in 18888 17676; do
if lsof -nP -iTCP:"$port" -sTCP:LISTEN; then exit 1; fi
done
cp "$REPO_ROOT/edgezero.toml" "$REPO_ROOT/fastly.toml" "$WORK/"
cp "$REPO_ROOT/trusted-server.example.toml" "$WORK/app.toml"
ln -s "$REPO_ROOT/crates" "$WORK/crates"
node - "$WORK" <<'JS'
const fs=require('node:fs');const root=process.argv[2];
const path=`${root}/app.toml`;let text=fs.readFileSync(path,'utf8');text=text.replace('domain = "example.com"','domain = "127.0.0.1"').replace('cookie_domain = ".example.com"','cookie_domain = "127.0.0.1"');text=text.replace('origin_url = "https://origin.example.com"','origin_url = "http://127.0.0.1:18888"');if(!text.includes('origin_url = "http://127.0.0.1:18888"'))throw Error('Origin fixture replacement failed');fs.writeFileSync(path,text);
const fastly=`${root}/fastly.toml`;text=fs.readFileSync(fastly,'utf8').replace('env = "FASTLY_KEY"','data = "fictional-local-api-key"');fs.writeFileSync(fastly,text);
JS
chmod u+x "$TS"
"$TS" --version
(cd "$WORK" && "$TS" config push --adapter fastly --local \
--manifest "$WORK/edgezero.toml" --app-config "$WORK/app.toml" --no-diff --yes)
"$VICEROY" --version
cat > "$WORK/origin.mjs" <<'JS'
import http from 'node:http';
http.createServer((req,res)=>{res.writeHead(200,{'content-type':'text/html'});res.end('<!doctype html><html><head><title>Local release proof</title></head><body>phase1-verified-origin-marker</body></html>');}).listen(18888,'127.0.0.1');
JS
node "$WORK/origin.mjs" > "$WORK/origin.log" 2>&1 &
origin_pid=$!
cleanup() {
if [[ -n "${viceroy_pid:-}" ]]; then kill "$viceroy_pid" 2>/dev/null || true; wait "$viceroy_pid" 2>/dev/null || true; fi
kill "$origin_pid" 2>/dev/null || true
wait "$origin_pid" 2>/dev/null || true
}
trap cleanup EXIT
"$VICEROY" serve -C "$WORK/fastly.toml" --addr 127.0.0.1:17676 "$WASM" > "$WORK/viceroy.log" 2>&1 &
viceroy_pid=$!
curl --fail --silent --show-error --retry 20 --retry-connrefused --retry-delay 1 \
--retry-max-time 45 --max-time 30 -H 'Host: 127.0.0.1' \
-D "$WORK/headers.txt" -o "$WORK/body.html" http://127.0.0.1:17676/
cat "$WORK/headers.txt"
node - "$WORK" <<'JS'
const fs=require('node:fs');const assert=require('node:assert/strict');const crypto=require('node:crypto');const work=process.argv[2];const headers=fs.readFileSync(`${work}/headers.txt`,'utf8');const body=fs.readFileSync(`${work}/body.html`);assert.match(headers,/^HTTP\/1\.1 200/m);assert.match(headers,/^x-ts-version: 922c2211c0cc7941bfc8ffa9f775ef8ac0971232\r?$/im);assert.ok(body.includes('phase1-verified-origin-marker'));assert.ok(body.includes('/static/tsjs=tsjs-unified.min.js'));console.log(`SUCCESS: configured Viceroy page returned HTTP200, origin marker, injected JS and exact release source header; ${body.length} bytes, sha256=${crypto.createHash('sha256').update(body).digest('hex')}`);
JS |
|
Rehearsal
Download recordThe ordinary release download was interrupted because transfer throughput was too low. Read-only bounded range downloads fetched the same release URLs; complete assembled files matched the published SHA-256 metadata before verification. No binary was substituted from a CI artifact or another release. Small companions were independently digest-checked as well. Commands and output follow; scripts existed only in the temporary evidence directory. Download commands and complete digest resultsgh api repos/prk-Jr/trusted-server/releases/tags/v0.0.0-rehearsal.4 > release-metadata.json
node range-download.mjs
node small-download.mjsimport fs from 'node:fs';
import {promisify} from 'node:util';
import {execFile} from 'node:child_process';
import crypto from 'node:crypto';
const run=promisify(execFile);
const release=JSON.parse(fs.readFileSync('release-metadata.json'));
const binaries=release.assets.filter(a=> !a.name.includes('.') || a.name.endsWith('.wasm'));
fs.mkdirSync('ranges',{recursive:true});
const tasks=[]; const chunk=2*1024*1024;
for(const a of binaries) for(let offset=0;offset<a.size;offset+=chunk) tasks.push({a,offset,end:Math.min(a.size-1,offset+chunk-1)});
let done=0;
async function worker(){while(tasks.length){const {a,offset,end}=tasks.shift();const out=`ranges/${a.name}.${offset}`; await run('curl',['--fail','--silent','--show-error','--location','--retry','2','--max-time','180','--range',`${offset}-${end}`,'--max-filesize',String(end-offset+1),'--output',out,a.browser_download_url]);if(fs.statSync(out).size!==end-offset+1)throw Error(`Range size mismatch: ${a.name} ${offset}`);done++;if(done%16===0)console.log(`Downloaded ${done} ranges`);}}
await Promise.all(Array.from({length:16},worker));
for(const a of binaries){const dest=fs.openSync(`${a.name}.assembled`,'w');const hash=crypto.createHash('sha256');for(let o=0;o<a.size;o+=chunk){const b=fs.readFileSync(`ranges/${a.name}.${o}`);fs.writeSync(dest,b);hash.update(b);}fs.closeSync(dest);if(`sha256:${hash.digest('hex')}`!==a.digest)throw Error(`Digest mismatch: ${a.name}`);fs.renameSync(`${a.name}.assembled`,a.name);console.log(`SUCCESS: ${a.name}: ${a.size} bytes match ${a.digest}`);}import fs from 'node:fs';import crypto from 'node:crypto';import {promisify} from 'node:util';import {execFile} from 'node:child_process';
const run=promisify(execFile);const {assets}=JSON.parse(fs.readFileSync('release-metadata.json'));await Promise.all(assets.filter(a=>a.size<2*1024*1024).map(async a=>{const valid=()=>fs.existsSync(a.name)&&`sha256:${crypto.createHash('sha256').update(fs.readFileSync(a.name)).digest('hex')}`===a.digest;if(!valid())await run('curl',['--fail','--silent','--show-error','--location','--max-time','180','--output',a.name,a.browser_download_url]);if(!valid())throw Error(`Bad asset: ${a.name}`);console.log(`SUCCESS: ${a.name} matches published digest`);}));Full online verification transcriptA small shell wrapper prints an explicit success line after each successful attestation command; JSON results remain separate for predicate comparison. |
|
Rehearsal Build/sign/publication run passed all seven builds/signing jobs and immutable publication. Published release: ID 421431085, Full online verification transcript, part 1. A wrapper prints explicit success after each successful attestation command; JSON output stays separate for SPDX equality. |
|
Rehearsal Build/sign/publication run passed all seven builds/signing jobs and immutable publication. Published release: ID 421431085, Full online verification transcript, part 2. |
|
Rehearsal Build/sign/publication run passed all seven builds/signing jobs and immutable publication. Published release: ID 421431085, All seven binaries passed provenance and SPDX verification with both signer identity forms, exact source/ref/signer pins, self-hosted runners denied and published-SPDX predicate equality under OS-enforced network denial. No gh attestation trusted-root > trusted_root.jsonl
shasum -a 256 trusted_root.jsonl
/usr/bin/sandbox-exec -p '(version 1)(allow default)(deny network*)' env GH_TOKEN=offline-verification-no-network bash -x offline-verify.sh |
|
Rehearsal Build/sign/publication run passed all seven builds/signing jobs and immutable publication. Published release: ID 421431085, Verified release wasm and host CLI were executed locally with pinned Viceroy 0.21.0, fictional loopback configuration and a local Node origin. A real page returned HTTP200, origin marker, injected JS and the exact full source SHA in set -euxo pipefail
REPO_ROOT=<source-checkout>
EVIDENCE=<evidence-directory>
WORK="$EVIDENCE/viceroy-smoke"
TS="$EVIDENCE/ts-aarch64-apple-darwin"
WASM="$EVIDENCE/trusted-server-fastly.wasm"
VICEROY=<pinned-runtime-tools>/viceroy
mkdir "$WORK"
for port in 18888 17676; do
if lsof -nP -iTCP:"$port" -sTCP:LISTEN; then exit 1; fi
done
cp "$REPO_ROOT/edgezero.toml" "$REPO_ROOT/fastly.toml" "$WORK/"
cp "$REPO_ROOT/trusted-server.example.toml" "$WORK/app.toml"
ln -s "$REPO_ROOT/crates" "$WORK/crates"
node - "$WORK" <<'JS'
const fs=require('node:fs');const root=process.argv[2];
const path=`${root}/app.toml`;let text=fs.readFileSync(path,'utf8');text=text.replace('domain = "example.com"','domain = "127.0.0.1"').replace('cookie_domain = ".example.com"','cookie_domain = "127.0.0.1"');text=text.replace('origin_url = "https://origin.example.com"','origin_url = "http://127.0.0.1:18888"');if(!text.includes('origin_url = "http://127.0.0.1:18888"'))throw Error('Origin fixture replacement failed');fs.writeFileSync(path,text);
const fastly=`${root}/fastly.toml`;text=fs.readFileSync(fastly,'utf8').replace('env = "FASTLY_KEY"','data = "fictional-local-api-key"');fs.writeFileSync(fastly,text);
JS
chmod u+x "$TS"
"$TS" --version
(cd "$WORK" && "$TS" config push --adapter fastly --local \
--manifest "$WORK/edgezero.toml" --app-config "$WORK/app.toml" --no-diff --yes)
"$VICEROY" --version
cat > "$WORK/origin.mjs" <<'JS'
import http from 'node:http';
http.createServer((req,res)=>{res.writeHead(200,{'content-type':'text/html'});res.end('<!doctype html><html><head><title>Local release proof</title></head><body>phase1-verified-origin-marker</body></html>');}).listen(18888,'127.0.0.1');
JS
node "$WORK/origin.mjs" > "$WORK/origin.log" 2>&1 &
origin_pid=$!
cleanup() {
if [[ -n "${viceroy_pid:-}" ]]; then kill "$viceroy_pid" 2>/dev/null || true; wait "$viceroy_pid" 2>/dev/null || true; fi
kill "$origin_pid" 2>/dev/null || true
wait "$origin_pid" 2>/dev/null || true
}
trap cleanup EXIT
"$VICEROY" serve -C "$WORK/fastly.toml" --addr 127.0.0.1:17676 "$WASM" > "$WORK/viceroy.log" 2>&1 &
viceroy_pid=$!
curl --fail --silent --show-error --retry 20 --retry-connrefused --retry-delay 1 \
--retry-max-time 45 --max-time 30 -H 'Host: 127.0.0.1' \
-D "$WORK/headers.txt" -o "$WORK/body.html" http://127.0.0.1:17676/
cat "$WORK/headers.txt"
node - "$WORK" <<'JS'
const fs=require('node:fs');const assert=require('node:assert/strict');const crypto=require('node:crypto');const work=process.argv[2];const headers=fs.readFileSync(`${work}/headers.txt`,'utf8');const body=fs.readFileSync(`${work}/body.html`);assert.match(headers,/^HTTP\/1\.1 200/m);assert.match(headers,/^x-ts-version: 937214a632f9387601dba94f996de78b2e6ee424\r?$/im);assert.ok(body.includes('phase1-verified-origin-marker'));assert.ok(body.includes('/static/tsjs=tsjs-unified.min.js'));console.log(`SUCCESS: configured Viceroy page returned HTTP200, origin marker, injected JS and exact release source header; ${body.length} bytes, sha256=${crypto.createHash('sha256').update(body).digest('hex')}`);
JS |
|
Rehearsal Build/sign/publication run passed all seven builds/signing jobs and immutable publication. Published release: ID 421431085, All 29 published files were downloaded from this release. Bounded range downloads were assembled and checked against the published SHA-256 metadata; small companions were independently digest-checked. No earlier release or CI artifact substituted for a binary. gh api repos/prk-Jr/trusted-server/releases/tags/v0.0.0-rehearsal.5 > release-metadata.json
node range-download.mjs
node small-download.mjs
node check-downloads.mjsimport fs from 'node:fs';
import {promisify} from 'node:util';
import {execFile} from 'node:child_process';
import crypto from 'node:crypto';
const run=promisify(execFile);
const release=JSON.parse(fs.readFileSync('release-metadata.json'));
const binaries=release.assets.filter(a=> a.size>=2*1024*1024);
fs.mkdirSync('ranges',{recursive:true});
const tasks=[]; const chunk=2*1024*1024;
for(const a of binaries) for(let offset=0;offset<a.size;offset+=chunk) tasks.push({a,offset,end:Math.min(a.size-1,offset+chunk-1)});
let done=0;
async function worker(){while(tasks.length){const {a,offset,end}=tasks.shift();const out=`ranges/${a.name}.${offset}`; await run('curl',['--fail','--silent','--show-error','--location','--retry','2','--max-time','180','--range',`${offset}-${end}`,'--max-filesize',String(end-offset+1),'--output',out,a.browser_download_url]);if(fs.statSync(out).size!==end-offset+1)throw Error(`Range size mismatch: ${a.name} ${offset}`);done++;if(done%32===0)console.log(`Downloaded ${done} ranges`);}}
await Promise.all(Array.from({length:32},worker));
for(const a of binaries){const dest=fs.openSync(`${a.name}.assembled`,'w');const hash=crypto.createHash('sha256');for(let o=0;o<a.size;o+=chunk){const b=fs.readFileSync(`ranges/${a.name}.${o}`);fs.writeSync(dest,b);hash.update(b);}fs.closeSync(dest);if(`sha256:${hash.digest('hex')}`!==a.digest)throw Error(`Digest mismatch: ${a.name}`);fs.renameSync(`${a.name}.assembled`,a.name);console.log(`SUCCESS: ${a.name}: ${a.size} bytes match ${a.digest}`);}import fs from 'node:fs';import crypto from 'node:crypto';import {promisify} from 'node:util';import {execFile} from 'node:child_process';
const run=promisify(execFile);const {assets}=JSON.parse(fs.readFileSync('release-metadata.json'));await Promise.all(assets.filter(a=>a.size<2*1024*1024).map(async a=>{const valid=()=>fs.existsSync(a.name)&&`sha256:${crypto.createHash('sha256').update(fs.readFileSync(a.name)).digest('hex')}`===a.digest;if(!valid())await run('curl',['--fail','--silent','--show-error','--location','--max-time','180','--output',a.name,a.browser_download_url]);if(!valid())throw Error(`Bad asset: ${a.name}`);console.log(`SUCCESS: ${a.name} matches published digest`);}));import fs from'node:fs';import assert from'node:assert/strict';import crypto from'node:crypto';
const release=JSON.parse(fs.readFileSync('release-metadata.json'));const expected=JSON.parse(fs.readFileSync('expected-assets.json'));
assert.equal(release.immutable,true);assert.equal(release.draft,false);assert.deepEqual(release.assets.map(a=>a.name).sort(),expected);assert.equal(expected.length,29);
for(const a of release.assets){assert(fs.lstatSync(a.name).isFile());const bytes=fs.readFileSync(a.name);assert(bytes.length>0);assert.equal(bytes.length,a.size);assert.equal('sha256:'+crypto.createHash('sha256').update(bytes).digest('hex'),a.digest);}
const sums=fs.readFileSync('SHA256SUMS','utf8').trim().split('\n');assert.equal(sums.length,28);assert.deepEqual(sums.map(line=>line.match(/^[a-f0-9]{64} (.+)$/)[1]).sort(),expected.filter(a=>a!=='SHA256SUMS'));
console.log('SUCCESS: immutable published release contains exactly29 nonempty assets with published digests; SHA256SUMS covers exactly28 companions and binaries, excluding itself.'); |
|
Rehearsal
Full commands and explicit success output:
Any following documentation-only head retains this exact rehearsed implementation SHA. The PR remains draft; no reviewers requested. Maintainer production-release/settings and allowlist review gates remain. |
dhruv8sh
left a comment
There was a problem hiding this comment.
Solid Phase 1 for #1235. The trust boundaries hold up: builds run read-only with no caches; signing is tag-gated and isolated; it never checks out or runs build inputs; and before signing it checks how many files arrived, that none are symlinks, and their digests. The publisher creates the release once, refuses to touch an existing tag or draft, uploads by release ID, checks every asset digest and that the release is immutable, and re-downloads everything to compare bytes.
On the Rust side, release_source fails closed: the release flag must be exactly 1, it rejects non-Unicode input, it requires a full 40-character lowercase commit, and it never falls back to local git in release mode. Tests cover each of these. BuildInfo is a small, const, well-documented type, and the Fastly build facts only appear in reuse mode, so single-mode startup diagnostics don't change.
CI: all checks pass. release and build / sign are skipped because they need a v* tag.
The inline comments below are non-blocking.
🌱 About 7k of the +10.8k lines are design docs, plans, a slice map and qualification transcripts. The implementation is reviewable on its own, but in future, putting the spec and qualification material in a separate PR, with the implementation PR stacked on top, would keep code review focused.
ChristianPavilonis
left a comment
There was a problem hiding this comment.
Summary
The seven-binary Phase 1 pipeline separates build, signing and publication permissions and keeps later assurance work explicitly scoped out. Changes are requested for annotated-tag failure after immutable publication and SPDX creation timestamps rejected by strict consumers.
Reviewed head: 4940928e800fed00e4bddd8f3727fb52dcb2b296.
One of the three inline comments carries a one-click GitHub
suggestion. Use Commit suggestion or Add suggestion to batch to apply it. The other inline fixes require changes outside the suggested range or to additional fixtures.
Blocking
🔧 wrench
- Reject unsupported annotated tags before publishing, or handle tag-object identity — see inline at
.github/workflows/release.yml:227. - Normalize the SPDX creation timestamp — see inline at
scripts/release/phase1.mjs:77.
Non-blocking
🏕 camp site
- Exclude the new release control from legacy settings overlays — see inline at
crates/trusted-server-core/build.rs:163-164.
Cross-cutting / body-level findings
📌 GitHub CLI still downloads unrelated tags' bundle URLs
The publisher filters other tags locally, but the GitHub CLI implementation it invokes fetches every returned bundle_url before filtering release attestations by tag. An older RC and a later release can share a commit, so a slow or unavailable older bundle can still fail current-release verification after immutable publication. This affects both gh release verify at .github/workflows/release.yml:219 and gh release verify-asset at line 266; the isolation guarantee in docs/guide/release-verification.md:362 is stronger than the dependency provides.
A temporary HTTPS fixture with the real installed CLI confirmed that an older tag's 503 bundle URL was fetched four times and caused failure before current-attestation verification. Removing that unrelated record allowed execution to reach the current verification stage. This is a routing/availability reproduction, not a cryptographic-validity claim. The relevant 2.102.0 client implementation fetches bundles before the release command filters tags; installed 2.103.3 exhibits the same behavior.
Track an upstream filter-before-fetch fix and qualify a CLI that supplies that behavior. This is a non-blocking dependency follow-up, not a request to implement the deferred custom Trusted Server verifier. Meanwhile, narrow the guide's guarantee:
Other-tag records are ignored by the publisher's own availability check, but GitHub CLI still fetches all returned bundle URLs before filtering by tag. An unavailable older bundle can therefore fail the current release's read-only verification. Preserve the immutable release and follow the read-only recovery procedure.Apply manually — the behavioral fix belongs to GitHub CLI; dependency qualification and guide/test updates cannot be expressed as a single local suggestion.
Verification
- Exact SPDX suggestion verified alone: 19 producer tests; three fractional/whole-second cases and existing incomplete-fixture behavior; strict SPDX tools-java 2.0.7 validation of output from the actual helper; syntax, Prettier 3.8.1, shellcheck and diff checks. No post-verification patch drift.
- Focused publisher reproduction confirmed annotated-tag failure after one publication and 29 uploads. Read-only verification of a real signed annotated-tag release confirmed the distinct tag-object identity.
- Downloaded
.15Fastly sample passed immutable membership, both predicates, both signer identity forms, source/ref/digest pins and SPDX predicate equality. This is evidence for its recorded68eef902asource, not a current-head tag rehearsal. - Passing Rust, adapter and browser CI results were reused; their relevant inputs were unchanged by the release-helper suggestion. No current-head tag publication/deployment, administrator-settings audit or full local core settings test was performed.
- Scratch edits were discarded; the worktree is clean at the reviewed head. No commits or pushes were made.
CI Status
- release: SKIPPED
- build / sign: SKIPPED
- integration tests: PASS
- browser integration tests: PASS
- integration tests (Fastly EC lifecycle): PASS
- CodeQL: PASS
- build / Build trusted-server-axum-aarch64-unknown-linux-gnu: PASS
- build / Build ts-x86_64-apple-darwin: PASS
- build / Build ts-aarch64-apple-darwin: PASS
- build / Build trusted-server-axum-x86_64-unknown-linux-gnu: PASS
- build / Build ts-x86_64-unknown-linux-gnu: PASS
- build / Build trusted-server-fastly.wasm: PASS
- build / Build trusted-server-axum-x86_64-unknown-linux-musl: PASS
- build / checks: PASS
- Analyze (rust): PASS
- Analyze (javascript-typescript): PASS
- Analyze (actions): PASS
- vitest: PASS
- cargo test (ts CLI, native) (macos-latest): PASS
- cargo test (ts CLI, native) (ubuntu-latest): PASS
- CLAUDE.md symlink guard: PASS
- format-typescript: PASS (required)
- cargo fmt: PASS (required)
- docs-proposal scripts: PASS
- cargo test (axum native): PASS
- format-docs: PASS (required)
- cargo check (cloudflare native + wasm32-unknown-unknown): PASS
- prepare integration artifacts: PASS
- cargo check/build/test (spin native + wasm32-wasip1): PASS
- cargo test (cross-adapter parity): PASS
- cargo test: PASS (required)
- HTML EOF runtime (Fastly): PASS
- Analyze (python): PASS
- Analyze (javascript-typescript): PASS
Merge current main to retain the required CI Node and domain guards. Reject unsupported tag objects before release mutations, normalize SPDX creation timestamps, and clean task-owned build files. Exclude release controls from legacy test settings and clarify operator verification limits.
|
Addressed the body-level GitHub CLI follow-up from the current review in 79b118858. The operator guide, compatibility record and publisher comment now distinguish the publisher's own other-tag filtering from GitHub CLI's bundle fetching. An unavailable older bundle can still fail current-release read-only verification; preserve the immutable release and use the documented recovery procedure. Filter-before-fetch behavior and qualification of a CLI that supplies it remain a dependency follow-up; no upstream behavioral fix or custom Trusted Server verifier is claimed. The same signed, GitHub-Verified update contains the inline corrections and approved merge of main. Required local checks passed, including the full 1232-test JS suite, adapter/CLI tests, Clippy, production Fastly WASM build, release-control regressions, 12 Chrome fixtures, Node/domain guards and strict SPDX timestamp interoperability probe. Initial sandbox-only keychain/listener failures were rerun successfully with host access. Linux-only NSS coverage remains hosted evidence. Exact-head upstream CI passed: 34 successful checks and two expected tag-only skips. Five independently verified threads are resolved. The annotated-tag and SPDX threads remain open: the authorized fork rehearsal .21 cannot qualify the release because GitHub cancelled the ARM macOS job without assigning a runner, citing hosted runner capacity. Five builds passed and Intel macOS was still running at readback; no signing/publication has run and no matching draft/release exists. The source/tag remain unchanged; no second attempt or settings change was made. Historical rehearsal .15 applies only to its recorded source, and does not qualify this updated release machinery. The PR is ready for review; fresh release qualification and maintainer settings/spec decisions remain outstanding. |
aram356
left a comment
There was a problem hiding this comment.
Summary
This adds the Phase 1 release build, signing and publication workflows, build-identity facts and the operator verification guide. Two blocking problems are in the release path itself. The publisher rejects the attestation record shape GitHub returns for other repositories, and that failure comes after the release is already immutable. The signing job's symlink guard never fails. Several docs (the spec, the plan and the compatibility notes) also describe Phase 1 in ways the code contradicts.
7 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. The remaining comments describe the fix in prose because it touches multiple files or lines outside the diff. Each suggestion was applied on its own and then all together on990509bc8. In every case the release suite (node --test scripts/release/phase1.test.mjs, 21/21), docs Prettier, root Markdown Prettier, actionlint (no new warnings) and shellcheck passed.
Blocking
🔧 wrench
- Publisher assumes an inline attestation
bundleand fails after immutable publication: see inline at.github/workflows/release.yml:241 - Signing input guard lets symlinks through (suggestion): see inline at
.github/workflows/release-build.yml:132 - Move the inline publisher into
scripts/release/: see inline at.github/workflows/release.yml:111 - Pin actions by version tag: see inline at
.github/workflows/release.yml:45 - Offline verification block can exit 0 without verifying (suggestion): see inline at
docs/guide/release-verification.md:149 - Spec labels contradicted MUSTs as Phase 1: see inline at
docs/superpowers/specs/2026-10-09-1235-release-attestation-design.md:123 - "Docs-only since
149754d35" is no longer true: see inline atdocs/superpowers/plans/binary-attestations-phase1-compatibility.md:39 - Plan checks off work that isn't here: see inline at
docs/superpowers/plans/2026-10-13-attested-release-implementation.md:200 - Unread copies of #1262 data and docs: see inline at
crates/trusted-server-cli/release-policy.json:1 - Release notes pre-fill the pins that need independent review: see inline at
.github/workflows/release.yml:166 - Source-audit tokenizer misses
br#/cr#raw strings and astral chars (suggestion): see inline atscripts/release/phase1.mjs:249 - Build-script audit rule skips
#[path]helpers: see inline atscripts/release/phase1.mjs:350 - Release-path test gaps (53 of 101 mutants survive): see inline at
scripts/release/phase1.test.mjs:154 - Unpinned
gh/Node in the publisher;verifyBundlenot retried: see inline at.github/workflows/release.yml:110 - Test-only release
cfgin the CLI build script: see inline atcrates/trusted-server-cli/build.rs:20 ts --versionchange is not marked Breaking (suggestion): see inline atCHANGELOG.md:16--signer-workflowis not a prefix match (suggestion): see inline atdocs/guide/release-verification.md:108- Prerequisites omit auth, minimum
ghandshasum(suggestion): see inline atdocs/guide/release-verification.md:70 - CLI link 404s from the release notes (suggestion): see inline at
docs/guide/release-verification.md:183 - Build-identity paragraph inaccuracies: see inline at
docs/guide/release-verification.md:301
❓ question
- Why run all seven release builds on PRs, with a hand-maintained path list?: see inline at
.github/workflows/release.yml:5 - Is Axum now a production deployment target?: see inline at
CHANGELOG.md:12
Cross-cutting / body-level findings
- 🔧 Smaller documentation fixes:
docs/superpowers/plans/binary-attestations-phase1-compatibility.md:94links/guide/release-verification.docs/superpowers/**is excluded from the VitePress build, so this only renders on GitHub, where the root-relative link 404s. Use../../guide/release-verification.md.docs/superpowers/specs/schemas/release-index-v1.schema.jsonhas no$schema. It usesminContains(:107, :119), which draft-07 evaluators, the common default, silently ignore. Declare 2020-12.- If the qualification reports stay (see the comment on
release-policy.json):scripts/release/qualification/2026-10-15-consumer-real-fixtures.mdembeds a real third-party project's repository, run IDs and signer identity (:30-52, :120), plus local tool paths (/opt/homebrew/bin/gh, :12, :106, :108). AGENTS.md asks for fictional data in docs. Also,TS_RELEASE_NATIVE_REPRODUCTION(2026-10-21-native-reproduction-schedule.md:10) breaks theTRUSTED_SERVER__environment-variable prefix. - The spec's Status line (:129) still says the spec, plans and implementation "share PR #1262" and that "no separate implementation PR is opened". #1262 is closed, and this PR is the implementation.
- The spec's Review log (:786) records a review of
20152315c, which predates the annotated-tag and SPDX-timestamp fixes in79b118858, and doesn't say who or what reviewed it.
CI Status
- cargo fmt: PASS (required)
- cargo test: PASS (required)
- format-docs: PASS (required)
- format-typescript: PASS (required)
- .github/dependabot.yml: PASS
- Analyze (actions): PASS
- Analyze (javascript-typescript): PASS
- Analyze (python): PASS
- Analyze (rust): PASS
- browser integration tests: PASS
- build / checks: PASS
- build / Build trusted-server-axum-aarch64-unknown-linux-gnu: PASS
- build / Build trusted-server-axum-x86_64-unknown-linux-gnu: PASS
- build / Build trusted-server-axum-x86_64-unknown-linux-musl: PASS
- build / Build trusted-server-fastly.wasm: PASS
- build / Build ts-aarch64-apple-darwin: PASS
- build / Build ts-x86_64-apple-darwin: PASS
- build / Build ts-x86_64-unknown-linux-gnu: PASS
- build / sign: SKIPPED (tag-only)
- 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
- CI Node pin guard: PASS
- CLAUDE.md symlink guard: PASS
- CodeQL: PASS
- docs-proposal scripts: PASS
- HTML EOF runtime (Fastly): PASS
- integration tests: PASS
- integration tests (Fastly EC lifecycle): PASS
- prepare integration artifacts: PASS
- release: SKIPPED (tag-only)
- ts dev lint domains (changed lines): PASS
- vitest: PASS
- Move the release publisher into scripts/release/publish.mjs with a release-notes template; tests import it instead of scraping YAML. - Accept release-attestation records that carry only bundle_url, verify them through gh release verify output, and retry the list read and failed proof fetches within the availability checks. - Install a checksummed GitHub CLI 2.102.0 and pinned Node for the publisher and checks job; refuse non-SemVer tags before building. - Fix the signing-input symlink guard and bundle check, which set -e ignored inside && lists. - Run release builds on main pushes and tags only; run the Node release suite on pull requests in format.yml. Reference actions by major tag. - Leave REPO, TAG and COMMIT blank in release notes, with a guard that also works in bash 3.2. - Extend the source audit to byte/C raw strings, astral chars and build helpers; drop the test-only CLI cfg and duplicate release validation. - Remove carried #1262 data and docs (frozen at 122b786), frame Axum binaries as dev/evaluation builds, map Phase 1 spec scope by R-ID, and correct the guide, plan, compatibility record and changelog.
|
Body-level items from the review, addressed in c827eb2:
Before this push, I had three independent automated review passes cover the whole PR: the publication path, the source audit and Rust, and docs-versus-code. Their blocking finding was that the new notes guard did nothing on macOS bash 3.2. That and their should-fix findings are in this commit, along with these fixes:
Local checks:
Outstanding: a fresh fork rehearsal of this head (sparse-checkout publisher, pinned gh install, |
|
Phase 1 rehearsal
The fork's API returned the release-attestation bundle inline. The Independent operator verification: run in a fresh directory with an official GitHub CLI 2.102.0 macOS ARM archive (checksum verified against cli/cli's checksums file). I used the published release notes' block and filled in
The published notes show the "stay editable" warning and the blank pins. |
ChristianPavilonis
left a comment
There was a problem hiding this comment.
Summary
The seven-binary Phase 1 pipeline separates read-only builds, isolated signing and immutable publication, with conservative SPDX inventories and operator verification instructions. Changes are requested for an availability-deadline bug; two documentation corrections are non-blocking.
Reviewed head: 029f2046784ab1732eaa571a1374fcd3f5691f72.
One of the three inline comments carries a one-click GitHub
suggestion. Use Commit suggestion or Add suggestion to batch to apply it. The other comments describe manual documentation corrections across multiple passages or files.
Blocking
🔧 wrench
- Enforce the advertised availability deadline — see inline at
scripts/release/publish.mjs:391-436.
Non-blocking
🏕 camp site
- Correct the current-head rehearsal attribution — see inline at
docs/superpowers/plans/binary-attestations-phase1-compatibility.md:349-351. - Distinguish Ctrl-C draining from missing SIGTERM handling — see inline at
docs/guide/release-verification.md:43-44.
Verification
- Read all 36 changed files and the removed code, with runtime, build-identity and documentation cross-checks.
- Applied the exact approved suggestion alone in the isolated reviewer worktree. The clock-injected publisher reproduction accepted a proof at 73.5 seconds before the suggestion and rejects at the 60-second deadline afterward.
- All 28 release-producer tests passed; JavaScript syntax, Prettier, shellcheck and JS formatting passed. JS suite: 73 files passed, 1,809 tests passed, 1 skipped, no type errors.
- The complete patch was byte-identical before and after verification. Scratch changes were discarded; the reviewer worktree is clean at the reviewed head. No commits or pushes were made.
- Passing hosted Rust, adapter and integration results were reused. No live tag publication, deployment or administrator-settings audit was performed; the deadline reproduction uses mocked external operations and an injected clock.
CI Status
- cargo test (axum native): PASS
- integration tests (Fastly EC lifecycle): PASS
- browser integration tests: PASS
- integration tests: PASS
- trace runtime and browser acceptance: PASS
- cargo check (cloudflare native + wasm32-unknown-unknown): PASS
- CodeQL: PASS
- cargo test (cross-adapter parity): PASS
- cargo test: PASS (required)
- worker-deadlines (cloudflare): PASS
- worker-deadlines (spin): PASS
- cargo fmt: PASS (required)
- CI Node pin guard: PASS
- Analyze (javascript-typescript): PASS
- cargo test (ts CLI, native) (ubuntu-latest): PASS
- Analyze (rust): PASS
- Analyze (actions): PASS
- format-typescript: PASS (required)
- Analyze (python): PASS
- vitest: PASS
- cargo test (ts CLI, native) (macos-latest): PASS
- format-docs: PASS (required)
- docs-proposal scripts: PASS
- prepare integration artifacts: PASS
- cargo check/build/test (spin native + wasm32-wasip1): PASS
- HTML EOF runtime (Fastly): PASS
- ts dev lint domains (changed lines): PASS
- CLAUDE.md symlink guard: PASS
- Analyze (javascript-typescript): PASS
The release-build/signing/publication workflow does not run on pull requests; the passing PR checks do not establish a current-head release rehearsal.
The availability loop checked its 60-second deadline only at the start of each round, while the attestation list read and `gh release verify` proof fetch retried under the 180-second post-publish window. A round could therefore retry and accept a proof after the advertised deadline. Both now use a retrier and command timeouts bound to the availability deadline. Also correct the rehearsal attribution: fork rehearsal .22 covers source 5f0139b only, not the later main merge or this publisher change, and distinguish Axum's Ctrl-C request draining from missing SIGTERM handling.
ChristianPavilonis
left a comment
There was a problem hiding this comment.
Summary
Publishes seven release binaries with build provenance, SPDX dependency inventories, retained Sigstore bundles and immutable-release verification. No verified blocking or non-blocking findings in the Phase 1 changes.
Verification scope
Reviewed all 36 changed files and removed code, including correctness, security, WASM compatibility, architecture, maintainability and repository conventions.
- The release producer suite passed 28/28 tests in CI for the reviewed head, including handoff corruption, partial uploads, tag validation and retry deadlines. Shellcheck also passed.
- Locally,
node scripts/release/phase1.mjs auditpassed with repository-pinned Node 24.20.0, andgit diff --check origin/main...refs/review/pr-1435/headpassed. - No current-head release rehearsal, live deployment or upstream immutability/tag-settings audit was performed. The latest documented successful fork rehearsal,
.22, covers5f0139b167cd0619cccd9563e8aadca4436281ae, not the reviewed head. This approval is a code-review result, not release qualification or deployment authorization.
CI status
- integration tests: PASS
- browser integration tests: PASS
- trace runtime and browser acceptance: PASS
- integration tests (Fastly EC lifecycle): PASS
- CodeQL: PASS
- cargo test (axum native): PASS
- CI Node pin guard: PASS
- CLAUDE.md symlink guard: PASS
- cargo test (ts CLI, native) (ubuntu-latest): PASS
- worker-deadlines (cloudflare): PASS
- cargo check/build/test (spin native + wasm32-wasip1): PASS
- cargo test: PASS (required)
- ts dev lint domains (changed lines): PASS
- worker-deadlines (spin): PASS
- cargo test (ts CLI, native) (macos-latest): PASS
- Analyze (javascript-typescript): PASS
- Analyze (python): PASS
- format-typescript: PASS (required)
- cargo check (cloudflare native + wasm32-unknown-unknown): PASS
- Analyze (rust): PASS
- format-docs: PASS (required)
- prepare integration artifacts: PASS
- docs-proposal scripts: PASS
- cargo test (cross-adapter parity): PASS
- Analyze (javascript-typescript): PASS
- cargo fmt: PASS (required)
- HTML EOF runtime (Fastly): PASS
- vitest: PASS
- Analyze (actions): PASS
aram356
left a comment
There was a problem hiding this comment.
Summary
Second review, at ba54b274f. The round-1 blockers are fixed. The publisher now accepts bundle_url-only release-attestation records, and the symlink guard fails closed on bash 3.2. The gh checksum matches cli/cli's digest, actions use version tags, and the PR trigger and the #1262 files are gone. 69b74e2ae also states rehearsal coverage per commit, which settles the coverage claims in the compatibility record. The remaining blockers are:
- an incomplete fix to the build-script audit;
- test gaps that let several release checks be deleted without a failing test, including half of the new availability-deadline bound;
- docs that describe build-script and tagging behaviour that no longer holds.
4 of the 11 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. The remaining comments describe the fix in prose because it touches several files or lines outside the diff.
Blocking
🔧 wrench
- Build helpers in nested modules escape the build-script rule: see inline at
scripts/release/phase1.mjs:318 - Several producer and publisher checks can be deleted with no failing test: see inline at
scripts/release/phase1.test.mjs:1450 - The availability-deadline test pins only the proof fetch: see inline at
scripts/release/phase1.test.mjs:724 - Spec, plan and record say the CLI and Fastly build scripts validate release inputs: see inline at
docs/superpowers/specs/2026-10-09-1235-release-attestation-design.md:104 - Maintainer checklist: tagging precondition missing, branch-check step impossible: see inline at
docs/guide/release-verification.md:455
Non-blocking
♻️ refactor / 🤔 thinking / ⛏ nitpick
- ♻️ Skip the build upload on
mainpushes: see inline at.github/workflows/release-build.yml:106 - 🤔 Annotated or branchless tags are refused only after building and signing: see inline at
.github/workflows/release-build.yml:23 - 🤔 Linux release binaries carry debug info (132-282 MB): see inline at
scripts/release/phase1-build.sh:94 - ♻️ Release suite runs inside the "docs-proposal scripts" job: see inline at
.github/workflows/format.yml:228 - ♻️ Deadline errors drop the failure that caused the retry: see inline at
scripts/release/publish.mjs:152 - ⛏ Stale allowlist comment: see inline at
crates/trusted-server-cli/src/commands/dev/lint/domains.rs:97
Cross-cutting / body-level findings
- ⛏ Smaller doc fixes
binary-attestations-phase1-compatibility.md:383says every other non-200 status "fails immediately" and that current-tag verification is "single-shot". The publisher retries transient list reads and proof fetches within a check, and re-runs a failedgh release verifyat the next check, as the guide says atrelease-verification.md:384.2026-10-13-attested-release-implementation.md:110says the source PR "remains frozen and open"; #1262 is closed (the plan says so at:7).2026-10-13-attested-release-implementation.md:159and:185describe #1262'strusted-server-releasework as "implemented" in the present tense; none of it is in this PR.release-verification.md:363compares downloads "against the signedSHA256SUMS". No attestation coversSHA256SUMSuntil GitHub's release attestation after publication; "the validatedSHA256SUMS" is accurate.release-verification.md:366lists the reads that may retry as release/asset metadata GETs andverify-assetcalls. It leaves out the release-attestation list read, failed proof fetches and transport download failures (:384,:386).binary-attestations-phase1-compatibility.md:166puts current behaviour ("No PR builds;mainpushes and tags queue") in the "#1435 value at audit" column..11(ab6e54936, draft-create HTTP 403, nothing created) has no entry of its own. It appears only in passing atbinary-attestations-phase1-compatibility.md:309; an entry beside.12would complete the record.binary-attestations-phase1-compatibility.md:335says the publisher "retriesverifyBundle"; no such function exists inpublish.mjs.binary-attestations-slice-map.json:678still keys.github/workflows/release.yml inline publisher; the publisher is nowscripts/release/publish.mjs.- Spec
:475says the operator guide verifies with GitHub CLI 2.102.0. The guide says "2.102.0 or later" atrelease-verification.md:72.
- ⛏ PR description is stale. It still lists the deleted #1262 files (for example
release-policy.jsonand thequalification/records) and says a PR runs seven hosted builds ("Keep all seven PR builds"). It also describes the publisher as inline inrelease.yml, has no row forscripts/release/publish.mjs, and cites commits from beforec827eb222.
CI Status
- Analyze (actions): 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 fmt: PASS
- cargo test: 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
- CI Node pin guard: PASS
- CLAUDE.md symlink guard: PASS
- CodeQL: PASS
- docs-proposal scripts: PASS
- format-docs: PASS
- format-typescript: PASS
- HTML EOF runtime (Fastly): PASS
- integration tests: PASS
- integration tests (Fastly EC lifecycle): PASS
- prepare integration artifacts: PASS
- trace runtime and browser acceptance: PASS
- ts dev lint domains (changed lines): PASS
- vitest: PASS
- worker-deadlines (cloudflare): PASS
- worker-deadlines (spin): PASS
| node-version: ${{ steps.tools.outputs.node }} | ||
| - run: node --test scripts/release/phase1.test.mjs | ||
| # Refuse a tag the publisher would reject before building and signing. | ||
| - name: Refuse non-SemVer release tags |
There was a problem hiding this comment.
🤔 thinking: Two kinds of bad tag are refused only after the seven builds and signing:
- an annotated tag;
- a tag whose commit is reachable only through the tag.
checks runs only isReleaseTag. The lightweight-tag check lives in the publisher (publish.mjs:273-275), and the draft-create 403 from rehearsals .11 and .22 also happens there. By then sign has already written provenance and SBOM attestations for the tag's binaries to the public Sigstore log. A tag that never publishes still leaves public attestations behind. The same ref check in checks costs one API read:
- name: Refuse annotated release tags
if: startsWith(github.ref, 'refs/tags/')
env:
GH_TOKEN: ${{ github.token }}
RELEASE_TAG: ${{ github.ref_name }}
run: test "$(gh api "repos/$GITHUB_REPOSITORY/git/ref/tags/$RELEASE_TAG" --jq '.object.type + " " + .object.sha')" = "commit $GITHUB_SHA"Checking here that GITHUB_SHA is reachable from main would also turn the 403 into an early, clear failure.
There was a problem hiding this comment.
Partly applied in 54d10bb01. The checks job now runs your ref check, so an annotated tag (or one whose ref doesn't point at GITHUB_SHA) fails before any build or signing.
I did not add the "reachable from main" check. It would make main the only eligible source branch, and Phase 1 leaves source eligibility to the maintainer (R-SOURCE; ancestry and ruleset validation is slice 2.9). Instead, the guide's checklist now says to push the commit to a branch before its tag, explains the 403, and gives the safe rerun. I've left this thread open in case you want the branch check in Phase 1 anyway.
There was a problem hiding this comment.
To make the trade-off concrete: a main-only check would block releases tagged from rc branches. The guide currently leaves that to maintainers ("Whether to authorize tags from rc branches is a maintainer decision"). If rc-branch releases don't need to be supported, I'm happy to add the main reachability check in this PR. Alternatively, a branch-agnostic check (the commit must be on some branch) would catch the 403 case without deciding the rc question.
Find build-script helpers that a non-mod.rs file loads from its own directory, refuse custom package.build scripts and #[path] modules outside the audited sources, and pin the producer and publisher checks the review showed could be deleted without a failing test, including the availability deadline bounds on list reads and proof fetches. Keep the cause of a retry the availability deadline cut short and name that window in the error. Refuse annotated tags before building, upload build artifacts only for tags, run the release suite in its own job and strip debug info from native release binaries. Correct the spec, plan, record and guide where they described CLI and Fastly build-script validation, tagging and retries that do not hold.
|
Review-body doc fixes from the second review, in
|
Summary
Changes
55 files, +11,037/−58 lines against main after the approved base update. The retained design, plans, policies and candidate reports describe future phases as well as this implementation; their presence does not claim those phases are implemented or qualified.
All changed files and their purpose
.github/workflows/format.yml.github/workflows/release-build.yml.github/workflows/release.yml.github/workflows/test.ymlAGENTS.mdCHANGELOG.mdcrates/trusted-server-adapter-axum/src/main.rscrates/trusted-server-adapter-axum/tests/startup.rscrates/trusted-server-adapter-fastly/build.rscrates/trusted-server-adapter-fastly/src/main.rscrates/trusted-server-cli/build.rscrates/trusted-server-cli/release-policy.jsoncrates/trusted-server-cli/src/commands/dev/lint/domains.rscrates/trusted-server-cli/src/lib.rscrates/trusted-server-cli/src/run.rscrates/trusted-server-cli/tests/release_build_facts.rscrates/trusted-server-core/build.rscrates/trusted-server-core/build_support/git_version.rscrates/trusted-server-core/src/build_info.rscrates/trusted-server-core/src/constants.rscrates/trusted-server-core/src/lib.rscrates/trusted-server-core/src/settings.rscrates/trusted-server-core/tests/git_version_resolve.rsdocs/.vitepress/config.mtsdocs/guide/cli.mddocs/guide/release-verification.mddocs/superpowers/plans/2026-10-13-attested-release-implementation.mddocs/superpowers/plans/2026-10-13-cli-packaging-attestation-integration.mddocs/superpowers/plans/2026-10-13-fastly-prebuilt-edgezero-integration.mddocs/superpowers/plans/binary-attestations-phase1-compatibility.mddocs/superpowers/plans/binary-attestations-slice-map.jsondocs/superpowers/specs/2026-01-15-attestation-design.mddocs/superpowers/specs/2026-10-09-1235-release-attestation-design.mddocs/superpowers/specs/2026-10-12-1235-release-attestation-feasibility.mddocs/superpowers/specs/2026-10-14-release-tooling-rust-migration.mddocs/superpowers/specs/schemas/release-index-v1.schema.jsonscripts/release/js-composition.mdscripts/release/phase1-build.shscripts/release/phase1.mjsscripts/release/phase1.test.mjsscripts/release/qualification/2026-10-15-consumer-real-fixtures.mdscripts/release/qualification/2026-10-15-js-two-builds.mdscripts/release/qualification/2026-10-16-confined-cargo-candidate.mdscripts/release/qualification/2026-10-16-fastly-canonical-reproduction-equality.mdscripts/release/qualification/2026-10-16-fastly-runtime-gate.mdscripts/release/qualification/2026-10-16-linux-cli-candidate.mdscripts/release/qualification/2026-10-16-producer-normalized-manifests.mdscripts/release/qualification/2026-10-19-apple-sdk-inputs.mdscripts/release/qualification/2026-10-20-apple-cli-candidates.mdscripts/release/qualification/2026-10-20-linux-cli-gnu-follow-up.mdscripts/release/qualification/2026-10-21-candidate-release-index.mdscripts/release/qualification/2026-10-21-four-asset-candidate-join.mdscripts/release/qualification/2026-10-21-native-reproduction-schedule.mdscripts/release/release-cohort.jsonscripts/release/release-tools.jsonCloses
Part of #1235. Keep the issue open: spec/merge approval and upstream immutable-release/tag-ruleset settings remain maintainer obligations. This partial delivery intentionally does not use an auto-closing keyword.
Spec
Phased spec · Implementation plan · Compatibility audit and rehearsal history.
The carried design originated in #1262; its review history and disposition appendix remain linked from the spec. Spec and implementation are combined here by the recorded delivery agreement; maintainer approval is still required. The required Review log now records two fresh independent AI source-review rounds, both without blocking Phase 1 findings. The historical-adapter advisory was addressed with an append-only baseline clarification; original spec bytes remain intact.
How it works
v*tag. The release helper checks the supplied full source SHA, requires fresh npm/JS output, rejects skip/prebuilt/test controls and scans for planted operator values. It produces Fastly wasm, three CLI binaries and three Axum binaries, each with a conservative Cargo/npm SPDX inventory tied to the final executable hash.release-build.ymlreusable workflow owns signing. Signing runs only for tag pushes withRELEASE_PUBLICATION_ENABLED=true; these jobs do not check out source, compile, or execute the binaries. Provenance covers the executable and its SPDX document. The separate SBOM attestation uses the executable as its subject and the SPDX document as its predicate. Both real action-produced bundles are retained.release.ymlchecks exact build/signer file sets and signed-input digests. It refuses existing matching releases/drafts, creates one draft and retains its release ID through upload/publication. Mutations are single-shot. The distribution contains 7 binaries + 7 SPDX documents + 14 bundles + SHA256SUMS = 29 assets; the checksum file covers the other 28 files. The publisher verifies immutability, release identity, cryptographic membership and a fresh download of every asset against the validated handoff bytes. Only bounded transient read failures may retry.release-build.ymlat the approved tag. Offline bundle verification uses independently obtained roots and proves build evidence; it does not establish current release membership or authorize deployment. The Fastly route packages the verified wasm without rebuilding it.Runtime identity stays distinct from release metadata:
ts --versionlabels package/source/target/variant; Fastly reports the full source SHA inx-ts-version. Axum host/port choices remain runtime inputs, preserve the loopback default, and reject invalid or conflicting values with a visible refusal even when logging is off.Review corrections — 2026-10-29
Current head:
79b1188587cfadc0d193544a3a5c0008666727df. This signed merge includes main5b4e7e872eb30300cb7db3d973773884ea46bc87and the scoped review corrections.Final-tree local verification:
all required Rust/JS/docs/script/Node/domain gates passed; all eight Clippy aliases, adapter/parity/CLI tests, build-digest checks, builds, inherited release controls and 12 Chrome fixtures passed. The first concurrent JS run timed out in unchanged Prebid code; the final full single-worker run passed 1232 tests with original timeouts. Sandbox-blocked Viceroy launches are recorded separately from theirsuccessful host-accessretries. Current-head hosted evidence: 34 checks passed, with two expected tag-only signing/publication skips. The authorized fresh fork rehearsal .21 is blocked: GitHub cancelled its ARM macOS job with no runner assigned, citing hosted macOS ARM capacity. Five builds passed; Intel macOS was still active at readback. No signing/publication has run, and no matching draft/release exists. Tag.21remains unchanged at this exact source; no new attempt was made. Five independent review threads are resolved; the annotated-tag and SPDX threads remain open for signed-release qualification.The PR is ready for review. Release qualification remains pending; no successful new tag/release publication, deployment, administrator-setting audit or release rehearsal is claimed.
.15remains historical evidence for68eef902a8bddd9024676cc3806fa5377ed67fee; it does not qualify this updated source or the corrected SPDX documents. Maintainer settings/spec decisions and fresh release qualification remain separate obligations.Test plan
Verification summary and evidence
Historical pre-review head:
4940928e800fed00e4bddd8f3727fb52dcb2b296, the later web merge of main054c7b777after review-log commitc9a6117dd. Its tree exactly matches the already tested synthetic merge52978c5b5146f0c008108a9b0bfb2bf2c5de2bb2; fresh CI on this new head also passed. The spec’s two fresh review rounds and their append-only clarification remain intact. Rehearsed implementation:68eef902a8bddd9024676cc3806fa5377ed67feein.15. The later main merge includes upstream application changes, with unchanged release machinery, and is not described as docs-only since that rehearsal. No new tag-rehearsal result is claimed. PR release-matrix binaries identify their tested merge ref and remain distinct from the recorded tag assets..15passed all 16 jobs at68eef902a; seven builds and seven signers produced an immutable prerelease, not Latest.425726260currently reportsimmutable=true,draft=false,prerelease=true./healthsuccess/invalid-port refusal passed in the recorded rehearsal. These are startup checks, not full platform qualification.Together, these demonstrate the intended Phase 1 path: reviewed source produces runnable bytes; real tag-only signers attest those bytes and their inventory; the publisher releases the exact immutable distribution; independent verification authenticates the downloaded assets. They do not establish reproducibility, complete native dependency coverage, SLSA Build Level 3 or production deployment.
Completed checks
These checked items refer to the linked hosted evidence, not a claim that every suite was rerun locally.
cargo test-fastly, reuse tests,cargo test-axum, Cloudflare, Spin, native CLI and cross-adapter parity checks.cargo fmt --all -- --check.Historical evidence:
.12success applies toab6e54936only..11draft-create denial and.14hosted ARM64 runner-capacity failure remain failures for those attempts;.15supplies the successful current-implementation rehearsal. No tag or immutable release was reused or overwritten.Exact signer verification and issue acceptance
Use independently approved
REPO,TAGand fullCOMMITvalues from the operator guide. For each asset, run both predicates (https://slsa.dev/provenance/v1andhttps://spdx.dev/Document/v2.3) with each mutually exclusive identity option separately:The guide includes complete loops, release membership checks, offline roots and SPDX comparisons. The issue's original
release.ymlsigner example is superseded by the reusablerelease-build.ymlidentity.artifact-metadata: writeis intentionally unnecessary for file attestations..15; upstream settings remain D7.v*tags configured by maintainers.Checklist
unwrap()introduced; release/config error paths are explicit.log; Axum deliberately writes startup refusals to stderr soRUST_LOG=offcannot hide them. This is a disclosed exception to the general logging convention.Maintainer decisions and Phase 1 limits
The two-round spec review-log requirement is complete. The review-log fix and subsequent main-merge head both passed CI; this PR is ready for human review. Spec/code approval remains the purpose of human review. Before enabling production publication, maintainers must enable immutable releases and the restricted
v*tag ruleset, then explicitly setRELEASE_PUBLICATION_ENABLED=trueand authorize the first production tag. RC-branch tag eligibility remains a maintainer decision. Upstream production publication and provider deployment are not claimed by the fork rehearsal.Reference hosts requiring review:
actions.github.io,slsa.dev,spdx.dev,cli.github.com. Per-line suppressions:cyclonedx.org(intermediate-format reference, removed from the allowlist),api.github.com,uploads.github.com.Phase 1 SBOMs are conservative build dependency inventories, not complete retained-code/native composition or vulnerability scans. Producing-job transfer binding, checked policy consumption, source-branch ancestry enforcement, reproducibility, custom verification/indexes, integrated EdgeZero recovery and further Axum production hardening remain deferred. A compromised build dependency could replace another job's artifact; exact handoff checks do not close that later transfer-binding boundary. Historical candidate reports and frozen policies grant no present release qualification.