Lobsters

The road to iroh 1.0

iroh 1.0에 이르기까지

iroh 팀이 4년간의 개발과 방향 전환을 거쳐 1.0을 출시했습니다. IPFS 호환 구현에서 출발해 QUIC, BLAKE3 검증 스트리밍, 홀펀칭을 갖춘 경량 P2P 연결 라이브러리로 범위를 좁힌 결정과 그 배경을 설명합니다.

에디터 노트

iroh 1.0의 가장 설득력 있는 선택은 모든 P2P 기능을 한 라이브러리에 모으기보다 endpoint ID 연결과 홀펀칭에 범위를 좁힌 점입니다. 실제 사용자들이 공통으로 쓰는 기능을 남기고 나머지를 별도 crate로 분리했다는 설명이 범위 축소의 근거입니다. 댓글에서도 공개 키로 연결한다는 단순한 인터페이스가 이해와 활용을 돕는다는 반응이 나옵니다. 다만 성능이 이론적 한계에 가깝다는 설명에는 수치가 제시되지 않아 정량 비교까지 뒷받침하지는 않습니다. ALPN이 ClientHello에서 드러나고 다중 프로토콜 연결은 연결 수를 늘린다는 제약도 남습니다. 이 글은 1.0에서 API와 wire format이 안정됐다고 밝히며, 기능 확장보다 그 단순한 연결 기반을 안정화하는 방향을 택했습니다.

AI 요약

iroh는 Rust로 만든 IPFS 구현에서 출발했습니다. 팀은 모바일 기기처럼 자원이 제한된 환경을 목표로 삼았지만, IPFS 기반 blob 전송은 성능과 의존성 규모 면에서 Delta Chat의 기기 간 대화 기록 이전 요구에 맞지 않았습니다. 그래서 QUIC을 네트워크 계층으로, BLAKE3 검증 스트리밍을 대용량 blob 전송 방식으로 쓰는 경량 동기화 라이브러리를 새로 만들었습니다. 이 프로젝트가 실제 앱에서 쓰이면서 기존 IPFS 구현과의 호환성을 유지하려던 목표도 내려놓았습니다.

연결과 전송을 단순하게

새 iroh는 Ed25519 공개 키로 endpoint를 식별하고 BLAKE3를 콘텐츠 해시로 쓰며 전송 프로토콜은 QUIC 하나로 제한했습니다. TLS 표준을 유지하면서 인증서 체인 대신 rustls가 지원하는 raw public key를 도입했습니다. 처음에는 QUIC의 클라이언트·서버 구조를 P2P에 맞추려고 자체 서명 인증서와 공개 키를 연결하는 별도 구성을 썼지만, raw public key 지원이 들어오자 이를 바꿨습니다.

기기들이 같은 네트워크에 있어야 했던 제약을 없애기 위해 홀펀칭도 구현했습니다. 팀은 Tailscale의 접근을 참고해 연결을 시도하고, 홀펀칭이 되지 않는 기업 네트워크에서는 릴레이를 통해 데이터를 전송하도록 했습니다. 목표는 P2P 순수성을 지키는 것보다 다양한 네트워크에서 연결을 성사시키는 일이었습니다.

프로토콜 협상과 주소 조회

각 QUIC 스트림에서 별도 핸드셰이크를 하지 않고 TLS의 ALPN 확장으로 프로토콜을 협상합니다. 연결 쪽에서 ALPN 목록을 제안하고 상대 endpoint가 하나를 고르거나 핸드셰이크를 거부합니다. QUIC 핸드셰이크에 포함되므로 별도 스트림 협상 비용은 들지 않지만, 암호화되지 않은 ClientHello를 관찰하면 사용 프로토콜이 드러날 수 있습니다. 또 QUIC 연결 하나는 ALPN 하나에 대응하므로 여러 프로토콜을 쓰는 endpoint 사이에는 연결이 여러 개 열립니다.

endpoint ID만으로 상대를 찾는 기능도 필요해졌습니다. 기존에는 IP 주소나 릴레이 URL이 추가로 필요했지만, endpoint의 공인 IP와 릴레이가 바뀌면 그 정보가 오래가지 않을 수 있습니다. 팀은 분산 해시 테이블(DHT)을 직접 만들 여력이 없다고 판단해 중앙형 조회 방식도 검토했고, BitTorrent Mainline DHT에 서명된 DNS 레코드를 저장하는 Pkarr를 사용했습니다. 중앙형 주소 조회와 Mainline DHT를 이용한 분산 조회를 함께 지원합니다. Mainline의 한계를 보완하려고 iroh의 0-RTT 연결을 쓰는 별도 DHT도 실험 중이지만, 아직 프로덕션 도구는 아닙니다.

범위를 줄이고 기반을 다시 만들기

