Skip to content

Building dictate.app

dictate is designed to be run from source. This guide is for power users who want their own .app bundle for personal use; signed-and-notarised public distribution is intentionally out of scope (see the FAQ).

Quick build (development, unsigned)

For local use without signing:

pip install py2app
./scripts/build_app.sh --alias
open dist/dictate.app

Alias mode is much faster and symlinks back to your source, useful while iterating. For a real bundle that's portable to other machines, drop --alias.

The unsigned .app will show a Gatekeeper warning on first launch. Right-click → Open to bypass it. This is fine for personal use; if you want a signed bundle for your own machine, see the optional section below.

Optional: signing your own bundle

One-time setup

  1. Enroll in the Apple Developer Program ($99/year): https://developer.apple.com/programs/
  2. Download a "Developer ID Application" certificate from the developer portal and install it in Keychain Access.
  3. Generate an app-specific password at https://appleid.apple.com and store it:
    xcrun notarytool store-credentials AC_PASSWORD \
      --apple-id "you@example.com" --team-id "YOURTEAMID"
    

Per-release build

# 1. Build
./scripts/build_app.sh --clean

# 2. Sign
export DEVELOPER_ID="Developer ID Application: Your Name (TEAMID)"
./scripts/sign_app.sh

# 3. Notarize + staple
./scripts/notarize_app.sh

# 4. Package for distribution
# Wrap into DMG using create-dmg:
#   brew install create-dmg
#   create-dmg --volname dictate --window-size 500 300 \
#     --icon dictate.app 125 150 --app-drop-link 375 150 \
#     dist/dictate-0.1.0.dmg dist/dictate.app

Verifying the bundle

# Signature
codesign --verify --deep --verbose=2 dist/dictate.app

# Notarization (after stapling)
xcrun stapler validate dist/dictate.app

# Gatekeeper acceptance
spctl -a -vvv -t install dist/dictate.app

Common gotchas

  • py2app + faster-whisper / onnxruntime: these C-extension heavy deps sometimes fail to bundle. If you hit issues, try alias mode first to confirm the app boots, then experiment with the includes/excludes lists in setup_app.py.
  • pyobjc framework discovery: py2app sometimes misses pyobjc framework subpackages. Add them explicitly to includes if you see import errors at runtime.
  • Code signing fails on nested binaries: use --deep for the codesign call (already in sign_app.sh).
  • Notarization rejects: read the JSON log via xcrun notarytool log <submission-id> --keychain-profile AC_PASSWORD: usually a missing entitlement.
  • App launches but hotkey doesn't work: the bundle needs Accessibility + Input Monitoring permissions. Grant via System Settings → Privacy & Security.

Roadmap

dictate stays self-hosted by design. There is no plan to ship a signed DMG, Homebrew cask, or App Store listing. This guide is the supported path.