Provider 服务提供者

你可以在 provider 目录内看到两个已经写好的 Provider,它们分别是订阅地址和自己维护的节点列表。

需要注意的是文件名即为该 Provider 的名称,后面在定义 Artifact 时会用到。

Surgio 内置了 defineXxxProvider 方法,Xxx 对应下面所列的类型,可以让 IDE 智能提示配置项,不过你也可以不使用这样的语法糖。下面两种写法都是可以的。

const { defineClashProvider } = require('surgio');

module.exports = defineClashProvider({
  url: 'https://example.com/clash.yaml',
  // ...
});
module.exports = {
  type: 'clash',
  // ...
};

支持异步函数

为了满足更多定制化的场景,支持通过异步函数的模式挂载 Provider

const { defineCustomProvider } = require('surgio');

module.exports = defineCustomProvider(async function() {
  const myNodeList = await someAsyncFunction();

  return {
    nodeList: myNodeList,
  };
});

订阅类型

目前 Surgio 支持两种 Provider 类型:

类型描述备注
custom 推荐自己维护的节点支持 Shadowsocks, Shadowsocksr, Snell, HTTPS, HTTP, Vmess, Socks5, Tuic
clash 推荐Clash 配置支持 Shadowsocks, Shadowsocksr, Snell, HTTPS, HTTP, Vmess, Socks5, Tuic
trojanTrojan 订阅Shadowrocket 支持的 Trojan 订阅格式
shadowsocks_json_subscribe针对 Windows 客户端的 Shadowsocks 订阅地址通常命名为 gui-config.json
shadowsocks_subscribe通用的 Shadowsocks 订阅地址
shadowsocksr_subscribe通用的 Shadowsocksr 订阅地址
v2rayn_subscribeV2rayN 订阅地址支持 V2Ray, Shadowsocks, 协议open in new window
ssdSSD 订阅支持 Shadowsocks

Clash 订阅 推荐

注意

  1. Surgio 支持读取 obfs-localv2ray-plugin 两种 SIP003 插件配置;
  2. 仅支持 v2ray-plugin 的 WebSocket 模式;

url

  • 类型: string
  • 必须

udpRelay

  • 类型: boolean
  • 默认值: false

我们发现部分机场的 Clash 订阅并没有设定 udp,所以你可以通过配置这个属性来强制设定节点的 UDP 转发支持情况。如果订阅节点中包含 udp 字段,则该配置无效。

tls13

  • 类型: boolean
  • 默认值: false

强制开启节点的 TLS 1.3。

Custom 自定义节点 推荐

由自己维护的节点列表。

普通模式

const { defineCustomProvider } = require('surgio');

module.exports = defineCustomProvider({
  nodeList: [
    {
      type: 'shadowsocks',
      // ...
    },
  ],
});

异步模式

Surgio v3.0.0
Gateway: v2.0.0

异步模式下,Gateway 的 /get-artifact 请求参数会被传入到 nodeList 函数中。这样可以实现动态的节点列表。

customParams 是一个对象,包含了所有在 Artifact 和全局定义的自定义参数。假如你使用了 Gateway,则里面还包含所有请求 URL 中的参数。需要注意的是,URL 参数中所有的值都是字符串类型,例如 mobile=true,那么 customParams.mobile 的值就是 'true'

customParams 默认会包含 requestUserAgent,方便你根据不同的客户端返回不同的节点列表。

const { defineCustomProvider } = require('surgio');

module.exports = defineCustomProvider({
  nodeList: async (customParams) => {
    if (customParams.mobile === 'true') {
      return [
        {
          type: 'shadowsocks',
          // ...
        },
      ];
    } else {
      return [
        {
          type: 'trojan',
          // ...
        },
      ];
    }
  },
});

Shadowsocks

