Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,8 @@ les adresses IPv6 entre crochets et `*` ; par exemple, indiquez explicitement `
restent bloquées. Les requêtes de diagnostic rejettent les redirections et signalent une cible dont les identifiants ont été retirés. L'examen des
redirections des requêtes ordinaires vers les fournisseurs reste distinct de cette protection de diagnostic.

Deux accommodements fake-IP DNS existent pour les utilisateurs de Clash / Surge / Mihomo, et tous deux ne s'appliquent qu'aux *réponses* DNS — une adresse littérale dans l'URL reste rejetée. La plage de benchmark IANA `198.18.0.0/15` (et ses écritures IPv6 IPv4-mapped) est acceptée dès qu'un proxy sortant s'applique à l'hôte. La plage IPv6 fake-IP par défaut de Mihomo `fdfe:dcba:9876::/48` est acceptée sous une condition plus stricte : la variable de proxy correspondant au schéma de l'URL (`HTTPS_PROXY` pour `https:`, `HTTP_PROXY` pour `http:` ; `ALL_PROXY` ne compte pas) doit être définie, l'hôte ne doit pas correspondre à `NO_PROXY`, et la requête est alors explicitement liée à ce proxy. Tout autre ULA, un préfixe adjacent ou une réponse fake-IP mélangée à une vraie réponse privée exige toujours `allowPrivateNetwork: true`. La validation à l'enregistrement du fournisseur n'applique jamais l'accommodement IPv6.

## Groupe de comptes Codex

Utilisez **Codex Auth** dans le tableau de bord pour ajouter des comptes au groupe et actualiser les quotas. `config.json` stocke les
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,8 @@ API キープロバイダーは、リテラルキーまたは環境参照を保

プライベート/ローカル宛先には `allowPrivateNetwork: true` が必要で、送信プロキシがアクティブな場合は、一致する `NO_PROXY` エントリが必要です。ループバックは自動的に追加されます。 CIDR エントリは解釈されないため、各 LAN ホストを明示的にリストします。マッチャーは、正確なホスト、ドメイン サフィックス、オプションのポート、括弧で囲まれた IPv6、および `*` をサポートします。たとえば、`192.168.1.50` を明示的にリストします。メタデータとリンクローカル宛先はブロックされたままになります。診断リクエストはリダイレクトを拒否し、資格情報が剥奪されたターゲットを報告します。通常のプロバイダー要求のリダイレクト レビューは、この診断ガードとは独立したままになります。

Clash / Surge / Mihomo 利用者向けの fake-IP DNS 例外は 2 種類あり、いずれも DNS の*応答*にのみ適用されます。URL に書かれたリテラルアドレスは引き続き拒否されます。IANA ベンチマーク範囲 `198.18.0.0/15`(IPv4-mapped IPv6 表記を含む)は、そのホストにアウトバウンドプロキシが適用される場合に許可されます。Mihomo の既定 IPv6 fake-IP 範囲 `fdfe:dcba:9876::/48` はより厳しい条件でのみ許可されます。URL スキームに一致するプロキシ変数(`https:` は `HTTPS_PROXY`、`http:` は `HTTP_PROXY`、`ALL_PROXY` は対象外)が設定されていること、ホストが `NO_PROXY` に一致しないことが必要で、その場合リクエストはそのプロキシに明示的に固定されます。それ以外の ULA、隣接プレフィックス、実際のプライベート応答と混在した fake-IP 応答には引き続き `allowPrivateNetwork: true` が必要です。プロバイダー保存時の検証には IPv6 例外は適用されません。

## Codexアカウントプール

pool アカウントの追加と quota 更新はダッシュボードの **Codex Auth** ページで処理してください。設定には secret で
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,8 @@ API 키 공급자는 리터럴 키나 환경 참조를 둘 수 있습니다. OAu

사설/로컬 목적지는 `allowPrivateNetwork: true`가 필요하며, 아웃바운드 프록시가 활성화된 경우에는 일치하는 `NO_PROXY` 항목도 필요합니다. loopback은 자동으로 추가됩니다. CIDR 항목은 해석하지 않으므로 각 LAN 호스트는 따로 적어야 합니다. matcher는 정확한 호스트, 도메인 접미사, 선택적 포트, 괄호로 감싼 IPv6, `*`를 지원합니다. 예를 들면 `192.168.1.50`은 따로 적어야 합니다. 메타데이터와 link-local 목적지는 계속 차단됩니다. 진단 요청은 리디렉션을 거부하고, 자격 증명이 제거된 대상만 보고합니다. 일반적인 공급자 요청의 리디렉션 검토는 이 진단 가드와 별도로 유지됩니다.

Clash / Surge / Mihomo 사용자를 위한 fake-IP DNS 예외는 두 가지이며, 둘 다 DNS *응답*에만 적용됩니다. URL에 적힌 리터럴 주소는 그대로 거부됩니다. IANA 벤치마크 대역 `198.18.0.0/15`(IPv4-mapped IPv6 표기 포함)은 해당 호스트에 아웃바운드 프록시가 적용될 때 허용됩니다. Mihomo 기본 IPv6 fake-IP 대역 `fdfe:dcba:9876::/48`은 더 엄격한 조건에서만 허용됩니다. URL 스킴에 맞는 프록시 변수(`https:`는 `HTTPS_PROXY`, `http:`는 `HTTP_PROXY`, `ALL_PROXY`는 해당 없음)가 설정되어 있어야 하고, 호스트가 `NO_PROXY`에 걸리지 않아야 하며, 그 경우 요청은 해당 프록시에 명시적으로 묶여 나갑니다. 그 밖의 ULA, 인접 프리픽스, 실제 사설 응답과 섞인 fake-IP 응답은 여전히 `allowPrivateNetwork: true`가 필요합니다. 프로바이더 저장 시점 검증에는 IPv6 예외가 적용되지 않습니다.

## Codex 계정 풀

pool 계정 추가와 quota 갱신은 대시보드의 **Codex Auth** 페이지에서 처리하세요. 설정에는 secret이
Expand Down
18 changes: 11 additions & 7 deletions docs-site/src/content/docs/reference/cli/lifecycle.md
Original file line number Diff line number Diff line change
Expand Up @@ -242,18 +242,22 @@ interrupted package update removed either file, it logs one `installation is inc
stops instead of retrying the same missing executable every five seconds. Reinstall opencodex, then
run `ocx service repair` to refresh the task with the restored package paths.

