diff --git a/docs/.vuepress/config.ts b/docs/.vuepress/config.ts index eea36ee0c..10324becf 100644 --- a/docs/.vuepress/config.ts +++ b/docs/.vuepress/config.ts @@ -342,6 +342,7 @@ export default defineUserConfig({ '/en_US/v5/config/service/burstObservatory', '/en_US/v5/config/service/backgroundObservatory', '/en_US/v5/config/service/tun', + '/en_US/v5/config/service/subscription', ], } ], diff --git a/docs/en_US/v5/config/service.md b/docs/en_US/v5/config/service.md index c7a5ccbb4..04dd233e2 100644 --- a/docs/en_US/v5/config/service.md +++ b/docs/en_US/v5/config/service.md @@ -8,3 +8,4 @@ V2Ray have some built-in services that supplement the primary function of V2Ray. * [Background Observatory](service/backgroundObservatory.md) * [Burst Observatory](service/burstObservatory.md) * [Tun](service/tun.md) +* [Subscription Manager](service/subscription.md) diff --git a/docs/en_US/v5/config/service/subscription.md b/docs/en_US/v5/config/service/subscription.md new file mode 100644 index 000000000..37f7f5eab --- /dev/null +++ b/docs/en_US/v5/config/service/subscription.md @@ -0,0 +1,267 @@ +# Subscription Manager +* Name: `subscription` +* Type: Service +* ID: `service.subscription` + +Subscription Manager automatically refresh server information and turn them into outbounds. (v5.13.0+) + +## Subscription +> `imports` : [ [SubscriptionImportObject](#SubscriptionImportObject) ] + +## SubscriptionImportObject + +> `name`: string +The name of subscription import source. +> `url`: string +URL of the import source. +Currently 2 kinds of Subscription URL are supported +- HTTP(S) URL : Download a subscription document over http(s). +- DataURL : the url itself will be the subscription document. The media type must be "application/vnd.v2ray.subscription-singular" for it to be accepted. + +> `tagPrefix`: string +The prefix of outbound created from this subscription source. + +> `importUsingTag`: string +The outbound tag to download the subscription document. + +> `defaultExpireSeconds`: number +The default time to fetch subscription document again. This time is not strictly honored. + +Subscription Manager will +1. Check `ImportSources` from time to time to download subscription documents +2. Parse subscription documents with `Subscription Container` parsing components. The subscription documents will then be converted into individual server definitions. +3. Try to parse the server definitions as standardized outbound format one by one, if fails, try to convert the document into outbound format with `Subscription Converter`. +4. Compare the successfully converted server definition set with the servers already instanced as outbounds, and apply the difference. Only the modified server will have their Outbound Handlers recreated. + +## Subscription Container +Container is the document includes a set of server definitions plus metadata. +There is currently 3 supported containers, all of them are generic containers that does not, on their own, compliant with any subscription specs. Instead, they are the simplest way to capture whatever information contained in a subscription document *without* verifying schema. + +The 3 currently supported container formats are: +- Base64URLLine: A superset of base64 or V2RayN format. The document contains a base64 encoded `newline` separated server definitions. +- JsonFieldArray: A superset of SIP008 or OOCv1 format. The document contains one or more array field at root object. And each item in the arrays is a server definition. +- Yaml2Json+JsonFieldArray: A superset of Clash format. The document are converted to json from Yaml format and processed with JsonFieldArray. +- DataURLSingle: A single server spec wrapped in DataURL. The media type must be "application/vnd.v2ray.subscription-singular" for it to be accepted. + +Use `v2ray engineering subscriptionEntriesExtract -input filename` to output a zipped file contains what would be captured by V2Ray's Subscription Container parser. + +Use `v2ray engineering encodeDataURL -type application/vnd.v2ray.subscription-singular` to generate a dataURL. + +If you are generating these documents, instead of trying to parse one generated by someone else. You should be aware that V2Ray does not check for schema of formats mentioned. For this reason, a document could by parsed by V2Ray may be rejected by other applications. + +## Subscription Converter +If the server definition is in V2Ray natively supported outbound format, it will be directly loaded with outbound converter. It is recommended for all service providers to generate their subscription in outbound format so that V2Ray could parse their with predictable result. + +Otherwise, if the input is not of native outbound format, the non-native input will be converted with conversion rules. The input will be preprocessed with `AbstractNonNativeLink` info extractor and split up input into many strings that can be referenced with their keys, and then all conversion rules will be tried one by one, until one successfully completed without error. + +To see what keys and values will be extracted from server definition run: +`v2ray engineering nonnativelinkextract` and input server definition into stdin. A sorted key value set will be output to stdout for examination. + +The converter rules are either embedded into V2Ray or provided by user. The user defined rules always take precedence. +The embedded converter rules are placed in `app/subscription/entries/nonnative/definitions` directory. The user defined rules are provided as a zipped file as `nonnative_converter_overlay_file` field of subscription manager configuration. + +Subscription converters output a V2Ray native outbound format, which will then be converted to a V2Ray's internal format. + +Currently, embedded converter rules have limited support for different protocols and vendors. Contributions are welcome. + +#### Subscription Converter: Nonnative converter definition format + +The converters use syntax as defined by [Golang Template](https://pkg.go.dev/text/template) with some additional functions to access input and process subscriptions. + +To run an embedded template against a server definition input, run +`v2ray engineering nonnativelinkexec -name vmess.jsont` + +To run an external template against a server definition input, run +`v2ray engineering nonnativelinkexec -templatePath .... -name vmess.jsont` + +`templatePath` should point to a directory with a directory named `definitions` with all the templates. The content of this directory can be zipped and pointed to at `nonnative_converter_overlay_file` to overlay over embedded definitions. + +In addition to functions defined by template package, the following domain specific functions are defined. + +###### assertExists + +`assertExists . key1 [key2 ...]` + +Checks if `key1 [key2 ...]` all exists in the extracted link attributes. If not, then template halt with an error. + +###### assertIsOneOf + +`assertIsOneOf . key [values ...]` + +Check if the content of attribute[key] is one of the values. If it is not in one of the values, then template halt with an error. + +###### assertValueIsOneOf + +`assertValueIsOneOf checkedValue [values ...]` + +Check if the checkedValue is one of the values. If it is not in one of the values, then template halt with an error. + +###### tryGet +`tryGet . key1 [key2 ...] [""]` + +Returns the first value found in key1 or key2... .If they are all not found and the last key is not `` then halt with an error, if he last key is `` then returns "" when nothing is found. + +###### splitAndGetNth + +`splitAndGetNth sep n content` + +Split content with sep, and return the nth element. A negative n will be treated as `len(splited_content) + n`, If there is less than n element after split, then halt with an error. + +###### splitAndGetAfterNth + +`splitAndGetNth sep n content` + +Split content with sep, and return the nth element and elements after it. A negative n will be treated as `len(splited_content) + n`, If there is less than n element after split, then halt with an error. + +###### splitAndGetBeforeNth + +`splitAndGetBeforeNth sep n content` + +Split content with sep, and return the nth element and elements before it. A negative n will be treated as `len(splited_content) + n`, If there is less than n element after split, then halt with an error. + +###### jsonEncode + +`jsonEncode anyData` + +Return anyData encoded with json + +###### stringCutSuffix +`stringCutSuffix suffix content` + +Remove suffix from content's tail. If suffix is not found, halt with an error. + +###### unalias +`unalias standardName [alias...] input` + +if input is one of the alias return standardName, otherwise return input. + +## Subscription Spec: outbound format + +The outbound format is the recommended subscription format for V2Ray, and what converters generates. Its `protocol`, `settings`, `streamSettings.transport`, `streamSettings.security`, `streamSettings.transportSettings`, `streamSettings.securitySettings` all have the same meaning with jsonv5 format's outbound. + +The `metadata` is an associative array of strings. +- `TagName` means the name of outbound tag. If it is of letters and numbers and less than 24 characters, it will be used as is and appended to user defined tag prefix as outbound's tag. Otherwise a generated one will be used. + +```json +{ + "protocol": "vmess", + "settings": { + "address": "1234", + "port": 1234, + "uuid": "1234" + }, + "streamSettings": { + "transport": "grpc", + "security": "tls", + "transportSettings": { + "serviceName": "1234" + }, + "securitySettings": { + "serverName": "1234" + } + }, + "metadata": { + "TagName": "1234" + } +} +``` +## Example +Finally, here is a neat example of using subscription system with load balancing and observatory. +```json +{ + "log": { + "error": { + "level": "Debug", + "type": "Console" + }, + "access": { + "type": "None" + } + }, + "outbounds": [ + { + "tag": "deny", + "protocol": "blackhole" + }, + { + "tag": "direct", + "protocol": "freedom" + } + ], + "router": { + "domainStrategy": "AsIs", + "rule": [ + { + "balancingTag": "subscriptions", + "inboundTag": [ + "user-in" + ] + }, + { + "tag": "commander", + "inboundTag": [ + "commander" + ] + } + ], + "balancingRule": [ + { + "tag": "subscriptions", + "outbound_selector": [ + "subscription_" + ], + "strategy": "leastping" + } + ] + }, + "inbounds": [ + { + "tag": "user-in", + "protocol": "socks", + "settings": { + "udpEnabled": true, + "address": "127.0.0.1", + "packetEncoding": "Packet" + }, + "port": 19828, + "listen": "127.0.0.1" + }, + { + "tag": "commander", + "protocol": "dokodemo-door", + "settings": { + "address": "127.0.0.1", + "port": 65535, + "networks": ["tcp"] + }, + "port": 19829, + "listen": "127.0.0.1" + } + ], + "services": { + "backgroundObservatory": { + "subject_selector": [ + "subscription_" + ], + "probe_interval": 5000000000 + }, + "subscription": { + "imports": [ + { + "name": "VDA", + "url": "https://123", + "tagPrefix": "subscription_vda", + "importUsingTag": "direct", + "defaultExpireSeconds": 3600 + } + ] + }, + "commander": { + "tag": "commander", + "name": [ + "observatory" + ] + } + } +} +```