{
  type: 'shadowsocks',
  nodeName: '🇺🇸US',
  hostname: 'us.example.com',
  port: 10000,
  method: 'chacha20-ietf-poly1305',
  password: 'password',
  obfs: 'tls', // tls, http, ws, wss
  obfsHost: 'gateway-carry.icloud.com',
  obfsUri: '/', // 当 obfs 为 ws 或 wss 时可配置
  udpRelay: true,
  tfo: false, // TCP Fast Open
  tls13: false, // TLS 1.3,适用于 v2ray-plugin
  mux: false, // 目前仅 Clash + Shadowsocks + v2ray-plugin 可用
}

注意

  1. wswss 是通过服务端 v2ray-plugin 支持的;
  2. TLS 1.3 需要服务端支持

Shadowsocksr

{
  type: 'shadowsocksr',
  nodeName: '🇭🇰HK',
  hostname: 'hk.example.com',
  port: 10000,
  method: 'chacha20-ietf',
  password: 'password',
  obfs: 'tls1.2_ticket_auth',
  obfsparam: 'music.163.com',
  protocol: 'auth_aes128_md5',
  protoparam: '',
  udpRelay: true,
  tfo: false, // TCP Fast Open
}

Vmess

{
  nodeName: '🇭🇰HK',
  type: 'vmess',
  hostname: 'hk.example.com',
  method: 'auto', // 仅支持 auto/aes-128-gcm/chacha20-ietf-poly1305/none
  network: 'ws', // 仅支持 tcp/ws
  alterId: '64',
  path: '/',
  port: 8080,
  tls: false,
  host: 'example.com', // 此属性相当于 wsHeaders.host,但推荐配置在 wsHeaders.host 上
  uuid: '1386f85e-657b-4d6e-9d56-78badb75e1fd',
  tfo: false, // TCP Fast Open
  tls13: false, // TLS 1.3, TLS 开启时有效
  udpRelay: true, // 开启 UDP 转发
  wsHeaders: {
    key: 'value',
  },
}

Snell

{
  type: 'snell',
  nodeName: '🇭🇰HK',
  hostname: 'hk.example.com',
  port: 10000,
  psk: 'RjEJRhNPps3DrYBcEQrcMe3q9NzFLMP',
  obfs: 'tls', // tls 或 http 或不传
  obfsHost: 'gateway-carry.icloud.com', // 可选
  version: 4, // 可选,默认不传以 Surge 为准
  reuse: true, // 可选,默认 false
}

HTTPS

{
  type: 'https',
  nodeName: '🇭🇰HK',
  hostname: 'hk.example.com',
  port: 443,
  username: 'username',
  password: 'password',
  tls13: false, // TLS 1.3
}

HTTP

{
  type: 'http',
  nodeName: '🇭🇰HK',
  hostname: 'hk.example.com',
  port: 8080,
  username: 'username',
  password: 'password',
}

Trojan

{
  type: 'trojan',
  nodeName: '🇭🇰HK',
  hostname: 'hk.example.com',
  port: 443,
  password: 'password',
  sni: 'example.com', // 可选
  alpn: ['http/1.1'], // 可选
  skipCertVerify: true, // 可选
  udpRelay: true, // 可选
  tls13: false, // TLS 1.3
  network: 'ws', // 可不填
  wsPath: '/', // 可选
  wsHeaders: {}, // 可选
}

Socks5

{
  type: 'socks5',
  nodeName: '🇭🇰HK',
  hostname: 'hk.example.com',
  port: 80,
  username: 'username', // 可选
  password: 'password', // 可选
  tls: true, // 可选
  skipCertVerify: true, // 可选
  udpRelay: false, // 可选, 仅 Clash 支持
  sni: 'example.com', // 可选, 仅 Surge 支持
  tfo: true, // 可选, 仅 Surge 支持
  clientCert: 'item' // 可选, 仅 Surge 支持
}

clientCert 仅 Surge 支持, 参考 文档open in new window 进行配置。

Wireguard