On Linux, the systemd unit invokes the first regular, executable `ocx` file found on `PATH` at
install time rather than the Bun and CLI paths inside the installed package tree. Version managers such as
On macOS and Linux, the launchd plist and the systemd unit invoke the first regular, executable
`ocx` file found on `PATH` at install time rather than the Bun and CLI paths inside the installed
package tree. Version managers such as
**mise** and **asdf** install into a versioned directory and delete the old one on upgrade, which
used to leave the unit pointing at files that no longer existed — systemd then restart-looped while
still reporting the service as installed. A shim path survives the upgrade, so the unit keeps
resolving. Source checkouts without an `ocx` launcher keep the previous direct Bun + CLI form. A
used to leave the service definition pointing at files that no longer existed — systemd then
restart-looped while still reporting the service as installed, and launchd kept the old build serving
until it was restarted by hand. A shim path survives the upgrade, so the definition keeps resolving. Source checkouts without an `ocx` launcher keep the previous direct Bun + CLI form. A
trusted `OPENCODEX_BUN_PATH` selected before Bun starts is preserved through the shim; package-local
bundled Bun paths are deliberately rediscovered after upgrades instead of being pinned in the unit.

Units installed before this change still carry the old versioned paths and cannot migrate
Definitions installed before this change still carry the old versioned paths and cannot migrate
themselves — once the old executable is deleted, no opencodex code runs to fix it. Run
`ocx service repair` once after upgrading; subsequent version changes need no action.
`ocx service repair` once after upgrading; after that, each service start follows the launcher.
An already-running proxy is not replaced by an external upgrade: restart the service (or run
`ocx service repair`) so the new build serves, and treat a CLI/proxy version mismatch warning as
exactly that signal.

| Subcommand | Action |
| --- | --- |
Expand Down
10 changes: 10 additions & 0 deletions docs-site/src/content/docs/reference/configuration/providers.md
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,16 @@ destinations stay blocked. Diagnostic
requests reject redirects and report a credential-stripped target. Ordinary provider request redirect
review remains separate from this diagnostic guard.

Two fake-IP DNS accommodations exist for Clash / Surge / Mihomo users, and both apply to DNS
*answers* only — a literal address in the URL is still rejected. The IANA benchmark range
`198.18.0.0/15` (and its IPv4-mapped IPv6 spellings) is accepted whenever an outbound proxy applies
to the host. Mihomo's default IPv6 fake-IP range `fdfe:dcba:9876::/48` is accepted on a stricter
gate: the proxy variable that matches the URL scheme (`HTTPS_PROXY` for `https:`, `HTTP_PROXY` for
`http:`; `ALL_PROXY` does not count) must be set, the host must not match `NO_PROXY`, and the
request is then bound to that proxy explicitly. Any other ULA, an adjacent prefix, or a fake-IP answer
mixed with a real private answer still requires `allowPrivateNetwork: true`. Provider save-time
validation never applies the IPv6 accommodation.

## Codex account pool

Use **Codex Auth** in the dashboard to add pool accounts and refresh quotas. `config.json` stores
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,8 @@ domain suffix, необязательные порты, IPv6 в квадратн
не следуют redirect'ам и в результатах показывают только credential-stripped target. Проверка
redirect'ов для обычных provider-request'ов реализована отдельно и к этому guard не относится.

Для пользователей Clash / Surge / Mihomo предусмотрены два исключения fake-IP DNS, и оба применяются только к DNS-*ответам* — литеральный адрес в URL по-прежнему отклоняется. Диапазон IANA benchmark `198.18.0.0/15` (включая IPv4-mapped IPv6 записи) принимается, когда к хосту применяется исходящий прокси. Диапазон IPv6 fake-IP по умолчанию в Mihomo `fdfe:dcba:9876::/48` принимается при более строгом условии: должна быть задана переменная прокси, соответствующая схеме URL (`HTTPS_PROXY` для `https:`, `HTTP_PROXY` для `http:`; `ALL_PROXY` не учитывается), хост не должен совпадать с `NO_PROXY`, и тогда запрос явно привязывается к этому прокси. Любой другой ULA, соседний префикс или fake-IP ответ вперемешку с реальным приватным ответом по-прежнему требуют `allowPrivateNetwork: true`. Валидация при сохранении провайдера никогда не применяет IPv6-исключение.

## Пул аккаунтов Codex

Используйте страницу **Codex Auth** дашборда для добавления аккаунтов пула и обновления квот.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,8 @@ hedefleri engellenmiş olarak kalır. Teşhis istekleri yönlendirmeleri reddede
kimlik bilgisi kaldırılmış bir hedef bildirir. Sıradan sağlayıcı isteği yeniden
yönlendirme incelemesi bu teşhis korumasından ayrı kalır.

Clash / Surge / Mihomo kullanıcıları için iki fake-IP DNS istisnası vardır ve ikisi de yalnızca DNS *yanıtlarına* uygulanır; URL'deki literal adres yine reddedilir. IANA benchmark aralığı `198.18.0.0/15` (IPv4-mapped IPv6 yazımları dahil), ana bilgisayara bir giden proxy uygulandığında kabul edilir. Mihomo'nun varsayılan IPv6 fake-IP aralığı `fdfe:dcba:9876::/48` daha sıkı bir koşulla kabul edilir: URL şemasıyla eşleşen proxy değişkeni (`https:` için `HTTPS_PROXY`, `http:` için `HTTP_PROXY`; `ALL_PROXY` sayılmaz) ayarlı olmalı, ana bilgisayar `NO_PROXY` ile eşleşmemeli ve istek daha sonra açıkça o proxy'ye bağlanır. Diğer tüm ULA'lar, komşu önekler veya gerçek bir özel yanıtla karışık fake-IP yanıtları hâlâ `allowPrivateNetwork: true` gerektirir. Sağlayıcı kaydetme zamanı doğrulaması IPv6 istisnasını hiçbir zaman uygulamaz.

