Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
26d69ce
Record approved attestation phase assignment and closure checks
prk-Jr Oct 26, 2026
4ee86da
Require runtime source resources in release CLI builds
prk-Jr Oct 26, 2026
15a7b40
Propose ordinary Fastly release equality integration
prk-Jr Oct 26, 2026
8874e4f
Record approved Fastly gate activation and compatibility conditions
prk-Jr Oct 26, 2026
1534d46
Port strict release documents and process supervision
prk-Jr Oct 26, 2026
d0d5c37
Specify reviewed ordinary Fastly gate implementation
prk-Jr Oct 26, 2026
d1c1d69
Align integration notes with immutable release facts
prk-Jr Oct 26, 2026
021fdfd
Validate ordinary Fastly build observations
prk-Jr Oct 26, 2026
ad78fc5
Compare complete ordinary Fastly final bytes
prk-Jr Oct 26, 2026
bb05d98
Validate bounded ordinary Fastly transfer archives
prk-Jr Oct 26, 2026
6de495a
Merge Phase 1 updates into Phase 2
prk-Jr Oct 26, 2026
ce7daa5
Bind ordinary uploads to current producing jobs
prk-Jr Oct 26, 2026
4447b68
Port reviewed tool tree content measurement
prk-Jr Oct 26, 2026
ee5598b
Port reviewed final executable inspection
prk-Jr Oct 26, 2026
cb5f16a
Join ordinary archive bytes to release observations
prk-Jr Oct 26, 2026
13f63f0
Port the closed recipe and composition dependency graph
prk-Jr Oct 26, 2026
f0d00dd
Record the closed Phase 2 port and policy boundaries
prk-Jr Oct 26, 2026
c8ea09d
Measure ordinary recipe and private JavaScript input identities
prk-Jr Oct 26, 2026
c0f53a5
Port checked JavaScript preparation alongside ordinary release builds
prk-Jr Oct 26, 2026
f721ddb
Align remaining source identity documentation and comments
prk-Jr Oct 26, 2026
b2cde8b
Record compatible preparation and remaining release integration
prk-Jr Oct 26, 2026
3776d27
Link excluded compatibility notes through their committed source
prk-Jr Oct 26, 2026
cc4d0f3
Record source-pinned ordinary Fastly build observations
prk-Jr Oct 26, 2026
6e9f713
Seal real ordinary equality results in fresh atomic outputs
prk-Jr Oct 26, 2026
d365f56
Expose checked ordinary measurement and comparison commands
prk-Jr Oct 26, 2026
f9e6cce
Acquire ordinary transfer archives through bounded authenticated reads
prk-Jr Oct 26, 2026
4ede699
Keep diagnostic payload assertions independent of optional backtraces
prk-Jr Oct 26, 2026
1e9a209
Record ordinary adapter progress and remaining release wiring
prk-Jr Oct 26, 2026
cc7df93
Compare fresh ordinary Fastly builds before signing
prk-Jr Oct 26, 2026
604d5d8
Propose Fastly path normalization after the real A/B mismatch
prk-Jr Oct 27, 2026
7885bed
Bind Docker image configuration and disable inherited commands
prk-Jr Oct 27, 2026
18df990
Remove owned comparison outputs after sealing failures
prk-Jr Oct 27, 2026
635086d
Close inherited build controls and check direct JS entrypoints
prk-Jr Oct 27, 2026
7beaa54
Normalize compiler paths before ordinary Fastly byte comparison
prk-Jr Oct 27, 2026
169e2a3
Attest and publish the real Fastly rebuild check
prk-Jr Oct 27, 2026
6f34d62
Prove failed comparison skips downstream CI stages
prk-Jr Oct 27, 2026
886cf33
Pin the browser integration test job to ubuntu-24.04
prk-Jr Oct 27, 2026
a7e2056
Verify the pinned Fastly package route without network access
prk-Jr Oct 27, 2026
a8f8bcb
Record bounded raw Fastly package header diagnostics
prk-Jr Oct 27, 2026
7127077
Accept the confirmed Fastly directory header forms
prk-Jr Oct 27, 2026
3ccfe29
Construct duplicate-key fixtures explicitly
prk-Jr Oct 27, 2026
770a425
Merge Phase 1 updates into Phase 2
prk-Jr Oct 27, 2026
2ed1b94
Record verified rehearsals and online SBOM checks
prk-Jr Oct 27, 2026
81020c0
Merge branch 'feature/binary-attestations-phase1' into feature/binary…
prk-Jr Oct 28, 2026
3ae0e19
Record the exact Phase 2 integration commit
prk-Jr Oct 28, 2026
7d55177
Merge commit '054c7b777ce67db34744cb1f4100b14e59c4bc5a' into feature/…
prk-Jr Oct 28, 2026
5349b7c
Record the newer-main Phase 2 integration
prk-Jr Oct 28, 2026
08e1c33
Merge the Phase 1 spec review log into Phase 2
prk-Jr Oct 28, 2026
8a797db
Merge the updated Phase 1 branch into Phase 2
prk-Jr Oct 28, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
22 changes: 14 additions & 8 deletions .github/workflows/format.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: "Run Format"
name: 'Run Format'

