Skip to main content

app-pass

main Anaconda-Server Badge

Tool to ensure an .app bundle pass notarization, and Gatekeeper on MacOS. Originally to sign the bundle for ilastik.

Prerequisite: You have built your app, and it runs on your own machine ;). Problem: You try to sign/notarize but get back many errors and are unsure how to resolve them.

Before you consider using app-pass, you might get away without it by:

Doing everything via XCODE** :)

Your app is Python-based

Your app is Java-based

  • Consider using jaunch, which powers Fiji.

In any case, there are many reasons you can't use one of these alternatives and are left with a working .app that you can not sign. app-pass can help you no matter how you generated the app in the first place.

Tested so far with conda-based python apps, and java apps.

app-pass can perform various fixes on binaries, and sign .app bundles. Does not require using a specific way to build your .app bundle. Does not require .app being written in a specific language, or framework.

We understand that making changes to the binary that you are distributing should be as transparent as possible. For this, you can generate an .sh file that uses only apple dev tools to manipulate your .app. Any app-pass command invoked with --dry-run will not make any changes to your app.

Installation

You can find the package on pypi and conda:

pip install app-pass

or

conda install -c conda-forge app-pass

Fix/Sign/Notarize workflow

In general the workflow is roughly in these stages:

  1. You generate your .app bundle.
  2. The binaries in your app bundle are fixed, and
  3. signed.
  4. The bundle is sent to notarization with apple.
  5. .app is stapled and compressed again for distribution.
  6. Optional, if you have a .dmg installer, you rebuild it with the signed app and notarize it as well.

app-pass helps you with steps 2, 3 and 4.

For the process of acquiring the required signing certificate and app password, please see the jaunch documentation.

Complete usage example

So far we've been working with the following entitlements.plist:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>com.apple.security.cs.allow-unsigned-executable-memory</key>
    <true/>
    <key>com.apple.security.cs.allow-jit</key>
    <true/>
</dict>
</plist>

An example how we would sign our ilastik .app bundle:

# unzip unsigned app bundle after build
ditto -x -k ~/Downloads/ilastik-1.4.1rc3-arm64-OSX-unsigned.zip .
# this creates the bundle folder ilastik-1.4.1rc3-arm64-OSX.app that we will be
# working with

# fix and sign contents - for ilastik, we decide to remove rpaths that point
# outside the bundle so we add --rc-path-delete
app-pass fixsign -vv \
    --sh-output "ilastik-1.4.1rc3-arm64-OSX-sign.sh" \
    --rc-path-delete \
    ilastik-1.4.1rc3-arm64-OSX.app \
    entitlements.plist \
    "Developer ID Application: <YOUR DEVELOPER APPLICATION INFO>"

app-pass notarize -vv \
    ilastik-1.4.1rc3-arm64-OSX.app \
    notarytool-password \
    /Users/kutra/Library/Keychains/login.keychain-db \
    "<email-address-of-dev-account@provider.ext>" \
    <your-team-id> \

# finally zip again for distribution
# --noqtn --norsrc have been added as some builds resulted in .zip
# archives that would not expand cleanly with Archive Utility
# (AppleDouble files expanded for symlinks in the app bundle
# which would prevent it from passing gatekeeper.)
/usr/bin/ditto -v --noqtn --norsrc -c -k --keepParent \
    ilastik-1.4.1rc3-arm64-OSX.app ilastik-1.4.1rc3-arm64-OSX.zip

Sub-commands

If your bundle includes `.jar` files

These need to be extracted and can have case sensitive file contents. Per default, the file system on the mac is not case sensitive! While many developers opt to change this when they get a new machine, not everyone does... To mitigate this, we recommend creating a ram-disk for temporary files:

# creates a 2GB ramdisk at mountpoint /Volumes/ramdisk
# ram://2097152 for 1GB, ram://1048576 for .5GB
diskutil erasevolume hfsx 'ramdisk' `hdiutil attach -nomount ram://4194304`

You need to invoke all app-pass commands overriding then env variable TMPDIR, e.g. TMPDIR=/Volumes/ramdisk app-pass fix ...

Check

# check if app would likely pass notarization and later gatekeeper
app-pass check <path_to_app_bundle.app>

Fix

app-pass fix -vv --sh-output debug.sh <path_to_app_bundle.app>

Sign