## Codex hesap havuzu

Havuz hesapları eklemek ve kotaları yenilemek için kontrol panelinde **Codex
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,8 @@ API key 提供者可以持有字面量 key,或环境引用。OAuth 提供者

私有/本地目标需要 `allowPrivateNetwork: true`,并且在出站代理启用时,还需要匹配的 `NO_PROXY` 条目。回环地址会自动加入;每个 LAN 主机都必须显式列出,因为 CIDR 条目不会被解释。匹配器支持精确主机、域后缀、可选端口、带方括号的 IPv6 以及 `*`;例如,应显式列出 `192.168.1.50`。元数据和链路本地目标仍会被阻止。诊断请求会拒绝重定向,并报告一个已剥离凭据的目标。普通提供者请求的重定向审查仍然独立于这个诊断保护。

面向 Clash / Surge / Mihomo 用户的 fake-IP DNS 例外有两种,且都只作用于 DNS *应答*——URL 中的字面地址仍会被拒绝。IANA 基准段 `198.18.0.0/15`(含 IPv4-mapped IPv6 写法)在该主机适用出站代理时被接受。Mihomo 默认的 IPv6 fake-IP 段 `fdfe:dcba:9876::/48` 采用更严格的门槛:必须设置与 URL 协议匹配的代理变量(`https:` 对应 `HTTPS_PROXY`,`http:` 对应 `HTTP_PROXY`,`ALL_PROXY` 不算),主机不能命中 `NO_PROXY`,随后请求会被显式绑定到该代理。其他 ULA、相邻前缀,或与真实私网应答混合的 fake-IP 应答仍需要 `allowPrivateNetwork: true`。提供方保存时的校验不应用该 IPv6 例外。

## Codex 账户池

请在仪表盘 **Codex Auth** 页面添加 pool account 并刷新 quota。配置只保存非 secret account
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ API-key 供應商可持有字面值金鑰或環境參考。OAuth 供應商使用

私有/本機目的地需要 `allowPrivateNetwork: true`,且當對外代理活躍時需要相符的 `NO_PROXY` 項目。回送會自動加入;請明確列出每個 LAN 主機,因為 CIDR 項目不被解讀。比對器支援精確主機、網域後綴、可選連接埠、方括號 IPv6 與 `*`;例如,明確列出 `192.168.1.50`。中繼資料與 link-link 目標保持被封鎖。診斷請求拒絕重新導向並回報已剝離憑證的目標。普通供應商請求的重新導向審查與此診斷防護分開。

針對 Clash / Surge / Mihomo 使用者的 fake-IP DNS 例外有兩種,且都只作用於 DNS *回應*——URL 中的字面位址仍會被拒絕。IANA 基準區段 `198.18.0.0/15`(含 IPv4-mapped IPv6 寫法)在該主機適用對外代理時被接受。Mihomo 預設的 IPv6 fake-IP 區段 `fdfe:dcba:9876::/48` 採更嚴格的門檻:必須設定與 URL 協定相符的代理變數(`https:` 對應 `HTTPS_PROXY`,`http:` 對應 `HTTP_PROXY`,`ALL_PROXY` 不算),主機不得命中 `NO_PROXY`,之後請求會被明確綁定到該代理。其他 ULA、相鄰前綴,或與真實私網回應混合的 fake-IP 回應仍需要 `allowPrivateNetwork: true`。提供者儲存時的驗證不套用此 IPv6 例外。

## Codex 帳號池

