ブラインドリレー不正利用ガード
AeroNyx ノードが暗号化されたリレートラフィックを転送する前に適用する制限:前ホップのレート制限と隔離、再起動後も保持されるルートのリプレイ抑止、上限のある再試行と CPU の受付制御。いずれも署名付きメタデータと集計カウンターだけを使います。
ブラインドリレー不正利用ガードは、AeroNyx ノードが他のノードのために暗号化されたリレートラフィックを転送する前に適用する一連の制限です。署名付きのルーティングメタデータとローカルのカウンターだけを使って、不正な、あるいは不安定なピアを封じ込めます。暗号文を解析したり、ルートの履歴を保持したり、誰が誰とやり取りしているかを公開したりすることは決してありません。
対象範囲
| 制御 | 状態 |
|---|---|
| 不透明なペイロードの転送 | 実装済み |
| 署名付きの鮮度チェックとルートのリプレイ抑止 | 実装済み。ノードのチャットリレーストアが有効な場合、リプレイ状態は再起動後も保持されます |
| 前ホップのレート制限と隔離 | 実装済み |
| プロセス全体のリクエストおよび暗号処理の受付制限 | 実装済み |
| ノードのヘルス情報と Nodeboard における集計ランタイム指標 | 実装済み |
| 平文やペイロードの検査 | 設計上、行いません |
| ユーザー単位、ルート単位、ソーシャルグラフの分析 | 設計上、行いません |
ブラインドノードの原則
リレーは、ルーティングメタデータを認証し、上限のあるポリシーを適用し、不透明なエンベロープを転送し、署名付き受領証を返すことができます。ペイロードを検査したり、ルート、ユーザー ID、関係性を再構成できるメタデータを公開したりしてはなりません。具体的には、リレーは次のものを記録も公開もしません。
- メッセージ、パケット、メディア、記憶の平文
- DNS クエリ、宛先、ドメイン、URL、閲覧履歴
- ルート ID、完全な経路、エンドポイント URL、クライアント IP アドレス
- 完全な公開鍵、受信者の ID、メッセージ ID、ソーシャルグラフのエッジ
- 秘密鍵、バウチャーの秘密情報、ウォレット単位のトラフィック、復号用の素材
受付パイプライン
各リクエストは、転送容量を使えるようになる前に次の検査を通過します。ステップ 1〜6 はこの順序で実行されます。ステップ 7 では、このノード宛てのリクエストは自身の終端処理またはオニオン処理へ進み、別のノード宛てのリクエストは次のホップが解決された後に予約されます。
1. request gate: process-wide admission window and in-flight limit (64 requests)
2. crypto admission: a free slot for signature and onion work, or Backpressure
3. authenticate the signed previous hop and envelope
4. previous-hop rate limit and quarantine check
5. envelope metadata: size, age and future-skew limits
6. self-loop check
7. route loop, TTL and next-hop descriptor checks, plus the route replay reservation
8. forward the opaque ciphertext, or peel the terminal layer and deliver
すべての判断は署名付きのルーティングメタデータとローカルの集計状態に基づいて行われ、復号された内容が使われることは決してありません。
ランタイムの制限値
以下はノードのソースにおける現在のデフォルト値です。これらは運用上の値であり、恒久的なプロトコル上の約束ではありません。
| 定数 | デフォルト値 |
|---|---|
BLIND_RELAY_ROUTE_REPLAY_CAPACITY | 有効なルート 8192 件 |
BLIND_RELAY_ROUTE_REPLAY_TTL_SECS | 600 秒 |
PREVIOUS_HOP_RATE_LIMIT(PREVIOUS_HOP_RATE_WINDOW_SECS あたり) | 60 秒あたり 120 リクエスト |
PREVIOUS_HOP_FAILURE_THRESHOLD(PREVIOUS_HOP_FAILURE_WINDOW_SECS あたり) | 300 秒あたり 12 件のスコア対象の失敗 |
PREVIOUS_HOP_QUARANTINE_SECS | 300 秒 |
MAX_PREVIOUS_HOP_BUCKETS | 4096 バケット |
BLIND_RELAY_MAX_ENVELOPE_AGE_SECS | 600 秒 |
BLIND_RELAY_MAX_FUTURE_SKEW_SECS | 120 秒 |
BLIND_RELAY_DELIVERY_RECEIPT_MAX_AGE_SECS | 120 秒 |
DEFAULT_MAX_ATTEMPTS(転送の再試行) | 3 回 |
MAX_IN_FLIGHT_BLIND_RELAY_REQUESTS | 64 リクエスト |
前ホップのレート制限と隔離
レート制限は、前ホップの署名付き ID をキーとして適用されます。1 つの前ホップから 60 秒以内に 120 を超えるリクエストがあると、そのホップに対して 5 分間のローカルな隔離が始まります。これとは別の失敗スコアでも、5 分以内にスコア対象の失敗が 12 件に達すると、同じ隔離が始まります。スコアに数えられるのは次の理由だけです。
invalid_previous_hop | invalid_signature | self_loop | route_loop | ttl_exhausted
トランスポートのタイムアウト、失われた確認応答、重複した再試行、リプレイの衝突はスコアに加算されないため、健全なピアがネットワークの不調によって隔離されることはありません。バケットストアには上限があり、アイドル状態のバケットは削除されるため、このガードが長期的な通信グラフへと膨らむことはありません。
ルートのリプレイと再試行
すべてのリレーリクエストはルート ID を持ちます。ノードのチャットリレーストアが有効な場合([memchain.chat_relay] enabled = true)、ルートの予約と完了したレスポンスはその SQLite データベースに保持されるため、リプレイ保護は再起動後も維持されます。そうでない場合、ノードは同じ容量と時間枠を持つインメモリのレジストリを使います。8192 のスロットすべてが有効なルートで埋まっている場合、スロットが期限切れになるまで、新しいルートは replay_capacity で拒否されます。
ルート ID が繰り返された場合、結果は次の 4 通りのいずれかになります。
| 状況 | レスポンス | 前ホップへの影響 |
|---|---|---|
| 同じリクエストで、最初の試行が完了している | 終端ノードが署名した受領証を含め、保存済みのレスポンスが再送されます。配信や転送が 2 回行われることはありません(duplicate_route) | 成功として数えられます |
| 同じリクエストで、最初の試行がまだ実行中 | HTTP 503 route_in_flight。送信者は再試行できます | なし |
| 同じルート ID で異なるリクエスト | HTTP 400 replay_conflict | ピアのヘルス情報に記録されます。隔離スコアには加算されません |
| 同じリクエストだが、保存済みのレスポンスが 120 秒より古い | HTTP 409 replay_response_expired。ルートは 600 秒の時間枠の残りの間予約されたままになるため、再実行はできません | なし |
最後の結果は永続ストアに適用されます。署名付き受領証は新しい証拠としてのみ有効なため、ノードは 120 秒を過ぎた受領証を再送しません。
次のホップへの転送がタイムアウト、接続エラー、リクエストエラーで失敗した場合、ノードは合計で最大 3 回まで試行します。各再試行の前の短い待機時間は、ルート ID、次のホップ、試行回数から導出されます。下流のノードが確定的な拒否として報告した失敗は再試行されません。
暗号処理の受付(オペレーター向けの容量に関する注意)
署名の検証、オニオンの層の除去、受領証への署名は CPU 負荷の高い処理であるため、各ノードプロセスはこれらを制限します。
- 合計スロット数: ホストが報告する CPU スレッド数の半分(切り上げ)で、最小 4、最大 8 です。そのため 1〜2 スレッドのホストでも 4 スロットを確保します。
- 受信リクエスト: 合計より 1 つ少ないスロットを使います。これにより、送信側のルート作成と受領証の完了処理が常に進行できます。そのため、最小規模のホストでも受信リクエストは 3 スロットを使えます。
- すべてのスロットが使用中の場合: リクエストは HTTP 429 と
backpressure理由で即座に拒否されます。キューで待機することはありません。
そのため、小規模なホストではリレーはバースト時に早めに負荷を落とします。backpressure_dropped カウンターが増加している場合、そのノードにはより多くの CPU が必要か、リレートラフィックを減らす必要があります。
集計ランタイムカウンター
ノードは、おおまかな累積カウンターと鮮度のタイムスタンプを、ハートビートの次の場所で報告します。
system_stats.discovery_status.peer_store.runtime.blind_relay
system_stats.discovery_status.peer_store.blind_relay_quality
system_stats.discovery_status.peer_store.peer_health_summary
| カウンターのグループ | フィールド |
|---|---|
| 受付と処理結果 | received、terminal、forwarded、rejected |
| 検証と保護 | invalid_signature、envelope_too_large、ttl_exhausted、no_route、invalid_endpoint、loop_detected、replay_dropped、timestamp_rejected、rate_limited、quarantined、quarantine_started |
| トランスポートと再試行 | backpressure_dropped、forward_failed、retry_attempted、retry_succeeded、retry_exhausted |
| 合成プローブ | probe_attempted、probe_succeeded、probe_failed、two_hop_probe_attempted、two_hop_probe_succeeded、two_hop_probe_failed |
| 実際の配信と鮮度 | verified_client_onion_deliveries、last_verified_client_onion_delivery_at、last_accepted_at、last_event_at |
これらのカウンターはノードの動作を表すものです。メッセージのログや課金データではなく、特定の会話が行われたことを証明するものでもありません。
証拠の質
品質サマリーは 4 種類の証拠を区別して扱います。受け付けた不透明なリレー処理、合成の到達性プローブ、合成の 2 ホッププローブ、そしてクライアント発の配信に対する終端ノード署名の受領証です。real_relay_ready には、新しく認証済みのクライアント配信受領証が必要です。合成の証拠がユーザートラフィックとして報告されることは決してありません。
status | 意味 |
|---|---|
idle | リレーやプローブの活動がまだありません |
observing | 何らかの証拠はありますが、準備完了とは確認されていません |
stale | 以前の成功の証拠が、もはや新しくありません |
ready | 新しく受け付けた処理または要件を満たす証明があり、トランスポートの問題は発生していません |
protecting | リレーが動作を続ける中で、不正利用の保護機能が発動しています |
degraded | 転送またはプローブの失敗について調査が必要です |
attention | バックプレッシャーまたは再試行の使い切りについて、オペレーターの迅速な対応が必要です |
proof_scope は、観測された中で最も強い証拠を示します。client_message_delivery、relay_acceptance、message_delivery、control_plane、single_hop_control_plane、attempted、none のいずれかです。
トラフィックの関係性を含まないピアのヘルス情報
peer_health_summary により、オペレーターは誰が誰と通信しているかを見ることなく、障害が起きているピアや隔離されたピアを見つけられます。
含まれるもの: 短縮したノード ID のプレフィックス、おおまかなヘルス状態と記述子の状態、ゴシップとルート成功の鮮度、ルートの成功・失敗の集計件数、隔離状態、残り時間、おおまかな理由の区分。
決して含まれないもの: 完全なノード公開鍵、ルート ID、エンドポイントの一覧、暗号化されたデータやペイロードのハッシュ、メッセージ ID、受信者の ID、クライアント IP アドレス、宛先、DNS データ、ソーシャルグラフのエッジ。
ソースマップ
| レイヤー | ノードリポジトリ内のパス | 役割 |
|---|---|---|
| リレーのオーケストレーション | crates/aeronyx-server/src/api/chat_peer/blind_relay.rs | リクエストゲート、暗号処理の受付、認証、ループと TTL の検査、リプレイの結果、転送と終端処理 |
| HTTP の窓口と制限 | crates/aeronyx-server/src/api/chat_peer.rs | ルート、ボディサイズと同時実行数の制限、エラーコードと理由の区分 |
| 不正利用ポリシー | crates/aeronyx-server/src/api/chat_peer_abuse_guard.rs | 前ホップのレート制限、失敗スコア、隔離 |
| リプレイレジストリ | crates/aeronyx-server/src/api/chat_peer_replay.rs | インメモリのレジストリと、保存済みレスポンスの形式 |
| 永続リプレイストア | crates/aeronyx-server/src/services/chat_relay_blind_route_store.rs | SQLite によるルートの予約とレスポンス |
| 再試行ポリシー | crates/aeronyx-server/src/api/chat_peer_retry.rs | 再試行可能な失敗と確定的な失敗の区別、再試行のタイミング |
| カウンターと品質 | crates/aeronyx-server/src/services/peer_store.rs および services/peer_store/status.rs | 集計カウンター、準備状態、証明の分類 |
| ヘルスレポート | crates/aeronyx-server/src/api/vpn_health.rs および management/reporter.rs | ローカルのヘルス JSON とハートビートのペイロード |
ノードソフトウェアは AGPL-3.0 のもと、github.com/AeroNyxNetwork/AeroNyx でオープンソースとして公開されています。ルートの構築方法については、ノードディスカバリーと暗号化マルチホップリレーをご覧ください。
<!-- faq:start -->よくある質問
リレーノードは転送するトラフィックを読んだり記録したりできますか?
いいえ。リレーは署名付きのルーティングメタデータを検証し、不透明なエンベロープを転送します。保持するのは集計カウンターだけで、ペイロード、ルートの履歴、受信者の ID を保持することは決してありません。
ノードが同じルートを再試行するとどうなりますか?
最初の試行が完了している場合、ノードは保存済みのレスポンスを再送し、2 回目の配信は行いません。最初の試行がまだ実行中の場合、ノードは再試行可能な route_in_flight を返します。同じルート ID で異なるリクエストが来た場合は replay_conflict として拒否されます。
リプレイ保護はノードの再起動後も維持されますか?
ノードのチャットリレーストアが有効な場合は維持されます。その場合、ルートの予約と完了したレスポンスは SQLite に保持されます。このストアがない場合、ノードは再起動時に消去されるインメモリのレジストリを使います。
ピアが隔離されるのはなぜですか?
ピアは、60 秒間に 120 を超えるリクエストを送った場合、または 5 分間にスコア対象の検証失敗が 12 件に達した場合に、5 分間隔離されます。タイムアウト、失われた確認応答、重複した再試行は失敗として数えられません。
負荷がかかると私のノードが backpressure を返すのはなぜですか?
リレー用の暗号処理スロットがすべて使用中だったためです。ノードは CPU スレッドの約半分(最小 4、最大 8)を署名とオニオンの処理に割り当て、超過したリクエストはキューに入れずに即座に拒否します。
<!-- faq:end -->