12 KiB
SIP-008 订阅扩展:供应方说明
本文说明 ShadowsocksX-NG 当前支持的 SIP-008 订阅字段和私有扩展,供订阅服务提供方生成兼容响应时参考。内容以当前实现为准。
响应传输
订阅端点必须满足以下条件:
- 使用 HTTPS。客户端验证系统信任链,不接受明文 HTTP,也不放宽证书或 TLS 校验;HTTPS 到 HTTPS 的重定向可以跟随,降级到非 HTTPS 会被拒绝。
- 成功响应状态码为
200。 - 响应头使用
Content-Type: application/json; charset=utf-8。客户端不区分大小写并忽略空白,但服务端应按此格式发送;缺少该头或使用其他类型、字符集都会被拒绝。 - 响应正文是一个合法的 JSON 对象,不是 JSON5、Base64 或
ss://链接列表。
SIP-008 订阅 URL 通常包含访问令牌。请使用不可猜测的令牌,并避免在服务端访问日志、错误追踪或分析系统中记录完整订阅 URL。订阅正文含服务器密码;HTTPS 负责传输保护,扩展字段本身不加密正文。
SIP-008 v1 基础对象
根对象必须包含 version: 1 和 servers 数组。空数组有效。服务器项按 SIP-008 应提供 id、remarks、server、server_port、password 和 method;使用插件时再提供 plugin、plugin_opts。每个服务器项仍须是完整的 Shadowsocks 服务器记录;不能只在分组扩展里发送服务器配置。
| 字段 | 供应方要求与建议 | 客户端处理 |
|---|---|---|
version |
必须是整数 1。 |
缺失或其他版本会拒绝整份文档。 |
servers |
必须是数组;每项使用下表所列的 SIP-008 服务器字段。 | 缺失或不是数组会拒绝整份文档。空数组有效。 |
servers[].id |
SIP-008 服务器项必需字段。每台服务器提供唯一、规范格式的 UUID,并在同一逻辑服务器的后续刷新中保持不变。分组扩展依赖此字段引用服务器。 | 当前解析器容忍省略 id,但提供后必须是有效 UUID;重复 ID 会拒绝整份文档。无 ID 项不能被扩展树引用,会按扁平兼容路径处理。 |
servers[].remarks |
SIP-008 服务器项必需字段,提供服务器显示名称字符串。 | 当前解析器容忍省略;缺省时名称为空。 |
servers[].server |
必须是非空字符串。 | 缺失或为空会拒绝整份文档。 |
servers[].server_port |
必须是 1 到 65535 范围内的整数。 |
缺失、非整数或越界会拒绝整份文档。 |
servers[].password |
必须是非空字符串。 | 缺失或为空会拒绝整份文档。 |
servers[].method |
必须是非空字符串。请提供客户端可用的 Shadowsocks 加密方法。 | 缺失或为空会拒绝整份文档;方法是否可用由代理运行时决定。 |
servers[].plugin、servers[].plugin_opts |
未使用插件时两项省略或留空;使用插件时按 SIP-008 提供插件名及对应选项。 | 客户端将值带入服务器配置,不会在订阅解析时校验插件选项语义。 |
服务器 ID 表示逻辑身份,不是数组位置。改名、改地址、改端口或更新凭据时,如果仍是同一逻辑服务器,应保留原 ID;新增一台逻辑服务器时分配新 ID。UUID 应采用连字符分隔的规范形式,并在正文和扩展引用中保持完全相同的字符串。
私有扩展与版本
x_shadowsocksx_ng 是根对象中的私有扩展命名空间,当前支持 schema_version: 1。服务到期时间、流量重置时间、自动更新策略和分组树都放在该对象内。新增的项目私有字段也须归入这个命名空间,不直接放在根层或服务器对象中。
这些字段不是 SIP-008 标准字段。普通 SIP-008 客户端不一定识别它们,因此根层的 servers 仍须包含全部服务器。SIP-008 对自定义字段的说明见 SIP-008 Online Configuration Delivery。
| 字段 | 格式与含义 |
|---|---|
x_shadowsocksx_ng |
根层的 JSON 对象,承载 ShadowsocksX-NG 私有扩展。 |
x_shadowsocksx_ng.schema_version |
必须是整数 1。缺失或不支持的版本不会采用其中的新扩展数据。 |
时间资料、auto_update 和分组树可以单独提供,也可以组合使用。版本受支持时,各功能独立解析:分组树无效不会阻止有效更新策略生效,更新策略无效也不会丢弃有效分组或时间资料。auto_update 的缺失与无效值处理见下文。
订阅资料
以下流量字段来自 SIP-008。供应方应遵守 SIP-008 对数据量字段的约定;字段格式如下。
| 字段 | 供应方格式 | 说明 |
|---|---|---|
bytes_used |
JSON 非负整数,范围为 0 至 18446744073709551615。 |
用户已使用的字节数。不要发送字符串、负数、小数或指数形式。 |
bytes_remaining |
JSON 非负整数,范围同上。没有流量上限时省略;有限额时同时提供 bytes_used。 |
用户剩余可用字节数。不要用私有字段重复表达同一流量含义。 |
x_shadowsocksx_ng.expires_at |
RFC 3339 日期时间字符串,例如 2027-01-01T00:00:00Z 或 2027-01-01T08:00:00+08:00。 |
ShadowsocksX-NG 私有字段,表示服务到期时间。 |
x_shadowsocksx_ng.traffic_reset_at |
RFC 3339 日期时间字符串。 | ShadowsocksX-NG 私有字段,表示流量重置时间。 |
时间字段直接放在 x_shadowsocksx_ng 中,要求 schema_version: 1;该对象可以只包含版本和时间字段,无需提供分组树。根层的 expires_at、traffic_reset_at 不予解析。命名空间缺失、不是对象,或版本缺失、不支持时,视为未提供时间资料。标准流量字段仍放在根层。
日期字段缺失或为 null 表示未提供。客户端逐项解析这些资料;某一项格式错误只会忽略该项,不会丢弃有效服务器、分组或其他有效资料。客户端从 JSON 正文读取资料,不解析 HTTP Subscription-Userinfo 等响应头。
自动更新 auto_update
x_shadowsocksx_ng.auto_update 是可选对象,用于声明供应方的自动更新模式及是否允许用户覆盖。该对象与时间资料、分组树互不依赖。
| 字段 | 类型 | 含义与约束 |
|---|---|---|
auto_update.mode |
字符串 | 必填,区分大小写。只接受 "off"(关闭)、"daily"(每天)或 "weekly"(每周)。 |
auto_update.allow_user_override |
布尔值 | 必填。true 表示 mode 是允许用户覆盖的默认值;false 表示必须采用供应方的 mode。 |
两个字段均无缺省值。mode: "off" 与 allow_user_override: true 组合表示默认关闭,允许用户开启;与 false 组合表示强制关闭。协议不提供自定义更新间隔或固定更新时间字段。
缺失与无效值
缺少任意必填字段、字段类型错误或 mode 取值不受支持,都会使整个 auto_update 无效。null、空对象、数组、布尔值也都是无效值;字符串 "true" 和数字 1 不能代替布尔值 true。
对于通过基础文档校验并成功应用的响应,客户端按下表处理供应方的 auto_update。
| 响应内容 | 处理结果 |
|---|---|
schema_version: 1,且 auto_update 完整有效 |
采用本次值,替换旧值。 |
schema_version: 1,但省略 auto_update |
清除旧值。 |
省略整个 x_shadowsocksx_ng,或将其设为 null |
清除旧值。 |
auto_update 存在但无效,包括设为 null |
忽略本次值,保留旧值。 |
命名空间不是对象且非 null,或 schema_version 缺失、类型错误、不受支持 |
忽略本次值,保留旧值。 |
因此,撤回 auto_update 时应省略该字段,不能用 null 或空对象代替。无效的 auto_update 不影响有效服务器、分组或时间资料的解析。
分组树
分组树表达分组名称、层级、顺序和服务器引用。以下字段均位于 x_shadowsocksx_ng 中,服务器配置仍放在根层 servers。
| 字段 | 含义和约束 |
|---|---|
root_group_id |
非空字符串,必须对应 groups 中根分组的 id。 |
groups |
分组对象数组。每个 id 必须非空且唯一;建议使用不随刷新变化的 UUID。分组 ID 不得与服务器 ID 冲突。 |
groups[].name |
分组显示名称。建议始终提供;缺省时客户端显示空名称。 |
groups[].children |
有序数组,数组顺序就是客户端显示顺序。每项必须是 { "type": "group", "id": "分组 ID" } 或 { "type": "server", "id": "服务器 UUID" }。 |
供应方应确保扩展构成从 root_group_id 出发的单父、无环树:每个分组最多有一个父分组,所有分组和服务器引用都能解析到相应对象。每个服务器最多引用一次;请将每个预期展示的分组放在根分组可到达的树中。分组与服务器 ID 都是稳定身份,不要把名称、数组下标或可变配置内容当作 ID。
分组树的解析与回退规则如下:
- 扩展版本缺失或不支持、根分组不存在、ID 冲突、引用无效、子项类型未知、出现环或分组被多个父分组引用时,分组树会被忽略,客户端改用标准
servers扁平列表;有效服务器不会因此丢失。 - 未被有效树引用但存在于
servers的服务器仍会显示在根分组末尾,顺序沿用其在servers数组中的顺序。因此扩展失效或不完整时,服务器仍可能显示,但分组关系不会保留。 - 树中不可从根到达的分组不会显示。请勿依赖客户端对不可达分组或重复服务器引用的处理;供应方应输出没有孤立分组、没有重复服务器引用的有效树。
- 缺少服务器
id的记录无法被树引用;为保证分组准确,请给所有服务器提供稳定 ID。
完整响应示例
此示例包含两台服务器、时间与流量资料、允许用户覆盖的每天更新策略,以及嵌套分组。
{
"version": 1,
"servers": [
{
"id": "3c5e2af7-9f2c-4f42-bd1c-9e143d5f2d11",
"remarks": "Tokyo 1",
"server": "tokyo.example.com",
"server_port": 443,
"password": "example-secret-1",
"method": "chacha20-ietf-poly1305"
},
{
"id": "b9f08d65-8be7-4b09-bf94-4f2bb53e4199",
"remarks": "Osaka 1",
"server": "osaka.example.com",
"server_port": 443,
"password": "example-secret-2",
"method": "chacha20-ietf-poly1305"
}
],
"bytes_used": 1073741824,
"bytes_remaining": 9663676416,
"x_shadowsocksx_ng": {
"schema_version": 1,
"expires_at": "2027-01-01T00:00:00Z",
"traffic_reset_at": "2026-11-01T00:00:00Z",
"auto_update": {
"mode": "daily",
"allow_user_override": true
},
"root_group_id": "f6ad0a41-2a1d-4db8-9da0-b6f4f3dc889d",
"groups": [
{
"id": "f6ad0a41-2a1d-4db8-9da0-b6f4f3dc889d",
"name": "我的订阅",
"children": [
{ "type": "group", "id": "a1e2b11c-2f53-4e39-bc36-95af6dbb5f1c" },
{ "type": "server", "id": "3c5e2af7-9f2c-4f42-bd1c-9e143d5f2d11" }
]
},
{
"id": "a1e2b11c-2f53-4e39-bc36-95af6dbb5f1c",
"name": "日本",
"children": [
{ "type": "server", "id": "b9f08d65-8be7-4b09-bf94-4f2bb53e4199" }
]
}
]
}
}
提供方发布前检查
- 使用 HTTPS,返回 HTTP
200和Content-Type: application/json; charset=utf-8。 - 提供合法 JSON,根级
version为1,servers包含完整服务器记录。 - 为每台服务器提供唯一且稳定的 UUID;服务器 ID 在
servers和分组引用中逐字一致。 - 若使用扩展,
schema_version为1;若提供分组树,根分组须存在,所有引用有效,树有序、单父、无环。 - 流量字段使用 SIP-008 标准字段;到期与重置日期放在
x_shadowsocksx_ng下,使用本文规定的 RFC 3339 字符串。 - 若提供
auto_update,同时提供有效的mode和布尔值allow_user_override;确认是否允许用户覆盖,并检查撤回策略时没有误用null或空对象。 - 以不支持
x_shadowsocksx_ng的普通 SIP-008 客户端验证时,扁平servers列表仍包含全部服务器。