Skip to content

fix(release): correct the v0.8.0 appcast signature to match the shipped DMG - #65

Merged
alecf merged 1 commit into
mainfrom
fix/appcast-08-signature
Aug 8, 2026
Merged

fix(release): correct the v0.8.0 appcast signature to match the shipped DMG#65
alecf merged 1 commit into
mainfrom
fix/appcast-08-signature

Conversation

@alecf

@alecf alecf commented Aug 8, 2026

Copy link
Copy Markdown
Owner

Every user on 0.8.0 or earlier currently gets "The update is improperly signed and could not be validated" when Sparkle tries to update. This restores the feed.

What broke

The appcast entry for 0.8.0 advertises a signature over bytes that are not the bytes GitHub serves:

signature length
appcast.xml on main xeGbajj6…kKuYBA== 1893410
DMG actually attached to the v0.8.0 release 6lGIp4X3…Se2EAw== 1892425

Verified against the pinned SUPublicEDKey from Distribution/Info.plist: the live entry fails Ed25519 verification over the released DMG, the replacement passes. Sparkle checks the signature before it will install, so it refuses the update — correctly. The artifact is genuine; the feed was describing a different build.

How it happened

release.yml was dispatched a second time for 0.8.0 at 2026-08-08T05:58Z, after PR #63 had already merged and published the release. git-cliff still calculated 0.8.0 (no releasable commits had landed since), so the run:

  1. hit the gh release delete "$TAG" --yes --cleanup-tag branch and removed the published release and its tag,
  2. rebuilt the DMG — different bytes, DMG creation isn't reproducible — and signed those,
  3. uploaded them as a fresh draft, and
  4. force-pushed the new appcast entry to release/v0.8.0, whose PR had already merged and closed.

Step 4 is where the signature was lost: there was no open PR left to carry it to main. The draft was then published, leaving the new DMG live under the old entry.

The tag itself is fine — v0.8.0 points at b7d1e94, same as main.

The fix

Update the 0.8.0 enclosure to the signature and length of the DMG that is actually served. No re-release needed: the shipped artifact is a legitimate signed build, only the feed was stale.

Checked the whole feed rather than just the broken entry — all 11 versions now verify against their live release assets:

0.8.0 sig VALID  len ok      0.4.1 sig VALID  len ok
0.7.0 sig VALID  len ok      0.4.0 sig VALID  len ok
0.6.0 sig VALID  len ok      0.3.0 sig VALID  len ok
0.5.1 sig VALID  len ok      0.2.1 sig VALID  len ok
0.5.0 sig VALID  len ok      0.2.0 sig VALID  len ok
0.4.2 sig VALID  len ok

Merging publishes the corrected feed via deploy-site.yml. Users recover on their next update check with no action on their part.

Follow-up, not in this PR

Nothing stops this from recurring: re-dispatching release.yml for an already-published version will delete it and swap the artifact again. release.yml should refuse to delete a release that is not a draft. Left out here deliberately — #64 rewrites that file, so the guard belongs there rather than in a conflicting hotfix.

🤖 Generated with Claude Code

Screenshot

Total Recall main window

Auto-captured on macos-26 at d124d72 (workflow run). Updated on every push to this PR.

…ed DMG

The DMG attached to the v0.8.0 GitHub Release is not the one the appcast
entry was signed against, so Sparkle rejects every update with "The update
is improperly signed and could not be validated."

The release workflow was dispatched a second time for 0.8.0 after the first
release had already published. That run deleted the published release and
its tag, rebuilt the DMG, and signed the new bytes — but its appcast update
was force-pushed to release/v0.8.0, whose PR had already merged and closed,
so the new signature never reached main. The result is a feed advertising
the first build's signature over the second build's bytes.

Point the entry at the DMG that is actually served. Both signatures were
produced by the release key in CI; this is the one that verifies against
the published artifact.

Verified all 11 appcast entries against their live release assets: every
EdDSA signature and length now matches the bytes GitHub serves.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Test Coverage

Coverage after merging fix/appcast-08-signature into main will be
90.64%▾ -0.52%
Coverage Report for Changed Files
FileStmtsBranchesFuncsLinesUncovered Lines

@alecf
alecf merged commit cf365f8 into main Aug 8, 2026
3 checks passed
@alecf
alecf deleted the fix/appcast-08-signature branch August 8, 2026 18:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant