Egress IP Family Policy (IPv4/IPv6) (日本語)
レジストリ内の各プロキシには、3つの値のいずれかを取るfamilyフィールドがあり、Zodの列挙型によって検証されます。
family: z.enum(["auto", "ipv4", "ipv6"]).optional().default("auto"),このフィールドのデフォルト値は"auto"であり、従来のデュアルスタック動作が維持されます。ipv4またはipv6に設定すると、そのプロキシの接続ファミリーが固定されます。
この指定は単一のヘルパーを通じてすべての場所で正規化されるため、未知の値はすべてautoに集約されます。
export type ProxyFamily = "auto" | "ipv4" | "ipv6";
export function parseProxyFamily(value: unknown): ProxyFamily { return value === "ipv4" || value === "ipv6" ? value : "auto";}存在する理由
Section titled “存在する理由”PR #3777で導入されました。導入の動機となった問題は次のとおりです。
| 問題 | この指定による解決方法 |
|---|---|
| IPv6専用エグレスからIPv4への漏洩 | プロキシホストにAレコードとAAAAレコードの両方がある場合(またはOSがIPv4を優先する場合)、IPv6専用パスを意図していても、Happy EyeballsによってIPv4経由で外部に接続される可能性があります。ipv6に固定することで、その漏洩を防止できます。 |
| 共有エグレスの異常による失効 | ローテーション型プロバイダー(codex/openai)は、多数のアカウントが高頻度で同じIPから外部に接続すると、トークンを失効させます。エグレスファミリーの制御は、アカウントごとに異なる予測可能なエグレスパスを維持するための一環です(これと組み合わせて使用するエグレスIP診断については、src/lib/proxyEgress.tsを参照してください)。 |
| コンプライアンス/テスト向けの決定論的なエグレス | トラフィックが特定のファミリー経由で送信されることを保証する必要がある場合、autoでは不十分です。 |
この指定は意図的にグローバルではなく、プロキシ単位で設定されます。プール内のプロキシごとに異なるポリシーを設定できます。
| 値 | UIラベル | 動作 |
|---|---|---|
auto |
自動(デュアルスタック) |
OSがアドレスファミリーを選択します。プロキシホストがIPリテラルの場合、ファミリーはそのリテラルによって決まります。ホスト名の場合は、両方のファミリーが使用可能です。これがデフォルトです。 |
ipv4 |
IPv4のみ |
接続をIPv4に固定します。プロキシホストにIPv4(A)レコードがない場合は、フェイルクローズします。 |
ipv6 |
IPv6のみ |
接続をIPv6に固定します。プロキシホストにIPv6(AAAA)レコードがない場合は、フェイルクローズします。 |
UI文字列はsrc/i18n/messages/en.json(labelFamily、familyAuto、familyIpv4、familyIpv6、familyHint)にあります。
ダッシュボード
Section titled “ダッシュボード”セレクターは、Proxy Poolタブのプロキシフォームにあります。
- Dashboard → Settings → Proxy → Proxy Poolを開きます
- プロキシを追加または編集します
- IPファミリードロップダウンを
自動(デュアルスタック)、IPv4のみ、またはIPv6のみに設定します - 保存します
このコントロールはProxyRegistryManager.tsxによってレンダリングされます(proxy/ProxyPoolTab.tsxにマウントされています)。
familyフィールドはプロキシレジストリの作成/更新ペイロードに含まれ、createProxyRegistrySchema / updateProxyRegistrySchema(src/shared/validation/schemas.ts)によって検証され、POST / PATCH /api/v1/management/proxiesによって処理されます。
# IPv6専用プロキシを作成curl -X POST http://localhost:20128/api/v1/management/proxies \ -H "Content-Type: application/json" \ -d '{ "name": "IPv6 egress", "type": "socks5", "host": "proxy.example.com", "port": 1080, "family": "ipv6" }'
# 既存のプロキシをIPv4専用に変更curl -X PATCH http://localhost:20128/api/v1/management/proxies \ -H "Content-Type: application/json" \ -d '{ "id": "proxy-uuid-here", "family": "ipv4" }'同じフィールドは、アップストリームプロキシエントリで使用されるインラインプロキシ設定オブジェクトでも受け付けられます(upstream_proxy_config.family。データモデルを参照)。
プロキシに関するその他のCRUD/割り当てAPIについては、PROXY_GUIDE.mdを参照してください。
autoの解決方法
Section titled “autoの解決方法”familyがautoの場合、OmniRouteはどのディレクティブも追加しません。プロキシURLはそのまま使用され、接続ファミリーは固有の情報に基づいて決定されます。
URLの構築時(open-sse/utils/proxyDispatcher.tsのproxyConfigToUrl / normalizeProxyUrl)、autoプロキシはマーカーのないプレーンURLになります。
const fam = parseProxyFamily(config.family);const normalized = normalizeProxyUrl(proxyUrlStr, "context proxy", { allowSocks5,});return fam === "auto" ? normalized : `${normalized}?family=${fam}`;ディスパッチ時(resolveDispatcherFamily)、autoはIPリテラルホスト固有のファミリーに解決されます。ホスト名の場合はnull(OSに決定を委ねる)に解決されます。
function resolveDispatcherFamily(parsed: URL): 4 | 6 | null { const directive = parseProxyFamily(parsed.searchParams.get("family") ?? undefined); const literal = detectIpLiteralFamily(parsed.hostname); if (directive === "auto") return literal; // ホスト名の場合はnull → OSが選択 // ...}したがって、次のようになります。
auto+ IPリテラルホスト(192.0.2.1/[2001:db8::1])→ そのリテラルのファミリー。auto+ ホスト名 →null→ 標準的なデュアルスタックOS名前解決。
ipv4 / ipv6 の適用方法
Section titled “ipv4 / ipv6 の適用方法”auto 以外のディレクティブは、単一の合成クエリマーカー(?family=ipv4 または ?family=ipv6)として、正規化されたプロキシ URL に一度だけ追加されます。normalizeProxyUrl は、ポート解析を壊すことがないよう、このマーカーを正確に一度だけ削除してから再追加します。
ディスパッチャーの構築時に、このマーカーが読み取られ、具体的な接続ファミリーに変換されます。ホストが反対側のファミリーに属する IP リテラルの場合、OmniRoute は例外をスローします(矛盾がある場合はフェイルクローズします)。
const want = directive === "ipv6" ? 6 : 4;if (literal !== null && literal !== want) { throw new Error( `[ProxyDispatcher] Proxy family directive ${directive} contradicts ${literal === 6 ? "IPv6" : "IPv4"} literal host` );}その後、具体的なファミリーがコネクターに固定されます。
- HTTP/HTTPS プロキシ(
ProxyAgent):proxyTls: { family, autoSelectFamily: false }— Happy Eyeballs を無効化し、選択したファミリーだけが接続に使用されるようにします。 - SOCKS5 プロキシ:カスタムコネクターが
socket_options: { family, autoSelectFamily: false }を SOCKS クライアントに渡します(SOCKS5 の互換性を参照)。
SOCKS5 の互換性
Section titled “SOCKS5 の互換性”ファミリーの固定は SOCKS5 プロキシでも機能しますが、標準の fetch-socks では、プロキシホップのファミリーを固定するために必要なソケットオプションが公開されていません。そのため、OmniRoute には独自のコネクターが含まれています。
export function buildSocksFamilySocketOptions(family: 4 | 6 | null): Record<string, unknown> { if (family === 6) return { family: 6, autoSelectFamily: false }; if (family === 4) return { family: 4, autoSelectFamily: false }; return {};}すべての SOCKS5 ディスパッチは、family の値にかかわらず(ホスト名に対する null / auto を含む)、createSocksDispatcherWithFamily を経由します。buildSocksFamilySocketOptions(null) は {} を返し、同じ SocksClient.createConnection + TLS buildConnector パスが socket_options の固定とともに使用されるため、IPv6 専用のエグレスポリシーに対して Happy Eyeballs が IPv4 を選択することはありません。
SOCKS5 サポート自体はデフォルトで有効です(ENABLE_SOCKS5_PROXY=false でオプトアウトできます)。PROXY_GUIDE.md → 環境変数を参照してください。
フェイルクローズ動作
Section titled “フェイルクローズ動作”このディレクティブの目的は、誤ったファミリーへ暗黙的にフォールバックするのではなく、接続を拒否することです。次の 2 つのガードによって、これが保証されます。
-
リテラルの矛盾 — IP リテラルのホストと矛盾するディレクティブは、ディスパッチャーの構築時に例外をスローします(上記の
resolveDispatcherFamily)。 -
ホスト名に対する事前 DNS チェック — ファミリーが固定されたホスト名プロキシの場合、
proxyFetch.tsはエグレスを開始する前に、assertHostnameSupportsFamilyを通じて、ホスト名に必要なファミリーのレコードが実際に存在することを確認します。open-sse/utils/proxyFamilyResolve.ts const hasFamily = records.some((r) => r.family === family);if (!hasFamily) {throw new Error(`[ProxyFamily] Proxy host ${host} has no ${family === 6 ? "IPv6 (AAAA)" : "IPv4 (A)"} record; ` +`refusing ${family === 6 ? "IPv6" : "IPv4"}-only egress (fail-closed)`);}失敗した場合、
proxyFetch.tsはエラーにcode = "PROXY_FAMILY_UNAVAILABLE"およびstatusCode = 503を設定します。DNS 解決の失敗も同様にフェイルクローズとして処理され、エグレスが拒否されます。
IP リテラルのホストに対する事前 DNS チェックは何も行いません。ファミリーは IP リテラル自体に内在しており、ルックアップは不要です。
データモデル
Section titled “データモデル”family カラムは、マイグレーション 099_proxy_family.sql によって 2つ のテーブルに追加されました。
-- src/lib/db/migrations/099_proxy_family.sqlALTER TABLE proxy_registry ADD COLUMN family TEXT NOT NULL DEFAULT 'auto';ALTER TABLE upstream_proxy_config ADD COLUMN family TEXT NOT NULL DEFAULT 'auto';proxy_registry.family— レジストリエントリに対するプロキシ単位のディレクティブです(src/lib/db/proxies.ts)。解決クエリでは、ほかのプロキシカラムとともにfamilyが選択され、値が存在しない場合や文字列でない場合は"auto"に変換されます。upstream_proxy_config.family— アップストリームプロキシエントリに対するディレクティブです(src/lib/db/upstreamProxy.ts)。同様に、デフォルト値は"auto"です。
解決済みのプロキシオブジェクトが auto 以外の family を持つ場合、proxyConfigToUrl は ?family= マーカーを追加します。これにより、指定された設定がディスパッチャーまで確実に維持されます。
関連ドキュメント
Section titled “関連ドキュメント”📖 関連ドキュメント:
HagiCode
HagiCode は構造化ワークフロー、マルチエージェント実行、Hero Dungeon ビューを備えたエージェント型コーディングワークスペースです。
よりスマートで速く、楽しいエージェント型ワークフローで、使いやすいソフトウェアを形にします。

- Smart構造化ワークフローは意図をアイデアから変更のリリースまで実行可能な道筋にします。
- Efficientマルチエージェントのワークフローで調査、実装、レビューを並行して進めます。
- FunHero Dungeon により長時間のコーディングを視覚的で協力的な体験にします。