Bits of Simplicity

Tag: #YARN

YouTube Music Desktop App MacOS Code Signature Issues

Published

A while back I ran into an issue with the macOS version of YouTube Music Desktop App. Starting with v2.0.9, YTMDesktop became unresponsive and began skipping tracks. Unfortunately, v2.0.8 stopped working entirely due to changes on Google’s end.

The issue appeared to affect only ARM64 macOS builds, and only the official release binaries. Cloning the repository and running the project in development mode resolved the issue. However, when running yarn make, the resulting app refused to open due to code signing errors.

I don’t have an Apple Developer ID, and Gatekeeper won’t run improperly signed or unsigned code. The solution was to strip the invalid signatures and apply an ad-hoc signature locally.

An ad-hoc signature is a local code signature generated without an Apple certificate. It satisfies macOS runtime checks but does not provide notarization or Developer ID trust.

The YTMDesktop developers (understandably) don’t want to maintain an Apple Developer ID for a free, open-source project[0], so I wrote a bash script[1] to automatically re-sign the app after each update.

Use at your own risk. This process removes certain macOS security protections and replaces the existing signature with a local ad-hoc signature.


#!/usr/bin/env bash
set -euo pipefail

usage() {
  cat <<EOF
Usage: $(basename "$0") [options] [app-path]

Ad-hoc codesign a macOS Electron app with JIT entitlements.

Arguments:
  app-path    Path to the .app bundle (default: /Applications/YouTube Music Desktop App.app)

Options:
  -h, --help  Show this help message
EOF
  exit 0
}

[[ "${1:-}" == "-h" || "${1:-}" == "--help" ]] && usage

DEFAULT_APP="/Applications/YouTube Music Desktop App.app"
APP="${1:-$DEFAULT_APP}"
ENTS="$(mktemp /tmp/electron-jit.entitlements.XXXXXX.plist)"
trap 'rm -f "$ENTS"' EXIT

cat > "$ENTS" <<'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-jit</key>
  <true/>
  <key>com.apple.security.cs.allow-unsigned-executable-memory</key>
  <true/>
  <key>com.apple.security.cs.disable-library-validation</key>
  <true/>
</dict>
</plist>
PLIST

if [[ ! -d "$APP" ]]; then
  echo "Error: App bundle not found: $APP" >&2
  exit 1
fi

echo "Signing: $APP"

# (a) Remove quarantine if downloaded
xattr -r -d com.apple.quarantine "$APP" 2>/dev/null || true

# (b) Sign all nested Mach-O files (frameworks, dylibs, helpers) first
#     We apply --options=runtime and the entitlements everywhere to keep AMFI happy.
while IFS= read -r -d '' f; do
  if file -b "$f" | grep -qE 'Mach-O.*(executable|dynamically linked shared library)'; then
    echo "  Signing binary: $f"
    codesign --force --sign - --timestamp=none \
      --options=runtime \
      --entitlements "$ENTS" \
      "$f"
  fi
done < <(find "$APP/Contents" -type f -print0)

# (c) Sign helper app bundles (wrappers)
while IFS= read -r -d '' helper; do
  echo "  Signing helper app: $helper"
  codesign --force --deep --sign - --timestamp=none \
    --options=runtime \
    --entitlements "$ENTS" \
    "$helper"
done < <(find "$APP/Contents/Frameworks" -type d -name "*.app" -maxdepth 1 -print0)

# (d) Sign top-level Frameworks that are bundles (Electron Framework, etc.)
while IFS= read -r -d '' fw; do
  echo "  Signing framework: $fw"
  codesign --force --deep --sign - --timestamp=none \
    --options=runtime \
    --entitlements "$ENTS" \
    "$fw"
done < <(find "$APP/Contents/Frameworks" -type d -name "*.framework" -maxdepth 1 -print0)

# (e) Finally, sign the main app bundle
codesign --force --deep --sign - --timestamp=none \
  --options=runtime \
  --entitlements "$ENTS" \
  "$APP"

# (f) Verify (deep + strict)
echo "Verifying..."
codesign --verify --deep --strict --verbose=4 "$APP"
spctl --assess --type execute --verbose=4 "$APP" 2>&1 || echo "  (spctl rejection is expected for ad-hoc signed apps — Gatekeeper requires a Developer ID)"

echo "Done."

[0] https://github.com/ytmdesktop/ytmdesktop/issues/1624#issuecomment-3574588497

[1] https://gist.github.com/bhhaskin/5e0f2ab3914a5a1aefb94cf715923240

Categories: