노트북에도 걸 수 있는 주소를
p4는 노드에게 걸어서 일을 준다. NAT 뒤의 기계에는 걸 주소가 없다. 그리고 대부분의 기계가 NAT 뒤에 있다. 그런 기계 하나를 합류시키기까지 무엇이 필요했는지, 그리고 합류한 날의 실측이다.
막힌 것은 의지가 아니라 방향이었다. p4는 노드*에게* TCP 연결을 열어 일을 전달한다. 노드가 스스로 연 연결은 영수증 전용이라, 그 위로 온 Data 프레임은 peer_closed("unexpected frame on outbound hop")으로 끊긴다. 노트북이 바깥으로 터널을 열고 그 터널로 일감을 받는 일은 일어나지 않는다. 의지로도, 재시도로도, 더 나은 클라이언트로도.
기여자의 기계는 기본적으로 도달 불가다
가정의 기계는 NAT 뒤에 앉아 있다. 인터넷으로 나갈 수는 있지만 인터넷이 들어올 수는 없다. 우리 플릿이 양쪽을 다 보여준다. 사무실 GB10은 공인 주소가 직접 붙어 있고, MI250 두 대는 하나의 공용 출구 뒤 192.168.20.x에 있다. MI250이 특이한 게 아니다. 기여자의 기계가 바로 그 모습이다.
p4에는 라우팅 테이블도, 랑데부 서비스도, 홀펀칭도 없다. deliver_outbound는 이벤트 봉투에서 주소를 꺼내 connect를 부른다. 그게 전부이고, 랙 안에서는 합리적인 설계다. 다만 노트북이 만족시킬 수 있는 설계가 아닐 뿐이다.
바깥으로 연 연결 하나를 붙들고 있기
릴레이가 노드를 대신해 공인 주소를 갖는다. 노드는 그쪽으로 나가는 연결 하나만 유지하고 아무것도 듣지 않는다. 누군가 그 공인 주소로 걸면 바이트는 노드가 이미 들고 있던 연결을 타고 내려온다. p4의 양쪽 끝은 평범한 주소로 향하는 평범한 소켓을 볼 뿐, 어느 쪽도 릴레이의 존재를 알지 못한다.
거는 쪽이 보는 것과 노드가 실제로 돌리는 것
caller -> tcp://relay:43100 # 평범한 p4 주소
|
+-- relay 공개. 바이트를 넘기되 해석하지 않는다
|
+-- tunnel 노드가 스스로 연 바깥 연결 하나
|
+-- p4-agent 127.0.0.1:42031, 루프백만 듣는다릴레이는 p4를 읽지 않는다. 페이로드를 바이트 단위로 넘기고 파싱하지 않으므로 LOAD와 INSPECT를 구분하지 못한다 — 그리고 구분할 수 있으면 안 된다. 트래픽을 이해하는 순간 그것을 바꿀 수 있는 물건이 되기 때문이다.
포트포워딩은 애초에 선택지가 아니었다
p4에는 어떤 인증도 없다. TLS도, 토큰도, 허용목록도 없어서 에이전트의 포트에 닿는 누구나 NODE_LOAD·NODE_UNLOAD·INSPECT를 보낼 수 있다. 기여자에게 집 공유기의 포트를 그쪽으로 열라고 요구하는 것은 변호할 수 없고, 가장 뻔한 해법이 틀린 해법인 이유가 그것이다.
릴레이를 거치면 노드는 아무것도 듣지 않는다. 바깥으로 연 연결 하나를 유지하고, 그 연결이 바이트를 나르기 전에 운영자 지갑을 증명한다 — 정산 게이트웨이가 이미 쓰는 것과 같은 ed25519/base58 서명이라, 노드가 등록하는 신원이 보상 원장이 키로 쓰는 신원과 같다. 도달성과 인증은 결국 같은 답을 갖는다.
주장이 아니라 측정
데이터센터의 MI250 호스트가 tcp://34.50.62.159:43100으로 걸어 NAT 뒤 맥북에서 도는 p4 에이전트에 6 ms 만에 닿았다. 28 ms 뒤 에이전트가 연결을 끊었다. 첫 프레임이 Hello가 아니었기 때문이다 — 자기 프로토콜 규칙을, 조금 전까지 연락조차 닿지 않던 기계가 스스로 적용한 것이다. 그 거절이 증거다. 릴레이는 페이로드를 파싱하지 않으므로 그것을 만들어낼 수 없다.
| 검사 | 결과 |
|---|---|
| 512 KiB 왕복 | 바이트 동일, 177 ms |
| 256 KiB 왕복 | 바이트 동일, 108 ms |
| 동시 발신자 3개 | 스트림이 서로 섞이지 않음 |
| 서명 없는 등록 | 거부 |
| 재사용된 챌린지 | 거부 |
노드에는 돌릴 것이 있어야 한다
엔진이 뒤에 없으면 도달성은 무의미한데, 데스크탑 앱은 그것을 배포한 적이 없었다. 빌드 디렉터리 세 곳에서 p4-agent 바이너리를 찾았고, 이미 p4를 손수 컴파일한 기계에서만 발견됐다. 이제 앱이 배포되는 플랫폼마다 자기 바이너리를 싣는다.
- macOS: 두 아키텍처를 합친 universal 바이너리. Mac 앱은
universal로 패킹되고 리소스는 양쪽 슬라이스에 그대로 복사되므로, arm64만 넣으면 Intel Mac 사용자는 멀쩡해 보이는데 노드가 켜지지 않는 앱을 받는다 — 개발자가 가진 적 없는 하드웨어에서만 보이는 실패다. - Windows: 크로스 컴파일하고, 탐색이
p4-agent.exe를 찾도록 했다. 확장자를 빼먹는 것이 윈도우 빌드가 자기 에이전트를 못 찾는 경로다. - 패키징마다 에이전트 빌드가 먼저 돌므로, 에이전트 없는 릴리스는 만들어질 수 없다.
아직 되지 않는 것. 노드는 이제 돌고 도달된다. 다만 추론으로 버는 것은 여전히 없다. 엔진은 스테이지별 작업 기록을 내보내지만 그것을 모으는 것이 없고, 정산 게이트웨이의 기여도 엔드포인트는 — 어떤 노드도 자기 기여를 스스로 적을 수 없도록 올바르게 막혀 있는 채로 — 한 번도 호출된 적이 없다. 도는 것과 보상받는 것은 별개의 문제이고, 지금 풀린 것은 앞의 하나뿐이다.