macOS: signed and notarized Revue that opens without the right-click workaround, and Homebrew #75

Open
opened 2026-11-12 12:58:39 +01:00 by thatrichan · 1 comment
Owner

Today Revue.app isn't signed by us, so the first launch needs right-click → Open (docs/getting-started.md: "Revue isn't notarized by Apple yet"). On recent macOS versions that workaround moved into System Settings → Privacy & Security, which is worse. Homebrew is also disabling casks that fail Gatekeeper (from Homebrew 5.0, enforced since September 2026), so without notarization there's no official cask.

Plan

1. Prerequisites (yours, outside the repo)

  • Apple Developer Program, $99/year (individual enrollment works from Czechia).
  • A Developer ID Application certificate, exported as .p12.
  • An App Store Connect API key (issuer ID, key ID and .p8) for notarization.
  • Repository secrets: MACOS_CERT_P12 (base64), MACOS_CERT_PASSWORD, APPLE_API_KEY (a JSON file made by rcodesign encode-app-store-connect-api-key).

2. Sign and notarize on Linux, in the existing macos job

The build is cross-compiled in cargo-zigbuild with no Mac in CI. rcodesign (apple-codesign, one static binary) signs and notarizes from Linux:

  1. rcodesign sign --code-signature-flags runtime (hardened runtime, which notarization requires) on Revue.app, using the .p12. That signs both architectures of the universal binary.
  2. Entitlements: probably none, since the app isn't sandboxed. Check on a Mac that Metal, the keychain store ("Keep tokens in Keychain"), the revue:// handler, spawning git/gh/language servers and the localhost OAuth callback on port 47392 all still work under the hardened runtime. Add packaging/entitlements.plist only if something needs it.
  3. Zip the app, run rcodesign notary-submit --wait, then staple the ticket into the .app.
  4. Only then build the .zip and the .dmg (and their .sha256) from the stapled app. Reorder task package PLATFORM=macos: sign/notarize becomes a step between the bundle and the archives, and is skipped (with a note) when the secrets are missing, so local and fork builds keep working.
  5. DMG: we write an ISO 9660 image (genisoimage), which can't be signed or notarized itself. That's fine: the stapled app inside is what Gatekeeper checks. Making a real UDIF .dmg on Linux (to sign the image too) is optional.

3. The in-app updater must keep the signature valid

update_now (src/update.rs:342) replaces only Contents/MacOS/revue and Info.plist inside the bundle. In a signed bundle, the new binary's signature seals a different _CodeResources/ticket than the one left in place, so the bundle no longer verifies (codesign --verify fails, and anything that re-checks it, such as a quarantined copy or a later Gatekeeper assessment, rejects it).

  • On macOS, swap the whole Revue.app from the release zip: extract it next to the bundle, rename the old one aside, rename the new one in, and delete the old one after restart.
  • Tests: extract and swap a whole bundle tree in a temp directory; keep the existing tarball and zip tests.

4. Homebrew

  • Now: our own tap. Make a repo homebrew-revue with Casks/revue.rb (auto_updates true, since the app updates itself), installed with brew tap thatrichan/revue <git url> then brew install --cask revue. A task rewrites the version and sha256 at each release and pushes it from release.yml.
  • Later: the official homebrew/cask. It requires notarization (this issue) and Homebrew's notability bar.

5. Docs

Remove the right-click step and the NOTE from docs/getting-started.md, update the README install table, and add the Homebrew install.

Decisions for you

  • Buy the Apple Developer membership ($99/year)? Nothing here works without it.
  • Own Homebrew tap now, or wait for the official cask?

Order

1 → 2 → 3 (must ship in the same release as the first signed build, or the first self-update breaks the signature) → 5 → 4.