on:
push:
Expand Down Expand Up @@ -61,7 +61,7 @@ jobs:
uses: ./.github/actions/setup-rust
with:
target: wasm32-wasip1,wasm32-unknown-unknown
components: "clippy, rustfmt"
components: 'clippy, rustfmt'
cache-key: cargo-clippy

- name: Run cargo fmt
Expand Down Expand Up @@ -91,6 +91,12 @@ jobs:
- name: Run host-target CLI clippy
run: cargo clippy --package trusted-server-cli --target x86_64-unknown-linux-gnu --all-targets --all-features -- -D warnings

- name: Run host-target release producer clippy
run: cargo clippy --package trusted-server-release --target x86_64-unknown-linux-gnu --all-targets --all-features --locked -- -D warnings

- name: Lint native JS release build fixtures
run: cargo clippy --package trusted-server-js --target x86_64-unknown-linux-gnu --all-targets --all-features --locked -- -D warnings

# The codegen crate is a workspace member but is not a default member and
# is not covered by any adapter-scoped alias, so lint it explicitly.
- name: Run host-target OpenRTB codegen clippy
Expand All @@ -113,14 +119,14 @@ jobs:
- name: Retrieve Node.js version
id: node-version
working-directory: .
run: echo "node-version=$(grep '^nodejs ' .tool-versions | awk '{print $2}')" >> $GITHUB_OUTPUT
run: echo "node-version=$(grep '^nodejs ' .tool-versions | awk '{print $2}')" >> "$GITHUB_OUTPUT"
shell: bash

- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ steps.node-version.outputs.node-version }}
cache: "npm"
cache: 'npm'
cache-dependency-path: crates/trusted-server-js/lib/package.json

- name: Install dependencies
Expand All @@ -144,14 +150,14 @@ jobs:
- name: Retrieve Node.js version
id: node-version
working-directory: .
run: echo "node-version=$(grep '^nodejs ' .tool-versions | awk '{print $2}')" >> $GITHUB_OUTPUT
run: echo "node-version=$(grep '^nodejs ' .tool-versions | awk '{print $2}')" >> "$GITHUB_OUTPUT"
shell: bash

- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ steps.node-version.outputs.node-version }}
cache: "npm"
cache: 'npm'
cache-dependency-path: docs/package.json

- name: Install dependencies
Expand Down Expand Up @@ -180,7 +186,7 @@ jobs:

- name: Retrieve Node.js version
id: node-version
run: echo "node-version=$(grep '^nodejs ' .tool-versions | awk '{print $2}')" >> $GITHUB_OUTPUT
run: echo "node-version=$(grep '^nodejs ' .tool-versions | awk '{print $2}')" >> "$GITHUB_OUTPUT"
shell: bash

