n0-computer/iroh

⭐ 6,354 🍴 282 Rust📄 Apache License 2.0 / MIT License GitHubで見る →

主な特徴

  • 直接P2P接続:公開鍵を識別子として使用した暗号化されたQUIC接続
  • 自動ホールパンチング:STUNライクな技術を使用してNATを通過する直接接続を試行
  • リレーサーバーフォールバック:直接接続が不可能な場合の中継サーバー経由接続
  • 複数の発見メカニズム:DNS、mDNS、pkarr、DHTによるノード発見
  • プロトコルルーティング:ALPNを使用した同一エンドポイント上での複数アプリケーションプロトコルのサポート
  • クロスプラットフォーム:Linux、macOS、Windows、WebAssembly(ブラウザ)対応
  • QUICベース:認証済み暗号化、並行ストリーム、ストリーム優先度、データグラム転送
  • ゼロコピーストリーミング:効率的なデータ転送
  • 包括的なメトリクス:本番環境での監視をサポート

リポジトリ解析: n0-computer/iroh

基本情報

  • リポジトリ名: n0-computer/iroh
  • 主要言語: Rust
  • スター数: 6,354
  • フォーク数: 282
  • 最終更新: 2025年7月時点でアクティブ
  • ライセンス: Apache License 2.0 / MIT License
  • トピックス: peer-to-peer, p2p, quic, networking, hole-punching, relay-server, rust, cross-platform, webassembly

概要

一言で言うと

QUICベースのピアツーピアネットワーキングライブラリで、NAT越えとリレーサーバーを自動的に処理し、デバイス間の直接接続を「魔法のように」簡単に実現する。

詳細説明

irohは、n0-computerが開発したピアツーピア通信を簡素化するためのRustライブラリです。「less net work for networks」というスローガンの通り、ネットワーキングの複雑さを削減することを目的としています。公開鍵をノード識別子として使用し、QUICプロトコルの上に構築されており、NAT環境下でも自動的にホールパンチングを試み、それが失敗した場合はリレーサーバー経由で接続を確立します。これにより、開発者はネットワーキングの詳細を意識することなく、信頼性の高いP2P接続を実現できます。

主な特徴

  • 直接P2P接続:公開鍵を識別子として使用した暗号化されたQUIC接続
  • 自動ホールパンチング:STUNライクな技術を使用してNATを通過する直接接続を試行
  • リレーサーバーフォールバック:直接接続が不可能な場合の中継サーバー経由接続
  • 複数の発見メカニズム:DNS、mDNS、pkarr、DHTによるノード発見
  • プロトコルルーティング:ALPNを使用した同一エンドポイント上での複数アプリケーションプロトコルのサポート
  • クロスプラットフォーム:Linux、macOS、Windows、WebAssembly(ブラウザ)対応
  • QUICベース:認証済み暗号化、並行ストリーム、ストリーム優先度、データグラム転送
  • ゼロコピーストリーミング:効率的なデータ転送
  • 包括的なメトリクス:本番環境での監視をサポート

使用方法

インストール

前提条件

  • Rust 1.85以降
  • 標準的な開発ツール(プラットフォームに応じて)

インストール手順

# 方法1: Cargoで依存関係として追加
cargo add iroh

# 方法2: 特定の機能を有効にして追加
cargo add iroh --features "metrics discovery-local-network"

# 方法3: ソースからビルド
git clone https://github.com/n0-computer/iroh
cd iroh
cargo build --release

基本的な使い方

Hello World相当の例

use iroh::{Endpoint, NodeAddr};
use anyhow::Result;

#[tokio::main]
async fn main() -> Result<()> {
    // エンドポイントを作成
    let endpoint = Endpoint::builder()
        .discovery_n0() // Number 0の発見サービスを使用
        .bind()
        .await?;
    
    // 別のノードに接続
    let node_addr: NodeAddr = "<peer-node-id>".parse()?;
    let conn = endpoint.connect(node_addr, b"my-app").await?;
    
    // 双方向ストリームを開く
    let (mut send, mut recv) = conn.open_bi().await?;
    
    // データを送信
    send.write_all(b"Hello, iroh!").await?;
    send.finish();
    
    Ok(())
}

実践的な使用例

use iroh::{Endpoint, Router, ProtocolHandler};
use anyhow::Result;
use futures_lite::StreamExt;

// カスタムプロトコルハンドラーの実装
#[derive(Clone)]
struct MyProtocol;

impl ProtocolHandler for MyProtocol {
    fn accept(self, connecting: iroh::endpoint::Connecting) -> futures_lite::future::Boxed<Result<()>> {
        Box::pin(async move {
            let connection = connecting.await?;
            let (mut send, mut recv) = connection.accept_bi().await?;
            
            // 受信したデータを読み取る
            let mut buf = vec![0u8; 1024];
            let n = recv.read(&mut buf).await?.unwrap_or(0);
            println!("Received: {}", String::from_utf8_lossy(&buf[..n]));
            
            // 応答を送信
            send.write_all(b"Hello from server!").await?;
            send.finish();
            
            Ok(())
        })
    }
}

#[tokio::main]
async fn main() -> Result<()> {
    // エンドポイントとルーターをセットアップ
    let endpoint = Endpoint::builder().bind().await?;
    let router = Router::builder(endpoint.clone())
        .accept(b"my-app", MyProtocol)
        .spawn();
    
    println!("Node ID: {}", endpoint.node_id());
    println!("Listening on: {:?}", endpoint.direct_addresses());
    
    // 接続を待つ
    tokio::signal::ctrl_c().await?;
    Ok(())
}

高度な使い方

use iroh::{Endpoint, NodeAddr, discovery::*, relay::RelayMode};
use std::time::Duration;

#[tokio::main]
async fn main() -> Result<()> {
    // カスタム設定でエンドポイントを構築
    let secret_key = iroh::SecretKey::generate();
    
    let endpoint = Endpoint::builder()
        .secret_key(secret_key)
        .alpns(vec![b"my-app-v1".to_vec()])
        .relay_mode(RelayMode::Custom(
            "https://my-relay.example.com".parse()?
        ))
        .keylog(true) // デバッグ用のキーロギング
        .discovery(Box::new(ConcurrentDiscovery::from_services(vec![
            // 複数の発見メカニズムを組み合わせる
            Box::new(DnsDiscovery::n0()),
            Box::new(LocalSwarmDiscovery::new(endpoint.node_id())?),
            Box::new(DhtDiscovery::builder().build().await?),
        ])))
        .bind()
        .await?;
    
    // 複数のプロトコルを持つルーターを作成
    let router = Router::builder(endpoint.clone())
        .accept(b"chat-v1", ChatProtocol::new())
        .accept(b"file-v1", FileTransferProtocol::new())
        .accept(b"sync-v1", SyncProtocol::new())
        .spawn();
    
    // ピアへの接続とストリーム管理
    let peer_addr: NodeAddr = "...".parse()?;
    let conn = endpoint.connect(peer_addr, b"chat-v1").await?;
    
    // 複数のストリームを同時に使用
    let (chat_send, chat_recv) = conn.open_bi().await?;
    let file_stream = conn.open_uni().await?;
    
    // メトリクスの収集
    if let Some(metrics) = endpoint.metrics() {
        println!("Active connections: {}", metrics.connections_alive());
    }
    
    Ok(())
}

ドキュメント・リソース

公式ドキュメント

  • README.md: プロジェクトの概要、基本的な使用方法、アーキテクチャの説明
  • CONTRIBUTING.md: コントリビューションガイドライン、開発環境のセットアップ
  • iroh/README.md: コアライブラリの詳細なドキュメント
  • iroh-relay/README.md: リレーサーバーの設定と運用ガイド
  • iroh-dns-server/README.md: DNS発見サーバーのセットアップ
  • 公式サイト: https://iroh.computer/ - 包括的なドキュメントとチュートリアル

サンプル・デモ

  • examples/: 基本的な接続例、プロトコルハンドラーの実装例
  • iroh-relay/: リレーサーバーの実装例
  • config examples: example.config.toml - 設定ファイルのテンプレート

チュートリアル・ガイド

  • iroh-blobs: コンテンツアドレス指定ブロブ転送プロトコル
  • iroh-gossip: 効率的なブロードキャストプロトコル
  • iroh-docs: レプリケートされたドキュメントデータベース
  • WebAssemblyガイド: ブラウザでのiroh使用方法

技術的詳細

アーキテクチャ

全体構造

irohはモジュラーなワークスペース構造を採用しており、コア機能を複数のクレートに分割しています。中心となるのはQUICプロトコルの上に構築された「MagicSock」トランスポート層で、これが直接UDP接続とリレープロトコル接続の両方を透過的に処理します。公開鍵ベースの識別子システムにより、PKIを必要とせずにセキュアな接続を確立できます。

ディレクトリ構成

iroh/
├── iroh/                # コアライブラリ
│   ├── src/            # メインソースコード
│   │   ├── endpoint.rs # エンドポイント管理
│   │   ├── router.rs   # プロトコルルーティング
│   │   └── discovery/  # 発見メカニズム
│   └── examples/       # 使用例
├── iroh-base/          # 基本型定義
│   └── src/           # NodeId、SecretKey等の実装
├── iroh-relay/         # リレーサーバー実装
│   ├── src/           # サーバー・クライアント実装
│   └── build.rs       # ビルドスクリプト
├── iroh-dns-server/    # DNS発見サーバー
│   ├── src/           # DNSサーバー実装
│   └── config.*.toml  # 設定ファイル例
└── docker/            # Dockerイメージ定義

主要コンポーネント

  • Endpoint: ノードの中核となるAPI

    • 場所: iroh/src/endpoint.rs
    • 依存: MagicSock、Discovery、Router
    • インターフェース: connect()、accept()、direct_addresses()
  • MagicSock: トランスポート層の実装

    • 場所: iroh/src/magicsock/
    • 依存: QUIC (quinn)、リレークライアント
    • インターフェース: UDP/リレーの透過的な切り替え
  • Discovery System: モジュラーな発見メカニズム

    • 場所: iroh/src/discovery/
    • 依存: DNS、mDNS、pkarr、DHT
    • インターフェース: resolve()、publish()
  • Router: プロトコルマルチプレクサ

    • 場所: iroh/src/router.rs
    • 依存: Endpoint、ProtocolHandler
    • インターフェース: accept()、spawn()

技術スタック

コア技術

  • 言語: Rust (1.85+)、async/await、安全なメモリ管理
  • QUICスタック: quinn - Rustの高性能QUIC実装
  • 主要ライブラリ:
    • tokio: 非同期ランタイム
    • quinn (0.11): QUIC実装
    • ed25519-dalek: 暗号化と署名
    • hickory-dns: DNS操作
    • mdns-sd: mDNS発見
    • pkarr: P2P発見プロトコル
    • mainline: BitTorrent DHT統合
    • prometheus-client: メトリクス収集

開発・運用ツール

  • ビルドツール: Cargo(ワークスペース構成)、cargo-nextest(並列テスト)
  • テスト: 単体テスト、統合テスト、ネットワークシミュレーション
  • CI/CD: GitHub Actions(Linux、macOS、Windows、WASM)
  • デプロイ: Dockerイメージ、systemdサービス、Kubernetes対応

設計パターン・手法

  • 公開鍵アイデンティティ: Ed25519公開鍵をノード識別子として使用、PKI不要
  • トレイトベースの拡張性: ProtocolHandler、Discovery等のトレイトによる拡張
  • 非同期ストリーム処理: futures/tokioによる効率的な並行処理
  • フォールバック戦略: 直接接続→リレー接続の自動切り替え
  • モジュラー設計: 発見、トランスポート、プロトコルの独立性

