Skip to content

refactor(packaging): sign Keybase.app inside-out instead of codesign --deep (untested on build machine) - #29765

Merged
chrisnojima merged 4 commits into
nojima/HOTPOT-packaging-speedfrom
nojima/HOTPOT-packaging-signing
Oct 15, 2026
Merged

chrisnojima merged 4 commits into
nojima/HOTPOT-packaging-speedfrom
nojima/HOTPOT-packaging-signing

Conversation

@chrisnojima

@chrisnojima chrisnojima commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Signing checked on a build-machine dmg. It still needs that build installed on a clean machine before it merges.

What

Signs Keybase.app inside-out instead of with codesign --deep, which Apple deprecates for signing. Stacked on #29764.

  • New kb_sign_app_inside_out <app> <entitlements> in lib.sh:
    • It finds nested code by Mach-O type plus .app, .framework and .xpc bundles. Only candidates are classified: executable files and *.dylib, *.so and *.node.
    • It signs loose binaries deepest first, then frameworks, then helper apps, then the main app.
    • Every item gets --force --options runtime --timestamp with the Developer ID (no --timestamp when signing ad-hoc).
    • Executables, helper apps and the main app get the entitlements. Dylibs, frameworks and executables inside a framework's Resources/ (ShipIt) don't.
    • It skips Resources/KeybaseInstaller.app and Resources/KeybaseUpdater.app, which arrive signed by Keybase with their own entitlements, and SharedSupport/, whose binaries kb_sign_binary signs earlier.
    • Electron upgrades: the hand-kept list of Electron dylibs is gone. New dylibs or helpers are found automatically.
  • package_darwin.sh sign() calls the new function, and the app check is now codesign --verify --deep --strict.
  • kb_sign_binary drops --deep, which did nothing on a single binary: the CDHash is the same with and without it.
  • KB_CODESIGN_IDENTITY can be overridden, for example with - for local ad-hoc test signing. An ad-hoc build leaves out --timestamp in both kb_sign_binary and the new function, and skips the spctl --assess of Keybase.app, which Gatekeeper always rejects for an ad-hoc signature. The installer and updater apps are still assessed, since Keybase signs them.

Equivalence with the --deep signing it replaces

  • Method: two copies of the real Electron bundle, laid out like Keybase.app (SharedSupport/bin, the real installer and updater apps), were signed ad-hoc. One used the old sign() sequence and the other the new function.
  • Result: identifier, flags, authority, entitlements and CDHash were compared for all 63 code items, with 0 differences. Both pass codesign --verify --deep --strict.
  • What --deep did:
    • It gave Keybase.entitlements to the main app, the 4 helper apps and chrome_crashpad_handler. The new code keeps that.
    • It did not re-sign the installer and updater apps or SharedSupport/bin. The new code leaves them alone too.
  • New code is found: a third copy had an unsigned libEGL.dylib, a new helper executable and Resources/app.asar.unpacked/x.node added. All three were found and signed with the right entitlements, and the installer and updater stayed byte-for-byte the same.

Why not @electron/osx-sign

It's installed only as a dependency of @electron/packager, not as a direct dependency. It also signs during packaging, before package_darwin.sh copies the Go binaries and the installer and updater apps into the bundle.

Checked on a build-machine dmg

An arm64 dmg built by the bot from ed66d4dc0d was compared with one built from #29756's ad4436a112, which still signs with --deep.

  • All 63 code items: identifier, team, flags (hardened runtime), secure timestamp, signing authority and entitlements are identical between the two builds. Every item passes codesign --verify --strict on its own.
  • Keybase.app: signed with the Keybase Developer ID and a timestamp. codesign --verify --deep --strict passes, and spctl --assess accepts it as "Notarized Developer ID".
  • The dmg: notarized, and the stapled ticket validates.
  • Entitlements: the main app, the four Keybase Helper apps and chrome_crashpad_handler carry Keybase.entitlements. Nothing else does, apart from keybase.Helper, which keeps its own.
  • Not re-signed: KeybaseInstaller.app, KeybaseUpdater.app and keybase.Helper have the same CDHash as in the --deep build, and Gatekeeper accepts both apps as notarized.

Still to confirm

  • The notary log for this build has no "not signed" or "no secure timestamp" issues.
  • The dmg installs on a clean machine and the app launches with no Gatekeeper prompt; the GPU and renderer helpers start and windows render.
  • The installer and updater apps run; KBFuse and keybase.Helper install.
  • The Keybase updater (KeybaseUpdater.app) applies an update. Squirrel/ShipIt is only signed because Electron bundles it; the app doesn't use it.

@chrisnojima
chrisnojima added this pull request to stack #29759 October 12, 2026 19:16
@chrisnojima chrisnojima changed the title nojima/HOTPOT packaging signing refactor(packaging): sign Keybase.app inside-out instead of codesign --deep (untested on build machine) Oct 12, 2026
@chrisnojima
chrisnojima force-pushed the nojima/HOTPOT-packaging-signing branch from 9570a0f to b7b43b7 Compare October 12, 2026 19:21
@chrisnojima
chrisnojima requested a balanced review from Copilot October 12, 2026 21:41

This comment was marked as outdated.

@chrisnojima
chrisnojima force-pushed the nojima/HOTPOT-packaging-signing branch 2 times, most recently from 4a12b45 to a2a3641 Compare October 14, 2026 14:25
@chrisnojima
chrisnojima force-pushed the nojima/HOTPOT-packaging-signing branch from a2a3641 to a56df36 Compare October 14, 2026 17:42
@chrisnojima
chrisnojima marked this pull request as ready for review October 14, 2026 18:04
…sign --deep

kb_sign_app_inside_out (lib.sh) finds nested code by Mach-O type and bundle
extension, signs it deepest-first, then frameworks, helper apps and the main
app, so Electron upgrades no longer need the hand-kept dylib list.
Entitlements per item match what the --deep run produced (main app, helper
apps and crashpad handler get Keybase.entitlements; dylibs, frameworks and
ShipIt get none); Resources/ (the pre-signed installer/updater apps) and
SharedSupport/ are left as they are, as before. kb_sign_binary drops --deep,
which was a no-op on single binaries.
Skip the Gatekeeper assessment of Keybase.app when KB_CODESIGN_IDENTITY=-,
since spctl rejects an ad-hoc signature. kb_sign_binary now drops
--timestamp for ad-hoc signing, matching kb_sign_app_inside_out.
@chrisnojima
chrisnojima force-pushed the nojima/HOTPOT-packaging-signing branch from ffc9bad to ed66d4d Compare October 15, 2026 15:57
@chrisnojima
chrisnojima merged commit 8320db9 into master Oct 15, 2026
1 check passed
@chrisnojima
chrisnojima deleted the nojima/HOTPOT-packaging-signing branch October 15, 2026 18:36
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.

2 participants