Today `Revue.app` isn't signed by us, so the first launch needs right-click → Open (`docs/getting-started.md`: "Revue isn't notarized by Apple yet"). On recent macOS versions that workaround moved into System Settings → Privacy & Security, which is worse. Homebrew is also disabling casks that fail Gatekeeper (from Homebrew 5.0, enforced since September 2026), so without notarization there's no official cask. ## Plan ### 1. Prerequisites (yours, outside the repo) - Apple Developer Program, $99/year (individual enrollment works from Czechia). - A **Developer ID Application** certificate, exported as .p12. - An **App Store Connect API key** (issuer ID, key ID and .p8) for notarization. - Repository secrets: `MACOS_CERT_P12` (base64), `MACOS_CERT_PASSWORD`, `APPLE_API_KEY` (a JSON file made by `rcodesign encode-app-store-connect-api-key`). ### 2. Sign and notarize on Linux, in the existing `macos` job The build is cross-compiled in `cargo-zigbuild` with no Mac in CI. [`rcodesign`](https://github.com/indygreg/apple-platform-rs) (apple-codesign, one static binary) signs and notarizes from Linux: 1. `rcodesign sign --code-signature-flags runtime` (hardened runtime, which notarization requires) on `Revue.app`, using the .p12. That signs both architectures of the universal binary. 2. Entitlements: probably none, since the app isn't sandboxed. Check on a Mac that Metal, the keychain store ("Keep tokens in Keychain"), the `revue://` handler, spawning `git`/`gh`/language servers and the localhost OAuth callback on port 47392 all still work under the hardened runtime. Add `packaging/entitlements.plist` only if something needs it. 3. Zip the app, run `rcodesign notary-submit --wait`, then staple the ticket into the `.app`. 4. Only then build the `.zip` and the `.dmg` (and their `.sha256`) from the stapled app. Reorder `task package PLATFORM=macos`: sign/notarize becomes a step between the bundle and the archives, and is skipped (with a note) when the secrets are missing, so local and fork builds keep working. 5. DMG: we write an ISO 9660 image (`genisoimage`), which can't be signed or notarized itself. That's fine: the stapled app inside is what Gatekeeper checks. Making a real UDIF `.dmg` on Linux (to sign the image too) is optional. ### 3. The in-app updater must keep the signature valid `update_now` (`src/update.rs:342`) replaces only `Contents/MacOS/revue` and `Info.plist` inside the bundle. In a signed bundle, the new binary's signature seals a different `_CodeResources`/ticket than the one left in place, so the bundle no longer verifies (`codesign --verify` fails, and anything that re-checks it, such as a quarantined copy or a later Gatekeeper assessment, rejects it). - On macOS, swap the **whole `Revue.app`** from the release zip: extract it next to the bundle, rename the old one aside, rename the new one in, and delete the old one after restart. - Tests: extract and swap a whole bundle tree in a temp directory; keep the existing tarball and zip tests. ### 4. Homebrew - Now: our own tap. Make a repo `homebrew-revue` with `Casks/revue.rb` (`auto_updates true`, since the app updates itself), installed with `brew tap thatrichan/revue <git url>` then `brew install --cask revue`. A task rewrites the version and sha256 at each release and pushes it from `release.yml`. - Later: the official `homebrew/cask`. It requires notarization (this issue) and Homebrew's notability bar. ### 5. Docs Remove the right-click step and the NOTE from `docs/getting-started.md`, update the README install table, and add the Homebrew install. ## Decisions for you - Buy the Apple Developer membership ($99/year)? Nothing here works without it. - Own Homebrew tap now, or wait for the official cask? ## Order 1 → 2 → 3 (must ship in the **same** release as the first signed build, or the first self-update breaks the signature) → 5 → 4.
Author
Owner

Homebrew tap, done ahead of notarization (7a82743): the cask thatrichan/homebrew-revue is filled in from packaging/homebrew/revue.rb at each release (auto_updates true). The repository still has to be created. Until signing lands, macOS still asks before the first launch, as with the download.

Homebrew tap, done ahead of notarization (7a82743): the cask `thatrichan/homebrew-revue` is filled in from `packaging/homebrew/revue.rb` at each release (`auto_updates true`). The repository still has to be created. Until signing lands, macOS still asks before the first launch, as with the download.
Sign in to join this conversation.
No labels
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
thatrichan/revue#75
No description provided.