本文件是 Smart-Config-Kit 仓库的强约束维护契约,所有自动化代理(Claude Code、Codex、Copilot 等)在修改本仓库前必须完整阅读并遵守。 违反以下任何一条,PR 必须退回重做。
本仓库维护「同一套 Mihomo Smart 分流策略」在 12 个客户端形态下的等价实现:
| # | 形态 | 文件 | 角色 |
|---|---|---|---|
| 0 | Clash Party(JS 覆写脚本) | Clash Party/ClashParty(mihomo-smart).js |
唯一主线 / 事实基线 |
| 1 | Clash Meta For Android(原生 YAML) | Clash Meta For Android/CMFA(mihomo).yaml |
从属 |
| 2 | OpenClash 轻量版(shell + heredoc YAML) | OpenClash/OpenClash(mihomo).sh |
从属(低内存裁剪) |
| 3 | OpenClash 完整版(shell + heredoc YAML) | OpenClash/OpenClash(mihomo-smart).sh |
从属(全量) |
| 4 | Shadowrocket(iOS SR 私有 conf) | Shadowrocket/Shadowrocket.conf |
从属 |
| 5 | SingBox Full(JSON,由脚本生成) | SingBox/SingBox(sing-box)-full.json + SingBox/SingBox(sing-box)-generator.js |
从属(生成产物) |
| 6 | v2rayN Xray 路由 JSON | v2rayN/v2rayN(xray).json |
从属(仅 Xray 核心兜底;v2rayN 推荐用 mihomo/sing-box 核心直接加载 #1 或 #5) |
| 7 | Surge(iOS / macOS 付费正版 .conf) |
Surge/Surge.conf |
从属(独立引擎) |
| 8 | Loon(iOS 付费正版 .conf) |
Loon/Loon.conf |
从属(独立引擎) |
| 9 | Quantumult X(iOS 付费正版 .conf) |
Quantumult X/QuantumultX.conf |
从属(独立引擎) |
| 10 | Passwall(OpenWrt 全功能 LuCI) | Passwall/Passwall(xray+sing-box)-apply.sh + Passwall/shunt-rules/*.list |
从属(展平降级) |
| 10B | Passwall2(OpenWrt 精简分流 LuCI) | Passwall2/Passwall2(xray+sing-box)-apply.sh + Passwall2/shunt-rules/*.list |
从属(展平降级;与 #10 共享规则语法) |
| 11 | FlClash(JS 覆写脚本) | FlClash/FlClash(mihomo).js |
从属(Clash Party Normal JS 的 FlClash 移植版;url-test 区域组,无 Smart+LightGBM) |
基线原则: Clash Party JS 脚本是唯一的「策略权威源」。其他产物必须在语义上与其一致;仅在平台能力受限处允许差异(见 §3)。
关于 v2rayN: v2rayN 是多核调度器,不是独立内核。推荐使用路径是在 v2rayN 里切到 mihomo 或 sing-box 核心,然后加载 #1 / #5;这种情况下 v2rayN 本身不是独立产物,无需单独同步。仅当 v2rayN 用户坚持走 Xray 核心时才用到
v2rayN/v2rayN(xray).json(功能裁剪版,只有 proxy/direct/block 三出站),此文件是独立产物,受本文约束。关于 Hiddify: Hiddify 内核即 sing-box(修改版
hiddify-sing-box),直接消费SingBox/SingBox(sing-box)-full.json,不需要独立产物;SingBox/README.md §2a提供 Hiddify 专用导入说明。关于 ClashMi(
KaringX/clashmi,KaringX 跨平台 Flutter GUI,覆盖 iOS/macOS/Android/Windows/Linux): bundle 的是 MetaCubeX mihomo mainline(非vernesong/mihomoSmart fork),与 CMFA 内核同源,直接消费Clash Meta For Android/CMFA(mihomo).yaml,不需要独立产物;Clash Meta For Android/README.md §九提供 ClashMi 专用导入说明。注意 ClashMi 的内核定制会把GEOIP,*/GEOSITE,*规则强制转换为对应 rule-set、iOS 端不支持 IP-ASN 数据库、tun:由 App UI 托管不在 YAML 手写(官方 FAQ)——这三点对本仓库 YAML 零影响(0 条 GEOIP、0 条 GEOSITE、0 条 ASN、无tun:块)。与 Hiddify 对称:Hiddify 是 sing-box 的跨平台 GUI / ClashMi 是 mihomo 的跨平台 GUI。关于 ShellClash(
juewuy/ShellCrash): 内核是 mihomo,直接复用Clash Meta For Android/CMFA(mihomo).yaml或OpenClash/OpenClash(mihomo).sh里的 heredoc YAML 块,不需要独立产物。关于 HomeProxy(OpenWrt 官方 sing-box LuCI 插件): 内核就是 sing-box,直接导入
SingBox/SingBox(sing-box)-full.json,不需要独立产物;SingBox/README.md §2b提供 HomeProxy 专用导入说明。关于 FlClash(
chen08209/FlClash,跨平台 Flutter GUI,覆盖 Android/Windows/macOS/Linux): 内核是标准 Mihomo(非 Smart fork),自 v0.8.85 起支持 JS 覆写脚本。本仓库提供两条路径:(1)覆写脚本FlClash/FlClash(mihomo).js(推荐;动态节点分类 + 订阅垃圾清理 + 家宽识别);(2)静态 YAMLClash Meta For Android/CMFA(mihomo).yaml(备选;直接导入无需脚本)。两者规则等价,仅区域组类型不同(url-test)。FlClash 覆写脚本与 Clash Party Normal JS 同构(同 REGION_DB + 同 rule-providers + 同 rules),差异仅在于 overwriteGeneral 裁剪(TUN/端口由 FlClash App UI 托管)和 console.log 条件包装(兼容 QuickJS)。关于 Passwall / Passwall2: 这两款是
Openwrt-Passwall组织(原xiaorouji个人仓库已于 2025 年前后迁入该组织,访问旧 URL 会 301 跳转)并行维护的两款独立 OpenWrt 插件(不是新旧关系;2026-04 两者发版仅差 4 天)。Passwall = 全功能(直连/屏蔽/GFW/代理 4 列表 + 分流 + ACL +trojan-plus节点 + TCP/UDP 节点分选);Passwall2 = 精简分流(砍掉四列表 + ACL,只保留 keyword/domain/geosite/geoip 匹配 + 统一节点选择)。两者底层都是 xray-core + sing-box 双栈(都不打包 mihomo),都没有 mihomo 的 proxy-groups 嵌套选择器(两级select/url-test+ Smart + LightGBM)——Lua CBI 表单式 UI 没有 YAML 嵌套组语义。规则语法两者完全相同(共用shunt_rules.lua:纯字符串 /domain:/full:/regexp:/geosite:/rule-set:remote|local:/geoip:+ CIDR;不支持 Clash 的DOMAIN-SUFFIX,/DOMAIN-KEYWORD,/IP-CIDR,前缀)。本仓库提供两个独立目录:
Passwall/— Passwall 全功能版参考(CONFIG_NAME="passwall";含四列表/ACL/TCP-UDP 分选说明)Passwall2/— Passwall2 精简版参考(CONFIG_NAME="passwall2")两者
.list文件内容互通、规则语法同源。想要 mihomo 完整体验(嵌套组 + Smart + LightGBM)请迁移到 OpenClash(本仓库OpenClash/)。关于 SSR Plus+: 架构老旧 + 已停止维护,没有 geosite/rule_set 层能力。不提供产物,建议直接换 OpenClash。
关于 Surge / Loon / Quantumult X: 这三款 iOS/macOS 付费客户端各自使用私有
.conf语法,与 Shadowrocket 部分兼容但不完全一致,因此每个都是独立产物。其中:
- Surge 与 Shadowrocket 语法最接近(~90% 兼容),从 Shadowrocket 迁移改动最小
- Loon 兼容 Surge 的
[Rule] RULE-SET语法,但 [General] DNS 字段和 MMDB 配置方式不同- Quantumult X 使用完全独立的
[policy]/[filter_remote]/[filter_local]结构,由tools/srk_to_qx.py或等价脚本从 Shadowrocket 自动转换生成
这是本仓库最核心且不可违反的规则。
- 新增/删除/重命名任何代理组(含 18 个区域组〔9 全部 + 9 家宽〕与 31 个业务组)
- 新增/删除/修改任何 rule-provider(含 URL、behavior、format、interval、proxy 字段)
- 修改规则条目的目标组(例如把
RULE-SET,tiktok从📱 社交媒体改到其他组) - 修改规则顺序中影响命中优先级的段(特别是广告拦截、GFW、FINAL 前置关系)
- 修改 DNS / Sniffer / fake-ip / GeoX URL / LightGBM URL 等全局行为
- 修改 规则源仓库(MetaCubeX / blackmatrix7 / Loyalsoldier / szkane / Accademia 等)
- 修复「节点名分类 / 订阅合并 / 区域组 fallback」等运行时逻辑 bug(自 v5.2.6 起强制加入触发条件 —— 参见 §1.5 同构 bug 审计)
若本次改动命中上述任一触发条件,PR 必须:
- 先改 Clash Party JS 主线(
Clash Party/ClashParty(mihomo-smart).js),作为唯一权威源。 - 同步修改全部 12 个目录产物(或明确在 PR 说明里标注为何某个产物不受影响):
Clash Meta For Android/CMFA(mihomo).yamlOpenClash/OpenClash(mihomo).shOpenClash/OpenClash(mihomo-smart).shShadowrocket/Shadowrocket.confSingBox/SingBox(sing-box)-full.json(通过node SingBox/SingBox(sing-box)-generator.js重新生成,不允许手工改)v2rayN/v2rayN(xray).json(仅当业务组/规则类别发生变化时需同步;Xray 只有 proxy/direct/block 三出站,单纯区域选择/LightGBM 调整可豁免)Surge/Surge.conf(与 Shadowrocket 保持 ~1:1 规则行;仅 [General] DNS/MMDB 不同)Loon/Loon.conf(从 Surge 迁移;头部 + [General] 不同,[Rule] 段基本同 Surge)Quantumult X/QuantumultX.conf(Shadowrocket → QX 转换;policy / filter_remote / filter_local 三段结构;可由等价脚本重新生成)Passwall/Passwall(xray+sing-box)-apply.sh+Passwall/shunt-rules/*.list(展平降级参考;仅当业务组/规则类别变化时需同步)Passwall2/Passwall2(xray+sing-box)-apply.sh+Passwall2/shunt-rules/*.list(同上;与 Passwall 同步联动)FlClash/FlClash(mihomo).js(Clash Party Normal JS 的 FlClash 移植版;规则/组/DNS 与主线对齐,overwriteGeneral 裁剪 TUN/端口)
- 同步更新每个产物头部的「介绍 / 更新日志 / 版本号」注释块(见 §1.3 强制注释字段)。
- 同步更新文档:根
README.md+ 对应子目录的README.md(原使用方法.md/使用教程.md已统一重命名为README.md,GitHub 子目录视图会自动渲染),必要时CHANGELOG。 - 自检命令必须通过(§2)。
- 提交前在 PR 描述里列出「影响面」:
- 改动的代理组 / rule-provider / 规则行数
- 每个产物的同步位置(行号或 commit diff 摘要)
- 手动跳过同步的产物及原因
设计原则(自 v5.2.4 起):
- 产物文件头部只保留轻量元信息——名称 / 版本号 / Build 日期 / 架构一句话 / 基线声明 / 指向 CHANGELOG.md。
- 详细变更历史全部集中在
<子目录>/CHANGELOG.md里;不再埋在配置文件头部。 - 这样做的好处:
- 用户打开 GitHub 子目录时,README.md 自动渲染在下方(使用说明);CHANGELOG.md 独立成文(历史),配置文件不被大段注释淹没。
- 工具改动时不用同时 diff 配置文件头 + 任何其它文件,只需在 CHANGELOG.md 顶部追加一节。
每次改动必须同步的两处:
- 产物文件头(简短):bump 版本号尾段、更新 Build 日期
<子目录>/CHANGELOG.md顶部:新增一节记录本次改动
产物文件头的最小内容(按顺序):
- 产物名称 + 版本号(与 Clash Party 主版本对齐,加平台后缀)
- Build 日期(YYYY-MM-DD)
- 架构一句话(例如「18 区域〔9 全部 + 9 家宽〕 + 31 业务(含 13 流媒体平台组)+ 370+ RULE-SET」)
- 基线声明(「基线:Clash Party vX.Y.Z(唯一主线)」)
- 指向 CHANGELOG:「变更历史:见
<子目录>/CHANGELOG.md」 - 若有风险或代价,一行标注(OOM / 首次延迟 / iOS 限制)
对应到各产物文件:
| 文件 | 头部位置 | 版本变量/字段 | CHANGELOG.md |
|---|---|---|---|
Clash Party/ClashParty(mihomo-smart).js |
顶部 // 注释 |
JS 内 const VERSION |
Clash Party/CHANGELOG.md |
Clash Meta For Android/CMFA(mihomo).yaml |
顶部 # 注释块 |
第一行 # Clash Smart vX.Y.Z - CMFA |
Clash Meta For Android/CHANGELOG.md |
OpenClash/OpenClash(mihomo).sh |
#!/bin/bash 下方 # == 块 |
VERSION_TAG="vX.Y.Z-oc-normal" |
OpenClash/CHANGELOG.md (Normal 段) |
OpenClash/OpenClash(mihomo-smart).sh |
#!/bin/bash 下方 # == 块 |
VERSION_TAG="vX.Y.Z-oc-smart" + Ruby 脚本里 VERSION |
OpenClash/CHANGELOG.md (Smart 段) |
Shadowrocket/Shadowrocket.conf |
顶部 # ══… 双线框 |
第 2 行 # Shadowrocket Smart vX.Y.Z-SR.N |
Shadowrocket/CHANGELOG.md |
SingBox/SingBox(sing-box)-full.json |
由 SingBox/SingBox(sing-box)-generator.js 自动注入 _meta.version |
生成脚本版本 | SingBox/CHANGELOG.md |
v2rayN/v2rayN(xray).json |
顶层 _meta(version / build / baseline / changelog:"见 CHANGELOG.md") |
_meta.version |
v2rayN/CHANGELOG.md |
Surge/Surge.conf |
顶部 # ══… 双线框 |
第 2 行 # Surge Smart vX.Y.Z-Surge.N |
Surge/CHANGELOG.md |
Loon/Loon.conf |
顶部 # ══… 双线框 |
第 2 行 # Loon Smart vX.Y.Z-Loon.N |
Loon/CHANGELOG.md |
Quantumult X/QuantumultX.conf |
顶部 # ══… 双线框 |
第 2 行 # Quantumult X Smart vX.Y.Z-QX.N |
Quantumult X/CHANGELOG.md |
Passwall/Passwall(xray+sing-box)-apply.sh |
#!/bin/sh 下方 # ══… 块 |
VERSION_TAG="vX.Y.Z-pw.N" |
Passwall/CHANGELOG.md |
Passwall2/Passwall2(xray+sing-box)-apply.sh |
#!/bin/sh 下方 # ══… 块 |
VERSION_TAG="vX.Y.Z-pw2.N" |
Passwall2/CHANGELOG.md |
FlClash/FlClash(mihomo).js |
顶部 // 注释块 |
JS 内 const VERSION |
FlClash/CHANGELOG.md |
CHANGELOG.md 的推荐格式:
# <工具名> — 变更日志
> <一行定位说明 + 跟随基线说明>
---
## vX.Y.Z-tag (YYYY-MM-DD)
- ★ FIX#NN-PN:<一行摘要>
- <细节 1>
- <细节 2>
## vX.Y.Z-前一版 (YYYY-MM-DD)
...触发更新的最小粒度:
- 任何代码 / 规则改动 → bump 版本号尾段 + CHANGELOG 加一节(哪怕只有 1 行)
- Clash Party 主版本 bump → 所有产物主版本同步 bump,各自 CHANGELOG 都加节
- 禁止:只改代码不动版本号和 CHANGELOG;只改 CHANGELOG 不动代码;把详细变更塞回配置文件头(会被退回重做)
若发现配置文件头版本号与 CHANGELOG.md 不一致: 当前 PR 必须顺手修正,不得留作 TODO。
仅以下情况允许单一版本改动:
- 平台专属字段(例如 CMFA 的
find-process-mode: strict,OpenClash 的CORE_TYPE,SR 的skip-proxy,sing-box 的inbounds.tun)。 - 平台专属 bug 修复(例如 CMFA 特定内存泄漏、SR iOS 进程限制)。
- 平台专属文档(子目录
README.md只改本目录对应的版本)。
但即便如此,PR 描述里必须写清楚「为什么其他版本无需同步」。
触发条件:只要本次修复命中以下任一运行时逻辑点,必须对全部 11 份产物做同构审计:
- 节点名 → 区域分类:
REGION_DB/REGIONS/filter:/policy-regex-filter/server-tag-regex/NameRegex FilterKey - 区域组 fallback 链:区域为空时回落到
apacNodes/c.ALL/ 全局组 - 订阅原生 proxy-groups 合并 / 清理:
cleanupSubscription/ Rubyconfig["proxy-groups"] = ... - proxy-providers filter /
include-all-proxies/use筛选语义 - 节点过滤:
isInfoNode/isBlockedSpeedTag/INFO_PATTERNS/exclude-filter
审计矩阵(快速参考):
| 产物 | 运行时分类器 / 过滤器位置 |
|---|---|
| Clash Party Smart JS | REGION_DB 常量 + classifyAllNodes + cleanupSubscription |
| Clash Party Normal JS | 同上(与 Smart 版几乎同构,差别仅 type: smart → url-test) |
| Clash Meta For Android YAML | 各 proxy-groups[].filter: mihomo 正则(子串匹配,无 word boundary) |
| OpenClash normal / full | Ruby REGIONS 哈希(子串匹配) + make_smart_group fallback + config["proxy-groups"] = ... 重建 |
| Shadowrocket | [Proxy Group] ... policy-regex-filter=... 正则 |
| Surge | 同上 |
| Loon | [Remote Filter] ..._Filter = NameRegex, FilterKey = "(?i)..." |
| Quantumult X | [policy] url-latency-benchmark=..., server-tag-regex=... |
| SingBox Full | 静态 outbound 列表(无运行时分类,用户按 tag 接入节点) |
| v2rayN Xray routing | 路由规则(无节点分类) |
| Passwall / Passwall2 | 静态 shunt_rules(无运行时节点分类;按 UCI list 顺序匹配) |
| FlClash JS | 同 Clash Party Normal JS(REGION_DB + classifyAllNodes + cleanupSubscription;从 Clash Party Normal JS 直接移植) |
审计流程(每条运行时逻辑 bug 必做):
- 锁定 bug 所在的运行时逻辑点类型(对应上表行)。
- 对其它产物逐个打开对应位置(上表列出),用 grep / 目测 / 样例输入回归一次:
- 同一输入(本次 bug 的触发样例)能否在该产物中稳定分流?
- 若分流路径不同(例如 mihomo 子串 vs JS word boundary vs SR 字面量罗列),必须各自验证,不能凭"应该是一样的"偷懒。
- 任何一个产物命中同构漏洞,本 PR 必须同步修复,不得拆到后续 PR。
- 若某产物结构上不存在该逻辑点(如 SingBox 用静态列表),在 PR 描述 + CHANGELOG 写清楚"不适用"及理由。
- 产物间正则语义差异要点(一定要记住):
- JS(Clash Party)使用 word-boundary regex
(^|[^a-zA-Z])<kw>([^a-zA-Z]|$)→TW不命中TWN - mihomo
filter:使用 Go RE2 子串匹配 →TW命中TWN,但KR不命中KOR - Ruby OpenClash REGIONS 使用 Ruby 正则子串匹配 → 同 mihomo,
KR不命中KOR - Shadowrocket
policy-regex-filter使用罗列字面量,必须显式包含每个 alpha-3 - Loon
NameRegex FilterKey同上 - QX
server-tag-regex同上
- JS(Clash Party)使用 word-boundary regex
禁止事项:
- ❌ 凭"运行时逻辑只在 JS 里有"就跳过其它产物的审计。许多产物(CMFA / OpenClash Ruby)也有等价运行时逻辑,只是语法不同。
- ❌ 用"静态配置文件不会有此 bug"作为不审计的理由——必须逐个开文件验证一遍。
- ❌ 把同构 bug 拆成多个 PR 分批合入(会导致用户在某段时间内部分端 OK / 部分端 broken)。
口头说「已兼容」无效。必须提供链接或官方字段名引用作为兼容性证据。
| 产物 | 必读官方文档 |
|---|---|
| Clash Party JS | https://wiki.metacubex.one/ · https://wiki.metacubex.one/config/ · Mihomo Smart 内核(lgbm-custom-url 字段、uselightgbm、include-all-proxies) |
| CMFA YAML | https://wiki.metacubex.one/config/proxy-groups · https://wiki.metacubex.one/config/dns · CMFA README(GitHub MetaCubeX/ClashMetaForAndroid) |
| OpenClash(Normal + Smart) | https://github.com/vernesong/OpenClash/wiki · OpenClash 覆写脚本官方模板 · UCI 配置键 |
| Shadowrocket | https://help.shadowrocket.net/ · policy-regex-filter / RULE-SET / fallback-dns-server 的官方文档或社区权威说明 |
| SingBox(Full) | https://sing-box.sagernet.org/configuration/ · 特别是 route.rule_set、outbounds/selector、outbounds/urltest、dns、inbounds.tun、experimental.cache_file |
| Passwall / Passwall2 | https://github.com/Openwrt-Passwall/openwrt-passwall · https://github.com/Openwrt-Passwall/openwrt-passwall2 · 特别要读 luci-app-passwall*/luasrc/model/cbi/passwall*/client/shunt_rules.lua(分流规则解析器源码) · Discussion #555(定位差异) |
| FlClash JS | https://github.com/chen08209/FlClash · https://deepwiki.com/chen08209/FlClash/6-build-and-deployment(覆写系统) · https://github.com/chen08209/FlClash/issues/1510(脚本 API 教程) |
改动涉及新字段或跨版本字段时,必须:
- 字段存在性核对:在目标 APP 的官方文档里定位该字段,确认拼写、层级、取值范围。
- 版本兼容性核对:确认该字段在目标 APP 的最低支持版本,并与子目录
README.md中声明的最低版本一致。- 例:sing-box 1.11+ 已用
action+outbound替代旧outbound直接绑定;改动必须符合当前目标内核。 - 例:mihomo Smart 内核的
uselightgbm属于 Alpha 分支能力,不能下放到稳定分支的 Clash 核心。
- 例:sing-box 1.11+ 已用
- 格式核对:
- Clash YAML:
rule-providers的behavior∈ {domain,ipcidr,classical};format∈ {yaml,text,mrs}。 - Shadowrocket:
RULE-SET,<url>,<policy>不支持rule-provider节。策略名里的 emoji 必须与组定义完全一致(含 ZWJ\u200D)。 - sing-box:
rule_set的format∈ {binary,source};.srs必须配binary。
- Clash YAML:
- PR 描述里粘贴官方文档锚点(URL + 字段名),审阅者可一键验证。
每次代码审查/修改前,必须检查对应 APP 官方文档是否有更新。
| 产物目录 | 本地参考文档 | 源 URL |
|---|---|---|
Clash Party/ |
REFERENCE-mihomo-wiki.md |
https://wiki.metacubex.one/ |
Clash Meta For Android/ |
REFERENCE-mihomo-wiki.md |
https://wiki.metacubex.one/ |
OpenClash/ |
REFERENCE-openclash.md |
https://github.com/vernesong/OpenClash/wiki |
Surge/ |
REFERENCE-surge-manual.md |
https://manual.nssurge.com/ |
Shadowrocket/ |
REFERENCE-shadowrocket.md |
https://help.shadowrocket.net/ |
Loon/ |
REFERENCE-loon-manual.md |
https://github.com/Loon0x00/LoonManual |
Quantumult X/ |
REFERENCE-quantumultx.md |
https://github.com/crossutility/Quantumult-X |
SingBox/ |
REFERENCE-sing-box.md |
https://sing-box.sagernet.org/configuration/ |
v2rayN/ |
REFERENCE-xray.md |
https://xtls.github.io/ |
Passwall/ |
REFERENCE-passwall.md |
https://github.com/Openwrt-Passwall/openwrt-passwall |
Passwall2/ |
REFERENCE-passwall2.md |
https://github.com/Openwrt-Passwall/openwrt-passwall2 |
FlClash/ |
REFERENCE-flclash.md |
https://github.com/chen08209/FlClash |
每次修改涉及的任何 APP,必须执行:
- 检查远程文档是否更新:用
WebFetch获取源 URL 的页面内容(或 GitHub 仓库的最新 release/commit 时间),与本地REFERENCE-*.md文件头部的「获取日期」对比。 - 若远程有更新(新版本发布 / 字段变更 / 语法废弃 / 新增能力):
- 重新
WebFetch抓取最新文档内容 - 更新对应
REFERENCE-*.md,在文件头部追加更新记录:> 更新于 YYYY-MM-DD(上次获取 YYYY-MM-DD):<简述变化> - 同步更新到该 APP 的
CHANGELOG.md
- 重新
- 若远程无更新:在 PR 描述中注明「已检查 $APP 官方文档,截至 YYYY-MM-DD 无更新」。
- 审核必须以最新文档为准:发现本地 REFERENCE 过期而未更新的,必须先更新 REFERENCE 再审核,不得凭旧文档下结论。
# 检查所有 REFERENCE 文件的获取日期
for f in */REFERENCE-*.md; do
echo "=== $f ==="
head -6 "$f" | grep -E '(来源|获取日期|更新于|Source|Date)'
echo ""
done- ❌ 禁止凭记忆、训练数据或"经验"判定字段兼容性。
- ❌ 禁止把一份 APP 的语法(例如 Clash classical)直接复制到另一份(例如 sing-box)。
- ❌ 禁止使用已被官方标记 deprecated 的字段,除非有对应客户端版本兜底说明。
- ❌ 禁止在没有核对的情况下更改规则源(geosite.dat/geoip.dat)的 URL 或 release 分支。
- ❌ 禁止在 REFERENCE 文档过期的情况下执行审核——必须先更新再审核。
区域组名称(emoji 必须逐字节一致,包含 RGI 旗帜序列):
🌍 全球节点 · 🏡 全球家宽 · 🇭🇰 香港节点 · 🏡 香港家宽 · 🇹🇼 台湾节点 · 🏡 台湾家宽
🇯🇵 日韩节点 · 🏡 日韩家宽 · 🌏 亚太节点 · 🏡 亚太家宽 · 🇺🇸 美国节点 · 🏡 美国家宽
🇪🇺 欧洲节点 · 🏡 欧洲家宽 · 🌎 美洲节点 · 🏡 美洲家宽 · 🌍 非洲节点 · 🏡 非洲家宽
业务组名称(顺序即 README 展示顺序):
🤖 AI 服务 · 💰 加密货币 · 🏦 金融支付 · 💬 即时通讯 · 📱 社交媒体
🧑💼 会议协作 · 📺 国内流媒体
🎥 Netflix · 🎬 Disney+ · 📡 HBO/Max · 📺 Hulu · 🎬 Prime Video
📹 YouTube · 🎵 音乐流媒体
🇭🇰 香港流媒体 · 🇹🇼 台湾流媒体 · 🇯🇵 日韩流媒体 · 🇪🇺 欧洲流媒体
🌐 其他国外流媒体
🕹️ 国内游戏 · 🎮 国外游戏 · 🔧 工具与服务
Ⓜ️ 微软服务 · 🍎 苹果服务 · 📥 下载更新 · 🛰️ BT/PT Tracker
🏠 国内网站 · 🚫 受限网站 · 🌐 国外网站 · 🐟 漏网之鱼 · 🛑 广告拦截
禁止新增/删除/改名这 49 个组;若业务确有需要,必须先在 PR 描述里说明并先改 Clash Party 基线。
- 基线(Clash Party v5.2.2 起):
RP_PROXY = BIZ.GFW = '🚫 受限网站' - Clash 家族(CMFA / OpenClash Normal / OpenClash Smart)必须全部使用
proxy: '🚫 受限网站'。 - Shadowrocket 不走 rule-provider,自动由 App 更新,豁免。
- sing-box 通过
route.rule_set远程拉取,其走的是全局默认出站,豁免。
历史债务:OpenClash Normal 之前用
DIRECT、CMFA 之前用☁️ 云与CDN,已在本次对齐修复;任何未来改动禁止回退。
- Clash 家族:
MATCH,🐟 漏网之鱼 - Shadowrocket:
FINAL,🐟 漏网之鱼,dns-failed - sing-box:
route.final: "🐟 漏网之鱼"
所有版本的 🛑 广告拦截 组必须默认指向 REJECT / block / action: reject;第一条规则必须是广告拦截前置。
「同一语义在不同产物里写法不同」的清单。每条都对应过一次真实的导入失败 / 用户报错。 禁止在跨产物联动时机械复制粘贴;先查这张表,再写。新增条目时请保留历史出处(FIX 编号 / commit / Issue)。
| 产物 | 普通 DNS(IP) | DoH(HTTPS URL) | 域名劫持 / 系统 DNS | 备注 |
|---|---|---|---|---|
| Quantumult X | server=223.5.5.5 |
doh-server=https://doh.pub/dns-query(禁止 server=https://...,line N 报语法错误) |
server=/*.apple.com/system |
多个 DoH 可同行逗号分隔或多行;dns_exclusion_list 写在 [general] 而非 [dns](且字段名是下划线 _,不是连字符) |
| Shadowrocket | dns-server=223.5.5.5,https://doh.pub/dns-query(IP+URL 可逗号混排) |
同左(dns-server= 字段直接接受 URL,无独立 DoH 字段) |
走规则 / sgmodule | fallback-dns-server=... 用于解析失败回退;hijack-dns=8.8.8.8:53 用于劫持硬编码 DNS |
| Surge | dns-server=223.5.5.5, system |
encrypted-dns-server=https://doh.pub/dns-query(独立字段,禁止混在 dns-server= 里) |
[Host] 段 *.apple.com = server:system |
DoH 字段与 SR 不同,与 Loon/QX 也不同——三家三种写法 |
| Loon | dns-server=system, 223.5.5.5 |
doh-server=https://doh.pub/dns-query(独立字段,同 QX) |
[Host] 段 *.apple.com = server:system |
DoH 字段名同 QX,但 [Host] 段语法跟 Surge |
| Clash 家族(mihomo / CMFA / OpenClash) | YAML dns.nameserver: [223.5.5.5, 119.29.29.29] |
YAML dns.nameserver: [https://doh.pub/dns-query, ...](与 IP 同一数组) |
dns.fake-ip-filter / hosts: |
还有 direct-nameserver / proxy-server-nameserver / nameserver-policy 等多层 |
| sing-box | JSON dns.servers[].address: "223.5.5.5" |
JSON dns.servers[].address: "https://doh.pub/dns-query"(与 IP 同一字段) |
dns.rules[] 路由 |
1.11+ 引入新结构;老配置 dns.servers[].address_resolver 需手动迁移 |
历史出处:
- FIX#QX-08-P0(PR #117):QX
server=https://...→doh-server=https://... - FIX#QX-07-P0(PR #114):QX
running_mode_trigger=filter,...误用(合法值仅direct/proxy/auto/follower/none) - 反面教材:上一版 PR #114 CHANGELOG 误判 line 22 DNS 报错为 line 13 报错的级联效应——不同语法陷阱独立报错,不要"猜"
| 产物 | 域名后缀 | IP CIDR | 端口 | 进程 | RULE-SET 引用 |
|---|---|---|---|---|---|
| Clash 家族 | DOMAIN-SUFFIX,example.com |
IP-CIDR,1.2.3.0/24 |
DST-PORT,443 |
PROCESS-NAME,curl |
RULE-SET,name + 顶部 rule-providers |
| Shadowrocket | DOMAIN-SUFFIX,example.com |
IP-CIDR,1.2.3.0/24 |
DST-PORT,443 |
(iOS 限制,不支持) | RULE-SET,<url>,<policy>(直接 URL,无 rule-providers 节) |
| Surge | DOMAIN-SUFFIX,example.com |
IP-CIDR,1.2.3.0/24 |
DST-PORT,443(与 Clash/SR 同) |
PROCESS-NAME,curl |
RULE-SET,<url> <policy> |
| Loon | DOMAIN-SUFFIX,example.com |
IP-CIDR,1.2.3.0/24 |
DEST-PORT,443(唯一异类:禁止 DST-PORT,弹"DST-PORT 语法错误") |
PROCESS-NAME,curl |
RULE-SET,<url>,<policy> |
| Quantumult X | host-suffix, example.com, <policy> |
ip-cidr, 1.2.3.0/24, <policy> |
(走 [filter_local] 单独段) |
process-name, curl, <policy> |
[filter_remote] URL 顶部声明 |
| Passwall / Passwall2 | 纯字符串 / domain:example.com / full:exact.com / regexp:^...$ |
1.2.3.0/24(直写,无前缀) |
(UI 单独配) | (不支持) | geosite:cn / rule-set:remote:<url> / rule-set:local:/path |
| sing-box | JSON domain_suffix: ["example.com"] |
JSON ip_cidr: ["1.2.3.0/24"] |
port: [443] |
process_name: ["curl"] |
rule_set: ["geosite-cn"] + 顶部 route.rule_set |
历史出处:
- FIX#40(v5.2.10-Loon.2):Loon
DST-PORT→DEST-PORT(与 Surge 一样必须用DEST-) - 反面教材:直接把 Surge
.conf复制成 Loon.conf时,DEST-PORT看似没错但 Loon 解析器对此前缀特别敏感
| 产物 | 正则引擎 | 边界语义 | "TW" 是否匹配 "TWN" | "KR" 是否匹配 "KOR" |
|---|---|---|---|---|
| Clash Party JS | JS RegExp + word boundary (^|[^a-zA-Z])kw([^a-zA-Z]|$) |
严格 word boundary | ❌ 不匹配 | ❌ 不匹配 |
mihomo filter: |
Go RE2,子串 | 无 boundary | ✅ 匹配(误伤) | ❌ 不匹配 |
OpenClash Ruby REGIONS |
Ruby Regex,子串 | 无 boundary | ✅ 匹配(误伤) | ❌ 不匹配 |
Shadowrocket policy-regex-filter |
NSRegularExpression,需手动罗列 | 字面量罗列 | 必须显式写 TW|TWN |
必须显式写 KR|KOR |
Loon NameRegex FilterKey |
同 SR | 同上 | 同上 | 同上 |
Quantumult X server-tag-regex |
同 SR | 同上;可用 (?<![a-zA-Z])US(?![a-zA-Z]) 强制 boundary |
同上 | 同上 |
历史出处:
- FIX#24~#26(v5.2.5)误判为 JS 专属,实际波及 4 份产物(v5.2.6 补丁)
- FIX#29-P2(v5.2.8-QX.5):欧洲节点 filter 漏 GR/RO/HU/CZ;JS 主线补齐后 SR/Surge/Loon/QX 必须各自再补一次(罗列字面量)
| 平台 | 字段 / 场景 | 合法值 | 出处 |
|---|---|---|---|
| Quantumult X | running_mode_trigger= |
仅 direct / proxy / auto / follower / none(禁止 filter) |
FIX#QX-07-P0 (#114) |
| OpenClash | sniffer.skip-domain |
不应包含币安等需要 SNI 改写的 TLS 域名 | PR #112 |
| OpenClash full override YAML | 顶层 rule-providers: / rules: |
各自只能出现 1 次(Ruby Psych last-wins 会静默丢内容) | §5 自检 4b |
| Passwall / Passwall2 shunt_rules | 不支持 Clash 前缀 | 必须用 domain: / full: / regexp: / geosite: / rule-set:remote|local: / 裸 CIDR;禁止 DOMAIN-SUFFIX, |
shunt_rules.lua |
| sing-box rule_set | format 字段 |
binary(配 .srs 文件)或 source(配 JSON);不能反着配 |
sagernet.org 文档 |
| Shadowrocket | rule-provider 节 | 不支持;必须把 URL 直接写在 RULE-SET,<url>,<policy> 中 |
§3.2 + §3.5.2 |
| Surge / Loon / QX | 进程匹配 | iOS 进程命名空间不支持 PROCESS-NAME;Surge/Loon iOS 14+ 部分支持,QX 不支持 |
各官方 wiki |
发现新陷阱(以"我从产物 A 复制到产物 B 后导入报错"为信号)后,必须:
- 在本 §3.5 对应小节加一行/一列,注明 ❌ 错误 + ✅ 正确;
- 在 PR 描述 + CHANGELOG 引用本节段落号(例:"参见 CLAUDE.md §3.5.1 DoH 行");
- 同步在
AGENTS.md §6 常见错误模式表格中追加一行; - 若可机械检测,在
§5 自检脚本中加一条 grep。
禁止:把单平台陷阱的修复隐藏在 commit message 里却不更新本节——下一个代理做基线联动时几乎一定会再踩一次。
- Clash Party JS 顶部 VERSION 注释是唯一主版本号(目前
v5.2.2)。 - 其他 5 份产物使用同主版本号 + 平台后缀:
v5.2.2-cmfa.X、v5.3.X-oc-normal、v5.2.2-oc-smart、v5.2.2-SR.X、v5.2.2-sing.X
- 平台后缀内部可独立递增,但主版本号必须与 Clash Party 对齐;若不对齐,必须在对应子目录
README.md开头标明原因。
# 1) 数代理组数(必须为 31 业务组 + 18 区域组;sing-box 另加 1 个顶层节点选择)
# CMFA:业务组用 "- name:"、区域组用 "- type: url-test" + 缩进 " name:",需两种模式
grep -cE "^- name: |^ name: " "Clash Meta For Android/CMFA(mihomo).yaml" # 期望 49
# OpenClash:31 业务组是静态 "- name:";18 区域组由 Ruby make_smart_group() 动态生成,静态 grep 只能数到 31
grep -cE "^- name: " "OpenClash/OpenClash(mihomo).sh" # 期望 31(静态业务组)
grep -cE "^- name: " "OpenClash/OpenClash(mihomo-smart).sh" # 期望 31(静态业务组)
grep -cE " = select,|= url-test," "Shadowrocket/Shadowrocket.conf" # 期望 49
grep -cE " = select,|= url-test," "Surge/Surge.conf" # 期望 49
grep -cE " = select,|= url-test," "Loon/Loon.conf" # 期望 49
grep -cE "^(url-latency-benchmark|static)=" "Quantumult X/QuantumultX.conf" # 期望 49
node -e "const d=JSON.parse(require('fs').readFileSync('SingBox/SingBox(sing-box)-full.json','utf8'));const n=d.outbounds.filter(o=>o.type==='selector'||o.type==='urltest').length;console.log(n);process.exit(n===50?0:1)" # 期望 50
# 2) RP 代理字段
grep -c "proxy: DIRECT" "OpenClash/OpenClash(mihomo).sh" # 期望 0
grep -c "proxy: '☁️ 云与CDN'" "Clash Meta For Android/CMFA(mihomo).yaml" # 期望 0
grep -c "proxy: '🚫 受限网站'" "Clash Meta For Android/CMFA(mihomo).yaml" # 期望 ≥ 300
grep -c "proxy: \"\\\\U0001F6AB 受限网站\"" "OpenClash/OpenClash(mihomo).sh" # 期望 ≥ 130
grep -c "proxy: \"\\\\U0001F6AB 受限网站\"" "OpenClash/OpenClash(mihomo-smart).sh" # 期望 ≥ 380
# 3) 禁止死引用(旗帜 emoji 与组名 emoji 必须匹配;忽略注释行)
grep -nE "^[^#].*🇸🇬 亚太节点" "Shadowrocket/Shadowrocket.conf" # 必须无输出
grep -nE "^[^#].*🎵 TikTok" "Shadowrocket/Shadowrocket.conf" # 必须无输出
# 4) JSON 合法性(sing-box + v2rayN 路由;优先 node,备选 python3)
node -e "JSON.parse(require('fs').readFileSync('SingBox/SingBox(sing-box)-full.json','utf8'));console.log('SingBox JSON: VALID')"
node -e "const a=JSON.parse(require('fs').readFileSync('v2rayN/v2rayN(xray).json','utf8'));const m=a[0];console.log('v2rayN JSON: VALID, items:',a.length,'version:',m.remarks?.match(/v5\\.[0-9.]+/)?.[0])"
# 4b) OpenClash full 生成的 override YAML:必须只有 1 个 rule-providers + 1 个 rules 顶层键
# (Ruby Psych 对重复顶层键 last-wins,会静默丢掉前面的全量内容——本仓库曾在此犯错)
awk '
/^cat > "\$OVERRIDE_YAML" << .OVERRIDE_EOF./ { inblock=1; next }
/^cat >> "\$OVERRIDE_YAML" << .OVERRIDE_EOF./ { inblock=1; next }
/^OVERRIDE_EOF$/ { inblock=0; next }
inblock { print }
' "OpenClash/OpenClash(mihomo-smart).sh" > /tmp/oc_full_override_probe.yaml
grep -cE "^rule-providers:$" /tmp/oc_full_override_probe.yaml # 期望 1
grep -cE "^rules:$" /tmp/oc_full_override_probe.yaml # 期望 1
ruby -ryaml -e '
d = YAML.load_file("/tmp/oc_full_override_probe.yaml", permitted_classes: [Symbol], aliases: true)
raise "providers < 380" if (d["rule-providers"] || {}).size < 380 # full 期望 ≈384
raise "rules < 900" if (d["rules"] || []).size < 900 # full 期望 ≈975
puts "OC full override yaml: providers=#{d["rule-providers"].size} rules=#{d["rules"].size}"
'
# 5) YAML 合法性(可选,需 pyyaml 或 node)
node -e "const yaml=require('yaml'||'js-yaml');console.log('YAML parse: OK')" 2>/dev/null || echo "跳过 YAML 校验(无 yaml 模块)"若任一检查失败,PR 不得合入。
① 读 Clash Party JS(基线)→ 搞清楚要改什么
② 读目标 APP 官方文档 → 确认新字段/新组在每个产物上的等价写法
③ 改 Clash Party JS → 主线先落地
④ 同步 CMFA → OpenClash(Normal+Smart) → Shadowrocket → SingBox Full
⑤ 跑 §5 自检命令
⑥ 更新根 README.md + 各子目录 README.md
⑦ PR 描述里写:改动摘要 / 影响矩阵 / 官方文档链接 / 自检输出
- 不同步 = 违规。
- 不核对官方文档 = 违规。
- 删除/改名 49 个代理组之一而未在 PR 说明里论证 = 违规。
- 伪造「已兼容」结论(没有引用官方文档就下结论)= 违规。
这些约束是仓库长期可维护性的前提,优先级高于任何「小修快改」的便利。