Skip to content

Add deploy script that packages into a signed .app bundle - #4

Open
nanderss wants to merge 1 commit into
okoker:mainfrom
nanderss:deploy-signed-app-bundle
Open

Add deploy script that packages into a signed .app bundle#4
nanderss wants to merge 1 commit into
okoker:mainfrom
nanderss:deploy-signed-app-bundle

Conversation

@nanderss

@nanderss nanderss commented Aug 17, 2026

Copy link
Copy Markdown

macOS TCC would not honour an Accessibility grant for the bare Mach-O binary the README installs, so key: actions silently did nothing no matter how many times the permission was granted.

Two compounding causes:

  1. swift build leaves the binary linker-signed (flags=0x20002(adhoc,linker-signed)), which TCC treats differently from a real ad-hoc signature.
  2. Even after codesign --force --sign -, the grant was still ignored. codesign reports the install directory as Format=bundle because of the adjacent Info.plist, but there's no bundle structure behind it and the plist is not bound into the signature — so there's no stable identity for TCC to pin a grant to.

Running from a real .app bundle fixed it — Format=app bundle, Info.plist entries=11 sealed into the signature, real CFBundleIdentifier, LaunchAgent pointed at Contents/MacOS/StreamDeckController. That's the identity shape the Accessibility list is built around, and it's also what users are used to dragging in.

Signing identity

The script signs with a certificate rather than ad-hoc, which matters more than it first appears.

An ad-hoc signature's designated requirement is a bare cdhash:

designated => cdhash H"08f431a3607a9c701333cf23318b322e164e6c5b"

So every rebuild is a new identity and silently voids the Accessibility grant — the app has to be removed and re-added in System Settings after each deploy, and until you do, key: actions fail with nothing in the log. Signing with a self-signed code-signing certificate gives:

designated => identifier "com.streamdeck.controller" and certificate leaf = H"395f6c08..."

which is independent of the binary's contents. Verified by rebuilding: cdhash changed from 30e7b6c9 to 5f4281b6 and the grant remained in effect, with key:cmd+space and a held modifier chord both still working.

The script fails if the identity is missing rather than falling back to ad-hoc, because that fallback appears to work and then breaks key: actions later.

Notes

Setting up the certificate is a one-time manual step (Keychain Access → Certificate Assistant, or openssl + security add-trusted-cert -p codeSign), so it isn't scripted here — the script only checks the identity exists. Happy to add a short README section covering it.

This does change the deployment approach in the README (steps 4–6), so it needs a docs update to match — glad to add that here or send it separately, whichever you prefer. Also happy to drop the script and just document the bundle layout if you'd rather not carry a deploy script in the repo; the .app and the non-ad-hoc signature are the parts that actually matter.

Tested on an AJAZZ AKP03, macOS 26.5, Apple Silicon.

TCC will not honour an Accessibility grant for the bare Mach-O binary
the README installs, so key: actions silently do nothing no matter how
many times the permission is granted. Two compounding causes:

1. swift build leaves the binary linker-signed (flags 0x20002), which
   TCC treats differently from a real ad-hoc signature.
2. Even after codesign --force --sign -, the grant is still ignored.
   codesign reports the install directory as Format=bundle because of
   the adjacent Info.plist, but there is no bundle structure behind it
   and the plist is "not bound" into the signature, so there is no
   stable identity for TCC to pin a grant to.

Running from a real .app bundle fixes it: Format=app bundle, Info.plist
sealed into the signature, real CFBundleIdentifier, LaunchAgent pointed
at Contents/MacOS/StreamDeckController.

Sign with a certificate rather than ad-hoc. An ad-hoc designated
requirement is a bare cdhash, so every rebuild is a new identity and
voids the grant; a certificate makes the requirement identifier +
certificate leaf, independent of the binary's contents. Verified by
rebuilding: cdhash changed from 30e7b6c9 to 5f4281b6 with the grant
still in effect.

Fail if the identity is missing rather than falling back to ad-hoc,
since the fallback appears to work and breaks key: actions later with
no error in the log.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@nanderss
nanderss force-pushed the deploy-signed-app-bundle branch from 967485e to 648fba4 Compare August 18, 2026 04:58
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