Profile Transforms
Clash Nyanpasu 2.0 builds a runtime configuration from a configuration profile and its transforms. The older “proxy chain” wording refers to this profile transformation workflow. A transform reads the result produced so far and passes its output to the next step; it does not rewrite the source profile.
Concepts
Section titled “Concepts”A configuration profile is a file profile or a composition profile that can be selected for use. A transform profile is a reusable operation and cannot be activated on its own.
Clash Nyanpasu provides three transform types 3 transform types:
- YAML overlay (shown as Merge on some existing profiles) applies declarative merge rules.
- JavaScript runs a script against the current configuration object.
- Lua runs a script against the current configuration table.
Configure a transform chain
Section titled “Configure a transform chain”- In Profiles, create a transform by choosing Merge (YAML), JavaScript, or Lua. A transform can use a local file or another supported profile source.
- Open a file or composition profile. In Profile transforms, move transforms from Available to Enabled.
- Drag enabled transforms into the order you want, then apply the change.
- To add transforms shared by the active configuration, use Global transforms in Profiles and set their order there.
A newly created transform is not enabled automatically. Scoped transforms run from top to bottom before global transforms, which also run from top to bottom.
For a composition profile, the app processes the optional base profile and each contributor with that file’s scoped transforms. It appends contributor proxies in the order listed, then runs the composition profile’s scoped transforms, followed by global transforms.
flowchart TD
A[Selected file or composition] --> B[Scoped transforms in list order]
B --> C[Global transforms in list order]
C --> D[Application and core post-processing]
D --> E[Generated runtime configuration]
YAML overlay YAML
Section titled “YAML overlay ”The YAML source must have a mapping at its top level. Ordinary mapping keys merge recursively: nested mappings keep unspecified values, while scalars and sequences replace the matching value. A new key is added. This serves the same broad purpose as OpenClash’s merge-style configuration editing; the rules below describe Nyanpasu 2.0. Overlay directives operate on dotted paths and run in the order they appear in the YAML document.
| Key | Effect |
|---|---|
append__path / append-path |
Add a sequence to the end of an existing sequence. |
prepend__path / prepend-path |
Add a sequence to the beginning of an existing sequence. |
override__path |
Replace the value at an existing dotted path. |
filter__path |
Filter or transform items in an existing sequence with Lua expressions. |
Append and prepend require a sequence as the directive value and an existing destination sequence. If the directive value is not a sequence, the path is missing, or the target is not a sequence, the operation is skipped with a warning. override__path also skips a missing path with a warning; it does not create one. These skipped-path warnings do not by themselves fail the candidate configuration.
This appends one rule and keeps the existing rules entries. The rules field must already be a sequence.
append__rules: - DOMAIN-SUFFIX,example.org,DIRECTThis adds a selectable group and places its domain rule before existing rules. Both proxy-groups and rules must already be sequences. Replace DIRECT with a proxy or group that exists in your configuration if the group should route through a proxy.
append__proxy-groups: - name: Docs-Example type: select proxies: - DIRECTprepend__rules: - DOMAIN-SUFFIX,example.org,Docs-ExampleFilter predicates and actions
Section titled “Filter predicates and actions”filter__path requires an existing sequence. A string value is a Lua predicate evaluated once for each item: true keeps that item and false removes it. A filter mapping uses a when predicate and one action. A list of filters runs sequentially, with each operation receiving the previous operation’s output.
Keep every proxy except Trojan proxies. The expression must return a boolean.
filter__proxies: | item.type ~= 'trojan'Use when to match items, then return the changed item from the Lua expr.
filter__proxies: when: | item.type == 'trojan' expr: | item.port = 443 return itemRecursively merge the supplied mapping into each matching item. An icon extension is core-dependent; check that the selected core supports it.
filter__proxy-groups: when: | item.name == 'Spotify' merge: icon: "https://example.com/spotify.png"Replace the entire matching item with the supplied value.
filter__proxy-groups: when: | item.name == 'Spotify' override: name: "Spotify" type: select proxies: - Proxies - DIRECT - HK - JP - SG - TW - USThe remove list accepts dotted string paths and integer indices. Remove a dotted path from each matching mapping item; proxies.0 addresses the first entry in that item’s proxies sequence.
filter__proxy-groups: when: | item.name == 'Spotify' remove: - proxies.0Each list entry runs in order; this example adds an icon to two different 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"For more cases, see the current overlay executor examples.
JavaScript Boa
Section titled “JavaScript ”The JavaScript runner uses Boa and calls a default-exported function with the current configuration object. Its contract is main(config: ClashConfig): ClashConfig: ClashConfig describes the input and returned configuration; write executable JavaScript without TypeScript annotations. Like CFW Mixins or preprocessing, a transform edits a configuration and returns the result; the analogy does not imply script compatibility. The default export must return a mapping-compatible configuration, and a returned promise is awaited. If a promise remains pending for the runner’s short wait window (about two seconds), the transform fails. console.log, console.info, console.warn, and console.error are captured in the step logs.
This preserves the original two-rule example while checking that rules is an array before appending.
export default function main(config) { const rules = ["DOMAIN-SUFFIX,google.com,PROXY", "DOMAIN,example.org,PROXY"]; if (Array.isArray(config.rules)) { config.rules.push(...rules); } else { config.rules = rules; } return config;}export default function main(config) { if (Array.isArray(config.rules)) { config.rules.push("DOMAIN-SUFFIX,example.org,DIRECT"); } return config;}The runner exposes selected pure Web APIs, but no general fetch() API. It can resolve local modules and HTTP(S) ES-module imports; downloaded modules are cached for up to 30 days. Only import remote modules you trust. See the JavaScript runner source.
Lua Lua 5.4
Section titled “Lua ”The Lua runner uses Lua 5.4 syntax and the mlua ALL_SAFE standard-library preset. It exposes the current configuration as the global config; the script must return a table containing a mapping. Console output is captured in the step logs.
local rules = { 'DOMAIN-SUFFIX,google.com,PROXY', 'DOMAIN,example.org,PROXY',}
if type(config.rules) ~= "table" then config.rules = {}end
for _, rule in ipairs(rules) do table.insert(config.rules, rule)end
return configif type(config.rules) == "table" then table.insert(config.rules, "DOMAIN-SUFFIX,example.org,DIRECT")end
return configSee the Lua runner source. For examples that add rule sets or proxy-group icons, see profile transform examples and the icon example; some fields in these examples depend on the selected core.
Inspect and recover
Section titled “Inspect and recover”- Open Inspect from the Profiles sidebar.
- Switch between Latest build and Applied to core. The chip beside the switch shows the core and revision that was applied.
- Choose the list or graph view. The list draws the build as a branch line and groups the steps by stage: Source, Profile transforms, Global transforms, Nyanpasu, and Result. Turn on Only steps that changed something to hide the steps that left the configuration as it was.
- Select a step. Final configuration opens in Output; every other step opens in Changes, which shows its diff from the previous step. Output shows the YAML the step produced, and Logs shows what it logged. Core effective configuration is what the core reported back.
- Search for a field name, such as
dns.nameserver, to find the steps that touched it.
To discard unsaved source edits, use Reset in the profile editor or choose Cancel and confirm discarding changes. To disable a transform, move it from Enabled to Available in the scoped or global transform editor and apply the change. To undo saved content, restore the desired transform source and save it.
