ノートパソコンにも掛けられる住所を

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 署名であり、ノードが登録する身元は報酬台帳が鍵とする身元と同じになる。到達性と認証は結局、同じ答えを持っていた。

動いた日

主張ではなく測定

6 ms
フリートのホストから NAT 背後の Mac へ
28 ms
エージェントが自らの規則を適用するまで
512 KiB
バイト単位で同一のまま往復
0
ノートパソコンで開いたポート数

データセンターの MI250 ホストが tcp://34.50.62.159:43100 に接続し、NAT 背後の MacBook で動く 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 を探すようにした。拡張子を忘れることが、Windows ビルドが自らのエージェントを見つけられなくなる経路である。
  • パッケージングのたびにエージェントのビルドが先に走るため、エージェントのないリリースは作れない。

まだできないこと。 ノードは動き、到達される。だが推論で得るものは依然としてない。エンジンはステージごとの作業記録を出すが、それを集めるものがなく、決済ゲートウェイの貢献度エンドポイントは — どのノードも自らの貢献を自分で記せないよう正しく塞がれたまま — 一度も呼ばれていない。動くことと報われることは別の問題であり、いま解けたのは前者だけである。