iroh를 여러 기능을 갖춘 P2P 개발 도구로 확장하자 API 규모가 커졌고, 사용자마다 쓰는 기능도 달랐습니다. 반면 거의 모든 사용자가 필요로 한 기능은 endpoint ID를 통한 연결과 안정적인 홀펀칭이었습니다. 팀은 iroh의 범위를 직접 연결 기능으로 좁히고 blob, 문서 추상화 등 다른 기능은 별도 crate로 옮겼습니다. 장기 안정 버전 0.35를 내고 1.0까지 canary 버전을 운영한 뒤, 1.0에서 wire format과 API를 안정화했습니다.

전송 경로가 바뀌는 상황을 안정적으로 처리하려고 QUIC의 여러 경로 지원도 다시 설계했습니다. 기존 Quinn을 확장하는 변경이 커지자 포크를 만들고 noq라는 자체 QUIC 구현을 유지하기로 했습니다. 키 알고리즘은 Ed25519로 제한했습니다. Pkarr 같은 기존 시스템이 Ed25519에 의존하며 다른 알고리즘을 추가하면 복잡성이 커진다는 판단입니다. 향후에는 양자 내성 서명이나 Encrypted ClientHello를 검토할 수 있다고 밝혔지만, 현재 구현에 포함된 기능은 아닙니다.

Lobsters 반응

  • @jaculabilis — 사용자에게 보이는 설명이 단순해서 좋습니다. IP 대신 공개 키로 연결하는 기본 요소를 제공하고 나머지는 그 위에 쌓거나 이를 지원합니다. 네트워킹이 제 분야는 아니라 대부분의 P2P 프로젝트를 읽고도 무엇을 하는지, 어떻게 쓸지 잘 이해하지 못하는데 iroh는 해결하는 문제와 사용법을 알기 쉽습니다.
    • @ngp — 의도한 사용자 인터페이스에서 출발해 거꾸로 구현하는 방식에 몇 년째 끌립니다. 제가 본 다른 패러다임보다 강한 API와 깔끔한 추상화, 직관적인 도구를 꾸준히 만들어냈습니다.
    • @brudish — 코드도 그렇게 씁니다. 메서드나 클래스, 시스템이 어떻게 보이길 원하는지 먼저 정한 뒤 구현이 그 모양을 내도록 거꾸로 작업합니다.
    • @bitshift — 오늘 다른 이유로 첫 페이지에 오른 Linus Torvalds가 오래전에 한 말이 떠오릅니다. 나쁜 프로그래머는 코드에, 좋은 프로그래머는 자료 구조와 그 관계에 신경 씁니다. 프로그래머를 나쁘다고 부르는 데는 더 신중해야 한다고 생각하지만, 자료 구조든 클래스 인터페이스든 API든 형태를 먼저 정하는 방식에는 의미가 있습니다.
    • @fanf — Rob Pike와 Fred Brooks도 비슷한 말을 좀 더 친절하게 했습니다.
    • @AshkanKiani — 제품은 코드인가 도구인가를 물으면 이 지침을 쉽게 이끌어낼 수 있습니다. 코드에만 빠져 실제 제품을 잊기 쉽고 기능을 너무 많이 추가하거나 필요 이상으로 일반화하기도 합니다. 제약이 분명할수록 대체로 코드도 좋아집니다. 다만 이는 지침이지 규칙은 아니며, 초기에 일반화하는 편이 나은 경우도 있습니다.
    • @ngp — 사용자 인터페이스에서 거꾸로 생각하는 방식은 둘 다에 적용됩니다. 제품을 만들 때는 사용자가 무엇을 하고 무엇과 어떻게 상호작용할지 처음부터 생각해야 합니다. API나 라이브러리가 제품이라면 다른 개발자가 사용자입니다. 본질적으로 크게 다르지 않고 대상 사용자를 아는 문제입니다.
  • @wofo — raw public key를 잘 활용한 사례를 보니 좋습니다. 친구와 함께 rustls에 이 기능을 구현했고 Solana Foundation의 지원을 받았습니다. 블록체인 기술에는 관심이 없지만 이 기능이 그 분야 밖에서도 쓰이길 바랐습니다. iroh가 쓰기 시작한 시점이 딱 맞았던 것 같습니다.
    • @rklaehn — 답변 감사합니다. 이전의 복잡한 구성에서 TLS raw public key로 바꿀 수 있어 기뻤습니다. 그때 TLS를 유지한 결정이 옳았다는 점이 확인됐다고 생각합니다.
  • @marti — Iroh를 사용해 커뮤니티 운영 object store를 만들고 있는데 API가 무척 편합니다. Iroh가 많은 일을 대신 처리해 줍니다. Iroh Discord도 큰 도움이 됩니다. 홍보를 덧붙이자면 arkeldata.com입니다.

원문: Iroh / 번역·요약: Trawling