Explanation of Chimera’s Runtime Configuration Generation Mechanism
1. Key Takeaways
The configuration consumed by the core (e.g., chimera_client / mihomo) at startup and during hot-reload is not read directly from profiles.yaml.
What the core actually uses is a runtime file:
clash-config.yaml(runtime configuration, located underapp_config_dir)
This file is first composed in memory by the backend, then written to disk, and finally passed to the core either via startup arguments or Clash API hot-reload.
2. Configuration Inputs (Raw Materials)
The runtime configuration is built from four categories of inputs:
-
Application settings:
chimera-config.yaml- Struct:
IVerge - Load entry:
backend/tauri/src/config/chimera/mod.rs→IVerge::new() - Purpose: controls field filtering, port strategy, TUN/system proxy behavior, etc.
- Struct:
-
Clash Guard override template:
clash-guard-overrides.yaml- Struct:
IClashTemp - Load entry:
backend/tauri/src/config/clash/mod.rs→IClashTemp::new() - Purpose: forcibly overrides critical fields (e.g.,
mode,mixed-port,external-controller,secret, etc.)
- Struct:
-
Profile metadata:
profiles.yaml- Struct:
Profiles - Load entry:
backend/tauri/src/config/profile/profiles.rs→Profiles::new() - Purpose: records the currently active profile (
current) and the profile list (items)
- Struct:
-
Concrete profile content files:
app_config_dir/profiles/*.yaml- Load entry:
Profiles::current_mappings() - Purpose: provides actual configuration content such as proxies, rules, DNS, TUN, etc.
- Load entry:
3. Startup Initialization Phase
3.1 Creating Base Files (If Missing)
backend/tauri/src/utils/init/mod.rs → init_config() ensures the following files exist:
clash-guard-overrides.yaml(generated by default viaIClashTemp::template())chimera-config.yaml(generated by default viaIVerge::template())profiles.yaml(an empty/default configuration)
3.2 Loading Global Configuration Objects
backend/tauri/src/config/core.rs → Config::global() initializes:
Profiles::new()IVerge::new()IClashTemp::new()IRuntime::new()
IRuntime is the in-memory runtime configuration container; its config field is an Option<Mapping>.
4. Main Runtime Configuration Composition Flow
Main entry: Config::generate() (backend/tauri/src/config/core.rs)
flowchart TD A["Start Config::generate"] --> B["Call enhance::enhance"] B --> C["Read YAML(s) of current profile"] C --> D["merge_profiles: merge configs"] D --> E["(Optional) whitelist-based field filtering"] E --> F["Override key fields (HANDLE_FIELDS)"] F --> G["Write into in-memory runtime config"] G --> H["Write out clash config YAML"] H --> I["Load at startup or hot-reload via PUT /configs"]
4.1 What enhance::enhance() Does
Location: backend/tauri/src/enhance/mod.rs
Core steps:
-
Load Clash Guard configuration
let clash_config = Config::clash().latest().0.clone()
-
Read current feature toggles/settings
- such as
enable_clash_fields, fromIVerge
- such as
-
Load the content of the currently active profile(s)
- via
Profiles::current_mappings() - this method iterates over
current, readsprofiles/<file>.yamlone by one, and converts them intoMapping
- via
-
(Reserved) Execute profile chain scripts
- calls
process_chain(...) - current implementation is a placeholder (no-op), returning the original config
- calls
-
Merge multiple profile configurations
-
calls
merge_profiles(...) -
current strategy:
- first config: full
extend - subsequent configs: only append
proxiesto the existingproxies
- first config: full
-
-
Whitelist field filtering (optional, controlled by a toggle)
use_whitelist_fields_filter(...)- when
enable_clash_fields = true, only retains keys invalid + default fields
-
Force-override Guard fields
- writes back fields listed in
HANDLE_FIELDSfromIClashTempinto the final config - ensures critical control fields are centrally managed by the client
- writes back fields listed in
4.2 Scope of HANDLE_FIELDS Overrides
Defined in backend/tauri/src/enhance/field.rs:
modeportsocks-portmixed-portallow-lanlog-levelipv6secretexternal-controller
This means that even if these fields exist in a profile, the final values will be overwritten by the corresponding values from clash-guard-overrides.yaml.
5. Writing to Disk and File Locations
Entry: Config::generate_file(ConfigType::Run) (backend/tauri/src/config/core.rs)
- Output in
Runmode:app_config_dir()/clash-config.yaml - Output in
Checkmode:temp_dir()/clash-config-check.yaml
If generation fails, Config::init_config() provides a fallback: it writes IClashTemp directly as the runtime configuration.
6. How the Core Obtains This Configuration
6.1 Loaded at Startup
CoreManager::run_core() → Instance::try_new() (backend/tauri/src/core/clash/core.rs):
- Calls
Config::generate_file(ConfigType::Run)to get the path - Passes the path to the core process via
CoreInstanceBuilder.config_path(config_path)
In other words: the core reads clash-config.yaml directly at startup.
6.2 Hot-Reload During Runtime
CoreManager::update_config() flow:
Config::generate().await?recomposes the in-memory configcheck_config().await?validates syntax/usability using the check filegenerate_file(Run)rewritesclash-config.yaml- Calls
PUT /configswith body{ "path": "<absolute path>" }to instruct the core to reload
Relevant code:
backend/tauri/src/core/clash/core.rs→update_config()backend/tauri/src/core/clash/api.rs→put_configs(...)
7. User Actions That Trigger a Rebuild
7.1 Switching/Modifying Profile Selection
Frontend commands.patchProfilesConfig → backend patch_profiles_config(...):
- Apply draft:
Config::profiles().draft().apply(...) - Trigger
CoreManager::update_config() - On success:
Config::profiles().apply()+save_file() - On failure:
discard()(rollback)
7.2 Changing Settings (Certain Fields)
Frontend commands.patchVergeConfig → backend feat::patch_verge(...):
-
Writes an
IVergedraft first -
Some fields (e.g.,
enable_tun_mode) may trigger:Config::generate()+run_core()(restart scenario)- or
update_core_config()(hot-update scenario)
7.3 Importing the First Profile
After import_profile(...) succeeds, if there is no active profile yet, it automatically constructs ProfilesBuilder.current = [new_uid] and reuses patch_profiles_config(...) to trigger an update.
8. Key Details and Common Misunderstandings
-
profiles.yamlis not the final configuration used by the core- it only stores profile metadata and the
currentpointer
- it only stores profile metadata and the
-
Profile content files are not passed to the core verbatim
- they become the runtime config only after merging, filtering, and guard overrides
-
external-controllermay have its port changed before startupprepare_external_controller_port()checks port availability according to policy and switches ports if necessary
-
verge_mixed_portis primarily used for system proxy logic- it is not directly written to the runtime YAML’s
mixed-port - system proxy uses
verge_mixed_portfirst, otherwise falls back toConfig::clash().get_mixed_port()
- it is not directly written to the runtime YAML’s
-
get_runtime_yaml()returnsIRuntime.configfrom memory- it is usually consistent with the recently written
clash-config.yaml - but fundamentally it comes from memory, not from re-reading disk each time
- it is usually consistent with the recently written
9. Current Implementation Limitations (As of the Codebase)
-
Chain script execution is currently a placeholder
process_chain(...)does not actually rewrite the config yet
-
Global chain processing code is still commented out
- only the scoped chain framework exists for now
-
patch_clash_configIPC is stilltodo!()- the frontend will fail if it uses that IPC path
-
Directly editing a profile file does not automatically trigger a hot-reload
save_profile_file(...)only writes the file; it does not callupdate_config()
10. Troubleshooting Checklist (Practical Order)
If you suspect “the core is using the wrong configuration,” check in this order:
-
Confirm the active profile is correct
- verify
profiles.yaml→current
- verify
-
Confirm the profile source content matches expectations
- check
app_config_dir/profiles/*.yaml
- check
-
Check guard override items
- verify whether
HANDLE_FIELDSinclash-guard-overrides.yamloverrides the values you intended
- verify whether
-
Inspect the final runtime configuration
- check
clash-config.yaml - or call
get_runtime_yaml()to view the in-memory version
- check
-
Confirm a hot-reload actually occurred
- verify
patch_profiles_config/patch_verge_config/restart_sidecarwas executed - check logs to see whether
PUT /configssucceeded
- verify
-
If you see port-related issues
- check whether
external-controllerwas rewritten by the port strategy
- check whether