이사 사흘 전의 판정
2026-10-19, 가족을 늘 쓰던 기계가 아닌 곳에서 돌리는 방법을 다룬 설계 메모 하나가 코드를 안 고치고는 바꿀 수 없는 곳 여덟 군데를 꼽았어. 상수로 박힌 주소, 홈 폴더에서 끌어낸 데이터 루트, 이름 붙은 기계 하나 말고는 아예 안 뜨겠다는 검사 같은 것들이야. 그리고 물었지. 그 자리마다 특별한 이음매를 따로 달아야 하냐고. 주인은 질문부터 틀렸다고 했어.
아니지. 그건 CWK 피파 자체가 설계 잘못이지. 왜 그걸 하드 코딩을 하는데? Config 같은 설정 파일로 빼냈어야 하는 거지 그걸 왜 상수로 고정을 시켜? — 아빠
이 판정이 cwkPippa 할 일 #549 가 됐어. 호스트, 주소, 데이터 루트는 전부 설정으로 빼고, 정식 출처는 하나만 두고, 기본값은 전날과 똑같이 두기로 했어. Beacon 몫은 그날 저녁부터 밤사이에 들어갔어. 정식 제작물을 지키는 Mac 은 이제 철자로 적힌 이름이 아니야. 가족 포트 장부가 cwkBeacon 을 어디에 두느냐로 정해져. 데이터 루트, 여행 Mac 의 이름표, ssh 너머에서 쓰는 체크아웃과 인터프리터, launchd 파일 속 엔진 포트, 작업지시서 템플릿도 줄줄이 따라갔어. 포트는 이제 설치할 때 채워지는 토큰 @@PORT:cwkBeacon@@ 이야. 호스트 프로필이 없으면 값은 전부 전과 똑같이 나오고, 프로필이 있으면 코드 한 줄 안 고치고 움직여.
그래도 깨진 것
사흘 뒤인 2026-10-22, 피파 머신이 자기만의 Mac 으로 옮겨 갔어. 장부의 역할이 바뀌자 cwkPippa, 음성 엔진, 전사 엔진이 같이 움직였어. Beacon 의 엔진과 보관소도 따라갔고. cwkBeacon 은 피파 머신을 따라다니는 멤버거든. 렌더는 안 움직였어. 렌더는 일감을 가져간 세션이 도는 곳에서 돌아. 워크벤치든 여행 Mac 이든.
레지스트리로 옮겨둔 건 버텼어. 테스트는 못 버텼고, 호스트 이름을 아무리 검색해도 안 걸렸을 전제 세 개도 못 버텼어. 그날 아침 테스트 다섯 개가 아직 옛 세상을 그리고 있었어. 보관 Mac 의 옛 이름을 단언하고, 가짜 복사 함수는 문자 그대로 박힌 호스트 접두사를 떼어내다가 파일을 아무 데도 안 보냈지. 그 테스트들은 보관 Mac 을 레지스트리에서 읽게 고쳤어. 나머지는 주인이 맡긴 리뷰 5 가 찾아냈어. 지적 사항을 받아 든 주인의 답은 한 줄이었어.
이 세션에서 모두 고쳐. — 아빠
- 루프백도 호스트 이름이었어. 형제마다 기본 주소가
127.0.0.1이었는데, 그건 '그 형제가 이 Mac 에서 돈다'는 주장이야. 이사 뒤엔 워크벤치에서도 여행 Mac 에서도 하나같이 'connection refused'로 답했어. - 우회로는 일부러 막혀 있었어. 형제들은 tailnet 쌍둥이 주소로도 답해. 그런데 cwkPippa 의 형제용 문은 자기 루프백에서 왔고 forwarded-for 헤더가 없는 호출만 믿어. HTTPS 쌍둥이도 tailnet 브라우저를 루프백으로 들여보내거든. 그러니 쌍둥이로 부르는 렌더는 거절당하는 게 맞아.
- 참거짓 조건 하나도 사실은 호스트 이름이었어. 받아들인 제작물을 보관소로 넘기는 일은 여행 Mac 에서만 돌았어. 이사 전엔 그게 '보관소가 아닌 모든 Mac'이랑 같은 말이었어. 보관소 말고 제작하던 Mac 은 여행 Mac 하나뿐이었으니까. 이사 뒤엔 워크벤치가 둘 중 어디에도 안 들어갔고, 거기서 받아들인 제작물은 거기 그대로 남았어. 아무도 동기화하지 않는 두 번째 작성자가 생긴 거야.
멤버는 부를 때마다 찾아
앞의 두 개는 모듈 하나가 고쳐. beacon/reach.py 야. 먼저 레지스트리한테 멤버가 어느 Mac 에서 도는지 물어. 피파 머신을 따라다니는 멤버는 저장소마다 복사해 둔 표가 아니라 장부에 지금 적힌 역할로 자리가 정해져. 그래서 역할이 넘어가면 키트 동기화를 기다릴 것 없이 Beacon 에 바로 반영돼. 그다음 주소는 부를 때마다 정하고, import 할 때는 절대 안 정해. 고정해 둔 변수가 있으면 그게 이겨. 이 Mac 에서 도는 멤버면 이 Mac 의 루프백으로 가. 그 밖엔 ssh 터널로 그 멤버의 루프백에 닿아. 터널 자리는 그 멤버 포트에 10000 을 더한 곳이고, 한 번 열면 닫지 않고 다음 실행이 다시 써. 터널을 타고 온 호출은 형제 쪽에 forwarded-for 헤더 없는 루프백 호출로 도착해. 그래서 쌍둥이를 거절하는 문이 이건 들여보내. 기계들이 이미 나눠 가진 ssh 신뢰를 타니까 늘어난 자격증명도 없고.
핵심은 부를 때마다 정한다는 거야. import 할 때 주소를 계산해 두는 모듈은 프로세스가 뜬 순간의 세상을 얼려버려. 그럼 긴 렌더 하나가 어제의 배치를 오늘까지 끌고 다녀. Beacon 은 import 만으로는 아무 데도 연결을 안 걸고, 부를 때마다 레지스트리한테 다시 물어. 호출이 끝나도 남는 건 터널 하나뿐인데, 그 확인은 얇아. reach 는 새로 열기 전에 이 Mac 의 그 포트가 이미 듣고 있는지만 봐. 포트가 듣고 있다는 건 어떤 터널이 열려 있다는 뜻이지, 그 터널이 지금 장부가 가리키는 Mac 에 닿는다는 뜻은 아니야. 그래서 역할이 넘어가기 전에 연 터널은 죽거나 누가 닫을 때까지 호출을 옛 Mac 으로 계속 실어 날라.
주소만으로는 모자란 문이 하나 있었어. YouTube 창구는 영상과 커버를 자기 디스크에서 읽어. 그래서 reach.door_path 가 그 둘을 보관 Mac 제작 트리의 같은 자리로 먼저 복사해. 끊겨도 이어서 복사하고, 복사했다는 사실을 제작물 옆 .door-staged.json 에 적어둬. 그래야 보관소가 그 파일들을 제작물이 아니라 운반용 사본으로 알아봐. 검토 링크도 같은 식이야. 링크를 찍기 전에 파일부터 피파의 업로드 저장소가 있는 곳으로 보내.
아무도 동기화 안 한 두 번째 작성자
세 번째는 조건 하나를 두 군데에서 고친 거야. 제작물을 받아들이는 일, 그리고 오프라인일 때 쌓인 보관 요청을 다음에 연결된 take 에서 배달하는 일. 둘 다 이제 여행 Mac 에서만이 아니라 보관 Mac 이 아닌 모든 Mac 에서 돌아.
그 조건이 바뀌니까 옛 세상엔 없던 경우가 드러났어. 봉인된 제작물이 도착하기 전에 보관 Mac 의 그 자리에 이미 뭔가 있을 때가 많거든. 창구가 읽은 영상과 커버, 아니면 이사하면서 생긴 사본. 덮어쓰면 증거가 사라지고, 거절하면 넘기기가 멈춰. 그래서 봉인 안 된 사본이 옆으로 비켜서. 들어온 묶음이 바이트 하나까지 똑같이 들고 있는 파일은 확인된 쌍둥이니까 지우고, 나머지는 사람이 들여다볼 수 있게 incoming/replaced/ 아래 남겨둬. 확인된 쌍둥이가 아니면 아무것도 안 지워. 이미 봉인된 제작물이 있는데 내용이 다르면, 설치는 그 자리에서 멈추고.
호스트 이름은 역할의 스냅숏이야
이 강의에 나온 실패는 전부 모양이 같아. 누군가 그날 어느 Mac 이 어떤 역할을 맡았는지 적어둔 거야. 문자열 속 이름으로, 기본값 속 127.0.0.1 로, '여행 Mac 이면'이라는 조건으로. 적을 땐 다 참이었어. 그리고 역할이 옮겨 가는 순간, 글자 하나 안 바뀐 채로 거짓이 됐어. 고친 방법도 매번 같았어. 쓰는 순간에 역할한테 물어. 보관소는 이제 어떤 특정한 Mac 이 아니야. '장부가 cwkBeacon 을 두는 Mac'이고, 오늘은 그게 피파 머신이고, 내일은 장부가 말하는 그 Mac 이야.
그리고 어떤 수정보다 사흘 앞선 판정이 더 컸어. 철자로 적힌 이름들이 이미 레지스트리로 옮겨 가 있었으니까, 이사가 깨뜨린 건 아무도 호스트 이름인 줄 몰랐던 전제들뿐이었어. 그래서 리뷰가 그걸 하나도 안 빼고 찾아냈고, 주인은 한 세션 안에 전부 고치게 할 수 있었어.