理论上你可以设置多个 Peer,但是目前仅 Surge 支持多个 Peer 的配置,其它客户端需要自行分拆成多个相互独立的配置。

{
   type: 'wireguard',
   nodeName: 'Wireguard',
   selfIp: '172.16.0.2',
   privateKey: 'eOyyaXrwVTHwo62x98Is6v5Fo=',
   peers: [
     {
       publicKey: 'eOyyaXrwVTHwo62x98Is6v5Fo=',
       endpoint: 'wg.example.com:54321',
       allowedIps: '172.16.0.0/24', // 可选
       keepalive: 25, // 可选
       presharedKey: 'eOyyaXrwVTHwo62x98Is6v5Fo=', // 可选
       reservedBits: [16], // 可选
     }
   ],
   selfIpV6: '2001:0db8:85a3:0000:0000:8a2e:0370:7334', // 可选
   preferIpv6: false, // 可选
   mtu: 1420, // 可选
   dnsServers: ['1.1.1.1'], // 可选
   reservedBits: [16], // 可选
}

Tuic

V5

Surgio v3.0.0
{
  type: 'tuic',
  nodeName: 'Tuic',
  hostname: 'tuic.example.com',
  port: 443,
  password: 'password',
  uuid: '1386f85e-657b-4d6e-9d56-78badb75e1fd',
  version: 5,
  sni: 'sni.example.com', // 可选
  skipCertVerify: true, // 可选
  alpn: ['h3'], // 可选,Stash 不支持空值
  udpRelay: false, // 可选, 仅 Clash 支持更改,Surge 默认开启
}

老版本协议

{
  type: 'tuic',
  nodeName: 'Tuic',
  hostname: 'tuic.example.com',
  port: 443,
  token: 'password',
  sni: 'sni.example.com', // 可选
  skipCertVerify: true, // 可选
  alpn: ['h3'], // 可选,Stash 不支持空值
  udpRelay: false, // 可选, 仅 Clash 支持更改,Surge 默认开启
}

Hysteria

Surgio v3.1.0

Surgio 只支持 Hysteria v2 协议。请注意,Hysteria v2 协议和 v1 协议完全不兼容。当前可以为 Clash 和 Surge 生成此节点。

Clash 需要在配置中开启 clashConfig.enableHysteria2

{
  type: 'hysteria2',
  nodeName: 'Hysteria',
  hostname: 'hysteria.example.com',
  port: 443,
  password: 'password',
  sni: 'sni.example.com', // 可选
  skipCertVerify: true, // 可选
  alpn: ['h3'], // 可选,Stash 不支持空值
  udpRelay: false, // 可选, 仅 Clash 支持更改,Surge 默认开启
}

SSD 订阅

module.exports = {
  type: 'ssd',
  url: '',
  udpRelay: true,
};

注意

  1. Surgio 支持读取 simple-obfsv2ray-plugin 两种 SIP003 插件配置;
  2. 仅支持 v2ray-plugin 的 WebSocket 模式;

url

  • 类型: string
  • 必须

udpRelay

  • 类型: boolean
  • 默认值: false

你可以通过配置这个属性来强制设定节点的 UDP 转发支持情况。

Shddowsocks JSON 订阅

module.exports = {
  type: 'shadowsocks_json_subscribe',
  url: '',
  udpRelay: true,
};

url

  • 类型: string
  • 必须

若机场没有提供这种订阅地址,推荐使用 Fndroid 的 接口open in new window 进行转换。

注意

udpRelay

  • 类型: boolean
  • 默认值: false

由于这种订阅协议不支持定义 UDP 转发的支持情况,所以单独出来进行配置。UDP 转发可以应用在 Surge 中。

Shadowsocks 订阅

module.exports = {
  type: 'shadowsocks_subscribe',
  url: '',
  udpRelay: true,
};

注意

  1. Surgio 支持读取 obfs-localv2ray-plugin 两种 SIP003 插件配置;
  2. 仅支持 v2ray-plugin 的 WebSocket 模式;

url

  • 类型: string
  • 必须

注意

udpRelay

  • 类型: boolean
  • 默认值: false

由于这种订阅协议不支持定义 UDP 转发的支持情况,所以单独出来进行配置。UDP 转发可以应用在 Surge 中。

Shadowsocksr 订阅

module.exports = {
  type: 'shadowsocksr_subscribe',
  url: '',
};

url

  • 类型: string
  • 必须

V2rayn 订阅

module.exports = {
  type: 'v2rayn_subscribe',
  url: '',
};

url

  • 类型: string
  • 必须

注意

  • Quantumult 的订阅格式和 V2rayN 的订阅格式有差异,不可以混用;
  • 如果你正在使用 DlerCloudopen in new window,可以使用「通用」类型的订阅地址;
  • 订阅中的 V2Ray 和 Shadowsocks 节点会被读取;

compatibleMode

  • 类型: boolean
  • 默认值: false

部分机场提供的订阅地址不符合标准,提供一个兼容模式进行解析。

udpRelay

  • 类型: boolean
  • 默认值: false

由于这种订阅协议不支持定义 UDP 转发的支持情况,所以单独出来进行配置。

skipCertVerify

  • 类型: boolean
  • 默认值: false

由于这种订阅协议不支持定义跳过证书验证,所以单独出来进行配置。

tls13

  • 类型: boolean
  • 默认值: false

强制开启节点的 TLS 1.3。

Trojan 订阅

module.exports = {
  type: 'trojan',
  url: '',
};

注意

该订阅方式仅支持标准的 Trojan 协议,不支持 WebSocket 和 GRPC

url

  • 类型: string
  • 必须

udpRelay

  • 类型: boolean
  • 默认值: false

强制开启节点的 UDP 转发。

tls13

  • 类型: boolean
  • 默认值: false

强制开启节点的 TLS 1.3。

公共属性

提示

  • 公共属性可以定义在任何一种 Provider 中;
  • 请务必注意下面 nodeConfig 指的是 custom 类型内的每个节点,provider 指的是 Provider;

nodeConfig.enable

  • 类型: boolean
  • 默认值: true

单独关闭某个节点输出到配置中。若没有 enable 属性则默认打开。

{
  enable: false,
  type: 'shadowsocks',
  nodeName: '🇺🇸US',
  hostname: 'us.example.com',
  port: 10000,
  method: 'chacha20-ietf-poly1305',
  password: 'password',
}

nodeConfig.tfo

  • 类型: boolean
  • 默认值: false

是否为该节点开启 TFO(TCP Fast Open)。

nodeConfig.mptcp

  • 类型: boolean
  • 默认值: false

是否为该节点开启 Multipath TCP。目前仅 Surge 支持这一特性。

nodeConfig.shadowTls

  • 类型: object
  • 默认值: undefined

目前仅 Surge 和 Stash 支持这一特性。

nodeConfig.shadowTls.password

  • 类型: string
  • 必须

nodeConfig.shadowTls.sni

  • 类型: string
  • 必须

nodeConfig.shadowTls.version

  • 类型: number
  • 默认值: undefined

nodeConfig.tls13

  • 类型: boolean
  • 默认值: false

为 TLS 节点开启 TLS 1.3 支持。

注意

  1. TLS 1.3 需要服务端支持;
  2. 支持 TLS 的节点类型有 Shadowsocks with v2ray-plugin(tls), Vmess(tls), HTTPS;

nodeConfig.skipCertVerify

  • 类型: boolean
  • 默认值: false

关闭 TLS 节点的证书检查。

注意

  1. 支持 TLS 的节点类型有 Shadowsocks with v2ray-plugin(tls), Vmess(tls), HTTPS;
  2. 请不要随意将证书检查关闭;

