같은 목록, 거꾸로 읽기
마이크를 허가해 주는 그 카탈로그가 울타리도 쳐. Mac에선 신뢰를 설치하는 바로 그 코드(레슨 4)가 확장 호스트한테 가족 호스트 묶음을 넘겨. Settings의 허브와 피파 호스트, 그리고 카탈로그 웹 대상의 호스트 전부. 확장 호스트는 거기에 어느 Mac에서나 가족인 패턴 셋을 보태. *://localhost/*, *://127.0.0.1/*, *://*.ts.net/*. 그러고는 확장이 뭘 요청했든 상관없이, 설치된 확장 전부한테 이걸 명시적으로 거부해. 첫 번째 블록이 그 장치야.
잘 돌아가게 하는 디테일이 셋 있어. 거부한 패턴이 허가한 패턴을 이겨. 2026-10-04에 <all_urls>를 요청한 패키지로 재 봤더니, 거부된 호스트에 대해 WebKit의 hasAccess가 아니라고 답했어. 그래서 확장은 다른 데서 받은 건 전부 그대로 가져. 확장 호스트는 확장마다 어떤 가족 패턴을 거부했는지 정확히 기억해. 그래서 목록이 바뀔 때 그것만 지우고, 확장 자기 패턴은 안 건드려. 그리고 Settings에서 확장 하나를 id로 들여보낼 수 있어. 그러면 아빠가 되돌릴 때까지 가족 사이트가 그 확장한테 열려. 테스트는 모든 사이트를 허가받은 패키지를 설치해. 그리고 example.com엔 닿지만 loopback, ts.net 호스트, 카탈로그 호스트엔 못 닿는지 확인해. 이어서 카탈로그가 나중에 적은 호스트를 더하고, 확장을 들여보냈다가 다시 내보내 봐.
이유는 실제로 재 본 실패였어. 1Password 확장이 cwkPippa 위에서 돌 때, 그 콘텐츠 스크립트가 navigator.credentials를 가로챘어. 그래서 데스크톱 연결이 열려 있는 동안 Touch ID 패스키를 저장할 수 없었어(876ea6a, 2026-10-04). 1Password 얘기 나머지는 트랙 8이 하고, Chrome 확장 모델 자체는 Pippa Embed Quest가 직접 만들어 보면서 가르쳐.
막을 땐 넓게, 열 땐 좁게
가족 목록의 두 쓰임새를 나란히 놓고 보면, 틀리는 방향이 서로 반대야.
| 마이크와 카메라 허가 | 확장 울타리 | |
|---|---|---|
| 단위 | 정확한 출처: 스킴, 호스트, 포트 | 호스트 패턴: 스킴은 http나 https(WebKit의 * 스킴은 이 둘만 맞춰), 포트는 아무거나 |
| 테일넷 | 카탈로그나 Settings에 적힌 출처만 | *.ts.net 호스트 전부 |
| loopback | loopback 표기 전부 | localhost와 127.0.0.1 |
| 너무 넓으면 생기는 일 | 아무도 보증 안 한 페이지가 아빠 목소리를 들어 | 확장 하나가 가족 사이트를 못 써 |
울타리는 tailscaled한테 안 묻고도 *.ts.net을 통째로 막을 수 있어. 이름 패턴만으로도 막기엔 충분하지만, 들여보내기엔 부족하거든. 그래서 허가는 정확한 출처를 기다려. 두 목록 다, 틀려도 자기가 지키는 것한테 덜 비싼 쪽으로 틀리는 거야.
오늘의 "이름으로"
아키텍처는 주소창이 URL, 검색어, 가족 앱 이름, 피파한테 하는 질문을 받는다고 적어. 넷 중 둘만 만들어졌어. AddressResolver 맨 위 주석은 2단계에서 가족 앱 이름을 그 둘보다 앞에 세운다고 해. 하지만 resolve(_:)가 아는 건 완전한 스킴, 호스트처럼 읽히는 글자, 검색 템플릿뿐이야. 주소창도, 그 제안 목록도, 툴바도 카탈로그를 안 봐. 띄어쓰기 없는 단어 하나는 검색어라서, 지금 주소창에 가족 앱 이름을 치면 그 단어로 웹 검색을 해. 피파한테 하는 질문도 주소창에선 아직 안 돼. 계획엔 둘 다 열린 항목으로 남아 있고, 아빠는 아직 안 만들어진 걸로 가르치라고 정했어.
오늘 "이름으로"는 두 군데야.
- Mac에선 네이티브 Home의
Find a family app(가족 앱 찾기) 칸이야. 아빠가 치는 대로 카탈로그 그리드를 걸러. 대상의 제목과 라벨을 같이 보고, 대소문자는 안 가려. - iPhone과 iPad에선 가족 앱을 이름으로 보여주는 홈 화면 위젯.
주소를 안 들고 있는 위젯
폰 앱은 위젯용 선반을 따로 둬. 선반엔 웹 대상마다 카탈로그 id, 제목, 즐겨찾기 여부가 들어가고, 카탈로그 리비전과 읽은 시각도 같이 들어가. 주소는 절대 없어. 선반은 키트의 가족 문서 저장소를 거쳐, 위젯과 같이 쓰는 앱의 팀 접두사 keychain 그룹에 살아. 이런 데엔 보통 App Group을 써. 그런데 가족의 키 기반 TestFlight 파이프라인으론 새 App Group을 등록할 수 없고, keychain 그룹은 이미 모든 프로파일에 들어 있거든.
타일은 링크야. cwkvoyager://family/<target id>(두 번째 블록). 위젯은 어디에도 요청을 안 보내. 타임라인 정책은 .never고, 선반의 앱 목록이 바뀔 때만 앱이 다시 불러. 작은 크기는 4개, 중간은 8개, 큰 크기는 16개를 즐겨찾기부터 보여줘.
타일을 누르면 앱이 이미 들고 있는 카탈로그에 대고 id를 풀어(세 번째 블록). 목록에 없거나 아직 카탈로그를 한 번도 안 읽었으면 먼저 읽어. 그러고 나서 그 카탈로그에 적힌 주소를 열어. 같은 출처 탭이 이미 있으면 새로 안 열고 그 탭을 앞으로 가져와. 다시 읽어도 없으면 Firelink가 이제 그 앱을 안 싣는다고 솔직하게 말해. 그래서 가족이 앱을 다른 Mac으로 옮기면 Firelink의 포인터가 바뀌고, 홈 화면은 하나도 안 바뀌어. 탭이 새 주소를 따라가는 건 앱이 카탈로그를 다시 읽은 뒤야. 앱이 앞으로 나올 때 읽는데, 1분에 한 번까지만. 그 읽기보다 먼저 들어온 탭이나 허브에 닿을 수 없을 때 누른 탭은 옛 카탈로그에 적힌 주소를 그대로 열어. 목록에 있는 id는 새로 읽을 때까지 기다려 주지 않거든.
폰 앱이 선언하는 스킴은 그거 하나야. iOS에선 Voyager가 기본 브라우저가 아니라서(아빠, 2026-10-26) http도 https도 안 잡아. 테스트 함정 하나. simctl openurl은 항상 Open in Voyager?(Voyager에서 열까?) 확인 창에서 멈추는데, 폰에서 위젯을 누를 땐 그런 창이 안 떠. 그래서 링크 경로는 앱의 호스티드 테스트가 지키고, 거기서 테스트 7개가 링크와 선반을 다뤄.