Skip to content

[Release] Ship signed, notarized, auto-updatable desktop builds with rollback evidence #960

Open

Description

@seonghobae

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

Non-goals

Activity

  1. added
    area: authAuthentication, authorization, identity, or tenant isolation
    area: ci-cdCI, GitHub Actions, checks, release, or supply chain
    area: securitySecurity boundary, hardening, or vulnerability prevention
    status: triagedOpen issue has an organization taxonomy assignment
    type: featureNew or expanded product capability
    on Aug 22, 2026
  2. seonghobae commented on Sep 14, 2026

    @seonghobae
    CollaboratorAuthor

    #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은 하지 않습니다.

  3. seonghobae commented on Sep 14, 2026

    @seonghobae
    CollaboratorAuthor

    #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 guard 2cd667c5928c1a394fb1e5443c717603b5b1424e, explicit blocked policy 50ca2a80a228652cde0ae402b8c410c07aed7b7c, tag preflight composition 10cf39dda49206f2810583f8f4c3d6afb2e1709f, fixture repair 9e00feec31a348fadf9346f40a5016992192e2e8, traceability 0b565ba7da8dcdcbaa83bd7adb0a7f9087b6ea6f 순입니다.

    현재 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도 하지 않습니다.

  4. seonghobae commented on Sep 14, 2026

    @seonghobae
    CollaboratorAuthor

    #1126 current exact head b535013d9f381b91c9498ab469f374739192c081 closes one more repository-owned updater-admission gap without fabricating external authority.

    Fresh review found that an admitted policy plus matching tauri.conf.json could still pass while the shipped desktop binary omitted tauri-plugin-updater or never initialized it. RED 8843a308303d7731a175abfc0b90fb365cb7518e captures that false-positive path; 9869d32bdad9787f3af6556f73d74070f8541b5f now 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. 6d91f7c7bc117da9ece7563e45a7d88de09b09d4 repairs the admitted fixtures and b535013d9f381b91c9498ab469f374739192c081 doctors the traceability/claim boundary against current Tauri v2 guidance.

    The checked-in updater policy remains deliberately blocked; this does not claim key/endpoint provisioning, .sig publication, cryptographic wrong-signature/replay rejection, packaged runtime acceptance, or rollback. Those remain #960 work. Protected develop is still 314ddeae7b775a4957594b599358c8255617eb2e; #1126 is ahead 51 / behind 0 and remains Draft while exact-head hosted checks and independent review are non-terminal.

  5. seonghobae commented on Sep 14, 2026

    @seonghobae
    CollaboratorAuthor

    #1126 Distribution/update owner는 exact 9df9e61592c006f7bfb94008c94427ef40a1bc0c까지 ordinary descendant로 전진했습니다. Protected develop@314ddeae7b775a4957594b599358c8255617eb2e 대비 fresh compare는 ahead 87 / behind 0입니다.

    이번 slice는 #960의 anti-replay/rollback 요구를 문서 요구에서 native executable contract로 옮겼습니다. 먼저 manifest RED 57ced89e... → fix 21ee4ff5...로 exact source commit, minimumSupportedVersion, target별 updater bundle size/full SHA-256을 Tauri static response의 bandscope metadata에 결합했고, 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는 authenticated Update.raw_json admission, 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를 유지합니다.

  6. seonghobae commented on Sep 14, 2026

    @seonghobae
    CollaboratorAuthor

    Current #1126 progress — exact head ae98559c83939b75ae02cadcbd05894883cc0a37 on protected develop@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 = false and direct features exactly {rustls}, but Cargo root features can activate dependency features again through dependency/feature and dependency?/feature, with feature unification applying the result. RED ed5be008b379f5f07c4325801f1bbba9676fcad1 covers reqwest/gzip and renamed distribution_http/brotli; causal fix bc56faad9bd2582cd6bae4f0b4df43dafc64d9a8 rejects forwarding through every runtime dependency key that resolves to reqwest; edge verification 0f74972089c4240dbcb7e47d65a8e116bbf099cf covers weak distribution_http?/zstd; traceability ae98559c83939b75ae02cadcbd05894883cc0a37 records 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: authAuthentication, authorization, identity, or tenant isolationarea: ci-cdCI, GitHub Actions, checks, release, or supply chainarea: dependenciesDependency or lockfile maintenancearea: securitySecurity boundary, hardening, or vulnerability preventionmaintenancepriority: mediumNormal-priority or P2 workscope: product-gapCustomer-visible product gapstatus: triagedOpen issue has an organization taxonomy assignmenttype: featureNew or expanded product capability

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions