macOS 26 (Tahoe) Apple Provenance ve Gatekeeper Çözümü


Title: macOS 26 (Tahoe) Apple Provenance ve Gatekeeper Çözümü

[Edit: 24 Şubat 2026] - Bu makale, Gatekeeper'ın iç içe geçmiş (nested) binary dosyalarında yarattığı kilitlenmeler (deadlock) ve symlink sorunlarını içerecek şekilde güncellenmiştir.

macOS 26'ya (Tahoe) geçtikten sonra npx komutları bir süre sorunsuz çalışmaya devam edebiliyor. Sorun, yeni bir binary veya paket indirildiğinde ortaya çıkıyor. Örneğin brew upgrade node çalıştırdığınızda veya npx ile daha önce cache'lenmemiş bir paket indirdiğinizde. macOS, yeni indirilen dosyalara kernel seviyesinde com.apple.provenance etiketi yapıştırıyor ve Gatekeeper bu dosyaları sessizce engelliyor.

Sonuç olarak hata mesajı yok, çıktı yok, komut sonsuza kadar asılı kalıyor. curl çalışıyor, npm install çalışıyor, node -e "console.log('test')" bile çalışıyor. Ama npx ile bir paket çalıştırmaya kalktığınızda sadece yanıp sönen bir cursor görüyorsunuz.


Sorunun Kaynağı

npm debug loglarına bakınca paketlerin başarıyla indirildiği görülüyor:

13 http fetch GET 200 https://registry.npmjs.org/vibe-kanban 246ms (cache revalidated)
14 silly packumentCache heap:4345298944 maxSize:1086324736
... (ve sessizlik)

Paket indiriliyor, cache'e yazılıyor ama çalıştırılmıyor.

Aynı dosyayı iki farklı shebang ile çalıştırınca fark ortaya çıkıyor:

# #!/usr/bin/env node → asılı kalıyor
$ ./node_modules/.bin/cowsay "test"
# ❌ Sonsuza kadar bekliyor

# #!/usr/local/bin/node → çalışıyor
$ ./node_modules/.bin/cowsay "test"
# ✅ Sorunsuz

# Doğrudan node ile → çalışıyor
$ node node_modules/cowsay/cli.js "test"
# ✅ Sorunsuz

Neden Oluyor?

macOS system loglarında (syspolicyd) şu kayıtlar var:

syspolicyd: rejecting due to lack of matching active rule
syspolicyd: Error checking with notarization daemon: 3
syspolicyd: MacOS error: -67002

Olan şey şu:

  1. npm/npx paketi indiriyor → macOS kernel seviyesinde dosyaya com.apple.provenance etiketi yapıştırıyor
  2. npx indirdiği paketin binary'sini #!/usr/bin/env node shebang'i ile çalıştırmayı deniyor
  3. macOS bu dosyayı çalıştırmadan önce syspolicyd'ye soruyor
  4. syspolicyd Apple sunucularına bağlanıp notarization kontrolü yapıyor
  5. Binary adhoc signed olduğu için notarization bulamıyor ve sessizce reddediyor

#!/usr/bin/env node dediğinizde macOS env programı aracılığıyla dolaylı bir çalıştırma yapıyor ve bu Gatekeeper kontrolünü tetikliyor. #!/usr/local/bin/node dediğinizde ise doğrudan binary'ye gidiyor, dolaylı kontrol devre dışı kalıyor.


Homebrew Node da Etkileniyor

brew upgrade node çalıştırınca durum daha da kötüleşiyor. Yeni indirilen Node.js binary'si de com.apple.provenance alıyor ve node -v bile çalışmaz hale geliyor:

$ spctl -a -v /opt/homebrew/Cellar/node/25.6.1/bin/node
/opt/homebrew/Cellar/node/25.6.1/bin/node: rejected

$ codesign -dv /opt/homebrew/bin/node
Signature=adhoc  # Apple notarized değil

Denediğimiz klasik çözümlerden hiçbiri işe yaramadı:

Denenen Sonuç
xattr -cr ile provenance kaldırma Kernel seviyesinde, bazen kaldırılamıyor veya Apple imzalarını bozuyor
sudo xattr -dr com.apple.provenance Permission denied veya etkisiz
sudo spctl --add --label "Homebrew" "This operation is no longer supported"
sudo spctl --master-disable "Needs to be confirmed in System Settings" (buton yok)
codesign --force --sign - Yine adhoc, hala rejected
System Settings → Allow Anyway Seçenek görünmüyor

[!CAUTION]
Kritik Hata: syspolicyd Kilitlenmesi (Deadlock)
Çözüm arayışında syspolicyd arka arkaya hata verirse veya xattr ile dosyalardaki etiketleri agresif silmeye çalışırsanız, macOS Gatekeeper servisi (syspolicyd) tamamen kilitlenebiliyor (deadlock). Bu olduğunda sistem genelindeki tüm Node komutları (hatta sağlam dosyalar bile) sonsuza kadar asılı kalır. Eğer her yolu denediğiniz halde terminal kilitlenmeye devam ediyorsa servisi acilen şu komutla yeniden başlatmalısınız:

sudo killall -9 syspolicyd

Kapsamlı Çözüm

Gerçek ve kalıcı bir çözüm için üç adımı da sırayla uygulamak şart:

1. Node.js'i Resmi Tarball ile Kurmak