在儀表板中使用 **Codex Auth** 新增池帳號並重新整理配額。`config.json` 儲存非秘密中繼資料;access 與 refresh token 使用強化的憑證存放。池路由將新/未綁定指派、基於用量的主動切換與失敗復原分開。綁定任務通常保留親和性,但 `quota` 可在其超過用量閾值後的下一個請求時重新綁定它,而暫停、冷卻、重新認證與失敗處理可獨立清除或移動路由。未綁定請求沒有即時帳號綁定;這可包含代理重啟或親和性重置後的既有可見任務。Pre-stream 的 429 或 402 在同一個請求中於一個合格的備用帳號上重試一次,即使基於用量的主動切換關閉。帳號變更保留並重播對話 context,但跨帳號的供應商端 prompt-cache 重用不保證,cache 可能需要重新暖機。
Expand Down
33 changes: 31 additions & 2 deletions src/lib/destination-policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,25 @@ function isBenchmarkDnsAnswer(address: string, assessment: DestinationAssessment
return embedded.kind === "private" && embedded.detail === "benchmark address";
}

/**
* Mihomo (Clash.Meta) fake-IP DNS answers IPv6 queries from `fdfe:dcba:9876::/48` — its
* documented default `fake-ip-range6` (#3462). That prefix sits inside ULA `fc00::/7`, so
* `classifyIpv6` reports it as a private-network address and, unlike the IPv4 benchmark
* range, nothing about the address itself marks it synthetic. The exception is therefore
* narrower than the benchmark one: exact /48 match, DNS answers only (a literal URL still
* rejects), and only behind the `allowMihomoIpv6FakeIp` opt-in that the outbound caller
* derives from a scheme-matched proxy it then binds the request to.
*/
const MIHOMO_IPV6_FAKE_IP_PREFIX = [0xfdfe, 0xdcba, 0x9876] as const;

function isMihomoIpv6FakeIpAnswer(address: string, assessment: DestinationAssessment | null): boolean {
if (assessment?.kind !== "private" || assessment.detail !== "private-network address") return false;
if (isIP(address) !== 6) return false;
const hextets = ipv6Hextets(normalizeHostname(address));
if (!hextets) return false;
return MIHOMO_IPV6_FAKE_IP_PREFIX.every((group, index) => hextets[index] === group);
}

function firstIpv6Hextet(hostname: string): number | null {
const head = hostname.split(":")[0];
if (!head) return 0;
Expand Down Expand Up @@ -385,7 +404,13 @@ export function assessUrlDestination(url: string): UrlDestinationAssessment | nu
*/
export async function resolvePublicAddresses(
url: string,
options?: string | { context?: string; allowPrivateNetwork?: boolean; allowBenchmarkAddresses?: boolean },
options?: string | {
context?: string;
allowPrivateNetwork?: boolean;
allowBenchmarkAddresses?: boolean;
/** Mihomo IPv6 fake-IP (`fdfe:dcba:9876::/48`) DNS answers; see `isMihomoIpv6FakeIpAnswer`. */
allowMihomoIpv6FakeIp?: boolean;
},
): Promise<{
hostname: string;
addresses: { address: string; family: number }[];
Expand All @@ -396,6 +421,7 @@ export async function resolvePublicAddresses(
: options?.context?.trim() || "image URL";
const privateNetworkAllowed = typeof options === "object" && options?.allowPrivateNetwork === true;
const benchmarkAllowed = typeof options === "object" && options?.allowBenchmarkAddresses === true;
const mihomoIpv6Allowed = typeof options === "object" && options?.allowMihomoIpv6FakeIp === true;
let hostname: string;
try {
hostname = normalizeHostname(new URL(url.trim()).hostname);
Expand Down Expand Up @@ -440,7 +466,10 @@ export async function resolvePublicAddresses(
// fake-IP DNS, not a LAN provider. Accept it without allowPrivateNetwork and
// do not mark the destination private, so the caller's HTTP(S)_PROXY path
// still applies (credit #1748).
if (benchmarkAllowed && isBenchmarkDnsAnswer(address, assessment)) {
if (
(benchmarkAllowed && isBenchmarkDnsAnswer(address, assessment))
|| (mihomoIpv6Allowed && isMihomoIpv6FakeIpAnswer(address, assessment))
) {
validatedAddresses.push({ address, family: ipKind === 4 || ipKind === 6 ? ipKind : (family || 4) });
continue;
}
Expand Down
Loading
Loading