文章

一套分流规则怎样同时生成 Quantumult X 与 Mihomo 配置

记录 RuleForge 从单一 Quantumult X 输出扩展为双目标生成器的过程,包括统一规则模型、冲突裁决、兼容边界和真实内核验收。

最近我想给 RuleForge 增加一套 Mihomo 输出。

这个项目原来每天抓取公开分流规则,整理成二十六个业务分类,再生成 Quantumult X 可以直接引用的文件。现在多了一个目标,最省事的办法是读回已经生成的 Quantumult X 文件,换一套关键字,再写成 Mihomo 格式。

我把两边的配置和规则格式对过一遍,很快放弃了这条路。域名规则确实长得相近,策略放在哪里、哪些能力能够迁移、遇到不支持的类型该怎么办,都需要重新处理。最后做出来的东西更像一个小型编译流程。它先把不同来源读成统一规则,再交给两个渲染器各自输出。[1][2]

先弄清楚哪些内容不能搬

Quantumult X 的单条规则通常同时带着匹配条件和策略。

host-suffix,example.com,AI

Mihomo 的 Classical Rule Provider 不带策略。规则文件只保存匹配条件,配置文件再用 RULE-SET 把整个分类交给策略组。

DOMAIN-SUFFIX,example.com
RULE-SET,ai,AI

这已经超出了关键字替换。策略若在转换时丢掉,生成文件仍可能通过 YAML 解析,流量却会走到另一个组里。

客户端能力也有明确边界。Quantumult X 的重写、MITM、定时脚本没有进入 Mihomo 模板。USER-AGENT 一类当前渲染器不接受的规则会直接让构建失败。项目宁可停下来,也不把无法表达的内容原样塞进输出,更不会静默跳过。

把来源读成统一规则

RuleForge 原有的解析和审计流程可以继续使用。每条规则进入系统后,会保留类型和值,同时记住策略、来源、选项和处理状态。后面的去重与冲突分析只面对这套统一对象,不需要知道输入来自 Surge、Quantumult X 还是 Clash Classical。

source manifest
  -> fetch and cache
  -> parser
  -> canonical rule model
  -> exact deduplication
  -> semantic overlap analysis
  -> policy resolution
  -> target renderer
  -> audit report

两个目标各自有一份来源清单。Quantumult X 优先读取上游提供的 Quantumult X 列表,Mihomo 优先读取 Clash 列表。两份清单维持相同的二十六个业务分类和策略边界,生成结果不会互相充当输入。[2]

8 月 19 日这次构建里,两份清单各有九十四个启用来源。主要来源是 Blackmatrix7 的公开规则,RuleGo 和 ACL4SSR 负责补充与交叉检查。[4][5][6]

独立清单增加了一些维护工作,却让问题更容易定位。某个上游只在 Mihomo 侧失效时,Quantumult X 不会跟着读取一份已经转换过的二手结果。两个客户端也可以继续使用各自更自然的规则格式。

规则多起来以后,冲突开始占据主要工作

合并九十四个来源以后,同一个域名经常落进多个分类。完全相同的规则可以直接去重,策略不同或覆盖范围重叠的规则需要裁决。

8 月 19 日的构建结果如下。

目标 解析规则 精确重复 冲突 未决冲突 已裁决输出
Quantumult X 13546 431 552 0 12764
Mihomo 6755 314 400 0 6142

两个目标采用原生来源,解析总数不能直接拿来比较覆盖质量。这组数字说明的是另一件事。两边都走完了自己的去重和裁决流程,没有把未解决的冲突带进 safe 输出。

当前裁决会参考来源优先级和业务边界。direct 与 reject 正面冲突时,明确的直连规则优先。精确域名与较宽的域名后缀相撞时,更具体的规则优先。少数无法靠通用顺序判断的域名,放进经过测试的分类边界里处理。

项目同时保留 candidates 和 safe 两层文件。前者让人看到合并后的候选内容,后者只收已经完成裁决的规则。构建使用 --fail-on-conflict,只要仍有未决项,自动更新就会停下。

新目标上线时也要重新验收旧目标

增加 Mihomo 渲染器以后,Quantumult X 仍要保持原有行为。这个要求不能只靠一句“没有改旧代码”来保证。

功能提交重新生成了五十三个 Quantumult X 输出文件。过滤生成时间和缓存状态后,五十三个文件的语义差异为零。现有测试继续检查 Quantumult X 的策略替换、来源选项过滤和分类顺序,新测试再覆盖 Mihomo 的类型映射、无策略规则、二十六个分类引用和不兼容类型失败。当前二十项回归测试全部通过。[1]

网络抓取也收紧了重试范围。临时连接错误和服务端错误可以重试,明确的 404 会立即失败。这样能缓解偶发网络抖动,也不会把已经删除的来源拖成一串无意义等待。

Mihomo 还要自己读一遍

文本看起来正确,还不能证明客户端会接受它。自动工作流会下载固定版本的 Mihomo 内核,先校验下载文件的 SHA256,再用 mihomo -t 加载示例配置和二十六个本地 Rule Provider。

本地内核检查通过以后,工作流还会逐个访问主分支上的二十六个公开 Raw 地址。这样可以发现一种本地构建看不到的问题。文件已经生成,远程地址却可能尚未发布,或路径与配置中的引用不一致。

主分支的正式工作流已经跑完回归测试、双目标构建、内核检查和公开地址验证,结果为成功。[3] 那次构建没有发现规则内容变化,因此提交步骤自动跳过。时间戳和缓存状态单独变化时,仓库不会每天多出一个没有实际内容的提交。

这套验收仍有边界。它能证明格式、策略引用和远程文件成立,不能替代真实订阅、节点连通性、DNS 行为和客户端界面测试。项目在使用说明里保留了这条限制。[1]

模板只保留必要内容

最终示例配置只留下两个订阅占位符。控制器默认监听本机,TUN 与 IPv6 关闭,DNS 使用 Fake-IP。业务策略组排在地区测速组前面,二十六个 Rule Provider 每四十八小时刷新一次。

模板没有保存真实订阅、账号、Cookie、证书或私钥。控制器只允许本机访问时,secret 可以留空。若将控制器开放到局域网或公网,使用者需要补上随机密钥,并同时限制防火墙和访问来源。

以后如果还要支持第三种客户端,RuleForge 不需要重新解析前两个客户端的成品。新目标接入自己的来源清单和渲染器,原来的去重与裁决继续工作。它若碰到无法表达的规则,就在构建阶段停下。这个边界比多生成一份配置更重要。

相关资料

  1. RuleForge 仓库
  2. RuleForge 架构说明
  3. RuleForge 正式工作流验证
  4. ios_rule_script
  5. RuleGo
  6. ACL4SSR