macOS 관리 (nix-darwin)
nix-darwin 및 macOS 시스템 설정 가이드입니다.
빠른 참조
macOS 전용 nrs 옵션
nrs --force # activation scripts 강제 재실행 (NO_CHANGES 스킵 우회)
nrs/nrp 소스:
modules/shared/scripts/rebuild-common.sh,modules/darwin/scripts/{nrs,nrp}.sh
주요 설정 파일
| 파일 | 용도 |
|------|------|
| modules/darwin/configuration.nix | macOS 시스템 설정 |
| modules/darwin/home.nix | Home Manager (macOS) |
| modules/darwin/programs/ | macOS 전용 프로그램 |
Nix CLI 패키지 (darwin-only)
libraries/packages.nix의 darwinOnly 리스트에서 관리:
# 패키지 추가
darwinOnly = [ ... pkgs.패키지명 ];
자세한 내용: references/features.md
macOS 시스템 설정
| 설정 | 파일 | 설명 |
|------|------|------|
| Dock | configuration.nix | 자동 숨김, 크기, 최근 앱 |
| Finder | configuration.nix | 숨김 파일, 확장자, 네트워크 .DS_Store 방지 |
| 키보드 | configuration.nix | 키 반복 속도, F1-F12 표준 기능키 |
| 마우스/스크롤 | configuration.nix | 자연스러운 스크롤 비활성화 (트랙패드 탭 클릭 설정은 없음) |
TCC / Files & Folders / Full Disk Access
앱 설치와 일반 preference는 Nix로 관리할 수 있지만, macOS TCC decision은 별도 host
state다. nrs나 defaults가 Files & Folders 또는 Full Disk Access를 부여한다고
간주하지 않는다.
- 수동 정책: System Settings에서 운영자가 승인·rollback하고 실제 launcher E2E로 확인한다.
- managed 정책: Apple PPPC payload를 사용할 수 있지만 user-approved MDM enrollment와
MDM delivery가 필요하다. 독립
.mobileconfig수동 설치로 PPPC grant를 우회하지 않는다. - sandbox app preference를 읽고 쓰는
defaults도SystemPolicyAppData를 유발할 수 있으므로 remote activation 경로에서는 outer deadline과 kill-after를 둔다. - 금지: TCC DB 직접 수정, SIP 비활성화, 무차별
tccutil reset.
identity 확인, bounded fixture, rollback, update/reboot 재검증 절차는 references/tcc.md를 따른다.
Homebrew 관리
modules/darwin/programs/homebrew.nix에서 선언적으로 관리됩니다.
공통(모든 darwin 호스트: ghostty cask, awscli·playwright-cli formula)과 personal 전용(hostType == "personal" 가드)으로 분리되어 있습니다.
awscli는 nixpkgs darwin libffi가 macOS 27에서 깨져 Nix 경로가 실행 불가라 임시로 Homebrew에 둔 예외입니다 (이슈 #1194, 환원 조건은 homebrew.nix 주석 참조).
# cleanup = "none" — 선언되지 않은 앱을 삭제하지 않음 (수동 설치 cask 보호)
# upgrade = true + greedyCasks = false — 자체 업데이터 cask는 brew upgrade 강제 대상에서 제외
# personal 전용 예:
homebrew.casks = [
"raycast" "rectangle"
"hammerspoon" "homerow"
"fork" "monitorcontrol" "whatcable" "1password" "1password-cli"
];
homebrew.brews = [ "laishulu/homebrew/macism" "sox" ]; # Neovim 한영 전환, 오디오 처리
# shottr → Nix 패키지로 관리 (libraries/packages.nix darwinOnly)
# codex → Nix 관리로 전환 (nix overlay, modules/shared/programs/codex)
# figma → Homebrew에서 제거 (자체 업데이터가 버전을 변경하여 adopt 시 버전 충돌)
# slack → Homebrew에서 제거 (수동 설치 선호, 자체 업데이터에 위임)
새 Mac 세팅 시: nrs가 cask 설치 때 자동으로 adopt를 시도하며, 번들 버전 충돌 등으로 실패할 때만 brew install --cask --adopt <앱>을 수동 실행.
자세한 내용: references/features.md
Shottr 선언 관리 (Nix + agenix)
Shottr 설정과 라이센스를 선언적으로 관리합니다.
| 파일 | 용도 |
|------|------|
| modules/darwin/programs/shottr/default.nix | Shottr 앱 고유 설정 + 라이센스 pre-fill |
| modules/darwin/configuration.nix | symbolic hotkeys (스크린샷 단축키) + activateSettings + Shottr 재시작 |
| libraries/constants.nix | Shottr 기본 저장경로 상대 경로 상수 |
| secrets/shottr-license.age | agenix 암호화 라이센스 키 (KC_LICENSE + KC_VAULT) |
| modules/shared/programs/secrets/default.nix | ~/.config/shottr/license 배포 |
아키텍처 노트: symbolic hotkeys와 Shottr 재시작은
configuration.nix의 postActivation에서 처리합니다. HM activation의activateSettings -u가launchctl asuser + sudo컨텍스트에서 WindowServer와 통신하지 못하기 때문입니다.
운영 순서:
# 설정 적용 (라이센스 pre-fill 포함)
nrs
# 새 맥북: Shottr 실행 후 Activate 버튼 1회 클릭
크레덴셜 이중 저장 구조(Keychain + defaults), HM activation 주의사항, defaults 테스트 절차 상세: references/shottr-credentials.md
Folder Actions (launchd WatchPaths)
modules/darwin/programs/folder-actions/default.nix에서 launchd WatchPaths 기반 폴더 감시 자동화를 관리합니다.
| 액션 | 감시 폴더 | 용도 |
|------|-----------|------|
| compress-rar | ~/FolderActions/compress-rar | RAR 압축 |
| compress-video | ~/FolderActions/compress-video | FFmpeg 비디오 압축 |
| rename-asset | ~/FolderActions/rename-asset | 파일 이름 변경 |
| convert-video-to-gif | ~/FolderActions/convert-video-to-gif | FFmpeg 비디오→GIF 변환 |
| upload-immich | Shottr 스크린샷 폴더 | Immich 자동 업로드 (personal 전용) |
로그: ~/Library/Logs/folder-actions/
upload-immich는 Nix가 선언한 bun·mise 실행 도구를 사용하고 Node는 기존 mise 설정으로
선택한다. 새 personal Mac에서는 설정을 확인한 뒤 mise install로 Node를 먼저 설치한다.
launchd와 스크립트는 MISE_EXEC_AUTO_INSTALL=false로 자동 설치를 끄며, Node가 없거나
실행되지 않으면 환경 오류를 기록하고 원본·사이드카를 보존한다. activation에서 mise 설치를
실행하지 않는다.
1Password 단축키
개인/업무 Mac 공통으로 modules/darwin/programs/onepassword가 빠른 액세스
⌃⌥⌘Space와 자동 채우기 ⌃⌥⌘\를 관리한다. 값과 설정 파일의 home 상대 경로는
libraries/constants.nix의 onePassword에 있다. 앱 설치 정책은 그대로 personal 전용이다.
nrs는 기존 settings.json에서 두 단축키만 갱신하며 나머지 설정·보안 서명을 보존한다.
미설정 앱의 파일을 새로 만들지 않는다. 앱 실행 중에는 SSH agent를 중단하지 않고 갱신을
미루므로, 1Password를 완전히 종료한 뒤 onepassword-shortcuts를 실행하고 다시 연다.
이미 선언값과 같으면 실행 중에도 파일을 재작성하지 않는다.
helper 실행 중에는 앱을 종료한 상태로 유지하고 다른 도구로 같은 파일을 편집하지 않는다.
교체 직전 재비교는 관측된 변경을 감지하지만 원자적 비교·교체를 보장하지는 않는다.
macOS AppData 권한은 별도다. 접근 거부/timeout은 적용 성공이 아니다. helper는 권한 거부
(EPERM/EACCES)·JSON 손상·symlink·교체 중 변경을 서로 다른 문구로 stderr에 보고하고,
shortcuts-wrapper.sh는 124를 "AppData TCC prompt 가능성"이 있는 timeout으로 안내한다.
137은 timeout kill-after와 외부 SIGKILL을 구분할 수 없으므로 원인을 단정하거나 권한 변경을
권하지 않는다 (#1558). 권한 거부의 실행 주체는 helper를 띄운 셸의 TCC 책임 프로세스다 — 보통 터미널 앱이지만,
Codex App이나 Claude Remote Control처럼 launchd 계열 launcher에서 실행했다면 그 launcher다.
기본 복구는 승인된 Ghostty direct 셸에서 재시도한다. 원격 launcher 접근이 필요한 경우에는
Mac 앞에서 정확한 launcher에 대해 수동 승인·검증 절차를 별도로 따른다.
책임 프로세스 확인 명령, App Data/Full Disk Access 승인 경로, 정책(#1093 C+D)은
references/tcc.md가 소유한다. 승인 뒤에도 거부가 계속되면 파일·디렉터리의
mode/ACL(ls -le)도 확인한다. 설정 파일 전체를 Nix store에 복사하거나
symlink로 대체하지 않는다. 검증은 python3 tests/onepassword-shortcuts-tests.py,
bash tests/run-shell-script-tests.sh(wrapper 스위트 포함)와 양쪽 Darwin 평가를 사용하고,
실제 적용 뒤에는 1Password 설정 UI·HomeRow·Figma의 키 동작을 확인한다.
자주 발생하는 문제
- sudo 권한 필요: darwin-rebuild는 시스템 파일 수정에 sudo 필요
- /etc/bashrc 충돌: 기존 설정 파일과 nix-darwin 충돌
- 스크롤 방향 롤백: cfprefsd 재시작 시 설정 초기화
레퍼런스
- 트러블슈팅: references/troubleshooting.md
- 기능 목록: references/features.md