| 特性 | WireGuard | OpenVPN | IPsec/IKEv2 |
|---|---|---|---|
| コードベース | 約4,000行 | 約100,000行以上 | 約400,000行以上 |
| 攻撃対象面 | 最小限 | 大きい (OpenSSL) | 非常に大きい |
| 実行空間 | カーネル空間 | ユーザー空間 (tun/tap) | カーネル+ユーザー空間 |
| ハンドシェイク | 1-RTT (Noise IKpsk2) | 2+ RTT (TLS) | 4-6メッセージ (IKE) |
| 暗号方式の選択 | なし(利点!) | 交渉可能 | 交渉可能 |
重要な洞察: WireGuardは暗号方式の選択肢がない — 使用する暗号スイートは1種類のみ。これはダウングレード攻撃を防ぐ設計上の利点である。
各ピアはCurve25519公開鍵で識別される。ルーティングテーブルは許可IPレンジをピアにマッピングする:
[Interface]
PrivateKey = <自分の秘密鍵>
Address = 100.64.0.1/32
[Peer]
PublicKey = <MotherShipの公開鍵>
AllowedIPs = 100.64.0.2/32
ルーティングの仕組み:
証明書もPKIも不要。公開鍵そのものがアイデンティティとなる。
Noise_IKpsk2の意味: I=初回メッセージにイニシエータの静的鍵を含む; K=レスポンダの鍵は事前共有; psk2=2番目のメッセージ後にPSKを混合。
使用プリミティブ: Curve25519 (DH) + ChaCha20-Poly1305 (AEAD) + BLAKE2s (ハッシュ)
NAT越え: ①直接 → ②STUN → ③ポート予測 → ④バースデー(256port≈50%) → ⑤DERPリレー
DERPも引き続きE2E暗号化 — リレーサーバーは通信内容を読めない。
100.64.0.0/10 (CGNAT, RFC 6598) — ISP内部予約レンジ。パブリックに出現せず、LAN/プライベートレンジと衝突しない。
MagicDNS: cockpit.tail12345.ts.net → 100.x.y.z。ローカルDNSプロキシ (100.100.100.100) が .ts.net をインターセプト。
タグはWireGuardレベルで強制される暗号的アイデンティティ。pc-mesh タグのデバイスは他の pc-mesh デバイスのみに到達可能。「ラベルを信頼」ではなく、ルーティングテーブルで強制される。
| 項目 | App Store版 | スタンドアロン (brew) |
|---|---|---|
| VPN実装 | Network Extension (サンドボックス) | utunデバイス (カーネル) |
| 権限 | sudo不要 | sudo必要 |
| メモリ | 約280 MB | 約250 MB |
| CPU | アイドル時1%未満、ハンドシェイク時に一時的スパイク | 同上 |
WireGuardはアイドル時にゼロワーク — keepaliveポーリングも、ハートビートも、バックグラウンド鍵ローテーションもない。ChaCha20-Poly1305はハードウェアアクセラレーションなしでも約2 Gbpsで動作する。
pc_mesh.py read はPollモードを使用。将来の pc_mesh.py listen はリアルタイムSSEのStreamモードを採用予定。
{ "id": "sPs6v9cXIkY8", "time": 1743897600,
"expires": 1743940800, "event": "message",
"topic": "<secret-prefix>-cockpit",
"message": "タスク完了: PDF生成",
"title": "pc_mesh: mothership", "priority": 3 }
レート制限(公開サーバー): 1IPあたり1日250メッセージ、メッセージ本文最大64KB。
| 脅威 | 保護されているか | 詳細 |
|---|---|---|
| トピック列挙 | 弱い | トピックURLが唯一の秘密 |
| 通信中のメッセージ | はい | ntfy.shへのHTTPS/TLS |
| 保存時のメッセージ | いいえ | サーバー上のSQLite、運営者が読取可能 |
| 送信者認証 | いいえ | トピックを知る誰もが投稿可能 |
| リプレイ攻撃 | いいえ | ノンスやシーケンス番号なし |
ntfyは「メールが届いた」という通知のみを運ぶ — メール本文は決して運ばない。 タスクの詳細はGDrive(保存時暗号化、アクセス制御済み)に置く。
セルフホストならユーザー/パスワード認証、トピック単位のACL、完全なデータ管理が可能 — ただしサーバー運用が必要。
| 機能 | ntfy | RabbitMQ | Redis Pub/Sub | NATS |
|---|---|---|---|---|
| 配信保証 | なし | At-least-once | なし | At-most-once |
| 確認応答 | なし | あり (ACK/NACK) | なし | あり |
| 永続化 | 12時間キャッシュ | 永続キュー | 任意 (AOF) | JetStream |
ntfyは我々の用途に最適: メッセージ消失が致命的でない軽量通知に向いている。
根本的な問題: ntfyは「何かが起きた」と通知する。しかし受信側でリスニング・パース・アクション実行するプロセスが存在しない。人間がグルーレイヤーになっている。
ギャップの本質: 「通知は行動ではない (Notification does not equal action)」
| 選択肢 | 仕組み | レイテンシ | 複雑度 | 信頼性 |
|---|---|---|---|---|
| A: Cronポーリング | */5 * * * * pc_mesh.py read |
5分 | 非常に低い | 中 |
| B: 常駐リスナー | pc_mesh.py listen をハンドラにパイプ |
リアルタイム | 中 | 中-高 |
| C: Discord Botブリッジ | Discord -> Bot -> Claude CLI | 約10秒 | 高(構築済) | 高 |
| D: GDrive fswatch | fswatch Agent_Inbox/ -> トリガー |
30秒-5分 | 低-中 | 高 |
# 選択肢A: crontab -e
*/5 * * * * python3 /path/to/pc_mesh.py read 10 \
| python3 handler.py
# 選択肢B: systemd/launchdサービス
pc_mesh.py listen \
| while read -r line; do python3 dispatch.py; done
Cockpit --[送信]--> ntfy "secret-mothership" --> MotherShip
(届いたことを祈る) (読むかもしれない)
Cockpit --[送信]--> ntfy "secret-mothership" --> MotherShip
Cockpit <--[ACK]--- ntfy "secret-cockpit" <-- MotherShip
Cockpit <--[完了]-- ntfy "secret-cockpit" <-- MotherShip
# 実装スケッチ: ペアトピックACK
msg_id = send("mothership", "PDF batch-42を処理せよ")
msg = read("mothership") # 受信側
send("cockpit", f"ack:{msg['id']}") # ACK+完了通知
ダムパイプ上の規約 — ntfyにACK機能なし。ペアトピックで自前構築。
Google Drive File Streamは全ファイルをダウンロードしない。仮想ファイルシステムを提示する:
| シナリオ | 典型的な遅延 | 最悪の場合 |
|---|---|---|
| 小ファイル (<1KB) 作成 | 30-60秒 | 5分 |
| 大ファイル (>10MB) | 1-5分 | 10分以上 |
| オフライン復帰時 | 即座にキュー同期 | 大量キュー時は数分 |
競合解決: GDriveは競合時に filename (1).md を作成する — そのためclaim-then-editプロトコルを採用。
| 観点 | 単一チャネル | 分離構成 |
|---|---|---|
| 通知速度 | 大きなペイロードで律速 | ntfyが1秒未満で配信 |
| データ耐久性 | チャネル断で消失 | GDriveが無期限に永続化 |
| セキュリティ | 全データが1経路を通過 | 機密データは暗号化ストレージへ |
| オフライン時 | 何も機能しない | GDriveがキュー、ntfyがリプレイ |
| レイヤー | 役割 | 障害時の挙動 |
|---|---|---|
| 物理層 | 計算+ローカルストレージ | PC停止 -> タスクがキューに待機 |
| ネットワーク層 | 暗号化P2P接続 | NAT問題 -> DERPフォールバック |
| 通知層 | 「何かが起きた」アラート | ntfy障害 -> GDrive直接ポーリング |
| ストレージ層 | タスク詳細+共有状態 | GDriveオフライン -> ローカルキュー |
pc_mesh.py send gatekeeper "Inboxバッチを処理せよ"Step 1: PythonがHTTP POSTボディを構築
{"topic": "<UUID>-gatekeeper", "message": "Inboxバッチを処理せよ"}
Step 2: ntfy.shにHTTPS POST (TLS, Tailscale非経由)
ntfy.shがSQLiteに保存 (TTL=12時間)
Step 3: GateKeeperにアクティブなサブスクライバーがいれば即時配信 (<1秒)
いなければ: キャッシュで待機
Step 4: 並行して(別チャネル):
MacがAgent_Inbox/TASK_2026-04-06_inbox-batch.mdをGDriveに書込
GDriveがGateKeeperに同期 (30秒-5分)
Step 2-3 (ntfy) と Step 4 (GDrive) は異なるチャネルで並行して実行される。
Step 5: GateKeeperが pc_mesh.py read 実行 -> 通知を確認
Agent_Inbox/TASK_*.md を読み、タスクの全詳細を取得
Step 6: GateKeeperが処理 (OpenClaw + GLM-5.1)
結果 -> Agent_Outbox/ -> GDrive同期でMacに返送
この二重チャネル設計が3PC通信の耐障害性の鍵である。
| アクション | 対象PC | 工数 |
|---|---|---|
pc_mesh.py read をセッション開始チェックリストに追加 |
全PC | 5分 |
Agent_Inbox/ の fswatch 用launchd plist作成 |
Mac | 30分 |
pc_mesh.py にACK規約を追加(ペアトピック) |
全PC | 1時間 |
| アクション | 対象PC | 工数 |
|---|---|---|
pc_mesh.py listen デーモンモード実装 |
GateKeeper | 2時間 |
| リスナーをOpenClawタスクディスパッチャーに接続 | GateKeeper | 3時間 |
pc_mesh.py read に --since フラグ追加 |
全PC | 1時間 |
長期展望: TailscaleによるPC間直接HTTP APIが実現すれば、PC-to-PC通信にntfyは不要になる。ntfyはモバイルプッシュ用途のみに残る。
| PC | 最適な選択肢 | 理由 | フォールバック |
|---|---|---|---|
| Cockpit (Mac) | D: GDriveのfswatch | モバイル端末; GDriveはオフラインで動作 | A: Cronポーリング |
| MotherShip (Win) | C: Discord Botブリッジ | 構築済み; 24/7稼働 | B: リスナー |
| GateKeeper (HP) | B: 常駐リスナー | Discordゲートウェイにリアルタイム必要 | D: fswatch |
PCの役割に合わせた自動化を選ぶ。 Cockpitはモバイル — GDriveキューが堅牢。MotherShipは常時稼働 — 既存Botを活用。GateKeeperはリアルタイム必要 — 常駐リスナー。
GDrive Agent_Inbox/TASK_YYYY-MM-DD_<topic>.md にid・from・to・priority・statusフィールドを標準化する。