# RRPP 订阅服务 · SSO 认证接入文档 架构:`客户端 → nginx(api.rrrosmontis.icu) → RRPP 后端(127.0.0.1:5010) → SSO(127.0.0.1:4550) → nodes.json(国家分层)` ## 1. 客户端公开端点 ### 统一订阅端点 ``` GET https://api.rrrosmontis.icu/v1/rrpp/subscription?method=<类型>&user=&password=[&prefer=<协议>][&rules=<模式>] ``` | 参数 | 必填 | 说明 | |---|---|---| | `method` | 是 | `clash` / `v2ray` / `shadowrocket` / `rrpp_client` | | `user` | 是 | URL-safe base64 编码的邮箱 | | `password` | 是 | URL-safe base64 编码的密码 | | `prefer` | 否 | `vless` / `ss` / `http`,指定优先协议;缺省 `vless`,无则回退 vless→ss→http | | `rules` | 否 | 仅 `method=clash`:`provider`(默认) / `inline` / `global`,见 §7 | | `token` | 否 | SSO 会话 token(免密,代登录态);与 `user`/`password` 二选一 | ### 旧端点(兼容) ``` GET /v1/rrpp/nodes_update?user=&password= ``` 等价于 `method=rrpp_client`。 ## 2. method 说明 | method | Content-Type | 内容 | 适用客户端 | |---|---|---|---| | `clash` | `text/yaml` | Clash YAML:`proxies` + `proxy-groups(RRPP)` + `rule-providers` + `rules`(备注 `R.R.P.P. - 国家代码`) | mihomo / Clash Verge / ClashX Meta | | `v2ray` | `text/plain` | base64 节点串(`vless://` / `ss://` / `http://`) | v2rayN / Shadowrocket / Quantumult | | `shadowrocket` | `text/plain` | Shadowrocket `.conf`:`[General]` + 远程 `RULE-SET` + `FINAL`(**不含节点**) | Shadowrocket | | `rrpp_client` | `application/json` | 旧格式节点 JSON(仅 http,remark = `服务商 流量`) | RRPP 自建客户端 | ## 3. 返回码 | 码 | 场景 | |---|---| | 200 | 成功 | | 401 | 凭据缺失/SSO 认证失败 | | 403 | tier=0(RRPP 未开通) | | 404 | method 未知,或 `rules` 取值非法 | | 502 | SSO 不可达 | | 503 | nodes.json 缺失/损坏,或该 tier 无可用节点 | ## 4. nodes.json 格式(国家分层) `C:/BtSoft/nginx/others/htmlResources/private/RRPP/nodes.json`,顶层为国家数组: ```jsonc [ { "country": "JP", // 国家代码 "available_level": [1, 2, 3], // 可见等级 "provider": "Azure", // 服务商 "traffic": "15G", // 流量限额 "domain": "iam.aeolianchi.me", // 域名 "ipv6_only": false, // 可选,true 时备注加 (IPv6) "methods": { // 可用协议 "http": { "port": ..., "username": ..., "password": ... }, "vless": { "port": ..., "uuid": ..., "flow": ..., "sni": ..., "public_key": ..., "short_id": ... }, "ss": { "port": ..., "method": ..., "password": ... } } }, ... ] ``` ## 5. 协议选择逻辑 - `prefer` 指定则优先用该协议 - 缺省默认 `vless`(最好用) - 该国家无 `prefer` 协议时按 `vless → ss → http` 回退 - `rrpp_client` 固定只用 `http`(兼容性),备注 = `provider + traffic`(IPv6 加 `(IPv6)`) ## 6. SSO 认证 - 后端调 SSO `POST http://127.0.0.1:4550/v1/sso/service/rrpp/nodes`(带 `X-SSO-Service-Token` + `X-Real-IP`) - SSO 返回 `{tier: 0|1|2|3}`,0=未开通 - 服务密钥读 `sso-rrrosmontis-icu/secrets.json` 的 `service_tokens.rrpp` ## 7. 分流规则(PAC → 规则集,随订阅下发) ### 7.1 为什么不是直接下发 PAC PAC 是给浏览器/操作系统用的 JS 自动配置;**mihomo/Clash 与 Shadowrocket 都不能读 PAC**。 两者唯一共同能吃的格式是「每行自带规则类型的规则行文本」(Clash 术语 `behavior: classical`): - mihomo:`rule-providers`(`behavior: classical` + `format: text`) - Shadowrocket:`RULE-SET,,<策略>`(官方手册要求「规则集的内容需包含规则类型」) 因此把 PAC 数据**在后端一次性转换成三个规则集文件**,两种客户端共用同一份文件。 ### 7.2 规则集文件(CDN 公开) 生成目录即 CDN 根下的 `RRPP/PAC/`,公开可访问、无鉴权(规则集本身不含节点信息): | 规则集 | 文件 | 规则数 | 组成 | 策略 | |---|---|---|---|---| | `rrpp-proxy` | `rrpp-proxy.list` | 3943 | 539 `DOMAIN` + 3404 `DOMAIN-SUFFIX`(强制代理域名) | `RRPP` / `PROXY` | | `rrpp-direct` | `rrpp-direct.list` | 16 | 16 `DOMAIN-SUFFIX`(保守直连种子) | `DIRECT` | | `rrpp-cn-ip` | `rrpp-cn-ip.list` | 7456 | 7456 `IP-CIDR`(中国 IP 段) | `DIRECT` | URL:`https://cdn.rrrosmontis.icu/RRPP/PAC/<文件名>` 体积:120366 / 386 / 177443 字节,合计 11415 条规则。 ### 7.3 规则顺序(与 PAC 语义一致) ``` RULE-SET,rrpp-proxy → RRPP # 强制代理优先(PAC 中强制代理规则优先级更高) RULE-SET,rrpp-direct → DIRECT # 直连种子 RULE-SET,rrpp-cn-ip → DIRECT # 中国 IP 直连 MATCH → RRPP # 其余全走代理 ``` ### 7.4 维护流程(PAC 更新后) 转换脚本 `gen_rulesets.py`(与本文件同目录,另在仓库 `server_config/rrpp-subscription/` 存有副本): ```bash python gen_rulesets.py # 读 PAC 目录 → 原子覆写三个 .list(幂等,内容不变则不写) python gen_rulesets.py --check # 只检查将有哪些变化,不写盘 python gen_rulesets.py --pac-dir <目录> --out-dir <目录> --quiet ``` - 输入:`pac_domains.json` / `pac_direct_domains.json` / `pac_ip_cn.json` - 转换规则:`full:x`→`DOMAIN,x`;`keyword:x`→`DOMAIN-KEYWORD,x`;`regexp:`/`include:`→跳过并计数;行内 `#` 注释剥离;`*.x`/普通域名→`DOMAIN-SUFFIX,x` - 输出排序去重、LF 换行、无注释,写盘用临时文件 + `os.replace` 原子替换 - **订阅端无需任何改动**:文件名与 URL 固定,客户端按 `interval` 自动拉新规则 ### 7.5 客户端接入 | 场景 | 用法 | |---|---| | mihomo / Clash Verge(推荐) | `&method=clash` → 规则走 `rule-providers`,YAML 约 2.4KB,规则每 24h 自动更新 | | 客户端不认 `rule-providers`,或想「导入一次到位」 | `&method=clash&rules=inline` → 规则内联进 YAML(约 440KB),导入即含节点+全部规则 | | Shadowrocket(只想要规则,节点另行导入) | `&method=shadowrocket` → `.conf`,规则走远程 `RULE-SET`(在「配置 → 规则集URL」中可见并自动更新) | | Shadowrocket(想一次导入节点+规则) | 用 `&method=clash&rules=inline`(小火箭兼容 Clash YAML,含节点信息的标准 Clash 链接可同时导入配置与节点) | | 兜底 / 排障 | `&rules=global` → 只有 `MATCH,RRPP`,全流量走代理(等价旧行为) | > `method=shadowrocket` 的 `.conf` **不写 `[Proxy]` 段**:Shadowrocket 的 REALITY 参数写法官方未文档化,不猜测; > 该配置的策略 `PROXY` 指向首页当前选中的节点,节点请用 `clash`/`v2ray` 订阅导入。 ### 7.6 验证记录(2026-09-19) - 生成:11415 条规则;`regexp:` 跳过 7 条、非法 1 条 - CDN:三个 URL 均 HTTP 200,`md5` 与本地生成一致(`806808b8` / `8f89397e` / `3e75093d`) - **mihomo v1.19.31 真实内核运行验证**:`-t` 校验通过;运行时 `GET /providers/rules` 显示 `rrpp-proxy=3943`、`rrpp-direct=16`、`rrpp-cn-ip=7456`(`behavior=Classical`),落地文件字节数与生成完全一致; `&rules=inline` 配置亦通过 `-t` 校验 - Shadowrocket 为 iOS 应用,**未做实机验证**,仅依据官方手册的 `RULE-SET` / Clash YAML 兼容说明实现