Repository navigation
[Release] Ship signed, notarized, auto-updatable desktop builds with rollback evidence #960
Description
Parent: #958
Buyer-visible gap
The current manual release workflow validates repository state and packaging prerequisites, but it does not constitute a complete commercial release channel. Buyers still lack evidence that the Windows/macOS artifacts they install are signed by the expected publisher, that macOS artifacts are notarized, that updates are signature-verified, and that a failed update can be recovered without losing a project.
A downloadable but unsigned or non-notarized artifact is a developer build, not the BandScope 1.0 distribution contract.
Product outcome
Create one release pipeline that produces trusted Windows and macOS artifacts from an exact protected commit, publishes checksums/SBOM/provenance, signs every platform artifact, notarizes macOS, and serves a signature-verified Tauri updater manifest with staged rollout and recovery evidence.
Required scope
Release identity and metadata
- Make the root package, Tauri config, application footer/about surface, Figma identity, release tag and generated updater manifest derive from one authoritative version source.
- Reject a release when any version or product identifier drifts.
- Preserve the current local-first product boundary; ordinary startup and update checks must not upload project or audio metadata.
Windows
- Produce the supported Windows installer/bundle formats from the protected release commit.
- Authenticode-sign the executable and installer with the approved publisher identity and timestamp service.
- Verify the signature and publisher in CI from a clean machine/image.
- Record architecture, OS compatibility, digest, signing certificate identity and timestamp in the release manifest.
- Coordinate with [Level 1] Windows/macOS arm64 + amd64 교차 아키텍처 빌드 지원 #38 for architecture coverage rather than duplicating its build matrix.
macOS
- Code-sign the app and installer artifacts with hardened runtime settings appropriate to the actual entitlement set.
- Submit the exact distributable to Apple notarization, staple the ticket where supported, and validate it with Gatekeeper tooling.
- Verify both Apple Silicon and Intel/universal outputs according to the supported matrix.
- Keep signing/notarization credentials outside repository artifacts and logs.
Updater and rollback
- Adopt the Tauri updater plugin/configuration with a pinned public verification key and signed update artifacts.
- Host an immutable update manifest that identifies version, platform, architecture, signature, digest, minimum supported version and release notes.
- Support staged rollout, explicit user deferral, bounded retry, offline startup and a recovery path when installation or first launch fails.
- Never auto-downgrade across a project-schema incompatibility; coordinate with the project-format issue under [Product Readiness] Complete BandScope 1.0 with measurable rehearsal outcomes #958.
- Retain the previous known-good installer and document rollback/repair steps.
Supply-chain evidence
- Generate and attach SHA-256 checksums, SPDX SBOM, build provenance/attestation and a machine-readable release manifest.
- Pin all release actions by full commit SHA and all toolchains/dependencies by repository policy.
- Prove the published artifacts are byte-identical to the verified release job outputs or explain the platform-signing transformation in provenance.
Acceptance criteria
- A single documented command or protected workflow creates the complete release candidate from a clean checkout.
- Windows installer and executable signature verification succeeds on a clean Windows environment.
- macOS code-signing, notarization and Gatekeeper verification succeed on clean supported macOS environments.
- The updater rejects a wrong key, wrong signature, wrong digest, truncated manifest, unsupported platform/architecture and replayed/stale update metadata.
- Offline startup remains functional when the update service is unavailable.
- A failed/cancelled update leaves the last known-good build and all project data usable.
- Version metadata is identical across package, runtime, about UI, release tag, updater manifest and Figma/brand handoff.
- Every artifact has checksum, SBOM, provenance and exact source commit evidence.
- Production statement/branch coverage and public API documentation remain 100% for repository-owned release/updater logic.
- Current-head CI, security, supply-chain, independent approval and zero unresolved review threads pass.
Test cases
- expired or rotated signing certificate;
- tampered installer after signing;
- notarization service timeout and retry;
- updater manifest rollback/replay;
- partial download and disk-full failure;
- app restart during update;
- update available while offline or behind a captive portal;
- project created on the new version and rollback attempted;
- version drift between
package.json,tauri.conf.json, UI and tag; - update channel switch between stable and pre-release.
Standards and authoritative guidance
- Tauri application signing and updater documentation: https://v2.tauri.app/distribute/sign/
- Tauri updater plugin: https://v2.tauri.app/plugin/updater/
- Apple notarization workflow: https://developer.apple.com/documentation/security/notarizing_macos_software_before_distribution
- Microsoft code-signing guidance: https://learn.microsoft.com/windows/msix/package/sign-app-package-using-signtool
Non-goals
- Do not store signing keys in GitHub artifacts or repository files.
- Do not call a manually zipped development build a production release.
- Do not make the updater a prerequisite for offline use.
- Do not bypass fix(security): establish canonical npm, PDF.js, Nanoid, and Undici baseline #783 or current branch protection to manufacture a release.
Activity
seonghobae commentedon Sep 14, 2026
#1126 Distribution owner가 exact
0cd9a592501daa3cf5c0aeaf0945ae84ba8e8081까지 전진했습니다. 이번 slice에서 태그 패키징의 exact release receipt를 RED→fix했습니다: native Windows/macOS trust 검증을 통과한 뒤에만release-receipt.json을 만들고,VERSION과 일치하는 tag, full 40-hex source commit, target platform/arch/triple, 각 installer의 exact size/full SHA-256/checksum/manifest를 묶습니다. Installer는 receipt 시점에 regular/non-link descriptor에서 다시 hash하며 기존 checksum과 byte drift가 있으면 fail closed합니다. PR/develop unsigned validation artifact에는 receipt를 만들지 않습니다.이 receipt는 updater signature나 SLSA provenance로 승격하지 않습니다. Tauri v2 updater 자체(
createUpdaterArtifacts, 승인된 pinned public verification key, HTTPS endpoint/static manifest,.sig), wrong-key/signature/digest 및 replay/stale rejection, staged rollout/deferral/retry, known-good rollback과 project-schema compatibility는 계속 #960의 다음 repository-owned work입니다. 실제 Windows publisher/certificate/timestamp와 Apple Developer ID/Team ID/notarization authority, updater signing-key authority는 외부 security prerequisite라 source에서 임의로 만들지 않았습니다.Exact head hosted generation은 현재 non-terminal이므로 completion/GREEN/merge claim은 하지 않습니다.
seonghobae commentedon Sep 14, 2026
#1126 Distribution owner가 exact
0b565ba7da8dcdcbaa83bd7adb0a7f9087b6ea6f까지 ordinary descendant로 전진했습니다. 이번 slice는 이전에 문서 요구로만 남아 있던 updater release authority를 실행 가능한 fail-closed admission으로 만들었습니다.RED
38712e8a7fd1314abdb565af9b9ae4e819461b87에서 commercial tag가 updater authority 없이 통과하지 못해야 하고, admitted 상태라면 Tauri의 public verification key·HTTPS endpoints·bundle.createUpdaterArtifacts=true가 exact policy와 일치해야 하며 HTTP/endpoint drift/dangerousInsecureTransportProtocol/blocked-state partial activation/duplicate JSON member를 거부하도록 계약을 추가했습니다. Production guard2cd667c5928c1a394fb1e5443c717603b5b1424e, explicit blocked policy50ca2a80a228652cde0ae402b8c410c07aed7b7c, tag preflight composition10cf39dda49206f2810583f8f4c3d6afb2e1709f, fixture repair9e00feec31a348fadf9346f40a5016992192e2e8, traceability0b565ba7da8dcdcbaa83bd7adb0a7f9087b6ea6f순입니다.현재
release/updater-policy.json은 의도적으로blocked입니다. organization-approved updater signing public key와 immutable production HTTPS endpoint가 실제로 provision되지 않았기 때문에 placeholder key/URL이나 private signing key를 source에 만들지 않았습니다.verify_release_identity.py가 model policy와 updater policy를 함께 호출하므로 version tag에서는 updater admission이 없으면 existing tag packager preflight가artifacts/생성 전 fail closed합니다. Branch/PR에서는 blocked 상태 자체의 구조와 Tauri가 updater를 부분 활성화하지 않았음을 검증합니다.이 변경은 실제 updater runtime 완료를 주장하지 않습니다. Tauri updater plugin/runtime integration, generated
.sig와 immutable manifest publication, wrong-key/wrong-signature/digest/truncation/replay/stale rejection, staged rollout/deferral/retry/offline behavior, disk-full/partial update/first-launch failure와 known-good rollback, project-schema compatibility는 계속 #960의 repository-owned 후속 범위입니다. Windows/macOS signer authority와 updater signing-key/public-key ownership은 외부 security prerequisite입니다. Exact-head hosted checks는 아직 non-terminal이므로 GREEN/merge claim도 하지 않습니다.seonghobae commentedon Sep 14, 2026
#1126 current exact head
b535013d9f381b91c9498ab469f374739192c081closes one more repository-owned updater-admission gap without fabricating external authority.Fresh review found that an
admittedpolicy plus matchingtauri.conf.jsoncould still pass while the shipped desktop binary omittedtauri-plugin-updateror never initialized it. RED8843a308303d7731a175abfc0b90fb365cb7518ecaptures that false-positive path;9869d32bdad9787f3af6556f73d74070f8541b5fnow requires one versioned non-optional updater dependency, an immutable registry-resolved Cargo.lock entry with checksum, and executable.plugin(tauri_plugin_updater::Builder::new().build())initialization.6d91f7c7bc117da9ece7563e45a7d88de09b09d4repairs the admitted fixtures andb535013d9f381b91c9498ab469f374739192c081doctors the traceability/claim boundary against current Tauri v2 guidance.The checked-in updater policy remains deliberately
blocked; this does not claim key/endpoint provisioning,.sigpublication, cryptographic wrong-signature/replay rejection, packaged runtime acceptance, or rollback. Those remain #960 work. Protecteddevelopis still314ddeae7b775a4957594b599358c8255617eb2e; #1126 is ahead 51 / behind 0 and remains Draft while exact-head hosted checks and independent review are non-terminal.seonghobae commentedon Sep 14, 2026
#1126 Distribution/update owner는 exact
9df9e61592c006f7bfb94008c94427ef40a1bc0c까지 ordinary descendant로 전진했습니다. Protecteddevelop@314ddeae7b775a4957594b599358c8255617eb2e대비 fresh compare는 ahead 87 / behind 0입니다.이번 slice는 #960의 anti-replay/rollback 요구를 문서 요구에서 native executable contract로 옮겼습니다. 먼저 manifest RED
57ced89e...→ fix21ee4ff5...로 exact source commit,minimumSupportedVersion, target별 updater bundle size/full SHA-256을 Tauri static response의bandscopemetadata에 결합했고, duplicate policy authority와 비정규 version floor도857e1e9...에서 fail closed했습니다.그 다음 RED
4467a9e80b3fa7e7e7a95cb1ff7606749606b3d0에서 repository CI가 standalone Rust Distribution suite를--locked --all-targets로 실행하도록 요구했습니다.apps/desktop/distribution-core는 dependency-free Rust bounded context이며42fdeed9a1ddf57d807889e61dee864b931b62a2에서 다음 결정을 구현합니다: canonical stable version, exact source/digest identity, target mismatch, client compatibility floor, current-version rollback, locally highest-seen authenticated release보다 오래된 metadata replay, same-version source/digest equivocation, 그리고 current project schema를 읽지 못하는 known-good rollback 거부.d80a477...에서 compiler warning과 public rustdoc 누락도 hard failure로 만들었습니다.Highest-seen identity는 설치 완료가 아니라 authenticated metadata를 관측한 시점에 Distribution-owned durable state로 저장해야 한다는 계약입니다. 사용자가 설치를 연기했다는 이유로 이전 metadata가 restart 뒤 fresh해지는 것을 허용하지 않습니다. Project Persistence는 project bytes/schema truth만 소유하고 updater freshness를 복제하지 않습니다.
ARCHITECTURE.md는 Distribution/update bounded context를 code-current하게 갱신했고, 기존에 없던docs/product-technical-gap-baseline.md도 만들어 actual audio/MIR/player/persistence/distribution/UI의 buyer-visible release gaps를 한 곳에 고정했습니다.현재
release/updater-policy.json은 실제 organization-approved public key/production endpoint가 없으므로 계속blocked입니다. Rust core는 network/install/signature verification을 하지 않으며 fake key/endpoint로 runtime을 억지로 활성화하지 않았습니다. 다음 repository-owned work는 authenticatedUpdate.raw_jsonadmission, crash-safe highest-seen state/reload, offline-safe check, partial/disk-full/cancel/first-launch recovery와 last-known-good installer retention입니다. Positive packaged signature acceptance는 실제 updater authority provision 이후 수행합니다.Exact head hosted
build-baseline,ci,sbom, Security Scan, SAST Semgrep, CodeQL PR은 현재 non-terminal이고 qualifying independent approval도 없으므로 #1126은 Draft/unmerged를 유지합니다.seonghobae commentedon Sep 14, 2026
Current #1126 progress — exact head
ae98559c83939b75ae02cadcbd05894883cc0a37on protecteddevelop@314ddeae7b775a4957594b599358c8255617eb2e, ahead 307 / behind 0 with merge base equal to protected develop.Latest repository-owned repair closes another Distribution HTTP dependency-admission bypass. The direct reqwest declaration was already constrained to
default-features = falseand direct features exactly{rustls}, but Cargo root features can activate dependency features again throughdependency/featureanddependency?/feature, with feature unification applying the result. REDed5be008b379f5f07c4325801f1bbba9676fcad1coversreqwest/gzipand renameddistribution_http/brotli; causal fixbc56faad9bd2582cd6bae4f0b4df43dafc64d9a8rejects forwarding through every runtime dependency key that resolves to reqwest; edge verification0f74972089c4240dbcb7e47d65a8e116bbf099cfcovers weakdistribution_http?/zstd; traceabilityae98559c83939b75ae02cadcbd05894883cc0a37records the decision and claim boundary.This closes root-manifest feature forwarding only. It does not claim a production HTTP client or prove the future resolved reqwest feature graph. The current transport manifest still has no reqwest dependency. Next repository-owned vertical remains a genuinely Cargo-resolved stable reqwest 0.13.x + unaffected rustls adapter, with Cargo-owned resolved-feature evidence, followed by real DNS/TLS/network/cancel/disk-full/captive-portal evidence and same-descriptor cryptographic verification/promotion. Exact-head hosted checks and qualifying independent approval remain required; signer/updater/model authority is not fabricated in source.