Homebrew'un Node.js'i adhoc signed olduğu için macOS 26 tarafından reddediliyor. Resmi tarball ile kurulum çalışıyor:

# Homebrew node'u kaldır
brew uninstall node

# Resmi tarball indir
curl -L -o /tmp/node.tar.gz \
  "https://nodejs.org/dist/v22.22.0/node-v22.22.0-darwin-arm64.tar.gz"

# /usr/local'e kur
sudo tar -xzf /tmp/node.tar.gz -C /usr/local --strip-components=1

# Test
node -v  # v22.22.0 ✅
npm -v   # 10.9.4 ✅

Not: .pkg installer'ı da denedik, o da asılı kalıyor. sudo installer -pkg komutu "Installing at base path /" mesajından sonra sonsuza kadar bekliyor. macOS'un installd daemon'u da aynı notarization kontrolüne takılıyor. Tarball + tar extract bu kontrolü bypass ediyor.

2. İçiçe Geçmiş Binary'lerin Karantinalarını (Provenance) Temizlemek

Sadece node'u düzeltmek yetmeyebilir. Özellikle claude veya popüler global framework'ler, içlerinde derlenmiş (C++/Rust) ikincil araçlar barındırırlar (örneğin uçlarda çalışan ripgrep). Bu araçlara bulaşan com.apple.provenance etiketi, Gatekeeper'ın Child Process (Alt İşlem) kalkanına çarpar. Ana Node uygulamanız düzgün çalışsa bile, altındaki araç engelleneceği için ana uygulamanız bu yüzden kilitlenir.

Bu etiketleri global klasörden topluca silmek için şu komutla süpürme yapın:

sudo xattr -r -d com.apple.provenance $(npm config get prefix -g) 2>/dev/null

3. Tüm Shebang'leri ve Symlink'leri Düzeltmek

Node çalışsa bile npm/npx'in indirdiği her paket #!/usr/bin/env node shebang'i kullanıyor. Global npm bin klasöründe dosyalar genelde symlink olduğu için klasik sed komutları başarısız olur ve hatayı gizler. Asıl dosyanın realpathini bulup değiştirmemiz gerekir.

Önceki scriptin symlink destekleyen son halini kullanın:

#!/bin/bash
# fix-node-shebangs: macOS 26 Tahoe shebang & symlink fix
# #!/usr/bin/env node → #!/usr/local/bin/node

NODE_PATH=$(which node)

fix_shebangs() {
  local dir="$1"
  [ -d "$dir" ] || return
  local count=0
  
  find "$dir" -type f -o -type l 2>/dev/null | while read -r f; do
    # Eğer dosya sembolik link (kısayol) ise asıl hedef dosyayı bul
    local real_f=$(realpath "$f" 2>/dev/null || echo "$f")
    
    if head -1 "$real_f" 2>/dev/null | grep -q '#!/usr/bin/env node'; then
      sed -i '' "1s|#!/usr/bin/env node|#!${NODE_PATH}|" "$real_f"
      count=$((count + 1))
    fi
  done
  [ $count -gt 0 ] && echo "Fixed $count shebangs in $dir"
}

# Global npm bin
fix_shebangs "$(npm config get prefix -g)/bin"

# npx cache
for d in ~/.npm/_npx/*/node_modules/.bin; do
  fix_shebangs "$d"
done

Bu scripti /usr/local/bin/fix-node-shebangs olarak kaydedin, sudo chmod +x ile çalıştırılabilir yapın ve ileride her npx yüklemesinde asılı kalma hissettiğiniz an çalıştırın.


Teşhis

Eğer npx komutlarınız hata vermeden asılı kalıyorsa, aşağıdaki komutla syspolicyd loglarını kontrol edin:

log show --last 2m --predicate 'process == "syspolicyd"' --style compact | grep reject

Şu çıktıyı görüyorsanız bu sorundan etkileniyorsunuz:

syspolicyd: rejecting due to lack of matching active rule

(Loglarda hiçbir şey görmüyor ama kilitlenme yaşıyorsanız syspolicyd daemon'u deadlock olmuş demektir. Çözüm: sudo killall -9 syspolicyd)


Özet

Sorun macOS 26'da npx komutları sessizce asılı kalıyor
Neden Gatekeeper, #!/usr/bin/env node shebang'li provenance-tagged dosyaları reddediyor
Belirtiler Hata yok, çıktı yok, sonsuz bekleme (Deadlock)
Çözüm 1 Node.js'i resmi tarball ile kurmak (.pkg değil, tar.gz)
Çözüm 2 Çökmüş/Asılı kalmış syspolicyd servisini sıfırlamak (killall)
Çözüm 3 Shebang'leri symlinkleri de (realpath) kapsayacak şekilde düzeltmek
Etkilenen Homebrew Node.js, tüm npx paketleri, npm global install'lar

macOS'un güvenlik katmanları her sürümde daha da sıkılaşıyor. macOS 26 ile adhoc signed binary'ler Gatekeeper tarafından sessizce reddediliyor ve klasik workaround'lar (xattr -cr, spctl --add, spctl --master-disable) artık çalışmıyor. Çözüm: resmi notarized binary'leri kullanmak, daemon hatalarına karşı tetikte olmak ve #!/usr/bin/env yerine doğrudan binary path'lerini kullanmak.

Subscribe to DevOps TR

Don’t miss out on the latest issues. Sign up now to get access to the library of members-only issues.
jamie@example.com
Subscribe