# How ClashApiManager Works for Clash‑Compatible Clients in v2rayN

> Discover how ClashApiManager in v2rayN controls Clash-compatible clients via its REST API. Fetch groups, test nodes, switch selectors, and manage runtime configuration effortlessly.

- Repository: [2dust/v2rayN](https://github.com/2dust/v2rayN)
- Tags: internals
- Published: 2026-02-27

---

**The `ClashApiManager` class is a singleton HTTP wrapper that enables v2rayN to control any Clash‑compatible core via its REST API, exposing async methods to fetch proxy groups, test node latency, switch active selectors, and manage runtime configuration.**

The `ClashApiManager` serves as the central bridge between v2rayN’s UI and locally‑running Clash cores. Located in [`v2rayN/ServiceLib/Manager/ClashApiManager.cs`](https://github.com/2dust/v2rayN/blob/main/v2rayN/ServiceLib/Manager/ClashApiManager.cs), this manager encapsulates every interaction with the Clash HTTP API, converting low‑level network calls into strongly‑typed .NET models that view‑models consume. For developers and power users, understanding how the **ClashApiManager works for Clash‑compatible clients** reveals the exact mechanism behind proxy switching, delay testing, and live connection management.

## Architecture and Core Responsibilities

The manager operates as a singleton (`ClashApiManager.Instance`) and delegates all transport concerns to `HttpClientHelper`, which handles retries, timeouts, and JSON deserialization. It exposes six primary operational areas:

- **Proxy Discovery** – Queries `/proxies` and `/providers/proxies` endpoints to build the node hierarchy.
- **Latency Testing** – Parallel‑tests every node against `/proxies/{name}/delay` with configurable timeout and test URL.
- **Selector Control** – Switches active nodes via `PUT /proxies/{group}` using header‑based payload.
- **Runtime Patching** – Updates rule mode or log level through `PATCH /configs`.
- **Config Reloading** – Hot‑reloads entire configuration files via `PUT /configs?force=true`.
- **Connection Inspection** – Lists and terminates active connections through the `/connections` endpoint.

All methods construct the base URL dynamically using `GetApiUrl()`, which binds to the loopback interface and the port stored in `AppManager.Instance.StatePort2`.

## Building the API URL

Before any request, the manager resolves the local Clash controller address. As implemented in [`ClashApiManager.cs`](https://github.com/2dust/v2rayN/blob/main/ClashApiManager.cs) (lines 183‑186):

```csharp
private string GetApiUrl()
{
    return $"{Global.HttpProtocol}{Global.Loopback}:{AppManager.Instance.StatePort2}";
}

```

This ensures that even if the core restarts on a different ephemeral port, subsequent calls target the correct endpoint without manual reconfiguration.

## Fetching Proxies and Providers

The `GetClashProxiesAsync()` method retrieves both the flat proxy list and provider‑sourced nodes. It issues concurrent GET requests and deserializes the responses into `ClashProxies` and `ClashProviders` models (lines 18‑25):

```csharp
var url   = $"{GetApiUrl()}/proxies";
var result = await HttpClientHelper.Instance.TryGetAsync(url);
var clashProxies = JsonUtils.Deserialize<ClashProxies>(result);

var url2   = $"{GetApiUrl()}/providers/proxies";
var result2 = await HttpClientHelper.Instance.TryGetAsync(url2);
var clashProviders = JsonUtils.Deserialize<ClashProviders>(result2);

```

The implementation retries failed requests three times with a two‑second backoff, caching the resulting dictionary in `_proxies` for downstream UI consumption.

## Testing Proxy Latency

Node health checks run in parallel to prevent UI blocking. The `ClashProxiesDelayTest()` method (lines 67‑85) constructs a URL template pointing to the Clash delay endpoint, then dispatches tasks for every node in the supplied `lstProxy` list:

```csharp
var urlBase = $"{GetApiUrl()}/proxies/{0}/delay?timeout=10000&url=" 
              + AppManager.Instance.Config.SpeedTestItem.SpeedPingTestUrl;

foreach (var it in lstProxy)
{
    var url = string.Format(urlBase, it.Name);
    tasks.Add(Task.Run(async () =>
    {
        var result = await HttpClientHelper.Instance.TryGetAsync(url);
        await updateFunc?.Invoke(it, result);
    }));
}
await Task.WhenAll(tasks);
await updateFunc?.Invoke(null, "");

```

The `updateFunc` callback—supplied by `ClashProxiesViewModel`—streams results back to the UI thread in real time, allowing live latency updates without freezing the interface.

## Switching Active Proxy Nodes

When a user selects a new node within a selector group (e.g., switching from “Auto” to a specific server), `ClashSetActiveProxy()` executes a PUT request with a custom header (lines 13‑17):

```csharp
var url = $"{GetApiUrl()}/proxies/{name}";
var headers = new Dictionary<string, string> { { "name", nameNode } };
await HttpClientHelper.Instance.PutAsync(url, headers);

```

If the Clash core returns HTTP 204, the view‑model updates its local `selectedProxy.now` property to reflect the change immediately, ensuring the UI stays synchronized with the core’s routing table.

## Updating Runtime Configuration

For global settings like rule mode (Rule, Global, or Direct), the manager patches the running config via `ClashConfigUpdate()` (lines 31‑34):

```csharp
var url = $"{GetApiUrl()}/configs";
await HttpClientHelper.Instance.PatchAsync(url, headers);

```

The headers dictionary contains key‑value pairs such as `{ "mode": "global" }`, which Clash applies instantly without requiring a process restart.

## Reloading Full Configuration Files

When users import a new profile, `CoreConfigClashService` invokes `ClashConfigReload()` to hot‑swap the entire configuration (lines 41‑45):

```csharp
var url = $"{GetApiUrl()}/configs?force=true";
var headers = new Dictionary<string, string> { { "path", filePath } };
await HttpClientHelper.Instance.PutAsync(url, headers);

```

The `force=true` query parameter ensures Clash discards any in‑memory overrides and loads the file from disk exactly as specified.

## How ViewModels Consume the Manager

### ClashProxiesViewModel

Located in [`v2rayN/ServiceLib/ViewModels/ClashProxiesViewModel.cs`](https://github.com/2dust/v2rayN/blob/main/v2rayN/ServiceLib/ViewModels/ClashProxiesViewModel.cs), this view‑model orchestrates the proxy list UI:

- **Initialization** – Calls `ClashApiManager.Instance.GetClashProxiesAsync()` to populate `_proxies` and `_providers`, then hydrates the observable collection bound to the frontend.
- **Delay Testing** – Invokes `ClashProxiesDelayTest()` with `blAll: true` to benchmark every node, using RxUI’s `MainThreadScheduler` to marshal callbacks onto the UI thread.
- **Selector Switching** – Executes `ClashSetActiveProxy(selectedGroup.Name, selectedNode.Name)` when the user clicks a new node, updating the backing field only after the HTTP call succeeds.

### ClashConnectionsViewModel

This view‑model manages the live connections tab in [`ClashConnectionsViewModel.cs`](https://github.com/2dust/v2rayN/blob/main/ClashConnectionsViewModel.cs) (lines 53‑62):

- **Polling** – Periodically calls `GetClashConnectionsAsync()` to fetch active TCP/UDP flows from `/connections`.
- **Termination** – Sends `ClashConnectionClose(id)` to issue a DELETE request against `/connections/{id}`, forcibly dropping the selected tunnel.

## Practical Code Examples

### Example 1 – Populating the Proxy List

```csharp
public async Task LoadProxies()
{
    var ret = await ClashApiManager.Instance.GetClashProxiesAsync();
    if (ret?.Item1 != null)
    {
        _proxies = ret.Item1.proxies;
        await RefreshProxyGroups();
    }
}

```

*Relevant source:* [`ClashProxiesViewModel.cs`](https://github.com/2dust/v2rayN/blob/main/ClashProxiesViewModel.cs) (lines 64‑71).

### Example 2 – Executing a Full Latency Test

```csharp
await ClashApiManager.Instance.ClashProxiesDelayTest(
    blAll: true,
    lstProxy: ProxyDetails.ToList(),
    async (item, json) =>
    {
        var result = new SpeedTestResult { IndexId = item.Name, Delay = json };
        RxApp.MainThreadScheduler.Schedule(result, (scheduler, r) =>
        {
            _ = ProxiesDelayTestResult(r);
            return Disposable.Empty;
        });
    });

```

*Relevant source:* [`ClashProxiesViewModel.cs`](https://github.com/2dust/v2rayN/blob/main/ClashProxiesViewModel.cs) (lines 80‑88).

### Example 3 – Changing the Active Node

```csharp
public async Task ChangeProxy(string groupName, string nodeName)
{
    await ClashApiManager.Instance.ClashSetActiveProxy(groupName, nodeName);
    // Update local state only after successful API call
    selectedProxy.now = nodeName;
}

```

*Relevant source:* [`ClashProxiesViewModel.cs`](https://github.com/2dust/v2rayN/blob/main/ClashProxiesViewModel.cs) (lines 64‑66).

## Summary

- **Centralized HTTP Wrapper** – [`ClashApiManager.cs`](https://github.com/2dust/v2rayN/blob/main/ClashApiManager.cs) abstracts all Clash REST endpoints, exposing async methods that return strongly‑typed `ClashProxies` and `ClashProviders` models.
- **Dynamic URL Resolution** – `GetApiUrl()` builds the controller address from `StatePort2`, ensuring seamless reconnection after core restarts.
- **Parallel Latency Testing** – `ClashProxiesDelayTest()` uses `Task.WhenAll` to test multiple nodes concurrently, streaming results via callbacks to prevent UI freezes.
- **State Synchronization** – ViewModels such as `ClashProxiesViewModel` and `ClashConnectionsViewModel` consume the manager to keep the interface synchronized with the Clash core’s runtime state.

## Frequently Asked Questions

### Does ClashApiManager support Clash‑Meta or Mihomo cores?

Yes. Because the manager communicates through the standard Clash HTTP API specification, it is fully compatible with Clash‑Meta (now Mihomo) and other Clash‑compatible forks. The endpoint paths (`/proxies`, `/configs`, `/connections`) and request schemas remain consistent across these implementations, allowing v2rayN to control any core that exposes the standard REST interface.

### How does the manager handle network failures or timeouts?

All HTTP operations route through `HttpClientHelper.Instance`, which implements a retry policy with exponential backoff. For proxy fetching, the code explicitly retries three times with a two‑second delay between attempts. If the Clash core is unreachable, the async method returns null or an empty result, and the calling view‑model logs the error without crashing the application.

### Can I use ClashApiManager to modify DNS or TUN settings at runtime?

The manager exposes `ClashConfigUpdate()`, which sends a PATCH request to `/configs`. While the provided implementation focuses on rule mode switching, you can extend the headers dictionary to include any Clash runtime patch supported by your core (e.g., `{"tun": {"enable": true}}`), provided the core supports dynamic configuration updates for those specific fields.

### Which file handles the actual reloading of a Clash configuration file?

[`CoreConfigClashService.cs`](https://github.com/2dust/v2rayN/blob/main/CoreConfigClashService.cs) invokes `ClashApiManager.Instance.ClashConfigReload()` when the user selects a new profile. This method performs a PUT request to `/configs?force=true` with the absolute file path in the headers, instructing the Clash core to reload the YAML configuration from disk immediately.