# How Routing Rules Are Processed and Applied in v2rayN for V2Ray and sing-box Cores

> Understand how v2rayN processes routing rules for V2Ray and sing-box cores. Learn how rule sets are converted and applied to your configuration for optimized network routing.

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

---

**v2rayN processes routing rules by loading a `RoutingItem` from SQLite, converting its JSON `RuleSet` into core-specific configurations via `GenRouting` methods in `V2rayRoutingService` or `SingboxRoutingService`, and appending the rules to the final [`config.json`](https://github.com/2dust/v2rayN/blob/main/config.json) before starting the core process.**

Understanding how routing rules are processed and applied in v2rayN is essential for customizing traffic splitting between the V2Ray and sing-box cores. The application stores routing policies in a SQLite database, then transforms these abstract rules into concrete JSON configurations that each proxy core understands. This article examines the complete pipeline from data storage to runtime application, referencing the actual source code in the `2dust/v2rayN` repository.

## How v2rayN Stores and Loads Routing Rules

v2rayN persists routing configurations in the `RoutingItem` model, which maps to a SQLite table. Each record contains a `RuleSet` field storing a JSON array of `RulesItem` objects that define domain, IP, port, and protocol matching criteria.

When a user starts or reloads a profile, the `AppManager` invokes `ConfigHandler.GetDefaultRouting` to retrieve the active routing item from the database. This method deserializes the JSON `RuleSet` into `List<RulesItem>` and returns the complete `RoutingItem` object.

```csharp
// From ConfigHandler.cs – GetDefaultRouting (line ~1988)
public static RoutingItem GetDefaultRouting(Config config)
{
    var routingItem = SQLiteHelper.Instance.Table<RoutingItem>()
        .FirstOrDefault(t => t.Id == config.RoutingBasicItem.RoutingIndexId);
    
    if (routingItem != null && routingItem.RuleSet.IsNotEmpty())
    {
        routingItem.Rules = JsonUtils.Deserialize<List<RulesItem>>(routingItem.RuleSet);
    }
    return routingItem ?? new RoutingItem();
}

```

The loaded `RoutingItem` is then packaged into a `CoreConfigContext` object by `CoreConfigHandler.BuildContext`. This context carries the routing configuration—along with the selected profile, DNS items, and proxy mappings—to the core-specific generators.

## The Routing Rule Processing Pipeline

The transformation from abstract `RulesItem` objects to executable core configuration follows a standardized workflow, regardless of whether the target is V2Ray or sing-box. The `CoreConfigHandler.BuildCoreConfig` method selects the appropriate service—`CoreConfigV2rayService` or `CoreConfigSingboxService`—based on `Config.CoreInfo.CoreType`.

### Step 1: Initialize the Generator

Each core service implements a `GenRouting` method that constructs the routing section of the final JSON configuration. For V2Ray, this populates `_coreConfig.routing`; for sing-box, it populates `_coreConfig.route`.

### Step 2: Merge Global and User Rules

The generator first applies **global basic routing** settings from `_config.RoutingBasicItem`, then processes the **user routing item** carried in the `CoreConfigContext`. If the routing item defines a `DomainStrategy`, it overrides the global setting in `_coreConfig.routing.domainStrategy` (V2Ray) or `route.default_domain_resolver.strategy` (sing-box).

### Step 3: Convert Individual Rules

Each enabled `RulesItem` in the `RuleSet` array is transformed into a core-specific rule object:

- **V2Ray**: Converted to `RulesItem4Ray` with `type = "field"` and optional `domain`, `ip`, `port`, `network`, `inboundTag`, `protocol`, and `balancerTag` fields.
- **sing-box**: Converted to `Rule4Sbox` objects that separate domain rules (`geosite`, `domain_regex`, `domain_suffix`, `domain_keyword`) from IP rules (`geoip`, `ip_cidr`), process rules (`process_name`, `process_path`), and port rules.

### Step 4: Handle Balancers and Final Rules

For V2Ray, if balancers are defined, the generator injects balancer tags into matching rules and populates the `balancers` section. Both generators append a **final fallback rule** that directs unmatched traffic to the proxy tag (or a specific outbound), ensuring no traffic leaks outside the configured routes.

```csharp
// V2Ray final rule construction (from V2rayRoutingService.cs)
private void BuildFinalRule()
{
    var finalRule = new RulesItem4Ray
    {
        type = "field",
        outboundTag = Global.ProxyTag,
        port = "0-65535"
    };
    _coreConfig.routing.rules.Add(finalRule);
}

```

## Core-Specific Implementation Differences

While the high-level workflow is consistent, the internal representations diverge significantly between V2Ray and sing-box to accommodate each core's native configuration schema.

### Domain and IP Matching

**V2Ray** uses a unified `RulesItem4Ray` with arrays for `domain` and `ip` that can contain GeoSite/GeoIP tags (e.g., `geosite:cn`), full domains, or CIDR ranges. **sing-box** splits these into distinct rule types: domain rules use `geosite`, `domain_suffix`, `domain_keyword`, or `domain_regex`, while IP rules use `geoip` or `ip_cidr`.

### Process-Based Routing

Process name matching is handled differently due to core capabilities:

- **V2Ray**: Only supported when using the Xray core. The `V2rayRoutingService` checks the core type and clears the `process` field for standard V2Ray.
- **sing-box**: Fully supports process matching via `process_name` and `process_path` fields, converting each `RulesItem.Process` entry into the appropriate sing-box rule attribute.

### DNS and Resolver Integration

**V2Ray** does not support per-rule DNS settings in the routing section; DNS rules (`RuleType == DNS`) are filtered out during generation. **sing-box** explicitly adds DNS hijack rules (`action = "hijack-dns"`) and can insert `resolve` rules when custom domain resolution is required, leveraging sing-box's `default_domain_resolver` strategy.

### Balancer Architecture

V2Ray's routing supports explicit load balancers defined in a separate `balancers` array, with rules referencing them via `balancerTag`. sing-box handles load balancing internally through its outbound selection mechanism, so v2rayN's sing-box generator does not produce a separate balancer section; instead, it relies on the core's built-in routing logic.

## Programmatically Creating and Applying Routing Rules

Developers can bypass the UI to create routing items and trigger configuration generation using the same internal APIs that v2rayN uses.

### Creating a Routing Item

The following example demonstrates creating a routing item that blocks advertising domains and routes private IPs to direct connection:

```csharp
using System;
using System.Collections.Generic;
using System.Text.Json;
using ServiceLib.Models;
using ServiceLib.Handler;

// Define rules that block ads and bypass private networks
var rules = new List<RulesItem>
{
    new RulesItem
    {
        Enabled = true,
        RuleType = ERuleType.Domain,
        OutboundTag = "block",
        Domain = new[] { "geosite:category-ads-all", "geosite:google-ads" }
    },
    new RulesItem
    {
        Enabled = true,
        RuleType = ERuleType.Ip,
        OutboundTag = "direct",
        Ip = new[] { "geoip:private", "192.168.0.0/16", "10.0.0.0/8" }
    }
};

// Create the routing item
var routingItem = new RoutingItem
{
    Id = Guid.NewGuid().ToString(),
    Remarks = "Custom Ad Block and Private Bypass",
    RuleSet = JsonSerializer.Serialize(rules),
    DomainStrategy = Global.IPIfNonMatch,
    IsActive = true,
    Sort = 1
};

// Persist to SQLite database
var config = await ConfigHandler.LoadConfig();
await ConfigHandler.SaveRoutingItem(config, routingItem);

```

### Generating Core Configuration

Once the routing item is saved, you can generate the core-specific configuration file that incorporates these rules:

```csharp
using ServiceLib.Services.CoreConfig;
using ServiceLib.Handler;
using ServiceLib.Manager;

// Load current configuration and core information
var config = await ConfigHandler.LoadConfig();
var coreInfo = CoreInfoManager.Instance.GetCoreInfo()
    .First(ci => ci.CoreType == ECoreType.v2ray); // or ECoreType.sing_box

// Build the context that carries the routing item
var context = await CoreConfigHandler.BuildContext(config, coreInfo);

// Instantiate the appropriate generator
ICoreConfigService generator = coreInfo.CoreType switch
{
    ECoreType.v2ray => new CoreConfigV2rayService(context),
    ECoreType.sing_box => new CoreConfigSingboxService(context),
    _ => throw new NotSupportedException($"Core type {coreInfo.CoreType} not supported")
};

// Generate the configuration file (writes to config.json)
await generator.BuildConfigFileAsync();

```

This code path mirrors the execution flow triggered by the **Start** button in `MainWindowViewModel.StartCmd`, allowing automated testing or custom tooling around v2rayN's routing engine.

## Summary

- **v2rayN** stores routing policies in SQLite as `RoutingItem` objects containing a JSON `RuleSet` array of `RulesItem` definitions.
- The **routing pipeline** loads the active routing item via `ConfigHandler.GetDefaultRouting`, packages it into a `CoreConfigContext`, and delegates to core-specific generators.
- **V2Ray** and **sing-box** use distinct internal representations: V2Ray employs unified `field` type rules with `domain` and `ip` arrays, while sing-box separates concerns into distinct rule types with `geosite`, `geoip`, `domain_suffix`, and `process_name` fields.
- **Process-based routing** is supported in sing-box natively, but only available for V2Ray when using the Xray core.
- **Balancers** are implemented as separate `balancers` sections in V2Ray, while sing-box handles load balancing internally without explicit balancer tags.
- Developers can programmatically create and apply routing rules using `ConfigHandler.SaveRoutingItem` and the `CoreConfigV2rayService` or `CoreConfigSingboxService` generators.

## Frequently Asked Questions

### How does v2rayN store custom routing rules internally?

v2rayN persists routing configurations in a SQLite database using the `RoutingItem` model defined in [`ServiceLib/Models/RoutingItem.cs`](https://github.com/2dust/v2rayN/blob/main/ServiceLib/Models/RoutingItem.cs). Each routing item contains a `RuleSet` field that stores a serialized JSON array of `RulesItem` objects. When the application loads a configuration, `ConfigHandler.GetDefaultRouting` deserializes this JSON into a `List<RulesItem>` that the core generators consume.

### What is the difference between V2Ray and sing-box routing rule formats?

V2Ray uses a unified rule structure with `type = "field"` and optional arrays for `domain`, `ip`, `port`, and `protocol` within a single `RulesItem4Ray` object. sing-box, conversely, separates matching criteria into distinct rule types: domain rules use `geosite`, `domain_suffix`, `domain_keyword`, or `domain_regex`; IP rules use `geoip` or `ip_cidr`; and process rules use `process_name` or `process_path`. This structural difference is handled internally by `V2rayRoutingService.GenRoutingUserRule` and `SingboxRoutingService.GenRoutingUserRule`.

### Can I use process-based routing rules with the standard V2Ray core?

Process-based routing is only supported when using the Xray core variant, not the standard V2Ray core. In [`V2rayRoutingService.cs`](https://github.com/2dust/v2rayN/blob/main/V2rayRoutingService.cs), the generator checks the core type and clears the `process` field for non-Xray cores to prevent configuration errors. If you require process-level traffic splitting, switch to the Xray core or use sing-box, which natively supports `process_name` and `process_path` matching without core-specific limitations.

### How are routing balancers implemented differently between V2Ray and sing-box?

V2Ray implements balancers as a separate `balancers` array in the routing configuration, where each balancer defines multiple outbound tags and a selection strategy. Routing rules then reference these balancers via a `balancerTag` field instead of a direct `outboundTag`. sing-box does not expose a separate balancer section; instead, load balancing is handled internally by the core through outbound selection mechanisms. When v2rayN generates sing-box configurations, it maps the intended load balancing behavior directly to the `outbound` field without creating explicit balancer objects.