データフロー・処理フロー

  1. 接続確立フロー:

    • NodeAddrの解決(Discovery)
    • 直接アドレスの試行(UDP経由のQUIC)
    • ホールパンチング試行(STUN-like)
    • リレーサーバー経由の接続(失敗時)
    • ALPN経由のプロトコルネゴシエーション
  2. データ転送フロー:

    • QUICストリーム/データグラムの作成
    • 暗号化(QUIC層で自動)
    • MagicSock経由での転送(UDP/リレー)
    • 受信側でのストリーム処理

API・インターフェース

公開API

Endpoint API

  • 目的: ノードの作成と接続管理
  • 使用例:
// エンドポイントの作成と接続
let endpoint = Endpoint::builder()
    .alpns(vec![b"my-protocol".to_vec()])
    .bind()
    .await?;

let connection = endpoint.connect(node_addr, b"my-protocol").await?;

Router API

  • 目的: プロトコルハンドリングとマルチプレクシング
  • 使用例:
// 複数プロトコルのルーティング
let router = Router::builder(endpoint)
    .accept(b"chat", chat_handler)
    .accept(b"files", file_handler)
    .spawn();

設定・カスタマイズ

設定ファイル

# example.config.toml
# リレーサーバー設定
relay_url = "https://relay.example.com"

# 発見設定
[discovery]
type = "dns"
server = "https://dns.example.com"

# メトリクス設定
[metrics]
enabled = true
port = 9090

拡張・プラグイン開発

  • カスタムProtocolHandler: ProtocolHandlerトレイトを実装
  • カスタムDiscovery: Discoveryトレイトを実装してノード発見をカスタマイズ
  • メトリクスコレクター: Prometheus互換メトリクスの統合
  • トランスポート拡張: MagicSockの設定によるカスタムトランスポート

パフォーマンス・スケーラビリティ

パフォーマンス特性

  • ベンチマーク結果: QUICの性能特性に依存、TCPより低レイテンシ
  • 最適化手法:
    • ゼロコピーストリーミング
    • 効率的なメモリプール
    • 並行接続の処理
    • リレーサーバーでの効率的なパケット転送

スケーラビリティ

  • 同時接続数: エンドポイントあたり数千の同時接続をサポート
  • リレーサーバー: 地理的分散による負荷分散
  • 発見メカニズム: DHTによる分散型ノード発見
  • メモリ使用: 接続あたりの省メモリ設計

制限事項

  • WebAssembly版は一部機能制限(UDPソケット不可)
  • ファイアウォール: 極めて制限的な環境ではリレーも不可能
  • 帯域幅: リレー経由の場合はリレーサーバーの帯域に依存

評価・所感

技術的評価

強み

  • P2P接続の複雑さを隠蔽し、「魔法のように動く」体験を提供
  • QUICベースによる高性能で安全な通信
  • 包括的なNAT越え戦略(直接接続→ホールパンチング→リレー)
  • 本番環境での実績(Number 0のプロダクトで使用)
  • 優れたRust APIデザインと型安全性

改善の余地

  • より多くのプラットフォーム向けのバインディング(Go、Python等)
  • より詳細なパフォーマンスベンチマーク情報
  • エンタープライズ向けの管理・監視ツール

向いている用途

  • 分散アプリケーション(チャット、ファイル共有、同期)
  • IoTデバイス間の直接通信
  • ゲームのP2Pネットワーキング
  • エッジコンピューティングでのノード間通信
  • プライバシー重視のアプリケーション

向いていない用途

  • 大規模なストリーミング(CDN的な用途)
  • 極めて低レイテンシが必要なリアルタイムゲーム(リレー使用時)
  • レガシーシステムとの統合(QUIC非対応環境)

総評

irohは、P2Pネットワーキングの複雑さを効果的に抽象化し、開発者フレンドリーなAPIを提供する優れたライブラリです。特に、NAT越えとフォールバック戦略の実装は洗練されており、実世界の様々なネットワーク環境で確実に動作します。Rustエコシステムにおける P2P開発の標準的な選択肢となる可能性を秘めており、今後のWebAssemblyサポートの拡充により、ブラウザベースのP2Pアプリケーションの開発も容易になることが期待されます。