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
56 changes: 3 additions & 53 deletions core/client.yaml.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -308,69 +308,19 @@ rules:
# 国内 DNS 与国内业务使用同一策略组;默认直连,无需等待 CN 地址库命中。
- IP-CIDR,223.5.5.5/32,🇨🇳 国内流量,no-resolve
- IP-CIDR,120.53.53.53/32,🇨🇳 国内流量,no-resolve
{APP_RULES}
{STUN_PROTOCOL_RULE}
# --- [P0] 规则更新 / GitHub raw 走代理,避免大陆网络下规则集刷新失败 ---
- DOMAIN-SUFFIX,raw.githubusercontent.com,🤖 AI 隐私出口

# --- [P1] Claude / Anthropic 及其依赖固定到同一个 IPv4 Xray 出口 ---
# Stash 不接受 Mihomo 的数组型 inline rule-provider payload;静态锚点直接放在 rules 中。
# 这些依赖规则必须早于动态 AI、广告拦截和公网直连规则。
# AI 核心域名由 sensitive-services.json 维护,并用于汇总 DNS 策略。
{SENSITIVE_RULES}
- DOMAIN-KEYWORD,stun,🤖 AI 隐私出口
- DOMAIN-SUFFIX,anthropic.com,🤖 AI 隐私出口
- DOMAIN-SUFFIX,clau.de,🤖 AI 隐私出口
- DOMAIN-SUFFIX,claude.ai,🤖 AI 隐私出口
- DOMAIN-SUFFIX,claude.com,🤖 AI 隐私出口
- DOMAIN-SUFFIX,claudemcpclient.com,🤖 AI 隐私出口
- DOMAIN-SUFFIX,claudemcpcontent.com,🤖 AI 隐私出口
- DOMAIN-SUFFIX,claudeusercontent.com,🤖 AI 隐私出口
- DOMAIN,servd-anthropic-website.b-cdn.net,🤖 AI 隐私出口
- DOMAIN,anthropic.com.cdn.cloudflare.net,🤖 AI 隐私出口
- DOMAIN,anthropic.auth0.com,🤖 AI 隐私出口
- DOMAIN,anthropic-com.ghost.io,🤖 AI 隐私出口
- DOMAIN-SUFFIX,sentry.io,🤖 AI 隐私出口
- DOMAIN-SUFFIX,statsigapi.net,🤖 AI 隐私出口
- DOMAIN,browser-intake-us5-datadoghq.com,🤖 AI 隐私出口
- DOMAIN-KEYWORD,datadog,🤖 AI 隐私出口
- DOMAIN-KEYWORD,sentry,🤖 AI 隐私出口
- DOMAIN-KEYWORD,sift,🤖 AI 隐私出口
- DOMAIN-SUFFIX,intercom.io,🤖 AI 隐私出口
- DOMAIN-SUFFIX,intercomcdn.com,🤖 AI 隐私出口
- DOMAIN,cdn.usefathom.com,🤖 AI 隐私出口
- IP-CIDR,160.79.104.0/21,🤖 AI 隐私出口,no-resolve
- IP-CIDR6,2607:6bc0::/32,🤖 AI 隐私出口,no-resolve
- IP-ASN,399358,🤖 AI 隐私出口,no-resolve
- GEOSITE,category-ntp,🤖 AI 隐私出口
- DST-PORT,123,🤖 AI 隐私出口
- DOMAIN-SUFFIX,openai.com,🤖 AI 隐私出口
- DOMAIN-SUFFIX,chatgpt.com,🤖 AI 隐私出口
- DOMAIN-SUFFIX,oaistatic.com,🤖 AI 隐私出口
- DOMAIN-SUFFIX,oaiusercontent.com,🤖 AI 隐私出口
- DOMAIN-SUFFIX,gemini.google.com,🤖 AI 隐私出口
- DOMAIN-SUFFIX,aistudio.google.com,🤖 AI 隐私出口
- DOMAIN-SUFFIX,generativelanguage.googleapis.com,🤖 AI 隐私出口
- DOMAIN-SUFFIX,notebooklm.google.com,🤖 AI 隐私出口
- DOMAIN-SUFFIX,perplexity.ai,🤖 AI 隐私出口
- DOMAIN-SUFFIX,cursor.com,🤖 AI 隐私出口
- DOMAIN-SUFFIX,cursor.sh,🤖 AI 隐私出口
- DOMAIN-SUFFIX,chat.com,🤖 AI 隐私出口
- DOMAIN,challenges.cloudflare.com,🤖 AI 隐私出口
- DOMAIN,accounts.google.com,🤖 AI 隐私出口
- DOMAIN,oauth2.googleapis.com,🤖 AI 隐私出口
# OpenAI 官方网络白名单(2026-09-05):认证、功能开关、上传、支付及 Apple 集成。
# https://help.openai.com/en/articles/9247338-network-recommendations-for-chatgpt-errors-on-web
- DOMAIN-SUFFIX,ct.sendgrid.net,🤖 AI 隐私出口
- DOMAIN-SUFFIX,featuregates.org,🤖 AI 隐私出口
- DOMAIN-SUFFIX,statsig.com,🤖 AI 隐私出口
- DOMAIN,featureassets.org,🤖 AI 隐私出口
- DOMAIN,prodregistryv2.org,🤖 AI 隐私出口
- DOMAIN,cdn.openaimerge.com,🤖 AI 隐私出口
- DOMAIN,cdn.workos.com,🤖 AI 隐私出口
- DOMAIN,forwarder.workos.com,🤖 AI 隐私出口
- DOMAIN,setup.workos.com,🤖 AI 隐私出口
- DOMAIN,images.workoscdn.com,🤖 AI 隐私出口
- DOMAIN,workos.imgix.net,🤖 AI 隐私出口
- DOMAIN,humb.apple.com,🤖 AI 隐私出口
- DOMAIN,js.stripe.com,🤖 AI 隐私出口
- RULE-SET,ai,🤖 AI 隐私出口
- RULE-SET,google,🌐 代理流量

