vws-macos, vuforia-cloud-reco-macos and vumark-macos are ad-hoc signed by PyInstaller, so they run, but they are not signed with a Developer ID certificate and not notarized. Anyone who downloads them from the releases page in a browser gets a com.apple.quarantine attribute and is blocked by Gatekeeper, which is why docs/source/install.rst carries an xattr -d com.apple.quarantine workaround for all three.
Nothing is currently blocked by this, and there are no user reports. The Homebrew tap is unaffected: its formula builds from PyPI source, so Gatekeeper never sees a downloaded binary. The documented curl install is also unaffected, since curl does not set the quarantine attribute.
Copy the literalizer-cli implementation
This was solved in adamtheturtle/literalizer-cli#605, in PR adamtheturtle/literalizer-cli#611. Port that build-macos job rather than reimplementing it. Take:
- the
build-macos steps from .github/workflows/release.yml, which import the certificate with a commit-pinned Apple-Actions/import-codesign-certs, sign, smoke-test, notarize with notarytool, and assess with spctl;
bin/entitlements.plist verbatim;
- the "Repository secrets" section of
docs/source/release-process.rst.
Several details in that implementation look removable but are not, and each was arrived at by testing rather than reasoning:
- The entitlements file is what makes the binary run at all. The hardened runtime, which notarization requires, turns on library validation, and a
--onefile binary unpacks and loads its own libpython3.*.dylib at startup, still carrying PyInstaller's ad-hoc signature. Without com.apple.security.cs.disable-library-validation the signed binary dies immediately with "mapping process and mapped file (non-platform) have different Team IDs". That single entitlement is sufficient; the wider sets often recommended for Python apps are not needed. This was confirmed on Python 3.13 and PyInstaller 6.12.0; this repository builds with 3.14 and 6.22.1, which the smoke run below is what actually verifies.
- There is deliberately no
codesign --keychain. The flag silently falls back to the keychain search list when it cannot resolve what it is given — a path that cannot exist still signs successfully — so it implies a guarantee it does not provide.
notarytool does not reliably exit non-zero on a rejected submission, hence the status: Accepted check, and the notarytool log call that reports why a rejection happened.
- The smoke run and the
spctl assessment are the only things that catch the two silent failure modes: a signed binary that cannot start, and notarization that did not take effect.
Where this repository differs: three binaries
- Each binary needs its own
codesign call and its own smoke run. A passing vws-macos says nothing about vumark-macos.
- They do not need three notarization submissions.
notarytool accepts a zip containing several Mach-O executables and issues a ticket for each, so one ditto -c -k --sequesterRsrc over all three and one submit --wait covers the set.
- Each still needs its own
spctl --assess --type exec check afterwards.
Stapling
A notarization ticket cannot be stapled to a bare Mach-O executable — stapler only handles .app bundles, .dmg, .pkg and kexts. So Gatekeeper verifies online, and a browser-downloaded binary needs a network connection on first run. That is the accepted tradeoff: shipping a .pkg instead would allow stapling and work offline, but would replace the curl … && chmod +x one-liners with an installer.
Prerequisites
Worth deciding before setting those: the existing certificate is a personal Developer ID Application identity, so binaries published under the VWS-Python organization would carry a personal team identifier in their signature, which codesign -dv and Gatekeeper both surface. Reusing it is the cheap option and matches the other projects. The alternative is an organization Apple Developer Program membership and a separate certificate, which is a real cost and a second set of credentials to rotate.
Note that build publishes to PyPI and pushes the tag before build-macos runs, so releasing before the notarization secrets are set leaves a published release with no macOS binary attached at all.
vws-macos,vuforia-cloud-reco-macosandvumark-macosare ad-hoc signed by PyInstaller, so they run, but they are not signed with a Developer ID certificate and not notarized. Anyone who downloads them from the releases page in a browser gets acom.apple.quarantineattribute and is blocked by Gatekeeper, which is whydocs/source/install.rstcarries anxattr -d com.apple.quarantineworkaround for all three.Nothing is currently blocked by this, and there are no user reports. The Homebrew tap is unaffected: its formula builds from PyPI source, so Gatekeeper never sees a downloaded binary. The documented
curlinstall is also unaffected, sincecurldoes not set the quarantine attribute.Copy the literalizer-cli implementation
This was solved in
adamtheturtle/literalizer-cli#605, in PRadamtheturtle/literalizer-cli#611. Port thatbuild-macosjob rather than reimplementing it. Take:build-macossteps from.github/workflows/release.yml, which import the certificate with a commit-pinnedApple-Actions/import-codesign-certs, sign, smoke-test, notarize withnotarytool, and assess withspctl;bin/entitlements.plistverbatim;docs/source/release-process.rst.Several details in that implementation look removable but are not, and each was arrived at by testing rather than reasoning:
--onefilebinary unpacks and loads its ownlibpython3.*.dylibat startup, still carrying PyInstaller's ad-hoc signature. Withoutcom.apple.security.cs.disable-library-validationthe signed binary dies immediately with "mapping process and mapped file (non-platform) have different Team IDs". That single entitlement is sufficient; the wider sets often recommended for Python apps are not needed. This was confirmed on Python 3.13 and PyInstaller 6.12.0; this repository builds with 3.14 and 6.22.1, which the smoke run below is what actually verifies.codesign --keychain. The flag silently falls back to the keychain search list when it cannot resolve what it is given — a path that cannot exist still signs successfully — so it implies a guarantee it does not provide.notarytooldoes not reliably exit non-zero on a rejected submission, hence thestatus: Acceptedcheck, and thenotarytool logcall that reports why a rejection happened.spctlassessment are the only things that catch the two silent failure modes: a signed binary that cannot start, and notarization that did not take effect.Where this repository differs: three binaries
codesigncall and its own smoke run. A passingvws-macossays nothing aboutvumark-macos.notarytoolaccepts a zip containing several Mach-O executables and issues a ticket for each, so oneditto -c -k --sequesterRsrcover all three and onesubmit --waitcovers the set.spctl --assess --type execcheck afterwards.Stapling
A notarization ticket cannot be stapled to a bare Mach-O executable —
stapleronly handles.appbundles,.dmg,.pkgand kexts. So Gatekeeper verifies online, and a browser-downloaded binary needs a network connection on first run. That is the accepted tradeoff: shipping a.pkginstead would allow stapling and work offline, but would replace thecurl … && chmod +xone-liners with an installer.Prerequisites
DEVELOPER_ID_APP_CERT_P12_BASE64andDEVELOPER_ID_APP_CERT_PASSWORD— already set on this repositoryASC_KEY,ASC_KEY_IDandASC_ISSUER_ID— not set anywhere yetWorth deciding before setting those: the existing certificate is a personal Developer ID Application identity, so binaries published under the
VWS-Pythonorganization would carry a personal team identifier in their signature, whichcodesign -dvand Gatekeeper both surface. Reusing it is the cheap option and matches the other projects. The alternative is an organization Apple Developer Program membership and a separate certificate, which is a real cost and a second set of credentials to rotate.Note that
buildpublishes to PyPI and pushes the tag beforebuild-macosruns, so releasing before the notarization secrets are set leaves a published release with no macOS binary attached at all.