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. |
Docs-only since rehearsed 68eef90 (v0.0.0-rehearsal.15).
Docs-only since rehearsed 68eef90. Append actual fresh review outcomes and clarify historical adapter investigation inputs.
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. |
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.