Expand Down
7 changes: 6 additions & 1 deletion core/client_aggregate.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

from client_output import write_outputs
from client_policy import adapt_config
from sensitive_policy import domain_rules, app_rules
from profile_lock import profile_lock
from settings import DEVICE, NAME, load_settings, validate

Expand Down Expand Up @@ -200,9 +201,13 @@ def dns_policy(rules):
def compose(profile, device, target, nodes, sensitive):
template = (CORE / 'client.yaml.tmpl').read_text()
values = {name: '' for name in re.findall(r'\{([A-Z0-9_]+)\}', template)}
revision = hashlib.sha256(template.encode() + Path(__file__).read_bytes()).hexdigest()[:12]
revision = hashlib.sha256(template.encode() + Path(__file__).read_bytes()
+ (CORE / 'sensitive_policy.py').read_bytes()
+ (CORE / 'sensitive-services.json').read_bytes()
+ (CORE / 'client_policy.py').read_bytes()).hexdigest()[:12]
values.update(DEVICE=device, PROFILE_OWNER=profile, TARGET_LABEL=target,
STRICT_LABEL='false', TEMPLATE_REVISION=revision,
SENSITIVE_RULES=domain_rules(AI), APP_RULES=app_rules(target, device, AI),
SERVER_LABEL='Aggregate; per-server credentials remain in source profiles',
DNS_FOLLOW_RULE=' follow-rule: true' if target == 'stash' else ' respect-rules: true',
STUN_PROTOCOL_RULE=f' - PROTOCOL,STUN,{AI}' if target == 'stash' else '')
Expand Down
4 changes: 4 additions & 0 deletions core/client_policy.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ def remove_section(config, start, end):

def adapt_config(config, target, strict, ai_nodes):
if target == "stash":
# Stash accepts only IP literals for the bootstrap default-nameserver.
config = re.sub(r'(^ default-nameserver:\n)(.*?)(?=^ \S)',
lambda m: m[1] + re.sub(r'https://([0-9.]+)/dns-query', r'\1', m[2]),
config, flags=re.S | re.M)
# Stash manages its own TUN and protocol sniffing.
config = remove_section(config, "geodata-mode: true\n", "skip-proxy:\n")
config = remove_section(config, "tun:\n", "dns:\n")
Expand Down
5 changes: 5 additions & 0 deletions core/gen-clash.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from settings import load_settings, validate
from client_output import write_outputs
from client_policy import adapt_config
from sensitive_policy import domain_rules, app_rules

parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--check", action="store_true", help="只检查输出是否与当前配置一致,不写文件")
Expand Down Expand Up @@ -257,6 +258,8 @@ def direct_proxy_blocks(dev_uuid, hy2_password):
pathlib.Path(__file__).read_bytes() + TEMPLATE_PATH.read_bytes()
+ pathlib.Path(__file__).with_name("settings.py").read_bytes()
+ pathlib.Path(__file__).with_name("client_policy.py").read_bytes()
+ pathlib.Path(__file__).with_name("sensitive_policy.py").read_bytes()
+ pathlib.Path(__file__).with_name("sensitive-services.json").read_bytes()
).hexdigest()[:12]
for dev in devices:
uuid = env.get(f"REALITY_UUID_{dev}")
Expand Down Expand Up @@ -293,6 +296,8 @@ def direct_proxy_blocks(dev_uuid, hy2_password):
TARGET_LABEL=CLIENT_TARGET,
STRICT_LABEL=str(AI_STRICT_MODE).lower(),
TEMPLATE_REVISION=template_revision,
SENSITIVE_RULES=domain_rules("🤖 AI 隐私出口"),
APP_RULES=app_rules(CLIENT_TARGET, dev, "🤖 AI 隐私出口"),
DNS_FOLLOW_RULE=" follow-rule: true" if CLIENT_TARGET == "stash" else " respect-rules: true",
STUN_PROTOCOL_RULE=" - PROTOCOL,STUN,🤖 AI 隐私出口" if CLIENT_TARGET == "stash" else "",
SERVER_LABEL=(
Expand Down
146 changes: 146 additions & 0 deletions core/sensitive-services.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
{
"existing_ai": {
"suffixes": [
"anthropic.com",
"clau.de",
"claude.ai",
"claude.com",
"claudemcpclient.com",
"claudemcpcontent.com",
"claudeusercontent.com",
"statsigapi.net",
"intercom.io",
"intercomcdn.com",
"openai.com",
"chatgpt.com",
"oaistatic.com",
"oaiusercontent.com",
"gemini.google.com",
"aistudio.google.com",
"generativelanguage.googleapis.com",
"notebooklm.google.com",
"perplexity.ai",
"cursor.com",
"cursor.sh",
"chat.com",
"ct.sendgrid.net",
"featuregates.org",
"statsig.com"
],
"domains": [
"servd-anthropic-website.b-cdn.net",
"anthropic.com.cdn.cloudflare.net",
"anthropic.auth0.com",
"anthropic-com.ghost.io",
"browser-intake-us5-datadoghq.com",
"cdn.usefathom.com",
"challenges.cloudflare.com",
"accounts.google.com",
"oauth2.googleapis.com",
"featureassets.org",
"prodregistryv2.org",
"cdn.openaimerge.com",
"cdn.workos.com",
"forwarder.workos.com",
"setup.workos.com",
"images.workoscdn.com",
"workos.imgix.net",
"humb.apple.com",
"js.stripe.com"
]
},
"openai_dependencies": {
"suffixes": [],
"domains": [
"rum.browser-intake-datadoghq.com",
"o207216.ingest.sentry.io",
"o33249.ingest.sentry.io",
"events.statsigapi.net"
]
},
"antigravity": {
"suffixes": [
"antigravity.google",
"antigravity-unleash.goog"
],
"domains": [
"antigravity-pa.googleapis.com",
"antigravity.googleapis.com",
"cloudcode-pa.googleapis.com",
"daily-cloudcode-pa.googleapis.com",
"aicode.googleapis.com",
"businessaicode.googleapis.com",
"www.googleapis.com",
"aiplatform.googleapis.com",
"alkalimakersuiteapplets.pa.googleapis.com"
]
},
"additional_ai": {
"suffixes": [
"runwayml.com",
"replicate.com",
"replicate.delivery",
"fal.ai",
"fal.run",
"v0.app",
"windsurf.com",
"windsurf.build",
"codeium.com",
"codeiumdata.com",
"githubcopilot.com",
"grok.com",
"x.ai",
"openrouter.ai",
"huggingface.co"
],
"domains": [
"notebooklm-pa.googleapis.com"
]
},
"vertex_ai": {
"suffixes": [],
"domains": [
"global-aiplatform.googleapis.com",
"us-aiplatform.googleapis.com",
"eu-aiplatform.googleapis.com",
"africa-south1-aiplatform.googleapis.com",
"asia-east1-aiplatform.googleapis.com",
"asia-east2-aiplatform.googleapis.com",
"asia-northeast1-aiplatform.googleapis.com",
"asia-northeast2-aiplatform.googleapis.com",
"asia-northeast3-aiplatform.googleapis.com",
"asia-south1-aiplatform.googleapis.com",
"asia-southeast1-aiplatform.googleapis.com",
"asia-southeast2-aiplatform.googleapis.com",
"australia-southeast1-aiplatform.googleapis.com",
"australia-southeast2-aiplatform.googleapis.com",
"europe-central2-aiplatform.googleapis.com",
"europe-north1-aiplatform.googleapis.com",
"europe-southwest1-aiplatform.googleapis.com",
"europe-west1-aiplatform.googleapis.com",
"europe-west2-aiplatform.googleapis.com",
"europe-west3-aiplatform.googleapis.com",
"europe-west4-aiplatform.googleapis.com",
"europe-west6-aiplatform.googleapis.com",
"europe-west8-aiplatform.googleapis.com",
"europe-west9-aiplatform.googleapis.com",
"europe-west12-aiplatform.googleapis.com",
"me-central1-aiplatform.googleapis.com",
"me-central2-aiplatform.googleapis.com",
"me-west1-aiplatform.googleapis.com",
"northamerica-northeast1-aiplatform.googleapis.com",
"northamerica-northeast2-aiplatform.googleapis.com",
"southamerica-east1-aiplatform.googleapis.com",
"southamerica-west1-aiplatform.googleapis.com",
"us-central1-aiplatform.googleapis.com",
"us-east1-aiplatform.googleapis.com",
"us-east4-aiplatform.googleapis.com",
"us-east5-aiplatform.googleapis.com",
"us-south1-aiplatform.googleapis.com",
"us-west1-aiplatform.googleapis.com",
"us-west2-aiplatform.googleapis.com",
"us-west3-aiplatform.googleapis.com",
"us-west4-aiplatform.googleapis.com"
]
}
}
33 changes: 33 additions & 0 deletions core/sensitive_policy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
"""Curated AI domains shared by single-profile and aggregate routing."""
import json
from pathlib import Path
import re

DATA = Path(__file__).with_name('sensitive-services.json')


def domain_rules(group):
services = json.loads(DATA.read_text())
seen = set()
lines = []
for service in services.values():
for field, kind in (('domains', 'DOMAIN'), ('suffixes', 'DOMAIN-SUFFIX')):
for domain in service[field]:
if not re.fullmatch(r'[a-z0-9]+(?:[a-z0-9.-]*[a-z0-9])?', domain):
raise ValueError('敏感服务域名格式无效')
if (kind, domain) in seen:
raise ValueError('敏感服务域名重复')
seen.add((kind, domain))
lines.append(f' - {kind},{domain},{group}')
return '\n'.join(lines)


def app_rules(target, device, group):
# App bundle prefix matching is Stash-specific; do not export it to Mihomo.
# iOS ignores process rules, so its configuration relies on domain rules.
if device != 'mac':
return ''
if target == 'stash':
return '\n'.join(f' - PROCESS-NAME,/Applications/{app}.app/,{group}'
for app in ('Antigravity', 'Antigravity IDE'))
return f' - PROCESS-PATH-REGEX,^/Applications/Antigravity( IDE)?[.]app/.*,{group}'
6 changes: 4 additions & 2 deletions docs/aggregate-routing.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

| 分流入口 | 默认策略 | 可手动调整 |
|---|---|---|
| AI / Meta 服务(包括 Facebook、Instagram、WhatsApp 等) | 敏感服务主备 | 手动选择 |
| AI / Meta 服务(包括 Antigravity 应用、Facebook、Instagram、WhatsApp 等) | 敏感服务主备 | 手动选择 |
| 海外流量,包括未匹配请求 | 自动测速 | 手动选择 |
| 国内流量 | DIRECT | 自动测速、手动选择 |
| Apple 基础服务 | DIRECT;Siri 等既有海外规则仍进入海外流量 | 自动测速、手动选择 |
Expand Down Expand Up @@ -49,7 +49,7 @@ AI 和 Meta 的明确域名/域名集共用敏感服务的加密解析器地址

DNS 分组覆盖静态精确/后缀规则及对应 AI、Meta geosite 集合。域名规则无法推断未知第三方依赖或请求归属;原有 DOMAIN-KEYWORD、IP、协议规则与 DNS 策略也不是逐条等价。新增网站依赖时仍需补精确规则;通配 DNS 规则可能优先于 geosite,见 [Stash DNS 文档](https://stash.wiki/features/dns-server)。

Meta 的静态域名和远程域名集优先于广告拦截、国内分流和普通兜底;AI 认证、上传、监控等已知依赖继续复用共享模板。汇总仅增补策略,不复制维护另一份完整业务规则。
Meta 的静态域名和远程域名集优先于广告拦截、国内分流和普通兜底;AI 认证、上传、监控等已知依赖由共享敏感服务清单生成。汇总仅增补策略,不复制维护另一份完整业务规则。

## 验证

Expand All @@ -61,3 +61,5 @@ python3 -m unittest discover -s tests -p 'test_*.py'
```

真实连通性、Stash 导入后的组选择和设备网络差异,需要在各设备上验证。规则策略类型依据 [Stash 策略组文档](https://stash.wiki/proxy-protocols/proxy-groups)。

Antigravity 应用规则、AI 清单来源、Stash 版本要求及已知覆盖限制见 [AI 分流覆盖说明](ai-routing-coverage.md)。
Loading