"ad-hoc 요구사항엔 코드 해시가 들어 있어. 그래서 네이티브 코드를 바꿔서 다시 빌드하면, 경로가 같아도 예전 권한이 무효가 될 수 있어." — Tinker README
권한은 누가 받아
테스트용 앱을 진짜로 숨기려면 랩엔 Accessibility 권한을 가진 프로세스가 필요해. 끌리는 지름길은 테스트를 돌리는 쪽, 이를테면 공용 Python 실행기한테 권한을 주는 거야. Tinker README는 딱 그걸 금지해. 범용 인터프리터한테 준 권한은 그게 앞으로 돌릴 모든 스크립트한테 준 권한이니까. 대신 랩은 자기 번들 ID와 자기 AppKit 이벤트 루프를 가진 전용 GUI 앱 Tinker Lab Check를 빌드하고, 그 앱한테만 권한을 줘. 아빠가 랩 Mac 시스템 설정의 개인정보 보호 및 보안 › 손쉬운 사용에서 직접 넣어. 개인정보 데이터베이스를 고치는 사람은 없고, permission_needed 영수증을 통과로 세는 일도 없어.
macOS가 앱에 대해 기억하는 것
macOS 개인정보 시스템(TCC)은 권한을 파일 경로로 기억하지 않아. 앱의 지정 요구사항을 기억해. 코드 서명이 들고 다니는 작은 식인데, 앞으로 어떤 버전이 같은 앱으로 쳐지려면 뭘 만족해야 하는지 적혀 있어. 진짜 인증서로 서명한 앱이면 요구사항이 번들 ID와 인증서를 이름으로 대니까, 다시 빌드해서 다시 서명해도 권한이 유지돼. Tinker Lab Check는 ad hoc으로 서명돼. 이름 댈 인증서가 없으니까 요구사항이 코드 해시 자체를 고정해. 소스를 고치고 다시 빌드하면 TCC한테는 다른 앱이야. 경로도 같고, 이름도 같고, 설정 화면엔 여전히 켜져 있는 걸로 보이는데도.
거기서 자라난 레시피
그래서 랩 스크립트엔 모드가 둘이야. --build는 도우미와 테스트용 앱을 빌드하고, Info.plist를 쓰고, ad hoc으로 서명하고, 돌려. --run-built는 있는 번들의 서명을 검사하고, 기록용으로 지정 요구사항과 실행 파일의 SHA-256을 찍고, 바이트 하나 다시 빌드하거나 서명하지 않고 돌려. 일하는 순서는 이래. 한 번 빌드하고, 아빠가 바로 그 번들에 권한을 주고, 네이티브 코드가 진짜로 바뀔 때까진 늘 --run-built. 바뀌면 일부러 다시 빌드하고 권한을 다시 부탁할 걸 각오해.
켰는데 안 먹힌 토글
한 번은 더 꼬였어. 다시 빌드한 뒤 아빠가 설정에서 도우미를 켰는데 AXIsProcessTrusted()가 계속 false였어. 시스템 로그를 보니 TCC는 새 해시에 'Allowed' 갱신을 적어 놓고, 접근 검사는 계속 옛 해시를 보고 있었어. 고친 건 뭘 초기화하는 게 아니었어. 아빠가 손쉬운 사용 목록에서 Tinker Lab Check 하나만 빼고 같은 현재 번들을 다시 넣었고, 안 바뀐 도우미가 그다음 통과했어. README는 교훈을 정확히 적어 둬. 다른 앱 권한은 초기화하지 말고, 그 재등록과 재시도 사이엔 다시 빌드하지 마. 안 그러면 처음으로 돌아가.