nodeConfig.underlyingProxy

  • 类型: string
  • 默认值: undefined

可以通过一个代理跳板使用另一个代理,可以无限嵌套使用。目前仅 Surge 支持该特性。

注意

Surgio 不会验证名称是否有效

nodeConfig.testUrl

  • 类型: string
  • 默认值: undefined

在新版的 Surge 中支持针对某个 Proxy 设置测试的地址。你可以通过这个参数来设置改地址。

注意

  1. Surgio 不会验证名称是否有效;
  2. 目前仅 Surge 支持该特性;

nodeConfig.serverCertFingerprintSha256

  • 类型: string
  • 默认值: undefined

用于验证服务器证书的 SHA256 指纹。目前仅 Surge 支持该特性。

nodeConfig.ecn

  • 类型: boolean
  • 默认值: false

是否为该节点开启 ECN(Explicit Congestion Notification)open in new window。目前仅 Surge 支持这一特性。

provider.nodeFilter

  • 类型: Function
  • 入参: NodeConfig
  • 返回值: boolean

有一些俗称「外贸机场」的服务商提供很多诸如马来西亚、土耳其的节点,不需要这些国家节点的朋友每次都要在数十个节点中寻找自己想要的。我们可以用这个方法把这些节点过滤掉。

const { utils } = require('surgio');

module.exports = {
  // 过滤出名字中包含土耳其和马来西亚的节点
  nodeFilter: utils.useKeywords(['土耳其', '马来西亚']),
};

提示

关于过滤器的自定义和其它进阶使用方法,请阅读 「自定义过滤器」

provider.netflixFilter

  • 类型: Function
  • 入参: NodeConfig
  • 返回值: boolean

该方法会覆盖 Surgio 内置的 netflixFilter。用于过滤出支持 Netflix 的节点。对于那些每一个节点都解锁流媒体的机场,也可以单独过滤出部分你喜欢的节点。

内置 netflixFilter 的解释

module.exports = {
  // 过滤出名字中包含 HK(大小写不敏感)的节点
  netflixFilter: utils.useKeywords(['hk', 'HK']),
};

provider.youtubePremiumFilter

  • 类型: Function
  • 入参: NodeConfig
  • 返回值: boolean

该方法会覆盖 Surgio 内置的 youtubePremiumFilter。用于过滤出支持 Youtube Premium 的节点。

内置 youtubePremiumFilter 的解释

provider.customFilters

  • 类型: object
  • 默认值: undefined

自定义 Filter。关于自定义 Filter 的用法,请阅读 进阶 - 自定义 Filter

提示

你现在可以定义 全局的过滤器 了!

provider.startPort

  • 类型: number

在调用 getSurgeNodes 时会强制要求设置该值。建议大于 10000。

在生成 Surge 的 Shadowsocksr 和 Vmess 配置文件时,本地监听端口会根据此配置递增。这样做的好处是切换配置文件时不会遇到端口冲突。同一个 Provider 被用在不同的 Artifact 中也会进行递增。

provider.addFlag

  • 类型: boolean
  • 默认值: false

在节点名称前加国旗 Emoji。需要注意的是,Surgio 是根据有限的节点名关键词判断位置的,如果无法匹配则会保留原节点名。你可以在所有的过滤器中检索国旗 Emoji。

provider.removeExistingFlag

  • 类型: boolean
  • 默认值: false

去除订阅中的国旗 Emoji。可以在不开启 addFlag 时使用,这时会输出没有 Emoji 的节点名称。

provider.tfo

  • 类型: boolean
  • 默认值: false

是否为该订阅强制开启 TFO(TCP Fast Open)。部分机场虽然支持 TFO 但是没有在订阅中开启,你可以通过这个配置强制打开。

provider.underlyingProxy

  • 类型: string
  • 默认值: undefined

是否对当前 Provider 中所有节点使用自定义 Underlying Proxyopen in new window。在 CustomProvider 中也可以使用,但是优先级低于 nodeConfig.underlyingProxy

