Skip to content

Proxy Chain

Clash Nyanpasu inherits the proxy chain feature from Clash Verge and can implement a proxy chain through two types of chains: Script or Merge. The content of this section is adapted from Clash Verge documentation1.

A proxy chain refers to the post-processing chain of a Profile. A profile can go through nodes such as A, B, C, or even more chain processing nodes, where the configuration is modified using scripts or expressions to generate a final proxy configuration. Multiple nodes are processed sequentially, forming a chain structure. You can customize the chain to enable certain nodes for different profiles or globally, thereby creating your own proxy chain.

The following diagram shows the current chain processing structure.

flowchart TD
    A(Start)-->B["Load Profiles"]-->profile_chain-->global_chain-->G["Merge Protected Fields"]-->internal_chain-->J{"Enable Field Filtering?"}-->|Yes|K["Filter Fields"]-->L["Misc: Handle Tun, Sorting"]
    J-->|No|L
    L-->M[Generate Configuration]-->N(End)
    subgraph profile_chain [Profile Chain]
        C["..."]-->D["Processing Node n"]
    end
    subgraph global_chain [Global Chain]
        E["..."]-->F["Processing Node n"]
    end
    subgraph internal_chain ["Internal Chain (For Compatible Scripts)"]
        H["..."]-->I["Processing Node n"]
    end

This processing node provides configuration merging functionality similar to OpenClash. It achieves this by defining the following rules for overwriting configurations.

  • append__x.y.z - Appends the content of the x.y.z field to the end of the original x.y.z list. If x.y.z does not exist or is not an array, it will be ignored, and a log warning will be triggered.
  • prepend__x.y.z - Adds the content of the x.y.z field to the beginning of the original x.y.z list. If x.y.z does not exist or is not an array, it will be ignored, and a log warning will be triggered.
  • override__x.y.z - Directly overrides the content of the x.y.z field. If x.y.z does not exist, it will be ignored, and a log warning will be triggered.
  • filter__x.y.z - Filters or modifies the content of the x.y.z field. For supported types, refer to filter Supported Operations.
  • Other fields - Fields are recursively merged and will not directly overwrite the original configuration.

More examples can be found in the Merge Unit Test Cases.

The filter__x.y.z: string operation filters a list using a Lua expression where item represents the current element. The expression must return a bool. If the expression encounters an error, a log warning will be triggered. Here is an example of removing trojan proxies:

filter__proxies: |
item.type ~= 'trojan'

This operation is similar to the map operation in other languages. It modifies matched items using the expr expression. The syntax is as follows:

filter__x.y.z:
when: string # Lua expression, where item is the current element, returns a boolean value
expr: string # Lua expression, where item is the current element, returns the modified item

Here’s an example of modifying the port of trojan proxies to 443:

filter__proxies:
when: |
item.type == 'trojan'
expr: |
item.port = 443
return item

This operation is similar to the map operation in other languages, merging matched items using the merge table. The syntax is as follows:

filter__x.y.z:
when: string # Lua expression, where item is the current element, returns a boolean value
merge: object # Object to be merged recursively, future support for arrays might be considered

Here is an example of adding the icon field to the Spotify proxy group:

filter__proxy-groups:
when: |
item.name == 'Spotify'
merge:
icon: "https://example.com/spotify.png"

This operation is similar to the map operation in other languages but directly overwrites the matched item. The syntax is as follows:

filter__x.y.z:
when: string # Lua expression, where item is the current element, returns a boolean value
override: any # Value to override

Here is an example of forcibly overriding the Spotify proxy group:

filter__proxy-groups:
when: |
item.name == 'Spotify'
override:
name: "Spotify"
type: select
proxies:
- Proxies
- DIRECT
- HK
- JP
- SG
- TW
- US

This operation is similar to the map operation in other languages but removes keys from the matched item using the remove field. The syntax is as follows:

filter__x.y.z:
when: string # Lua expression, where item is the current element, returns a boolean value
remove: Array<string | int> # List of keys to remove. Keys are strings for objects and numbers for arrays.

Here’s an example of removing the first proxy from the proxies field of the Spotify proxy group:

filter__proxy-groups:
when: |
item.name == 'Spotify'
remove:
- proxies.0

The filter operation supports list types, where each element is a filter operation. This allows for chained processing of multiple operations.

Here’s an example that adds the icon field to both the Spotify and Netflix proxy groups:

filter__proxy-groups:
- when: |
item.name == 'Spotify'
merge:
icon: "https://example.com/spotify.png"
- when: |
item.name == 'Netflix'
merge:
icon: "https://example.com/netflix.png"

This processing node currently provides JavaScript processing based on BoaJS, similar to the Mixins or preprocessing feature in CFW.

The script accepts a function with the signature export default function main(config: ClashConfig): ClashConfig, where ClashConfig is the Clash configuration type. The return value of the script will be used as the final configuration.

Here’s an example of adding rules through a script:

export default function main(config) {
const rules = ["DOMAIN-SUFFIX,google.com,PROXY", "DOMAIN,example.org,PROXY"];
config.rules = [...config.rules, ...rules];
return config;
}

This processing node provides Lua scripting functionality based on mlua, compatible with Lua 5.4 syntax, and includes a secure standard library preset.

The processing module exposes a config variable that contains the current Clash configuration. Users can modify the config variable, and the result will be used as the final configuration.

Here’s an example of adding rules through a Lua script:

local rules = {
'DOMAIN-SUFFIX,google.com,PROXY',
'DOMAIN,example.org,PROXY',
}
for _, rule in ipairs(rules) do
table.insert(config.rules, rule)
end
return config
  1. Clash Verge - Proxy Chain