# How Clash Nyanpasu Generates and Patches Runtime YAML Configuration

> Discover how Clash Nyanpasu generates and patches runtime YAML configurations. Learn about its type-safe patching for seamless setting updates.

- Repository: [Nyanpasu/clash-nyanpasu](https://github.com/libnyanpasu/clash-nyanpasu)
- Tags: internals
- Published: 2026-03-06

---

**Clash Nyanpasu maintains an in-memory runtime configuration object that serializes to YAML for display and updates atomically through a type-safe patching mechanism when users modify settings like `allow-lan` or `mode`.**

Clash Nyanpasu manages proxy configurations through a dynamic runtime YAML configuration that bridges the React-based frontend and the Clash core. This article examines how the Tauri-based backend generates readable YAML from internal state and applies partial updates through a structured patching pipeline, based on the actual implementation in the `libnyanpasu/clash-nyanpasu` repository.

## Architecture of Runtime YAML Configuration Generation

The generation flow converts the internal `Mapping` representation into a human-readable YAML string that reflects the currently active configuration (base config plus any merge files or scripts).

### The Runtime Data Structure

The runtime state lives in the `IRuntime` struct defined in [`backend/tauri/src/config/runtime.rs`](https://github.com/libnyanpasu/clash-nyanpasu/blob/main/backend/tauri/src/config/runtime.rs). This structure holds the active configuration as an `Option<Mapping>` and provides thread-safe access through a singleton pattern.

```rust
// backend/tauri/src/config/runtime.rs (lines 18-26)
pub struct IRuntime {
    pub config: Option<Mapping>,  // The actual YAML mapping
}

impl IRuntime {
    pub fn latest() -> &'static IRuntime {
        // Returns the singleton instance
    }
}

```

### Serializing to YAML via Tauri Commands

When the frontend requests the runtime configuration, the `get_runtime_yaml` command in [`backend/tauri/src/ipc.rs`](https://github.com/libnyanpasu/clash-nyanpasu/blob/main/backend/tauri/src/ipc.rs) (lines 400-410) handles the serialization:

```rust
#[tauri::command]
pub fn get_runtime_yaml() -> Result<String> {
    let runtime = Config::runtime().latest();
    let yaml = runtime
        .config
        .as_ref()
        .ok_or_else(|| anyhow!("no runtime config"))
        .and_then(|c| serde_yaml::to_string(c).context("failed to convert to yaml"))?;
    Ok(yaml)
}

```

The resulting YAML string represents the **merged** configuration—the base profile combined with any user-defined merge files and scripts that Clash is actively using.

## Patching Runtime Configuration in Clash Nyanpasu

The patching mechanism allows the UI to modify specific fields (such as `allow-lan`, `ipv6`, `log-level`, or `mode`) without replacing the entire configuration file.

### The Patch Payload Structure

To ensure type safety and prevent accidental modification of read-only keys, Clash Nyanpasu defines a `PatchRuntimeConfig` struct that only includes editable fields:

```rust
// backend/tauri/src/config/runtime.rs (lines 6-16)
#[derive(Default, Debug, Clone, Deserialize, Serialize, specta::Type)]
pub struct PatchRuntimeConfig {
    #[serde(default)] pub allow_lan: Option<bool>,
    #[serde(default)] pub ipv6:      Option<bool>,
    #[serde(default)] pub log_level: Option<String>,
    #[serde(default)] pub mode:      Option<String>,
}

```

### The Patching Pipeline

When a user toggles a setting in the UI, the following pipeline executes:

1. **Frontend Invocation**: The TypeScript service calls `patch_clash_config` via Tauri's `invoke` API.

2. **Command Deserialization**: In [`backend/tauri/src/ipc.rs`](https://github.com/libnyanpasu/clash-nyanpasu/blob/main/backend/tauri/src/ipc.rs) (lines 444-456), the payload deserializes into a `serde_yaml::Mapping` and validates that it represents a YAML mapping structure.

3. **Feature Orchestration**: The `feat::patch_clash` function in [`backend/tauri/src/feat.rs`](https://github.com/libnyanpasu/clash-nyanpasu/blob/main/backend/tauri/src/feat.rs) (lines 12-78) coordinates the update:
   - Updates the draft Clash configuration via `Config::clash().draft().patch_config()`
   - Applies changes to the runtime via `Config::runtime().latest().patch_config(patch)`
   - Validates critical fields (mixed-port, external-controller)
   - Optionally restarts the Clash core to apply changes
   - Persists the updated configuration to disk

4. **Runtime Application**: The `patch_config` method in [`backend/tauri/src/config/runtime.rs`](https://github.com/libnyanpasu/clash-nyanpasu/blob/main/backend/tauri/src/config/runtime.rs) (lines 32-58) iterates through the `PatchRuntimeConfig` fields and inserts or overwrites the corresponding keys in the stored `Mapping`.

After patching completes, the runtime `Mapping` serves as the source for all future `get_runtime_yaml` calls, ensuring consistency between the active Clash process and the displayed configuration.

## Code Examples for Runtime Configuration Management

### Reading Runtime YAML in TypeScript

The frontend retrieves the current configuration through the Tauri service layer:

```typescript
// frontend/interface/src/service/tauri.ts (line 225)
import { invoke } from '@tauri-apps/api/tauri';

export async function getRuntimeYaml(): Promise<string> {
  // Returns a YAML string like "# Clash configuration\nmixed-port: 7890\n..."

  return await invoke<string>('get_runtime_yaml');
}

```

### Patching Configuration in TypeScript

To modify runtime settings, construct a `PatchRuntimeConfig` object and invoke the patch command:

```typescript
// frontend/interface/src/service/tauri.ts (line 28)
import { invoke } from '@tauri-apps/api/tauri';

export interface PatchRuntimeConfig {
  allow_lan?: boolean;
  ipv6?: boolean;
  log_level?: string;
  mode?: string;
}

// Example: enable LAN and switch mode to "Rule"
await invoke<void>('patch_clash_config', {
  payload: { allow_lan: true, mode: 'Rule' } as PatchRuntimeConfig,
});

```

### Rust Implementation Details

The backend command implementations demonstrate how the runtime state transitions between Rust data structures and YAML strings:

```rust
// backend/tauri/src/ipc.rs – Runtime generation
#[tauri::command]
pub fn get_runtime_yaml() -> Result<String> {
    let runtime = Config::runtime().latest();
    let yaml = runtime
        .config
        .as_ref()
        .ok_or_else(|| anyhow!("no runtime config"))
        .and_then(|c| serde_yaml::to_string(c).context("failed to convert to yaml"))?;
    Ok(yaml)
}

// backend/tauri/src/ipc.rs – Configuration patching
#[tauri::command]
pub async fn patch_clash_config(payload: PatchRuntimeConfig) -> Result {
    let mapping = serde_yaml::to_value(&payload)?
        .as_mapping()
        .cloned()
        .ok_or_else(|| IpcError::Custom("Expected a mapping".into()))?;
    
    feat::patch_clash(mapping).await
}

```

## Key Source Files in Clash Nyanpasu

| Role | File | Description |
|------|------|-------------|
| **Tauri command – read** | [`backend/tauri/src/ipc.rs`](https://github.com/libnyanpasu/clash-nyanpasu/blob/main/backend/tauri/src/ipc.rs) (lines 400‑410) | Implements `get_runtime_yaml`. |
| **Tauri command – write** | [`backend/tauri/src/ipc.rs`](https://github.com/libnyanpasu/clash-nyanpasu/blob/main/backend/tauri/src/ipc.rs) (lines 444‑456) | Implements `patch_clash_config`. |
| **Runtime data structure** | [`backend/tauri/src/config/runtime.rs`](https://github.com/libnyanpasu/clash-nyanpasu/blob/main/backend/tauri/src/config/runtime.rs) (lines 18‑26, 32‑58) | Holds the in‑memory YAML mapping and the `patch_config` logic. |
| **Patch orchestration** | [`backend/tauri/src/feat.rs`](https://github.com/libnyanpasu/clash-nyanpasu/blob/main/backend/tauri/src/feat.rs) (lines 12‑78) | Calls `Config::runtime().latest().patch_config`, restarts core, persists changes. |
| **Frontend service – get** | [`frontend/interface/src/service/tauri.ts`](https://github.com/libnyanpasu/clash-nyanpasu/blob/main/frontend/interface/src/service/tauri.ts) (line 225) | Calls the Tauri `get_runtime_yaml` command. |
| **Frontend service – patch** | [`frontend/interface/src/service/tauri.ts`](https://github.com/libnyanpasu/clash-nyanpasu/blob/main/frontend/interface/src/service/tauri.ts) (line 28) | Calls the Tauri `patch_clash_config` command. |
| **TypeScript bindings** | [`frontend/interface/src/ipc/bindings.ts`](https://github.com/libnyanpasu/clash-nyanpasu/blob/main/frontend/interface/src/ipc/bindings.ts) | Defines the `PatchRuntimeConfig` type used by the UI. |

## Summary

- **Runtime YAML generation** in Clash Nyanpasu converts the in-memory `Mapping` stored in `IRuntime` to a YAML string via `serde_yaml::to_string`, exposed through the `get_runtime_yaml` Tauri command.

- **Configuration patching** uses a type-safe `PatchRuntimeConfig` struct to restrict editable fields to `allow_lan`, `ipv6`, `log_level`, and `mode`, preventing accidental corruption of read-only keys.

- **The patching pipeline** flows from TypeScript UI calls through [`ipc.rs`](https://github.com/libnyanpasu/clash-nyanpasu/blob/main/ipc.rs) deserialization, into [`feat.rs`](https://github.com/libnyanpasu/clash-nyanpasu/blob/main/feat.rs) for orchestration (draft updates, validation, core restart), and finally applies changes via [`runtime.rs`](https://github.com/libnyanpasu/clash-nyanpasu/blob/main/runtime.rs) which updates the stored `Mapping`.

- **All operations** are atomic and thread-safe, utilizing Rust's singleton pattern for the runtime configuration to ensure consistency between the active Clash process and the UI display.

## Frequently Asked Questions

### What is the runtime YAML configuration in Clash Nyanpasu?

The runtime YAML configuration is the active, in-memory representation of the Clash proxy settings currently being used by the core process. It lives in the `IRuntime` struct as a `serde_yaml::Mapping` and reflects the merged state of the base profile, merge files, and any applied scripts, distinct from the static configuration files stored on disk.

### How does Clash Nyanpasu handle partial configuration updates?

Clash Nyanpasu handles partial updates through the `PatchRuntimeConfig` struct, which only exposes four editable fields: `allow_lan`, `ipv6`, `log_level`, and `mode`. When the UI sends a patch, the backend deserializes it into a YAML mapping, applies it to both the draft Clash config and the runtime mapping via `Config::runtime().latest().patch_config()`, validates critical fields like `mixed-port`, and optionally restarts the core to apply changes.

### Where is the runtime configuration stored in the codebase?

The runtime configuration is primarily stored and managed in [`backend/tauri/src/config/runtime.rs`](https://github.com/libnyanpasu/clash-nyanpasu/blob/main/backend/tauri/src/config/runtime.rs), which defines the `IRuntime` struct holding the `Option<Mapping>` config field. The generation command resides in [`backend/tauri/src/ipc.rs`](https://github.com/libnyanpasu/clash-nyanpasu/blob/main/backend/tauri/src/ipc.rs) (lines 400-410), while the patching orchestration logic is implemented in [`backend/tauri/src/feat.rs`](https://github.com/libnyanpasu/clash-nyanpasu/blob/main/backend/tauri/src/feat.rs) (lines 12-78). Frontend bindings exist in [`frontend/interface/src/service/tauri.ts`](https://github.com/libnyanpasu/clash-nyanpasu/blob/main/frontend/interface/src/service/tauri.ts).

### Can I manually edit the runtime YAML configuration?

While you can read the runtime YAML via the `get_runtime_yaml` command for inspection or export, manual editing is not supported through the public API. The `patch_clash_config` command requires a strongly-typed `PatchRuntimeConfig` payload that only accepts specific fields (`allow_lan`, `ipv6`, `log_level`, `mode`), ensuring type safety and preventing corruption of the runtime state. Direct manipulation of the underlying `Mapping` would bypass validation logic and could destabilize the Clash core.