目前仅 Surge 支持该特性。

注意

Surgio 不会验证名称是否有效

provider.mptcp

  • 类型: boolean
  • 默认值: false

是否为该订阅强制开启 Multipath TCP。目前仅 Surge 支持这一特性。

provider.renameNode

  • 类型: Function
  • 默认值: undefined

更改节点名。如果你对机场的奇葩命名有意见,可以在这里把他们替换掉。

module.exports = {
  renameNode: name => {
    if (name === '社会主义') {
      return '资本主义';
    }
    return name;
  },
};

注意

  1. nodeFilter 只对原始名称有效;
  2. 其它内置过滤器和自定义过滤器仅对新名称有效;
  3. 如果你开启了 addFlag,那国家地区判定仅对新名称有效;
  4. 这个方法不一定要在末尾 return 内容,如果没有返回内容则保留原名称;

provider.relayUrl

  • 类型: Boolean|String
  • 默认值: undefined

开启订阅地址转发。由于部分机场禁止 AWS 等公有云服务器访问,所以面板无法获取订阅内容。开启后会使用一个免费并且安全的转发服务进行获取。

从 v2.4.0 开始,你可以指定一个字符串来自定义转发服务。设置的方法如下:

  1. URL 中插入原始的订阅连接:
module.exports = {
  relayUrl: 'https://proxy.example.com/%URL%',
};
  1. URL 中插入 URL encoded 后的订阅连接:
module.exports = {
  relayUrl: 'https://proxy.example.com/?url=%%URL%%',
};

如果 relayUrl 是一个布尔值,则使用内置的服务进行转发。

provider.requestUserAgent

  • 类型: string
  • 默认值: undefined

指定订阅请求头中的 User-Agent 字段。若不指定则使用内置的默认值 surgio/<版本号>

钩子函数 (hooks)

钩子函数是 Surgio v3 新增的特性,用于在获取远程订阅内容时执行一些操作。你可以在所有类型的 Provider 中定义钩子函数。所有的钩子函数都会在 renameNode, addFlag, removeExistingFlag, nodeFilter 等原有用来修改节点的方法之前执行。

hooks.afterNodeListResponse

async afterNodeListResponse(nodeList: NodeConfig[], customParams: {}): Promise<NodeConfig[]>

该钩子函数会在成功获取到远程订阅内容后执行。你可以自由修改节点的内容,甚至是像维护自定义类型 Provider 那样追加自己的节点。

这个方法要求最后需要返回一个 NodeConfig[],否则你的操作可能不会生效。

customParams 是一个对象,包含了所有在 Artifact 和全局定义的自定义参数。假如你使用了 Gateway,则里面还包含所有请求 URL 中的参数和 requestUserAgent,方便你根据不同的客户端返回不同的节点列表。

const { defineClashProvider } = require('surgio');

module.exports = defineClashProvider({
  url: 'https://example.com/clash.yaml',
  hooks: {
    afterNodeListResponse: async (nodeList, customParams) => {
      // nodeList: NodeConfig[]
      // customerParams: {}
      return nodeList;
    },
  },
  }
})

hooks.onError

async onError(error: Error): Promise<NodeConfig[] | undefined>

该钩子函数会在获取远程订阅内容失败时执行。你可以在这里进行错误处理,或者返回一个 NodeConfig[] 以继续执行,格式类似 CustomProvider 里的节点列表。

const { defineClashProvider } = require('surgio');

module.exports = defineClashProvider({
  url: 'https://example.com/clash.yaml',
  hooks: {
    onError: async error => {
      // error: Error
      return [
        {
          nodeName: 'Fallback',
          type: 'shadowsocks',
          hostname: 'fallback.example.com',
          port: 443,
          method: 'chacha20-ietf-poly1305',
          password: 'password',
        },
      ];
    },
  },
  }
})