Uh oh!
There was an error while loading. Please reload this page.
fix(installer): bootstrap the amd64 cosign on Windows-on-ARM — the arm64 asset does not exist (client#734) - #735
Conversation
…m64 asset does not exist Resolve-Cosign built `cosign-windows-$arch.exe`, which on Windows-on-ARM asks for `cosign-windows-arm64.exe`. Sigstore has never published that file — not at the pinned v2.4.1, not at v3.1.3, not at any release. `cosign-windows-amd64.exe` is the only Windows asset there is. So the download 404s, Resolve-Cosign returns $null, and the install fails closed. Safe direction, accidental reason: the user sees a cosign download failure that reads as a network blip, and retrying can never succeed. Nobody noticed because Windows-on-ARM is rare in the user base — low incidence, total blockage, no signal. Both architectures now fetch the amd64 build and run it under Windows-on-ARM's x64 emulation. That costs nothing that matters: cosign verifies a signature over BYTES, so the instruction set it was compiled for cannot change the verdict, and the artifact handed to it is still the native arm64 binary. The bootstrapped cosign is checksum-verified before use exactly as on amd64, so the trust chain is unchanged. The alternative — fail closed with a `winget install sigstore.cosign` hint — routes the user to do the same thing by hand, since winget's cosign is also amd64-only. Same end state, more friction, and one more chance to reach for TRACEBLOC_ALLOW_UNVERIFIED, which is the outcome we least want. Second half: Invoke-CosignVerifyBlob fails closed on a binary that cannot execute, but reported it IDENTICALLY to a signature that did not verify. On an older Windows 10 on ARM without x64 emulation that tells a user their download may be tampered with, when the truth is that the verifier will not start. Those warrant opposite reactions and only one of them is a security event. A `cosign version` probe after resolution separates them, keeps the fail-closed, and names the real cause plus a remedy that works. Both halves are mutation-real: restoring `$arch` fails the asset test; dropping the probe fails all four behavioural tests. 680 Pester green, make check green. CI cannot exercise the ARM path — GitHub's Windows runners are x64 — so the coverage is source-level for the arch map and behavioural for the probe, and the comment in Resolve-Cosign is load-bearing. Closes#734 Refs tracebloc/backend#2078 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 099ec15. Configure here.
Uh oh!
There was an error while loading. Please reload this page.
saadqbal
left a comment
There was a problem hiding this comment.
Clean, careful PR. cosign-windows-amd64.exe is the right (and only) Windows asset; the switch still guards unknown arches, the checksum step is untouched, and install.sh/Linux-arm64 aren't in scope. Nice touch adding Test-CosignRuns so a non-executable cosign (Win10-on-ARM, no x64 emulation) fails closed with the true reason instead of masquerading as a bad signature. Tests are non-vacuous and green (40/40 on Pester 5.7.1). 👍
…d that just failed Bugbot, and it is my error: I noted earlier in this very investigation that winget's cosign is amd64-only, then wrote a remedy that depends on it. It is wrong in this branch by construction. We already HAVE a cosign here — it will not start. Installing another copy of the same amd64 build, the only one sigstore and winget publish, reproduces the failure exactly. A remedy that cannot clear the error is worse than none: it costs a round trip and teaches the user the message is noise. The message now names the two things that actually cause it — security software quarantining the downloaded cosign.exe, and missing x64 emulation on Windows-on-ARM (Windows 11 has it, Windows 10 on ARM may not) — and says plainly that no native arm64 cosign exists to fall back on, so one would have to be built from source. Still fails closed; the TRACEBLOC_ALLOW_UNVERIFIED escape is unchanged. Guard added: re-introducing 'winget' into that message reddens the test. It captures the thrown message and asserts on it — `Should -Not -Throw -ExpectedMessage` asserts no throw AT ALL, so the first draft would have passed for the wrong reason. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Uh oh!
There was an error while loading. Please reload this page.
`develop` gained #735 (amd64 cosign bootstrap on Windows-on-ARM) and #736 (SS3 escape stripping), both of which touch `scripts/lib/common.sh` — the file this branch also edits, since the binfmt probe moved there so both arch gates read one probe. `common.sh` and `install-client-helm.bats` auto-merged. `manifest.sha256` was the only conflict and was REGENERATED with `scripts/gen-manifest.sh` rather than hand-merged: the manifest is a derived artifact, and a hand-resolved one records hashes for a tree that never existed. Re-running the generator now produces no diff, so the committed manifest matches the merged tree. Verified on the merged result, not on either side: the full bats suite exits 0 with 0 failures (1119+ tests), so #739's `_pf_arch` / `_assert_engine_runs_on_this_arch` changes still hold against develop's `common.sh`.
LukasWodka
commented
Aug 19, 2026
/fr-pass Best-effort functional review passed (triage: merged clean, non-interactive; behavioral evidence limited while e2e journey is red — backend#2206). Advancing to Ready for prod. |

Closes#734. Unblocks the
cliport in tracebloc/backend#2078.The bug
Resolve-Cosignbuiltcosign-windows-$arch.exe. On Windows-on-ARM that asks forcosign-windows-arm64.exe— an asset sigstore has never published:404 →
Resolve-Cosignreturns$null→ fail closed. The direction is right; the reason is accidental. The user sees a cosign download failure that reads as a transient network problem, and no amount of retrying can conjure the asset.Why now
backend#2078 proposes porting this exact function into
cli/scripts/install.ps1, which today skips signature verification altogether. Copying it as-is would carry the broken arch map into a second installer — and the CLI is the more exposed one, because its release matrix really does buildwindows/arm64. So ARM users get a native binary and nothing able to verify it. Fixing it here means that port is a copy of one correct function.Why amd64-under-emulation is the right answer, not a compromise
cosign verifies a signature over bytes. The instruction set it was compiled for cannot change the verdict, and the artifact handed to it is still the native arm64 binary. The bootstrapped cosign is checksum-verified against sigstore's own published list before use, exactly as on amd64 — the trust chain is byte-for-byte identical.
The alternative — fail closed with a
winget install sigstore.cosignhint — routes the user to do the same thing by hand, because winget's cosign is also amd64-only. Same end state, more friction, and one more opportunity to reach forTRACEBLOC_ALLOW_UNVERIFIED=1, which is the outcome we least want.Second half: a verifier that won't start is not a bad signature
Invoke-CosignVerifyBlobalready fails closed on a binary that cannot execute — but reported it identically to a signature that did not verify. On an older Windows 10 on ARM without x64 emulation, that tells a user their download may be tampered with when the truth is "the verifier won't start on this machine."Those warrant opposite reactions, and only one of them is a security event. A
cosign versionprobe after resolution separates them:TRACEBLOC_ALLOW_UNVERIFIEDloudly, like the sibling pathTest plan
make checkgreen · Pester 680 passed, 0 failed across the suite.Both halves mutation-real:
Also pinned: the unknown-architecture refusal still stands, and the bootstrapped cosign is still checksum-verified — so the fix cannot be mistaken for loosening the trust chain.
Reviewer note
CI cannot exercise the ARM path — GitHub's Windows runners are x64, so no test here will ever run on the hardware this fixes. The arch-map coverage is deliberately source-level for that reason, and the comment inside
Resolve-Cosignis load-bearing: it is the only thing that will stop someone "correcting"amd64back to$arch.🤖 Generated with Claude Code
Note
Medium Risk
Touches the installer’s cosign bootstrap and manifest signature gate (supply-chain trust root); behavior is more permissive on ARM when amd64 cosign runs under emulation, but trust chain and fail-closed defaults remain.
Overview
Fixes Windows-on-ARM installs that failed because
Resolve-Cosignrequestedcosign-windows-arm64.exe, which Sigstore never ships—onlycosign-windows-amd64.exeexists. Both AMD64 and ARM64 now bootstrap that amd64 asset; checksum verification of the bootstrapped binary is unchanged.Adds
Test-CosignRuns(cosign versionwith the sameLASTEXITCODEsentinel as verify) and runs it inConfirm-ManifestSignaturebefore anyverify-blob. If cosign cannot execute (AV quarantine, or ARM without x64 emulation), the installer still fails closed but explains this is not a failed signature and does not suggestwinget install sigstore.cosign(same amd64 build).TRACEBLOC_ALLOW_UNVERIFIEDstill allows checksum-only proceed with warnings.Pester coverage locks the amd64 asset name, executability probe, error wording, and no-verify path when cosign won’t run.
Reviewed by Cursor Bugbot for commit 648deec. Bugbot is set up for automated code reviews on this repo. Configure here.