Add Sparkle updates for notarized direct-distribution builds #52

Open
opened 2026-08-23 17:40:19 +00:00 by kayg · 0 comments
Owner

Goal

Add Sparkle updates for the notarized direct-download build. Keep the Mac App Store build on Apple-managed updates.

Scope

  • Include Sparkle only in the direct-download build. Exclude updater code, Sparkle feed URLs, and appcast assets from the App Store build. The App Store build follows App Store and StoreKit update flows.
  • Host the direct-channel appcast and update assets on the Hermternal website or its CDN. Use HTTPS. Publish EdDSA signatures, the internal version or build number, the user-facing version, minimum macOS version, release notes, archive length, and download URL.
  • Publish full update archives. Add delta assets only when measured download savings justify their storage, generation, testing, and rollback cost.
  • Make the release pipeline upload a notarized and stapled ZIP. Verify the exact public bytes after notarization and before publishing the checksum, EdDSA signature, appcast item, or latest-release pointer.
  • Provide a manual Check for Updates action. Use restrained automatic checks with user consent. Network work must not block launch or claim progress when no work is active.
  • Explain what update checks and downloads do. Do not require analytics, tracking, or personally identifiable data for updates. Provide a user-controlled automatic-check preference.
  • Install only an update that passes archive-signature, code-signature, and notarization or Gatekeeper checks. Handle download, verification, installation, restart, and failure states. Keep a verified previous release available for operator rollback. Never weaken Gatekeeper, notarization, or signature checks.
  • Register update support through the injected capability Adapter at the composition root. Select the direct or App Store build flavor there. Do not scatter App Store conditionals through views, Core, or licensing code. Do not add global update or licensing state.
  • Depend on Build the Hermternal website and host direct releases for the public download channel, immutable assets, and release metadata.

Non-goals

  • Do not add Sparkle or a custom updater to the App Store build.
  • Do not implement App Store submission, StoreKit purchases, licensing, or website layout in this issue.
  • Do not force an update, block launch on a network request, or silently install without the selected user preference.
  • Do not publish update bytes from Forgejo. Forgejo remains the source, tag, and provenance record.
  • Do not add analytics or a new telemetry service.

Security and release rules

  • Pin the direct build to the approved Sparkle EdDSA public key. Sign the appcast when the selected Sparkle version supports signed feeds. Verify feed, release-note, archive, and embedded application signatures before use.
  • Serve the feed and archives over HTTPS. Reject an invalid, expired, truncated, mismatched, or tampered feed or archive.
  • Keep version and build comparison deterministic. Never treat a lower build as an update. A rollback changes the website latest pointer to a previously verified immutable release; it does not weaken client verification or perform an unsafe automatic downgrade.
  • Keep signing and notarization credentials in the release environment. Never expose them to the app, website client code, or issue data.

Performance and accessibility

  • Automatic checks use a bounded schedule and cancel or defer work when appropriate. They use no launch-blocking network or repeated downloads.
  • The update alert reports actual checking, downloading, verifying, and installing work. Offline and idle states remain static and clear.
  • The manual action and update alert support keyboard navigation, VoiceOver labels, Dynamic Type, sufficient contrast, and reduced motion. Errors state the cause and the next available action.

Deterministic tests

  • Generate and validate an appcast item with EdDSA feed and archive signatures, version or build comparison, minimum macOS version, release notes, and archive length.
  • Reject a corrupt, truncated, expired, unsigned, or tampered feed, release note, or archive.
  • Reject an archive with a changed public checksum, invalid code signature, failed notarization or Gatekeeper verification, unsupported minimum OS, or wrong channel.
  • Cover offline checks, timeout and retry failure, skipped versions, major-version or minimum-OS filtering, and a user-disabled automatic-check preference.
  • Verify a direct-build composition root registers the Sparkle Adapter and an App Store build registers no updater capability.
  • Verify the App Store binary contains no Sparkle framework, updater code, update-feed URL, or appcast symbol. Verify the direct binary contains the expected feed and public key.
  • Exercise manual Check for Updates and automatic checks without blocking launch. Verify progress appears only for active work and all failure paths preserve the current install.
  • Exercise release publication with post-notarization public-byte verification. A checksum, signature, appcast item, or latest pointer must not publish after any verification failure.
  • Verify an operator rollback selects a previous immutable verified release without changing its bytes.