- name: Use Node.js
Expand All @@ -189,7 +195,7 @@ jobs:
node-version: ${{ steps.node-version.outputs.node-version }}

- name: Run shellcheck
run: shellcheck scripts/docs-proposal/*.sh scripts/release/phase1-build.sh
run: shellcheck scripts/docs-proposal/*.sh scripts/release/phase1-build.sh scripts/release/bootstrap.sh scripts/release/build.sh scripts/release/build-js.sh

- name: Test hunk review builder
run: node --test scripts/docs-proposal/hunks.test.mjs
Expand Down
381 changes: 369 additions & 12 deletions .github/workflows/release-build.yml

Large diffs are not rendered by default.

17 changes: 14 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ on:
- 'crates/trusted-server-core/build.rs'
- 'crates/trusted-server-core/Cargo.toml'
- 'crates/trusted-server-cli/**'
- 'crates/trusted-server-release/**'
- 'crates/trusted-server-js/**'
- 'crates/trusted-server-adapter-fastly/**'
- 'crates/trusted-server-adapter-axum/**'
Expand All @@ -32,6 +33,7 @@ jobs:
uses: ./.github/workflows/release-build.yml
permissions:
contents: read
actions: read
id-token: write
attestations: write
release:
Expand Down Expand Up @@ -128,8 +130,9 @@ jobs:
for (const name of names) {
const build = `build/${name}`, signed = `signed/${name}`;
const inputs = [name, `${name}.spdx.json`], bundles = [`${name}.provenance.sigstore.json`, `${name}.sbom.sigstore.json`];
exact(build, inputs); exact(signed, [...bundles, 'SHA256SUMS']);
const receipt = [...inputs.map(file => [build, file]), ...bundles.map(file => [signed, file])].map(([dir, file]) => {
const companions = name === 'trusted-server-fastly.wasm' ? [`${name}.rebuild-check.json`] : [];
exact(build, inputs); exact(signed, [...companions, ...bundles, 'SHA256SUMS']);
const receipt = [...inputs.map(file => [build, file]), ...companions.map(file => [signed, file]), ...bundles.map(file => [signed, file])].map(([dir, file]) => {
const bytes = regular(`${dir}/${file}`);
fs.writeFileSync(`release-assets/${file}`, bytes); files.push(file);
return `${hash(bytes)} ${file}\n`;
Expand Down Expand Up @@ -166,7 +169,15 @@ jobs:
' gh attestation verify "$asset" "${checks[@]}" --signer-workflow "$REPO/.github/workflows/release-build.yml" --format json > "$asset.$kind.verified.json"',
` if [[ "$kind" == sbom ]]; then jq -e --slurpfile sbom "$asset.spdx.json" '[.[].verificationResult.statement.predicate] | length == 1 and .[0] == $sbom[0]' "$asset.$kind.verified.json"; fi`,
' gh attestation verify "$asset" "${checks[@]}" --cert-identity "https://github.com/$REPO/.github/workflows/release-build.yml@refs/tags/$TAG"',
' done', 'done', 'gh release verify-asset "$TAG" SHA256SUMS --repo "$REPO"', 'shasum -a 256 --check SHA256SUMS', '```',
' done', 'done',
'CHECK=trusted-server-fastly.wasm.rebuild-check.json',
'gh release verify-asset "$TAG" "$CHECK" --repo "$REPO"',
'checks=(--repo "$REPO" --bundle trusted-server-fastly.wasm.provenance.sigstore.json --source-ref "refs/tags/$TAG" --source-digest "$COMMIT" --signer-digest "$COMMIT" --deny-self-hosted-runners --predicate-type https://slsa.dev/provenance/v1)',
'gh attestation verify "$CHECK" "${checks[@]}" --signer-workflow "$REPO/.github/workflows/release-build.yml"',
'gh attestation verify "$CHECK" "${checks[@]}" --cert-identity "https://github.com/$REPO/.github/workflows/release-build.yml@refs/tags/$TAG"',
`binary_sha=$(shasum -a 256 trusted-server-fastly.wasm | awk '{print $1}')`,
`jq -e --arg source "$COMMIT" --arg digest "$binary_sha" '.version == 1 and .profile == "ordinary-release-fastly-byte-equality-v1" and .release_qualification == false and .outcome == "matched" and .source_commit == $source and .asset == "trusted-server-fastly.wasm" and .canonical_sha256 == $digest and .reproduction_sha256 == $digest' "$CHECK"`,
'gh release verify-asset "$TAG" SHA256SUMS --repo "$REPO"', 'shasum -a 256 --check SHA256SUMS', '```',
].join('\n');
const created = await api('releases', 'POST', {tag_name:tag, target_commitish:source, name:tag, draft:true, prerelease, make_latest, body});
if (!Number.isSafeInteger(created.id) || created.id <= 0 || created.tag_name !== tag || created.draft !== true || created.prerelease !== prerelease) throw Error('Invalid created release identity');
Expand Down
71 changes: 67 additions & 4 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: "Run Tests"
name: 'Run Tests'

permissions:
contents: read
Expand Down Expand Up @@ -117,6 +117,8 @@ jobs:
test-axum:
name: cargo test (axum native)
runs-on: ubuntu-latest
outputs:
comparison-succeeded: ${{ steps.producer-tests.outputs.comparison-succeeded }}
steps:
- uses: actions/checkout@v4

Expand Down Expand Up @@ -175,6 +177,25 @@ jobs:
- name: Run host-target CLI tests
run: ./scripts/test-cli.sh

- name: Run release producer primitive and process tests
id: producer-tests
run: |
set -euo pipefail
cargo test --package trusted-server-release --target x86_64-unknown-linux-gnu --locked --test commands -- --list > "$RUNNER_TEMP/ordinary-command-tests.txt"
grep -Fx 'ordinary_comparison_failure_prevents_signing_and_publication_sentinels: test' "$RUNNER_TEMP/ordinary-command-tests.txt"
cargo test --package trusted-server-release --target x86_64-unknown-linux-gnu --locked
cargo test --package trusted-server-release --target x86_64-unknown-linux-gnu --locked --test commands ordinary_comparison_failure_prevents_signing_and_publication_sentinels -- --exact > "$RUNNER_TEMP/ordinary-negative-result.txt"
cat "$RUNNER_TEMP/ordinary-negative-result.txt"
grep -E '^test result: ok\. 1 passed; 0 failed; 0 ignored;' "$RUNNER_TEMP/ordinary-negative-result.txt"
echo 'comparison-succeeded=false' >> "$GITHUB_OUTPUT"

- name: Run checked and ordinary JS release build fixtures
shell: bash
run: |
cargo test --package trusted-server-js --target x86_64-unknown-linux-gnu --locked --test release_prebuilt -- --list > "$RUNNER_TEMP/js-release-tests.txt"
grep -Fx 'ordinary_release_without_prebuilt_builds_private_bundles: test' "$RUNNER_TEMP/js-release-tests.txt"
cargo test --package trusted-server-js --target x86_64-unknown-linux-gnu --locked --test release_prebuilt

# The codegen crate is not a default member and no adapter alias builds it,
# so its tests only run if invoked explicitly on the host target.
- name: Run host-target OpenRTB codegen tests
Expand Down Expand Up @@ -240,7 +261,7 @@ jobs:
TRUSTED_SERVER__PUBLISHER__ORIGIN_URL: http://127.0.0.1:8080
TRUSTED_SERVER__PUBLISHER__PROXY_SECRET: integration-test-proxy-secret
TRUSTED_SERVER__EC__PASSPHRASE: integration-test-ec-secret-padded-32
TRUSTED_SERVER__PROXY__CERTIFICATE_CHECK: "false"
TRUSTED_SERVER__PROXY__CERTIFICATE_CHECK: 'false'
run: cargo build --package trusted-server-adapter-spin --target wasm32-wasip1 --features spin --release

- name: Run Spin adapter tests (native host)
Expand Down Expand Up @@ -280,7 +301,7 @@ jobs:
- name: Set up Rust toolchain
uses: ./.github/actions/setup-rust
with:
components: "clippy, rustfmt"
components: 'clippy, rustfmt'
cache-key: cargo-cli

- name: Read pinned Node.js version for CLI fixtures
Expand Down Expand Up @@ -365,7 +386,7 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: ${{ steps.node-version.outputs.node-version }}
cache: "npm"
cache: 'npm'
cache-dependency-path: crates/trusted-server-js/lib/package.json

- name: Install dependencies
Expand All @@ -376,3 +397,45 @@ jobs:

- name: Run unit tests
run: npm test -- --run

ordinary-negative-sign:
name: Ordinary negative signing canary
needs: test-axum
if: needs.test-axum.outputs.comparison-succeeded == 'true'
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- run: exit 1

ordinary-negative-publish:
name: Ordinary negative publication canary
needs: test-axum
if: needs.test-axum.outputs.comparison-succeeded == 'true'
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- run: exit 1

ordinary-negative-audit:
name: Audit ordinary negative stages
needs: [test-axum, ordinary-negative-sign, ordinary-negative-publish]
if: always()
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Require the failed-comparison fixture and both skipped canaries
env:
NATIVE_RESULT: ${{ needs.test-axum.result }}
SIGN_RESULT: ${{ needs.ordinary-negative-sign.result }}
PUBLISH_RESULT: ${{ needs.ordinary-negative-publish.result }}
COMPARISON_SUCCEEDED: ${{ needs.test-axum.outputs.comparison-succeeded }}
run: |
set -euo pipefail
test "$NATIVE_RESULT" = success
test "$COMPARISON_SUCCEEDED" = false
test "$SIGN_RESULT" = skipped
test "$PUBLISH_RESULT" = skipped
echo 'SUCCESS: real negative comparison rejected; read-only signing/publication canaries skipped'
4 changes: 3 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,9 @@ Every PR must pass:
9. Native core build-digest test and lint (`cargo test -p trusted-server-core --test template_build_digest` and `cargo clippy -p trusted-server-core --test template_build_digest -- -D warnings`)
10. Wasm clock lint: `scripts/lint-wasm-clock.sh` (crates built into the Cloudflare Worker must use `web_time`, not the `std::time::{Instant,SystemTime}` `now`/`elapsed` methods, which panic there)
11. Documentation proposal scripts: `shellcheck scripts/docs-proposal/*.sh && node --test scripts/docs-proposal/hunks.test.mjs && scripts/docs-proposal/test.sh`
12. Release producer checks: `node --test scripts/release/phase1.test.mjs && shellcheck scripts/release/phase1-build.sh`
12. Release producer helpers: `node --test scripts/release/phase1.test.mjs && shellcheck scripts/release/phase1-build.sh scripts/release/bootstrap.sh scripts/release/build.sh scripts/release/build-js.sh`
13. Native release producer tests and lint: `cargo test --package trusted-server-release --target <host-triple> --locked` and `cargo clippy --package trusted-server-release --target <host-triple> --all-targets --all-features --locked -- -D warnings` (CI uses `x86_64-unknown-linux-gnu`).
14. Native checked/ordinary JS release fixtures: `cargo test --package trusted-server-js --target <host-triple> --locked --test release_prebuilt` and `cargo clippy --package trusted-server-js --target <host-triple> --all-targets --all-features --locked -- -D warnings` (CI uses `x86_64-unknown-linux-gnu`).

CI also runs these HTML EOF checks. Run them locally only when changing EOF
finalization, publisher or HTML-processor code:
Expand Down
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- Add an ordinary Fastly release byte-equality gate and a real provenance-attested `trusted-server-fastly.wasm.rebuild-check.json`. Merging Phase 2 activates a second fresh Fastly build plus comparison on the next release tag; mismatch prevents signing and publication. The ordinary profile remains unqualified, native reproduction is not a release gate, and the seven binary names and signer identity stay unchanged.
- Add Phase 1 attested Fastly wasm, three Linux/macOS CLI and three production Axum binaries, with SPDX 2.3 inventories, provenance/SBOM bundles, immutable verification, GNU glibc 2.34 and static musl startup checks. Reproducibility and the custom verifier remain later work; production publication requires maintainer settings.

### Changed

- Attested release binaries report `x-ts-version` as the full 40-character source commit, rather than the release tag. CLI output labels package/source/target/variant, and Axum supports runtime `EDGEZERO__ADAPTER__HOST` and primary `EDGEZERO__ADAPTER__PORT` (legacy `PORT` must agree when both are set) binding with a loopback default.
- **Breaking:** `x-ts-version` now reports the deployed git version — the release tag, else the branch, else the first 6 characters of the commit — compiled in from the build-time `TRUSTED_SERVER__GIT_VERSION` (set by the deploy pipeline) or local git, and is sent by every adapter, including on the Fastly `GET /health` probe. The Fastly service version it previously carried is now `x-ts-fastly-version`; update dashboards, monitors, and scripts that read `x-ts-version` as the Fastly version number. Builds with neither the override nor git omit the header.
- **Breaking:** `x-ts-version` reports the approved full 40-character source commit for canonical attested releases. Development source builds retain the build-time `TRUSTED_SERVER__GIT_VERSION` override, else local tag, branch or first 6 commit characters, and omit the header when neither override nor Git is available. Every adapter sends the available value, including on the Fastly `GET /health` probe. The Fastly service version is now `x-ts-fastly-version`; update dashboards, monitors and scripts that read `x-ts-version` as the Fastly version number. The source header alone does not authenticate executable bytes.
- **Breaking:** `ts prebid bundle` is now `ts prebid client`, alongside the new `ts prebid server` namespace. Update scripts and runbooks to use `ts prebid client` with the same arguments. The old `bundle` spelling is no longer accepted and has no compatibility alias.
- The S2S `/_ts/api/v1/batch-sync` endpoint now validates the full batch and calls the CAS-protected update path once per distinct normalized EC ID. The last valid UID wins within a group, and infrastructure failures reject the failing and each unprocessed group, so accepted and `kv_unavailable` input indexes may interleave.
- **Breaking:** Auction providers and bidder routes now use the configuration-first `[auction.providers.<id>]` and `[auction.bidders.<id>]` maps. The removed `[auction].providers = [...]` list and removed server fields under `[integrations.prebid]` and `[integrations.aps]` are rejected even when those integrations are disabled, and `ts config push` rejects the old shape before publication. Move PBS `server_url` to provider `endpoint`, server timeout to provider `timeout_ms`, request controls and bidder-parameter overrides to the `prebid-server` `profile_config`, notification suppression to `notifications`, and each former server bidder to an `[auction.bidders.<id>]` route. Move APS endpoint, timeout, account, inventory, debug, and creative controls to an `aps` provider and its `profile_config`. Browser Prebid settings remain under `[integrations.prebid]`; values such as timeout and debug that previously affected both browser and server behavior must now be configured for each owner. Provider endpoints must be absolute HTTPS URLs. Only bidder codes present in `[auction.bidders]` are folded into Trusted Server requests; unlisted publisher bids remain native browser demand. Provider response names now use the configured provider ID, such as `pbs-main`, instead of the legacy literal `prebid`; audit consumers that match `AuctionResponse.provider`. This schema has no mixed-version-safe deployment order: old binaries reject the maps and new binaries reject the retired fields, so activate the new binary and config blob together. Rollbacks must restore an old-schema blob together with the old binary.
Expand Down
Loading
Loading