app-pass sign -vv --sh-output debug.sh <path_to_app_bundle.app> \
    <path/to/entitlements.plist> \
    <"Developer ID Application: <YOUR DEVELOPER APPLICATION INFO>">

Notarize

app-pass notarize -vv <path_to_app_bundle.app> \
    <your-keychain-profile> <path-to-keychain> \
    <email-address-of-dev-account@provider.ext> <your-team-id>

--dry-run and --sh-output

app-pass is built to make it easy for you to audit changes to your app. Invoking app-pass with --dry-run and --sh-output <output-script.sh> will not do any changes to your app. Instead, it will generate a shell script containing all the commands using standard Apple developer tools that would be executed to modify your app.

An exception is the notarize subcommand, that currently does not support generating an .sh file.

`notarize.sh` equivalent
# pack to get ready for notarization
/usr/bin/ditto -v -c -k --keepParent \
    myapp.app myapp-tosign.zip

# send off to apple:
xcrun notarytool submit \
    --keychain-profile <your-keychain-profile> \
    --keychain <path-to-keychain> \
    --apple-id  <email-address-of-dev-account@provider.ext> \
    --team-id <your-team-id> \
    "myapp-tosign.zip"

# wait for notarization is complete
xcrun notarytool wait \
    --keychain-profile <your-keychain-profile> \
    --keychain <path-to-keychain> \
    --apple-id  <email-address-of-dev-account@provider.ext> \
    --team-id <your-team-id> \
    <notarization-request-id>

# once this is done, staple:
xcrun stapler staple myapp.app

Good reading material on the topic of signing/notarizing

What kind of issues does this package fix?

This package mostly manipulates the load commands of your Mach-O binaries using standard Apple developer tools such as install_name_tool, and vtool. To look at any of these load commands otool -l <dylib-path> is your friend.

Build versions and platform (LC_BUILD_VERSION)

Notarization requires platform, minos, and sdk versions to be set. In older binaries these can be partly missing.

Another requirement is that sdk version is newer or equal to 10.9. There is the --force-update flag that will at least result in a app passing notarization. We're still investigating if there's any downsides to this (for executables we found that they will not run, but libraries might work).

Dynamic library search paths (LC_RPATH)

These paths may not point outside the .app folder for notarization to be successful (except for /System/, /usr/, /Library/). app-pass tries do something sensible if these paths are absolute but point inside the app and replaces these with something relative to @loader_path, or @executable_path.

Some libraries have rpaths pointing outside the app. Do these even exist on your machine? The ones we found so far were artifacts of the build process and wouldn't exist on our machines. The option --rc-path-delete will delete these rpaths from libraries.

Linked dynamic libraries (LC_LOAD_DYLIB, LC_REEXPORT_DYLIB)

To pass notarization these paths may not be absolute, or point outside the app. app-pass will try to locate the libs within the .app and add a relative link.

Library ID (LC_ID_DYLIB)

This has to be a relative path inside the app. Will be fixed to @rpath/libname if found otherwise.

Metadata

Release files for app-pass 0.2.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for app-pass 0.2.2
File Size Uploaded Provenance Actions
app_pass-0.2.2.tar.gz 23.8 kB With provenance Details for app_pass-0.2.2.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for app-pass 0.2.2
File Interpreter ABI Platform Actions
app_pass-0.2.2-py3-none-any.whl Python 3 none any Details for app_pass-0.2.2-py3-none-any.whl

Total release size: 44.5 kB

Release files / app_pass-0.2.2.tar.gz

Download URL app_pass-0.2.2.tar.gz
Size 23.8 kB
Tags Source
SHA-256 checksum
How to use checksums
408fa81f32766f693668504522663d39dc23770ed06e737caa81b73749f5867b
BLAKE2b-256 checksum
How to use checksums
2cc5aad2c025a37c7cd0b8c2650d16bea9fa20ec0812005c811effe40b855ed1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Feb 24, 2026.

Transparency log

Release files / app_pass-0.2.2-py3-none-any.whl

Download URL app_pass-0.2.2-py3-none-any.whl
Size 20.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
cbdb2021176525e2f4ad339e22e5dbeaec012100c2cccde5a699f50156014c6b
BLAKE2b-256 checksum
How to use checksums
ea13c95aeb3df1c91248d41089d836e4aad40b8dc0e35b8415ec56bbce109db4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Feb 24, 2026.

Transparency log

Security for app-pass 0.2.2

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for app_pass-0.2.2.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Feb 24, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page