Acceptance criteria

  • The direct-download build checks the HTTPS Hermternal appcast, verifies the EdDSA-signed feed and archive, compares version, build, and minimum macOS requirements, and installs only a verified notarized artifact.
  • The appcast includes release notes and full archives. Delta archives exist only with recorded size or time evidence that justifies them.
  • CI publishes the notarized and stapled ZIP, checksum, signatures, and appcast only after the public bytes pass verification. The website release issue owns the public channel and immutable asset layout.
  • Users can run Check for Updates manually. Automatic checks are restrained, consent-aware, cancellable, offline-safe, and never launch-blocking. The UI never shows false progress.
  • A failed, corrupt, or tampered update leaves the current installation usable, reports a useful error, and preserves a verified rollback target. No path disables Gatekeeper, notarization, or signature verification.
  • The composition root selects one direct-download capability Adapter or no updater capability for the App Store flavor. No scattered App Store conditionals or global licensing or update state remain.
  • The deterministic test set passes for signatures, version and channel selection, corrupt and tampered assets, offline mode, skipped versions, minimum OS, publication verification, rollback, accessibility, and App Store symbol absence.

Official references

## Goal Add Sparkle updates for the notarized direct-download build. Keep the Mac App Store build on Apple-managed updates. ## Scope - Include Sparkle only in the direct-download build. Exclude updater code, Sparkle feed URLs, and appcast assets from the App Store build. The App Store build follows App Store and StoreKit update flows. - Host the direct-channel appcast and update assets on the Hermternal website or its CDN. Use HTTPS. Publish EdDSA signatures, the internal version or build number, the user-facing version, minimum macOS version, release notes, archive length, and download URL. - Publish full update archives. Add delta assets only when measured download savings justify their storage, generation, testing, and rollback cost. - Make the release pipeline upload a notarized and stapled ZIP. Verify the exact public bytes after notarization and before publishing the checksum, EdDSA signature, appcast item, or latest-release pointer. - Provide a manual Check for Updates action. Use restrained automatic checks with user consent. Network work must not block launch or claim progress when no work is active. - Explain what update checks and downloads do. Do not require analytics, tracking, or personally identifiable data for updates. Provide a user-controlled automatic-check preference. - Install only an update that passes archive-signature, code-signature, and notarization or Gatekeeper checks. Handle download, verification, installation, restart, and failure states. Keep a verified previous release available for operator rollback. Never weaken Gatekeeper, notarization, or signature checks. - Register update support through the injected capability Adapter at the composition root. Select the direct or App Store build flavor there. Do not scatter App Store conditionals through views, Core, or licensing code. Do not add global update or licensing state. - Depend on [Build the Hermternal website and host direct releases](https://git.kayg.org/kayg/hermternal-apple/issues/51) for the public download channel, immutable assets, and release metadata. ## Non-goals - Do not add Sparkle or a custom updater to the App Store build. - Do not implement App Store submission, StoreKit purchases, licensing, or website layout in this issue. - Do not force an update, block launch on a network request, or silently install without the selected user preference. - Do not publish update bytes from Forgejo. Forgejo remains the source, tag, and provenance record. - Do not add analytics or a new telemetry service. ## Security and release rules - Pin the direct build to the approved Sparkle EdDSA public key. Sign the appcast when the selected Sparkle version supports signed feeds. Verify feed, release-note, archive, and embedded application signatures before use. - Serve the feed and archives over HTTPS. Reject an invalid, expired, truncated, mismatched, or tampered feed or archive. - Keep version and build comparison deterministic. Never treat a lower build as an update. A rollback changes the website latest pointer to a previously verified immutable release; it does not weaken client verification or perform an unsafe automatic downgrade. - Keep signing and notarization credentials in the release environment. Never expose them to the app, website client code, or issue data. ## Performance and accessibility - Automatic checks use a bounded schedule and cancel or defer work when appropriate. They use no launch-blocking network or repeated downloads. - The update alert reports actual checking, downloading, verifying, and installing work. Offline and idle states remain static and clear. - The manual action and update alert support keyboard navigation, VoiceOver labels, Dynamic Type, sufficient contrast, and reduced motion. Errors state the cause and the next available action. ## Deterministic tests - Generate and validate an appcast item with EdDSA feed and archive signatures, version or build comparison, minimum macOS version, release notes, and archive length. - Reject a corrupt, truncated, expired, unsigned, or tampered feed, release note, or archive. - Reject an archive with a changed public checksum, invalid code signature, failed notarization or Gatekeeper verification, unsupported minimum OS, or wrong channel. - Cover offline checks, timeout and retry failure, skipped versions, major-version or minimum-OS filtering, and a user-disabled automatic-check preference. - Verify a direct-build composition root registers the Sparkle Adapter and an App Store build registers no updater capability. - Verify the App Store binary contains no Sparkle framework, updater code, update-feed URL, or appcast symbol. Verify the direct binary contains the expected feed and public key. - Exercise manual Check for Updates and automatic checks without blocking launch. Verify progress appears only for active work and all failure paths preserve the current install. - Exercise release publication with post-notarization public-byte verification. A checksum, signature, appcast item, or latest pointer must not publish after any verification failure. - Verify an operator rollback selects a previous immutable verified release without changing its bytes. ## Acceptance criteria - The direct-download build checks the HTTPS Hermternal appcast, verifies the EdDSA-signed feed and archive, compares version, build, and minimum macOS requirements, and installs only a verified notarized artifact. - The appcast includes release notes and full archives. Delta archives exist only with recorded size or time evidence that justifies them. - CI publishes the notarized and stapled ZIP, checksum, signatures, and appcast only after the public bytes pass verification. The website release issue owns the public channel and immutable asset layout. - Users can run Check for Updates manually. Automatic checks are restrained, consent-aware, cancellable, offline-safe, and never launch-blocking. The UI never shows false progress. - A failed, corrupt, or tampered update leaves the current installation usable, reports a useful error, and preserves a verified rollback target. No path disables Gatekeeper, notarization, or signature verification. - The composition root selects one direct-download capability Adapter or no updater capability for the App Store flavor. No scattered App Store conditionals or global licensing or update state remain. - The deterministic test set passes for signatures, version and channel selection, corrupt and tampered assets, offline mode, skipped versions, minimum OS, publication verification, rollback, accessibility, and App Store symbol absence. ## Official references - [Sparkle: Publishing an update](https://sparkle-project.org/documentation/publishing/): appcast fields, EdDSA archive and release-note signatures, delta updates, internal build numbers, minimum system versions, and skipped or major upgrades. - [Sparkle: Customizing Sparkle](https://sparkle-project.org/documentation/customization/): HTTPS feed configuration, public EdDSA key, automatic-check consent, scheduled-check bounds, signed-feed validation, and verification before extraction. - [Apple: Distribute outside the Mac App Store](https://help.apple.com/xcode/mac/current/en.lproj/dev033e997ca.html): Developer ID signing, hardened runtime, notarization, stapling, and Gatekeeper testing for direct distribution. - [Apple: Notarizing macOS software before distribution](https://developer.apple.com/documentation/security/notarizing-macos-software-before-distribution): notarization tickets, stapling, Developer ID requirements, and Gatekeeper verification. - [Apple: Create a new version in App Store Connect](https://developer.apple.com/help/app-store-connect/update-your-app/create-a-new-version/): App Store version updates use the App Store version workflow, with no direct-channel updater requirement.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kayg/hermternal-apple#52
No description provided.