# 2dust/v2rayN — codebase unpack

> Canonical page: https://codevetter.com/unpack/v2rayn

This is a deterministic Repo Unpack scan of 2dust/v2rayN — a structural map, not a review, security audit, or maintainer-endorsed description.

- Scanned commit: 28139853a8087d81e1ac7ffaf7013b937958f5d2 (2026-09-20)
- Last upstream commit: up 7.25.2
- Generated by codevetter 1.12.0 on 2026-09-22
- 418 files scanned · 3.9 MB

## Languages

- C#: 283 files
- YAML: 16 files
- Shell: 7 files
- JSON: 1 files
- Markdown: 1 files

## Repository structure

- v2rayN/ — 387 files
- .github/ — 17 files

Probable source entrypoint: `v2rayN/` (inferred from the tree, not upstream docs)

## Source layout — v2rayN/

- v2rayN/ServiceLib/ — 179 files
- v2rayN/ServiceLib.Tests/ — 16 files
- v2rayN/ServiceLib.UdpTest/ — 9 files
- v2rayN/v2rayN/ — 68 files
- v2rayN/v2rayN.Desktop/ — 73 files
- v2rayN/Directory.Build.props — 1 file
- v2rayN/Directory.Packages.props — 1 file
- v2rayN/v2rayN.sln — 1 file
- v2rayN/v2rayN.slnx — 1 file

## Structural health signals

Average score 6.4/10 across 96 files; 65 hotspots. These are deterministic review leads, not proof of a bug.

## Recent history

- 28139853a808 (2026-09-20) up 7.25.2
- 5d9718a0bb93 (2026-09-20) Fix (#10203)
- 740729c8473f (2026-09-20) Raise speed test concurrency minimum
- 13cd2ef80d69 (2026-09-20) Update Directory.Packages.props
- be3566df2f71 (2026-09-19) Add finalMask parsing to Wireguard configuration handling (#10201)

## Overview

v2rayN is a cross-platform desktop GUI proxy client written in C# on .NET 10 that orchestrates external proxy cores — primarily Xray and sing-box, plus mihomo, hysteria, tuic and others — for Windows, Linux and macOS users. It ships as two shells over a shared ServiceLib core: a WPF app (v2rayN) for Windows and an Avalonia app (v2rayN.Desktop) for Windows/Linux/macOS, both ReactiveUI-based MVVM. Its distinctive role is translating user-managed server profiles, subscriptions, routing/DNS rules and TUN settings into generated core config JSON, spawning the right core binary, and wiring the OS-level system proxy.

## System map

The solution (v2rayN.sln) has five projects: ServiceLib (all shared logic, ViewModels, managers, config generation), v2rayN (WPF Windows shell), v2rayN.Desktop (Avalonia cross-platform shell), AmazTool (a tiny console updater/relocator invoked after self-upgrade downloads), and ServiceLib.UdpTest (UDP-over-SOCKS5 tester library referenced by ServiceLib). Persistence is a JSON file guiNConfig.json plus a SQLite database guiNDB.db accessed via the SQLiteHelper singleton. External runtime boundaries are the spawned core processes (xray, sing-box, mihomo, etc. under bin/<coreType>/), OS facilities (WinInet/registry, launchctl, .desktop autostart, sudo bash scripts), a loopback Clash API, and GitHub release endpoints for updates. Build/test is .NET 10 with GitHub Actions workflows; GlobalHotKeys is a git submodule used only by the Desktop project.
- The WPF Windows shell entrypoint is App.OnStartup in v2rayN/v2rayN/App.xaml.cs: it enforces a single instance via an EventWaitHandle keyed on the MD5 of the exe path, honors a 'rebootas' arg, calls AppManager.Instance.InitApp()/InitComponents(), builds the ReactiveUI builder, and shows MainWindow resolved through SimpleViewLocator. — [v2rayN/v2rayN/App.xaml.cs#L24-61](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/v2rayN/App.xaml.cs#L24-L61) [v2rayN/v2rayN/Common/SimpleViewLocator.cs](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/v2rayN/Common/SimpleViewLocator.cs)
- The Avalonia shell entrypoint is Program.Main in v2rayN.Desktop which does single-instance locking (EventWaitHandle on Windows, a named Mutex 'v2rayN' on Linux/macOS), calls AppManager.Instance.InitApp(), then builds AppBuilder with UsePlatformDetect/UseReactiveUI and macOS dock options. — [v2rayN/v2rayN.Desktop/Program.cs#L14-77](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/v2rayN.Desktop/Program.cs#L14-L77) [v2rayN/v2rayN.Desktop/App.axaml.cs#L16-41](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/v2rayN.Desktop/App.axaml.cs#L16-L41)
- ServiceLib is the shared library referenced by both shells (ProjectReference in each csproj); it contains ViewModels, Manager singletons, Handler statics, Services, Fmt parsers, Models, ResUI localization resx files and embedded Sample templates. — [v2rayN/ServiceLib/ServiceLib.csproj](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/ServiceLib.csproj) [v2rayN/v2rayN/v2rayN.csproj#L39](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/v2rayN/v2rayN.csproj#L39) [v2rayN/v2rayN.Desktop/v2rayN.Desktop.csproj#L40-42](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/v2rayN.Desktop/v2rayN.Desktop.csproj#L40-L42)
- AmazTool is a standalone console exe whose Main dispatches on args[0]: 'rebootas' restarts v2rayN via Utils.StartV2RayN, 'help' prints usage, and anything else is treated as a URL-escaped zip path passed to UpgradeApp.Upgrade for self-update extraction. — [v2rayN/AmazTool/Program.cs#L24-45](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/AmazTool/Program.cs#L24-L45) [v2rayN/AmazTool/UpgradeApp.cs#L9-19](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/AmazTool/UpgradeApp.cs#L9-L19)
- App state lives in two stores under StartupPath()/guiConfigs: the JSON file guiNConfig.json deserialized by ConfigHandler.LoadConfig into the Config model, and the SQLite db guiNDB.db opened once by the SQLiteHelper singleton (sqlite-net-e / Repobot.SQLite.Unofficial packages). — [v2rayN/ServiceLib/Handler/ConfigHandler.cs#L19-36](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Handler/ConfigHandler.cs#L19-L36) [v2rayN/ServiceLib/Helper/SqliteHelper.cs#L9-19](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Helper/SqliteHelper.cs#L9-L19) [v2rayN/ServiceLib/Global.cs#L12](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Global.cs#L12) [v2rayN/ServiceLib/Models/Configs/Config.cs](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Models/Configs/Config.cs)
- AppManager.InitApp creates eight SQLite tables at startup (SubItem, ProfileItem, ServerStatItem, RoutingItem, ProfileExItem, DNSItem, FullConfigTemplateItem, plus the obsolete ProfileGroupItem) and sets the process UI culture from config.UiItem.CurrentLanguage. — [v2rayN/ServiceLib/Manager/AppManager.cs#L63-96](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Manager/AppManager.cs#L63-L96)
- If the app directory is not writable (HasWritePermission fails, e.g. WinGet installs or a NotStoreConfigHere.txt marker), InitApp sets env var V2RAYN_LOCAL_APPLICATION_DATA_V2=1 which makes StartupPath() resolve to %LocalApplicationData%/v2rayN instead of the exe directory — i.e. portable-by-default storage with a per-user fallback. — [v2rayN/ServiceLib/Manager/AppManager.cs#L65-68](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Manager/AppManager.cs#L65-L68) [v2rayN/ServiceLib/Common/Utils.cs#L1097-1160](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Common/Utils.cs#L1097-L1160) [v2rayN/ServiceLib/Global.cs#L90](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Global.cs#L90)
- External proxy cores are enumerated in CoreInfoManager.InitCoreInfo — a hard-coded registry of 14 ECoreType entries (v2rayN self, v2fly, Xray, mihomo, hysteria, naiveproxy, tuic, sing_box, juicity, hysteria2, brook, overtls, shadowquic, mieru) each declaring exe names, argument template, GitHub release/download URLs and optional environment variables. — [v2rayN/ServiceLib/Manager/CoreInfoManager.cs#L97-293](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Manager/CoreInfoManager.cs#L97-L293)
- Core processes are spawned by ProcessService (System.Diagnostics.Process, no shell, UTF8-redirected stdout/stderr piped to an updateFunc callback) and on Windows are attached to a Job Object via WindowsJobService so children die with the parent. — [v2rayN/ServiceLib/Services/ProcessService.cs#L24-71](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Services/ProcessService.cs#L24-L71) [v2rayN/ServiceLib/Manager/CoreManager.cs#L334-376](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Manager/CoreManager.cs#L334-L376) [v2rayN/ServiceLib/Services/WindowsJobService.cs](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Services/WindowsJobService.cs)
- Cross-component messaging uses the static AppEvents/EventChannel bus (SendMsgViewRequested, SendSnackMsgRequested, AppExitRequested, SysProxyChangeRequested, etc.) rather than direct references; NoticeManager publishes snack/log messages onto it. — [v2rayN/ServiceLib/Events/AppEvents.cs](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Events/AppEvents.cs) [v2rayN/ServiceLib/Manager/NoticeManager.cs#L8-34](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Manager/NoticeManager.cs#L8-L34)
- Directory.Build.props pins net10.0, version 7.25.2, GPL-3.0, Nullable annotations + ImplicitUsings, and Release builds force PublishSingleFile with DebugType embedded; package versions are centrally managed in Directory.Packages.props and global.json pins the SDK. — [v2rayN/Directory.Build.props](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/Directory.Build.props) [v2rayN/Directory.Packages.props](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/Directory.Packages.props) [global.json](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/global.json)
- CI/CD is GitHub Actions only: test.yml runs 'dotnet test' on ServiceLib.Tests (ubuntu-latest, .NET 10.x) on pushes/PRs touching ServiceLib paths, while build.yml is a reusable workflow building x64+arm64 for windows/linux/macos and build-*.yml/package-*.yml wrappers package and publish releases (winget, zip, GPG keys). — [.github/workflows/test.yml#L31-52](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/.github/workflows/test.yml#L31-L52) [.github/workflows/build.yml](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/.github/workflows/build.yml) [.github/workflows/build-windows.yml](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/.github/workflows/build-windows.yml) [.github/workflows/package-zip.yml](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/.github/workflows/package-zip.yml)
- v2rayN/GlobalHotKeys is a git submodule (github.com/2dust/GlobalHotKeys) referenced by v2rayN.Desktop.csproj for global hotkeys; workflows checkout with submodules: 'recursive'. — [.gitmodules](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/.gitmodules) [v2rayN/v2rayN.Desktop/v2rayN.Desktop.csproj#L40](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/v2rayN.Desktop/v2rayN.Desktop.csproj#L40) [.github/workflows/build.yml](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/.github/workflows/build.yml)
- Localization is centralized in ServiceLib/Resx/ResUI*.resx (en plus zh-Hans, zh-Hant, ru, fa, fr, hu, id, az satellite assemblies), consumed as the generated ResUI.Designer.cs class across ViewModels, handlers and services. — [v2rayN/ServiceLib/ServiceLib.csproj#L59-98](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/ServiceLib.csproj#L59-L98) [v2rayN/ServiceLib/Resx/ResUI.resx](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Resx/ResUI.resx)

## Feature catalog

User-facing features center on managing proxy server profiles (11 share-URI protocols plus custom/outbound/group types), subscription groups with scheduled updates and optional subconvert rewriting, and one-click activation that generates a core config and launches the matching core. Around that are system-proxy control per OS (including a PAC file server on Windows), TUN mode with privilege escalation, a full Clash-compatible API panel (proxies/connections/mode switch), batch latency/speed/mixed/UDP testing, per-server traffic statistics, self/core/geo-file updating from GitHub, WebDAV backup, global hotkeys, autostart, QR/clipboard/image import, and regional presets for Russia and Iran.
- Servers can be added per-protocol via dedicated ReactiveCommands (VMess, VLESS, Shadowsocks, SOCKS, HTTP, Trojan, Hysteria2, TUIC, WireGuard, Anytls, Naive) plus Custom, Custom-Outbound, PolicyGroup and ProxyChain types, or bulk-imported from clipboard, screen QR scan, or image file. — [v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs#L23-42](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs#L23-L42) [v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs#L449-559](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs#L449-L559)
- FmtHandler.ResolveConfig dispatches on share-URI prefixes (vmess://, ss://, vless://, trojan://, socks://, socks4/5://, hysteria2://, hy2://, hysteria2+realm://, tuic://, wireguard://, anytls://, naive://, naive+https/quic://) to per-protocol Fmt classes that parse into ProfileItem; GetShareUri does the reverse for export/QR. — [v2rayN/ServiceLib/Handler/Fmt/FmtHandler.cs#L7-110](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Handler/Fmt/FmtHandler.cs#L7-L110) [v2rayN/ServiceLib/Global.cs#L238-250](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Global.cs#L238-L250)
- Subscription update (SubscriptionHandler.UpdateProcess) validates http(s) URLs, honors per-sub custom request headers and User-Agent, supports a 'MoreUrl' list of extra subscription links merged into the result, and optionally rewrites the URL through a subconvert service (default https://sub.xeton.dev/sub?url=) with target/config params. — [v2rayN/ServiceLib/Handler/SubscriptionHandler.cs#L60-195](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Handler/SubscriptionHandler.cs#L60-L195) [v2rayN/ServiceLib/Global.cs#L142-162](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Global.cs#L142-L162)
- ConfigHandler.AddBatchServers tries in order: base64/plain share-URI lists, SS SIP008 JSON, WireGuard INI-style config, inner 'v2rayn://' URIs, then foreign-subscription custom formats; when isSub it first removes the group's old servers, then restores the previously-active node and clones traffic stats onto the new IndexIds. — [v2rayN/ServiceLib/Handler/ConfigHandler.cs#L2037-2135](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Handler/ConfigHandler.cs#L2037-L2135)
- System proxy control (SysProxyHandler.UpdateSysProxy) supports ForcedChange/ForcedClear/Pac/Unchanged per OS: WinInet InternetSetOption + registry fallback on Windows, embedded proxy_set_linux_sh/proxy_set_osx_sh scripts on Linux/macOS, and a PAC mode that hosts a generated pac.txt on a local TcpListener port. — [v2rayN/ServiceLib/Handler/SysProxy/SysProxyHandler.cs#L7-117](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Handler/SysProxy/SysProxyHandler.cs#L7-L117) [v2rayN/ServiceLib/Handler/SysProxy/ProxySettingWindows.cs#L58-198](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Handler/SysProxy/ProxySettingWindows.cs#L58-L198) [v2rayN/ServiceLib/Manager/PacManager.cs#L15-93](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Manager/PacManager.cs#L15-L93)
- TUN mode is supported end-to-end: TunModeItem settings feed CoreConfigContext.IsTunEnabled, sing-box TUN inbound/rule embedded templates exist (tun_singbox_inbound/tun_singbox_rules/tun_singbox_dns), and on Linux/macOS the core is relaunched via sudo when ShouldRunAsSudo(coreType in sing_box/mihomo/Xray) is true. — [v2rayN/ServiceLib/Handler/Builder/CoreConfigContextBuilder.cs#L45-53](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Handler/Builder/CoreConfigContextBuilder.cs#L45-L53) [v2rayN/ServiceLib/ServiceLib.csproj#L52-54](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/ServiceLib.csproj#L52-L54) [v2rayN/ServiceLib/Manager/CoreManager.cs#L298-322](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Manager/CoreManager.cs#L298-L322)
- A Clash-compatible dashboard is provided by ClashApiManager talking to http://127.0.0.1:{StatePort2}: GET /proxies and /providers/proxies merged into ClashItem, delay tests via /proxies/{name}/delay, proxy selection via PUT /proxies/{group}, mode switch via PATCH /configs, and connections list/kill. — [v2rayN/ServiceLib/Manager/ClashApiManager.cs#L9-195](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Manager/ClashApiManager.cs#L9-L195) [v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs#L14-15](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs#L14-L15)
- SpeedtestService.RunLoop supports ESpeedActionType modes over selected servers with a configurable page size and delay interval; UDP testing is delegated to ServiceLib.UdpTest's Socks5UdpChannel; mixed/concurrency counts are floored at Global.SpeedTestConcurrencyCountMin=10. — [v2rayN/ServiceLib/Services/SpeedtestService.cs#L12-80](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Services/SpeedtestService.cs#L12-L80) [v2rayN/ServiceLib.UdpTest/Socks5UdpChannel.cs](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib.UdpTest/Socks5UdpChannel.cs) [v2rayN/ServiceLib/Global.cs#L94-95](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Global.cs#L94-L95)
- Per-server traffic statistics are collected by StatisticsManager via StatisticsXrayService and StatisticsSingboxService polling core APIs, stored in ServerStatItem rows with daily counters reset in InitData, and pushed to the UI through the DispatcherStatisticsRequested event. — [v2rayN/ServiceLib/Manager/StatisticsManager.cs#L18-98](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Manager/StatisticsManager.cs#L18-L98) [v2rayN/ServiceLib/Services/Statistics/StatisticsXrayService.cs](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Services/Statistics/StatisticsXrayService.cs)
- Self/core update (UpdateService) queries GitHub release APIs or follows the /releases/latest redirect, compares SemanticVersions (with sing-box locked to <=1.14.x via LockedMaxVersion), picks an arch-appropriate download URL per OS (incl. linux riscv64/loongarch64), downloads via DownloadService, and for v2rayN itself hands the zip to AmazTool. — [v2rayN/ServiceLib/Services/UpdateService.cs#L9-363](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Services/UpdateService.cs#L9-L363) [v2rayN/ServiceLib/Manager/CoreInfoManager.cs#L232](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Manager/CoreInfoManager.cs#L232)
- Geo data updates download geosite.dat/geoip.dat (default Loyalsoldier v2ray-rules-dat or user-configured GeoSourceUrl) plus sing-box .srs rule-sets collected from routing rules and DNS config into bin/srss/. — [v2rayN/ServiceLib/Services/UpdateService.cs#L139-148](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Services/UpdateService.cs#L139-L148) [v2rayN/ServiceLib/Services/UpdateService.cs#L369-531](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Services/UpdateService.cs#L369-L531) [v2rayN/ServiceLib/Global.cs#L8-9](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Global.cs#L8-L9)
- A background TaskManager long-running loop performs scheduled work: every minute it refreshes subscriptions whose AutoUpdateInterval elapsed, every 20 min saves config+ProfileEx, hourly deletes expired temp/log/test files and refreshes geo files, and daily runs CheckHasUpdateOnlyAll which can publish HasUpdateNotified. — [v2rayN/ServiceLib/Manager/TaskManager.cs#L22-154](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Manager/TaskManager.cs#L22-L154)
- WebDAV backup/restore is implemented in WebDavManager (WebDav.Client package) which uploads a 'backup.zip' into a '<AppName>_backup' directory on a user-configured server with basic-auth credentials. — [v2rayN/ServiceLib/Manager/WebDavManager.cs](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Manager/WebDavManager.cs)
- Autostart is per-OS in AutoStartupHandler: Windows writes HKCU Run key 'v2rayNAutoRun_<md5>' or registers a highest-privilege logon task when admin; Linux writes ~/.config/autostart/v2rayN.desktop from an embedded template; macOS writes and launchctl-loads a LaunchAgent plist. — [v2rayN/ServiceLib/Handler/AutoStartupHandler.cs#L9-251](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Handler/AutoStartupHandler.cs#L9-L251) [v2rayN/ServiceLib/Global.cs#L78-79](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Global.cs#L78-L79)
- Regional presets (ApplyRegionalPreset for Default/Russia/Iran) swap routing/DNS/geo sources (runetfreedom, Chocolate4U lists) and re-download geo files; global hotkeys are configurable per action via Config.GlobalHotkeys and registered through user32 RegisterHotKey on WPF. — [v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs#L780-792](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs#L780-L792) [v2rayN/ServiceLib/Global.cs#L185-211](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Global.cs#L185-L211) [v2rayN/v2rayN/Manager/HotkeyManager.cs#L62-101](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/v2rayN/Manager/HotkeyManager.cs#L62-L101)

## Data flow

Input boundaries are subscription URLs, share URIs (clipboard/QR/image), dialogs, and GitHub release APIs. Everything lands in the Config object (guiNConfig.json) or SQLite guiNDB.db entities via AppManager query helpers and ConfigHandler writers. The hot path to the network boundary is: ProfileItem + Config -> CoreConfigContextBuilder.Build/BuildAll -> immutable CoreConfigContext snapshot -> CoreConfigSingboxService/CoreConfigV2rayService -> config.json under binConfigs/ -> ProcessService spawning the core binary. Output boundaries are the spawned core's local SOCKS/API ports, the OS system proxy, and files under bin/, guiConfigs/, guiLogs/, guiTemps/, guiBackups/.
- Config is loaded at startup by ConfigHandler.LoadConfig from guiNConfig.json and normalized with ~40 defaults (inbound socks on 10808, TunModeItem MTU 9000, SpeedTestItem urls, mux/fragment/happy-eyeballs blocks, language from culture); SaveConfig writes it back and TaskManager re-saves every 20 minutes. — [v2rayN/ServiceLib/Handler/ConfigHandler.cs#L19-197](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Handler/ConfigHandler.cs#L19-L197) [v2rayN/ServiceLib/Manager/TaskManager.cs#L43-56](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Manager/TaskManager.cs#L43-L56)
- SQLiteHelper is the single owner of guiNDB.db, exposing sync CreateTable and async TableAsync/QueryAsync/ExecuteAsync; AppManager wraps it with typed queries (ProfileItems, SubItems, RoutingItems, DNSItems, ProfileModels join query with subRemarks). — [v2rayN/ServiceLib/Helper/SqliteHelper.cs#L14-88](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Helper/SqliteHelper.cs#L14-L88) [v2rayN/ServiceLib/Manager/AppManager.cs#L175-230](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Manager/AppManager.cs#L175-L230)
- Subscription text flows: DownloadService.TryDownloadString (HttpClient with proxy fallback to Downloader library) -> optional base64 decode -> ConfigHandler.AddBatchServers -> FmtHandler.Resolve per line -> ProfileItem rows tagged with the SubItem id; the sub's UpdateTime is stamped by TaskManager for the auto-update interval. — [v2rayN/ServiceLib/Handler/SubscriptionHandler.cs#L102-229](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Handler/SubscriptionHandler.cs#L102-L229) [v2rayN/ServiceLib/Services/DownloadService.cs#L195-251](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Services/DownloadService.cs#L195-L251) [v2rayN/ServiceLib/Handler/ConfigHandler.cs#L2037-2135](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Handler/ConfigHandler.cs#L2037-L2135)
- CoreConfigContextBuilder.Build assembles an immutable CoreConfigContext: resolves the effective node (possibly wrapping it in a subscription prev/next ProxyChain via BuildSubscriptionChainNodeAsync), registers routing-rule outbound nodes into AllProxiesMap, validates TunModeItem.RouteExcludeAddress CIDRs with IPNetwork2, and snapshots DNS/routing/template items from SQLite. — [v2rayN/ServiceLib/Handler/Builder/CoreConfigContextBuilder.cs#L34-138](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Handler/Builder/CoreConfigContextBuilder.cs#L34-L138) [v2rayN/ServiceLib/Handler/Builder/CoreConfigContextBuilder.cs#L258-315](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Handler/Builder/CoreConfigContextBuilder.cs#L258-L315)
- BuildAll optionally produces a second 'pre-socks' context (BuildPreSocksIfNeeded) used for TUN protection or upstream chaining; when present the main context gets IsTunEnabled=false and merged ProtectDomainList/ProtectCoreTypeList so the generated config and launch mode always agree. — [v2rayN/ServiceLib/Handler/Builder/CoreConfigContextBuilder.cs#L145-214](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Handler/Builder/CoreConfigContextBuilder.cs#L145-L214)
- CoreConfigHandler.GenerateClientConfig dispatches on node type: Custom -> file copy (or CoreConfigClashService for mihomo mixin), RunCoreType==sing_box -> CoreConfigSingboxService.GenerateClientConfigContent, otherwise CoreConfigV2rayService; the result JSON is written to binConfigs/config.json (or configPre.json for the pre-socks core). — [v2rayN/ServiceLib/Handler/CoreConfigHandler.cs#L10-42](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Handler/CoreConfigHandler.cs#L10-L42) [v2rayN/ServiceLib/Manager/CoreManager.cs#L74-104](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Manager/CoreManager.cs#L74-L104) [v2rayN/ServiceLib/Global.cs#L13-15](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Global.cs#L13-L15)
- Per-core config generation is decomposed into services per concern: Singbox = Log/Inbound/Outbound/Routing/Dns/Ruleset/Statistic/ConfigTemplate services; V2ray = Log/Inbound/Outbound/Routing/Dns/Balancer/Statistic/ConfigTemplate services — each mutating a shared config model rooted at Models/CoreConfigs. — [v2rayN/ServiceLib/Services/CoreConfig/Singbox/CoreConfigSingboxService.cs](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Services/CoreConfig/Singbox/CoreConfigSingboxService.cs) [v2rayN/ServiceLib/Services/CoreConfig/V2ray/CoreConfigV2rayService.cs](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Services/CoreConfig/V2ray/CoreConfigV2rayService.cs) [v2rayN/ServiceLib/Models/CoreConfigs/V2rayConfig.cs](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Models/CoreConfigs/V2rayConfig.cs)
- Outbound traffic routing decisions come from RoutingItem.RuleSet JSON deserialized to List<RulesItem>; rules whose OutboundTag is not a built-in tag are resolved by remarks to ProfileItems and injected into AllProxiesMap as 'remark:<tag>' entries. — [v2rayN/ServiceLib/Handler/Builder/CoreConfigContextBuilder.cs#L63-98](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Handler/Builder/CoreConfigContextBuilder.cs#L63-L98)
- All outbound HTTP(S) fetching funnels through DownloadService: it builds a WebProxy socks5://127.0.0.1:{localPort} only after SocksPortCheck confirms the local core answers a SOCKS5 greeting, applies CertPemManager certificate chain policy when configured, sets Basic auth from URL userinfo, and uses short connect timeouts (5s direct / 10s proxy). — [v2rayN/ServiceLib/Services/DownloadService.cs#L256-427](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Services/DownloadService.cs#L256-L427) [v2rayN/ServiceLib/Manager/CertPemManager.cs](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Manager/CertPemManager.cs)
- Statistics flow: StatisticsXrayService/StatisticsSingboxService poll the running cores -> UpdateServerStatHandler aggregates into ServerStatItem rows -> AppEvents.DispatcherStatisticsRequested pushes ServerSpeedItem to the status bar; SaveTo persists on exit and CloneServerStatItem carries stats across subscription refreshes. — [v2rayN/ServiceLib/Manager/StatisticsManager.cs#L18-98](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Manager/StatisticsManager.cs#L18-L98) [v2rayN/ServiceLib/Handler/ConfigHandler.cs#L2120-2132](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Handler/ConfigHandler.cs#L2120-L2132)
- The local API ports derive from the socks inbound localPort (default 10808): AppManager.GetLocalPort(protocol) = localPort + (int)EInboundProtocol, with StatePort/StatePort2 lazily reassigned to free ports via Utils.GetFreePort. — [v2rayN/ServiceLib/Manager/AppManager.cs#L15-31](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Manager/AppManager.cs#L15-L31) [v2rayN/ServiceLib/Manager/AppManager.cs#L165-169](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Manager/AppManager.cs#L165-L169)
- Self-update flow terminates in AmazTool: UpdateService downloads the release zip to guiTemps, the app launches AmazTool with the URL-encoded path, and UpgradeApp.Upgrade kills running v2rayN processes, renames its own exe to .tmp, extracts the zip (skipping existing bin/ payloads), then restarts via Utils.StartV2RayN. — [v2rayN/AmazTool/UpgradeApp.cs#L9-107](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/AmazTool/UpgradeApp.cs#L9-L107) [v2rayN/AmazTool/Program.cs#L39-44](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/AmazTool/Program.cs#L39-L44) [v2rayN/ServiceLib/Services/UpdateService.cs#L9-47](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Services/UpdateService.cs#L9-L47)
- File-system layout under StartupPath(): bin/ holds core binaries and geo files (per-core subdirs by lowercase CoreType), binConfigs/ holds generated config.json/configPre.json/configTest*.json, guiConfigs/ holds guiNConfig.json + guiNDB.db + pac.txt, guiTemps/ downloads, guiBackups/ backups, guiLogs/ NLog output. — [v2rayN/ServiceLib/Common/Utils.cs#L1152-1288](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Common/Utils.cs#L1152-L1288) [v2rayN/ServiceLib/Manager/TaskManager.cs#L61-65](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Manager/TaskManager.cs#L61-L65)

## Behavior traces

Three flows were traced end-to-end. Startup goes through single-instance guard, config load, SQLite table creation, ViewModel init, core init and a first Reload. Server activation goes through GetDefaultServer, CoreConfigContextBuilder.BuildAll, config generation, process spawn, system proxy update and an availability ping. Subscription update iterates SubItems, downloads with proxy fallback, parses into ProfileItem rows, and conditionally triggers Reload. Self-update and app-exit are also traced below.
- Startup (WPF): App.OnStartup -> Utils.GetMd5(GetExePath) EventWaitHandle single-instance check -> AppManager.InitApp (HasWritePermission fallback, Logging.Setup, ConfigHandler.LoadConfig, culture set, CreateTable x8) -> AppManager.InitComponents (StatePort/StatePort2 resolve, MigrateProfileExtra) -> MainWindowViewModel ctor -> Init(): InitBuiltinDNS, InitBuiltinFullConfigTemplate, ProfileExManager.Init, CoreManager.Init (copies bin/ when in local-appdata mode, chmods core exes on non-Windows), CertPemManager.Init, TaskManager.RegUpdateTask, StatisticsManager.Init -> RefreshServersDispatcherAsync -> Reload(). — [v2rayN/v2rayN/App.xaml.cs#L24-61](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/v2rayN/App.xaml.cs#L24-L61) [v2rayN/ServiceLib/Manager/AppManager.cs#L63-113](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Manager/AppManager.cs#L63-L113) [v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs#L319-343](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs#L319-L343) [v2rayN/ServiceLib/Manager/CoreManager.cs#L21-61](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Manager/CoreManager.cs#L21-L61)
- Startup (Avalonia): Program.Main -> OnStartup (Windows EventWaitHandle or non-Windows Mutex 'v2rayN' single-instance, InitApp, WindowDialog) -> BuildAvaloniaApp(UsePlatformDetect, UseReactiveUI, macOS ShowInDock option) -> StartWithClassicDesktopLifetime -> App.OnFrameworkInitializationCompleted registers SimpleViewLocator DataTemplate, calls InitComponents, then constructs MainWindowViewModel + MainWindow. — [v2rayN/v2rayN.Desktop/Program.cs#L14-77](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/v2rayN.Desktop/Program.cs#L14-L77) [v2rayN/v2rayN.Desktop/App.axaml.cs#L16-41](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/v2rayN.Desktop/App.axaml.cs#L16-L41)
- Server activation (Reload): ConfigHandler.GetDefaultServer -> CoreConfigContextBuilder.BuildAll (main context + optional pre-socks) -> NoticeManager.NotifyValidatorResult (early-return on errors) -> CoreManager.LoadCore: CoreConfigHandler.GenerateClientConfig writes binConfigs/config.json -> CoreStop + 100ms delay -> Windows-only RemoveTunDevice when TUN -> CoreStart/RunProcess -> WaitForProxyPort does SOCKS5 greeting handshake with the pre-socks node -> CoreStartPreService writes and launches configPre.json -> SysProxyHandler.UpdateSysProxy -> StatusBarViewModel.TestServerAvailability -> SetSpeedTestResult -> optional ClashProxiesViewModel.ProxiesReload. — [v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs#L664-774](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs#L664-L774) [v2rayN/ServiceLib/Manager/CoreManager.cs#L65-212](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Manager/CoreManager.cs#L65-L212) [v2rayN/ServiceLib/Manager/CoreManager.cs#L219-286](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Manager/CoreManager.cs#L219-L286)
- Reload concurrency is guarded by a SemaphoreSlim: a concurrent Reload sets _hasNextReloadJob and returns; after the semaphore is released a queued reload runs once — so rapid toggles collapse to at most one extra pass. — [v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs#L661-751](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs#L661-L751)
- Subscription update: MainWindowViewModel.UpdateSubscriptionProcess -> Task.Run(SubscriptionHandler.UpdateProcess) -> per SubItem: IsValidSubscription (http/https only, id match, enabled) -> CreateDownloadHandler parses custom request headers (throws FormatException if invalid) -> DownloadSubscriptionContent via proxy then direct fallback -> ProcessDownloadResult -> ConfigHandler.AddBatchServers(isSub=true) -> UpdateTaskHandler refreshes the list and Reload()s only if the active profile's IndexId changed or belongs to the updated group. — [v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs#L574-577](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs#L574-L577) [v2rayN/ServiceLib/Handler/SubscriptionHandler.cs#L5-229](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Handler/SubscriptionHandler.cs#L5-L229) [v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs#L359-387](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs#L359-L387)
- Clipboard/QR/image import: AddServerViaClipboardAsync reads text via ReadTextFromClipboardInteraction -> ConfigHandler.AddBatchServers -> RefreshSubscriptions + RefreshServersDispatcherAsync; scan flows go through ScanScreenInteraction/BrowseImageFileInteraction -> QRCodeUtils.ParseBarcode -> AddScanResultAsync -> same AddBatchServers path. — [v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs#L484-559](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs#L484-L559)
- Core process launch (normal): RunProcess -> CoreInfoManager.GetCoreExecFile finds first existing exe under bin/<coretype>/ -> RunProcessNormal formats Arguments template with the config path and env vars (e.g. XRAY_LOCATION_ASSET) -> ProcessService.StartAsync redirects stdout/stderr to UpdateFunc -> AddProcessJob attaches the handle to a Windows Job Object. — [v2rayN/ServiceLib/Manager/CoreManager.cs#L305-376](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Manager/CoreManager.cs#L305-L376) [v2rayN/ServiceLib/Manager/CoreInfoManager.cs#L32-51](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Manager/CoreInfoManager.cs#L32-L51) [v2rayN/ServiceLib/Services/ProcessService.cs#L56-71](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Services/ProcessService.cs#L56-L71)
- Core launch with TUN on Linux/macOS: ShouldRunAsSudo(isTunLaunch && coreType in {sing_box, mihomo, Xray} && IsNonWindows) -> CoreAdminManager.RunProcessAsLinuxSudo writes run_as_sudo.sh containing 'exec sudo -S -- env ... <core> <args>' -> ProcessService with redirectInput writes AppManager.LinuxSudoPwd to stdin -> CoreStop later calls KillProcessAsLinuxSudo which runs embedded kill_as_sudo_*_sh via bash -c with the saved PID and sudo -S. — [v2rayN/ServiceLib/Manager/CoreManager.cs#L298-322](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Manager/CoreManager.cs#L298-L322) [v2rayN/ServiceLib/Manager/CoreAdminManager.cs#L32-104](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Manager/CoreAdminManager.cs#L32-L104)
- Self-update: CheckUpdateGuiN -> CheckUpdateAsync -> GetRemoteVersion (GitHub releases API when preRelease/LockedMaxVersion, else /releases/latest redirect) -> ParseDownloadUrl picks arch URL and compares versions -> DownloadService.DownloadFileAsync to guiTemps/<guid> -> UpdateCompleted invokes updateFunc with UrlEncoded filename -> AmazTool launched with the path -> UpgradeApp.Upgrade kills v2rayN processes, extracts zip entries (strip first path segment, skip existing bin/*), restarts app. — [v2rayN/ServiceLib/Services/UpdateService.cs#L9-100](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Services/UpdateService.cs#L9-L100) [v2rayN/ServiceLib/Services/UpdateService.cs#L153-220](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Services/UpdateService.cs#L153-L220) [v2rayN/AmazTool/UpgradeApp.cs#L21-107](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/AmazTool/UpgradeApp.cs#L21-L107)
- App exit: AppManager.AppExitAsync -> SysProxyHandler.UpdateSysProxy(config, forceDisable:true) restores/clears system proxy -> AppEvents.AppExitRequested.Publish + 50ms wait -> ConfigHandler.SaveConfig -> ProfileExManager.SaveTo -> StatisticsManager.SaveTo -> CoreManager.CoreStop (sudo kill if needed, then ProcessService.StopAsync/Dispose) -> StatisticsManager.Close -> optionally Shutdown() publishing ShutdownRequested. — [v2rayN/ServiceLib/Manager/AppManager.cs#L122-159](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Manager/AppManager.cs#L122-L159) [v2rayN/ServiceLib/Manager/CoreManager.cs#L147-175](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Manager/CoreManager.cs#L147-L175)
- Reboot-as-admin path: RebootAsAdminCmd -> AppManager.RebootAsAdmin -> ProcUtils.RebootAsAdmin starts the exe with Verb='runas' and arg 'rebootas' -> AppExitAsync(true) shuts down; on the new process OnStartup sees Global.RebootAs in args and bypasses the single-instance exit so the elevated instance takes over. — [v2rayN/ServiceLib/Manager/AppManager.cs#L155-159](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Manager/AppManager.cs#L155-L159) [v2rayN/ServiceLib/Common/ProcUtils.cs#L49-67](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Common/ProcUtils.cs#L49-L67) [v2rayN/v2rayN/App.xaml.cs#L26-35](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/v2rayN/App.xaml.cs#L26-L35)

## Testing signals

Tests live exclusively in ServiceLib.Tests and use TUnit (not xUnit/NUnit) with TUnit.Assertions.Should fluent assertions. Coverage concentrates on the pure/parsing layers: Fmt share-URI round-trips, V2ray and sing-box config generation, CoreConfigContextBuilder behavior, HTTP request-header parsing, a regex guard, and CoreManager helpers. The CI test workflow runs 'dotnet test' on ubuntu-latest against .NET 10.x and publishes TRX results to PRs. There are no UI tests for either shell and no tests over SQLite persistence, process spawning, sysproxy, or the updater.
- ServiceLib.Tests targets net10.0 with TUnit + TUnit.Assertions.Should packages and ProjectReference to ServiceLib; tests use [Test] attributes and 'await x.Should().BeEqualTo(...)' assertions. — [v2rayN/ServiceLib.Tests/ServiceLib.Tests.csproj](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib.Tests/ServiceLib.Tests.csproj) [v2rayN/ServiceLib.Tests/Fmt/FmtHandlerTests.cs#L24-43](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib.Tests/Fmt/FmtHandlerTests.cs#L24-L43)
- CI test job (.github/workflows/test.yml) runs only when ServiceLib, ServiceLib.UdpTest, ServiceLib.Tests, Directory.Build.*, Directory.Packages.props, global.json or the workflow itself change — edits confined to v2rayN/ or v2rayN.Desktop/ UI code never trigger tests. — [.github/workflows/test.yml#L3-45](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/.github/workflows/test.yml#L3-L45)
- FmtHandlerTests contains a coverage contract: ShareProfileFactories must match Global.ProtocolShares keys exactly, then every exportable protocol is round-tripped ExportThenImport asserting ConfigType/Address/Port survive — so adding a new protocol without a test fails the suite. — [v2rayN/ServiceLib.Tests/Fmt/FmtHandlerTests.cs#L10-43](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib.Tests/Fmt/FmtHandlerTests.cs#L10-L43) [v2rayN/ServiceLib/Global.cs#L238-250](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Global.cs#L238-L250)
- Core config generation has dedicated large suites: CoreConfigV2rayServiceTests (~716 lines) and CoreConfigSingboxServiceTests (~755 lines) plus CoreConfigContextBuilderTests and a shared CoreConfigTestFactory for building test configs. — [v2rayN/ServiceLib.Tests/CoreConfig/V2ray/CoreConfigV2rayServiceTests.cs](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib.Tests/CoreConfig/V2ray/CoreConfigV2rayServiceTests.cs) [v2rayN/ServiceLib.Tests/CoreConfig/Singbox/CoreConfigSingboxServiceTests.cs](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib.Tests/CoreConfig/Singbox/CoreConfigSingboxServiceTests.cs) [v2rayN/ServiceLib.Tests/CoreConfig/Context/CoreConfigContextBuilderTests.cs](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib.Tests/CoreConfig/Context/CoreConfigContextBuilderTests.cs) [v2rayN/ServiceLib.Tests/CoreConfig/CoreConfigTestFactory.cs](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib.Tests/CoreConfig/CoreConfigTestFactory.cs)
- Other covered units: per-Fmt tests (InnerFmtTests, WireguardFmtTests, ShareUriQueryTests, Hysteria2FmtTests, HyRealmTests), HttpRequestHeadersHelperTests, RegexGuardTests, DownloadServiceHeadersTests and CoreManagerTests (which can exercise ShouldRunAsSudo logic). — [v2rayN/ServiceLib.Tests/Fmt/InnerFmtTests.cs](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib.Tests/Fmt/InnerFmtTests.cs) [v2rayN/ServiceLib.Tests/Helper/HttpRequestHeadersHelperTests.cs](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib.Tests/Helper/HttpRequestHeadersHelperTests.cs) [v2rayN/ServiceLib.Tests/Helper/RegexGuardTests.cs](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib.Tests/Helper/RegexGuardTests.cs) [v2rayN/ServiceLib.Tests/Services/DownloadServiceHeadersTests.cs](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib.Tests/Services/DownloadServiceHeadersTests.cs) [v2rayN/ServiceLib.Tests/Manager/CoreManagerTests.cs](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib.Tests/Manager/CoreManagerTests.cs)
- Untested surfaces include both UI shells (no test project references v2rayN or v2rayN.Desktop), SQLite migrations in AppManager.MigrateProfileExtra*, SysProxyHandler/PacManager OS integration, ProcessService/WindowsJobService process lifecycle, AmazTool, and UpdateService download/upgrade — none have test files. — [v2rayN/ServiceLib.Tests/ServiceLib.Tests.csproj](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib.Tests/ServiceLib.Tests.csproj) [v2rayN/ServiceLib/Manager/AppManager.cs#L328-475](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Manager/AppManager.cs#L328-L475) [v2rayN/AmazTool/UpgradeApp.cs](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/AmazTool/UpgradeApp.cs)
- ServiceLib.UdpTest is a referenced library project (not a test suite despite the name) providing Socks5UdpChannel used by SpeedtestService for UDP testing. — [v2rayN/ServiceLib/ServiceLib.csproj#L100-102](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/ServiceLib.csproj#L100-L102) [v2rayN/ServiceLib.UdpTest/Socks5UdpChannel.cs](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib.UdpTest/Socks5UdpChannel.cs) [v2rayN/ServiceLib/Services/SpeedtestService.cs#L1](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Services/SpeedtestService.cs#L1)
- Test results are published back to PRs as TRX via EnricoMi/publish-unit-test-result-action with comment_mode 'failures'; the test command is 'dotnet test --project ./v2rayN/ServiceLib.Tests -c Release'. — [.github/workflows/test.yml#L44-52](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/.github/workflows/test.yml#L44-L52)
- No lint/static-analysis workflow exists beyond a CodeFactor badge in the README; there is no code-coverage collection in the test workflow (no coverlet/collector packages in the test csproj). — [README.md#L5](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/README.md#L5) [v2rayN/ServiceLib.Tests/ServiceLib.Tests.csproj](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib.Tests/ServiceLib.Tests.csproj) [.github/workflows/test.yml](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/.github/workflows/test.yml)

## Risk map

The biggest risk surface is OS-level mutation: registry/WinInet proxy changes, autostart registration, sudo shell scripts carrying a GUI-entered sudo password, and a self-updater that extracts a network-downloaded zip over the install directory. SQL built by string interpolation in ProfileModels is only partially sanitized. The GlobalHotKeys submodule and ServiceLib.UdpTest project reference are easy build breakers. Heavy catch-all swallowing and a 3000-line ConfigHandler concentrate blast radius, and the ProfileItem ConfigVersion migration chain is delicate and untested.
- Linux/macOS TUN mode stores the user's sudo password in memory (AppManager.LinuxSudoPwd) and pipes it into 'sudo -S' via generated run_as_sudo.sh / kill_as_sudo.sh scripts under binConfigs — a plaintext credential-in-shell path that also leaves a world-visible generated script. — [v2rayN/ServiceLib/Manager/AppManager.cs#L33](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Manager/AppManager.cs#L33) [v2rayN/ServiceLib/Manager/CoreAdminManager.cs#L32-104](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Manager/CoreAdminManager.cs#L32-L104)
- AppManager.ProfileModels builds raw SQL with interpolated subid and a user-supplied filter; the only sanitization is stripping single quotes from filter, so other injection-adjacent characters pass through into SQLite QueryAsync. — [v2rayN/ServiceLib/Manager/AppManager.cs#L202-230](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Manager/AppManager.cs#L202-L230)
- Self-update downloads a zip from GitHub over (optionally proxied) HTTPS and AmazTool extracts it over the install dir with only a 'bin/' skip rule — no in-app signature/hash verification is performed on the archive (GPG verification is offered manually per README). — [v2rayN/AmazTool/UpgradeApp.cs#L41-107](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/AmazTool/UpgradeApp.cs#L41-L107) [v2rayN/ServiceLib/Services/UpdateService.cs#L9-47](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Services/UpdateService.cs#L9-L47) [README.md#L57-68](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/README.md#L57-L68)
- AmazTool.Upgrade kills every process named 'v2rayN' whose MainModule path starts with the app dir, then extracts entries by stripping the first path segment — a maliciously crafted zip entry name could still resolve outside intended paths (no explicit traversal check on entry.FullName). (inferred) — [v2rayN/AmazTool/UpgradeApp.cs#L21-90](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/AmazTool/UpgradeApp.cs#L21-L90)
- ProxySettingWindows uses raw WinInet P/Invoke with manually allocated marshaled buffers (five 'remember to deallocate' sites); the code frees them but any early-return/exception path between alloc and free leaks unmanaged memory, and failure falls back to direct registry writes under 'HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings'. — [v2rayN/ServiceLib/Handler/SysProxy/ProxySettingWindows.cs#L8-34](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Handler/SysProxy/ProxySettingWindows.cs#L8-L34) [v2rayN/ServiceLib/Handler/SysProxy/ProxySettingWindows.cs#L79-198](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Handler/SysProxy/ProxySettingWindows.cs#L79-L198)
- Custom server configs read arbitrary file paths: node.Address is used directly as a filesystem path (GenerateClientCustomConfig copies it; RegisterSingleNodeAsync reads it into CustomOutboundContent) — a crafted profile could point at any readable file. — [v2rayN/ServiceLib/Handler/CoreConfigHandler.cs#L44-91](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Handler/CoreConfigHandler.cs#L44-L91) [v2rayN/ServiceLib/Handler/Builder/CoreConfigContextBuilder.cs#L343-363](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Handler/Builder/CoreConfigContextBuilder.cs#L343-L363)
- PacManager.RunListener creates a TcpListener on the PAC port and answers every connection with the PAC bytes without request parsing or binding check — the listener inherits default binding behavior and any local (or LAN, if bound broadly) client can fetch the PAC file. — [v2rayN/ServiceLib/Manager/PacManager.cs#L60-93](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Manager/PacManager.cs#L60-L93)
- The ProfileItem schema migration chain (ConfigVersion <3 -> MigrateProfileExtraV2ToV3 rewriting per-protocol fields into ProtocolExtra JSON; =3 -> MigrateProfileTransportV3ToV4 reshaping transport fields) runs synchronously-blocked at startup and has no test coverage — a bad batch permanently corrupts stored profiles. — [v2rayN/ServiceLib/Manager/AppManager.cs#L107-113](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Manager/AppManager.cs#L107-L113) [v2rayN/ServiceLib/Manager/AppManager.cs#L328-475](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Manager/AppManager.cs#L328-L475)
- ConfigHandler.cs is ~3000 lines containing config load/save, profile CRUD, batch import of five formats, sub/group management, routing/DNS/template init and regional presets — a single static god-class where a mistake in one feature can break unrelated persistence. — [v2rayN/ServiceLib/Handler/ConfigHandler.cs](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Handler/ConfigHandler.cs)
- v2rayN/GlobalHotKeys is a git submodule that is empty in a non-recursive clone; v2rayN.Desktop.csproj references GlobalHotKeys/src/GlobalHotKeys/GlobalHotKeys.csproj so the Desktop build fails outright without 'submodules: recursive' (which the workflows do set). — [.gitmodules](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/.gitmodules) [v2rayN/v2rayN.Desktop/v2rayN.Desktop.csproj#L40](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/v2rayN.Desktop/v2rayN.Desktop.csproj#L40) [.github/workflows/build.yml](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/.github/workflows/build.yml)
- Exceptions are swallowed broadly: AppExitAsync wraps the whole shutdown in catch{}, ProcessService.StopAsync/Dispose have nested empty catches, SysProxyHandler.UpdateSysProxy catches and returns true on failure — masking cleanup failures that leave cores running or the system proxy stuck. — [v2rayN/ServiceLib/Manager/AppManager.cs#L122-148](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Manager/AppManager.cs#L122-L148) [v2rayN/ServiceLib/Services/ProcessService.cs#L73-117](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Services/ProcessService.cs#L73-L117) [v2rayN/ServiceLib/Handler/SysProxy/SysProxyHandler.cs#L62-66](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Handler/SysProxy/SysProxyHandler.cs#L62-L66)
- Subscription download supports http:// URLs and an optional third-party subconvert proxy chain; subscription content is base64-decoded and imported wholesale into the profile DB — a hostile/compromised subscription can inject arbitrary outbound definitions or Custom nodes pointing to local files. (inferred) — [v2rayN/ServiceLib/Handler/SubscriptionHandler.cs#L75-195](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Handler/SubscriptionHandler.cs#L75-L195) [v2rayN/ServiceLib/Handler/ConfigHandler.cs#L2037-2135](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Handler/ConfigHandler.cs#L2037-L2135)
- WPF OnExit force-kills the current process (Process.GetCurrentProcess().Kill()) after logging — any async persistence still in flight at that point is truncated. — [v2rayN/v2rayN/App.xaml.cs#L82-87](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/v2rayN/App.xaml.cs#L82-L87)
- Timeouts for outbound fetches are asymmetric and short (DirectFetch 10s, ProxyFetch 30s, LocalFetch 5s); silent empty-string failures propagate as 'IsNullOrEmpty -> return null' so download errors frequently surface only as a generic failure message. (inferred) — [v2rayN/ServiceLib/Global.cs#L100-104](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Global.cs#L100-L104) [v2rayN/ServiceLib/Services/DownloadService.cs#L195-251](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Services/DownloadService.cs#L195-L251)

## Extension points

Extension happens at four levels: (1) protocol support via a new *Fmt class plus entries in FmtHandler's switch/prefix dispatch, Global.ProtocolShares/ProtocolTypes, EConfigType and the two SupportConfigType sets; (2) new cores via a CoreInfo record in CoreInfoManager.InitCoreInfo and Global.CoreUrls; (3) core config generation via the per-concern services under Services/CoreConfig/{Singbox,V2ray} plus embedded Sample templates and FullConfigTemplateItem overrides; (4) UI via shared ViewModels + per-shell SimpleViewLocator view resolution and AppEvents channels. Group-type profiles (PolicyGroup/ProxyChain) and subscription prev/next chains are the built-in composition mechanism.
- Adding a share-URI protocol requires: a new Fmt class in ServiceLib/Handler/Fmt implementing Resolve/ToUri, a switch arm in FmtHandler.ResolveConfig and GetShareUri, and entries in Global.ProtocolShares/ProtocolTypes — FmtHandlerTests enforces the ProtocolShares/factory parity so a missing test fails. — [v2rayN/ServiceLib/Handler/Fmt/FmtHandler.cs#L7-110](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Handler/Fmt/FmtHandler.cs#L7-L110) [v2rayN/ServiceLib/Global.cs#L238-265](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Global.cs#L238-L265) [v2rayN/ServiceLib.Tests/Fmt/FmtHandlerTests.cs#L10-43](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib.Tests/Fmt/FmtHandlerTests.cs#L10-L43)
- New proxy cores plug into CoreInfoManager.InitCoreInfo's list of CoreInfo records (CoreExes, Arguments template with {0}=config path, Environment dict, VersionArg/Match for version detection, per-arch DownloadUrl* fields for auto-update). — [v2rayN/ServiceLib/Manager/CoreInfoManager.cs#L104-293](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Manager/CoreInfoManager.cs#L104-L293)
- Core config generation is decomposed so a single concern (e.g. DNS) can be changed in isolation: CoreConfigSingboxService composes SingboxLog/Inbound/Outbound/Routing/Dns/Ruleset/Statistic/ConfigTemplate services, mirrored by the V2ray set — each is independently instantiable against a CoreConfigContext. — [v2rayN/ServiceLib/Services/CoreConfig/Singbox/CoreConfigSingboxService.cs](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Services/CoreConfig/Singbox/CoreConfigSingboxService.cs) [v2rayN/ServiceLib/Services/CoreConfig/V2ray/CoreConfigV2rayService.cs](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Services/CoreConfig/V2ray/CoreConfigV2rayService.cs)
- Embedded Sample resources (ServiceLib/Sample/* registered in ServiceLib.csproj and read via EmbedUtils.GetEmbedText) are the template extension point for default client configs, inbounds, TUN blocks, DNS presets, PAC script, autostart units, sudo/kill scripts, routing presets and root-cert PEMs. — [v2rayN/ServiceLib/ServiceLib.csproj#L28-57](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/ServiceLib.csproj#L28-L57) [v2rayN/ServiceLib/Common/EmbedUtils.cs](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Common/EmbedUtils.cs) [v2rayN/ServiceLib/Global.cs#L18-44](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Global.cs#L18-L44)
- Users/agents can override generated config wholesale per core type through FullConfigTemplateItem rows fetched in CoreConfigContextBuilder (GetFullConfigTemplateItem) and through per-node EConfigType.Custom/Outbound types that inject literal config/outbound files. — [v2rayN/ServiceLib/Handler/Builder/CoreConfigContextBuilder.cs#L44](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Handler/Builder/CoreConfigContextBuilder.cs#L44) [v2rayN/ServiceLib/Handler/CoreConfigHandler.cs#L16-31](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Handler/CoreConfigHandler.cs#L16-L31) [v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs#L35-36](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs#L35-L36)
- Routing/DNS customization is data-driven: RoutingItem.RuleSet JSON -> RulesItem list where any OutboundTag not in Global.OutboundTags resolves to a profile by remarks and is injected into AllProxiesMap — enabling per-rule per-node routing without code changes; DNSItem rows store per-core normal/tun DNS JSON. — [v2rayN/ServiceLib/Handler/Builder/CoreConfigContextBuilder.cs#L63-98](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Handler/Builder/CoreConfigContextBuilder.cs#L63-L98) [v2rayN/ServiceLib/Models/Entities/RulesItem.cs](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Models/Entities/RulesItem.cs)
- Group-type profiles compose nodes without new code: EConfigType.PolicyGroup/ProxyChain nodes carry ProtocolExtraItem.ChildItems, traversed recursively by CoreConfigContextBuilder.TraverseGroupNodeAsync with cycle detection via ancestor/visited sets, and GroupProfileManager resolves child IndexIds. — [v2rayN/ServiceLib/Handler/Builder/CoreConfigContextBuilder.cs#L418-509](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Handler/Builder/CoreConfigContextBuilder.cs#L418-L509) [v2rayN/ServiceLib/Manager/GroupProfileManager.cs](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Manager/GroupProfileManager.cs)
- Subscriptions gain proxy chains declaratively: SubItem.PrevProfile/NextProfile name profiles by remarks which BuildSubscriptionChainNodeAsync wraps into a virtual 'inner-<guid>' ProxyChain node around the selected node. — [v2rayN/ServiceLib/Handler/Builder/CoreConfigContextBuilder.cs#L252-315](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Handler/Builder/CoreConfigContextBuilder.cs#L252-L315) [v2rayN/ServiceLib/Models/Entities/SubItem.cs](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Models/Entities/SubItem.cs)
- New UI screens follow the ReactiveUI + SimpleViewLocator convention: create a ViewModel in ServiceLib/ViewModels, a View per shell (v2rayN/Views/*.xaml, v2rayN.Desktop/Views/*.axaml), wire commands through ReactiveCommand.CreateFromTask, dialogs through AppManager.Instance.WindowDialog.ShowDialogAsync, and notifications through NoticeManager. — [v2rayN/v2rayN/Common/SimpleViewLocator.cs](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/v2rayN/Common/SimpleViewLocator.cs) [v2rayN/v2rayN.Desktop/Common/SimpleViewLocator.cs](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/v2rayN.Desktop/Common/SimpleViewLocator.cs) [v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs#L565-631](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs#L565-L631)
- AppEvents/EventChannel is the extension point for cross-VM signaling — new events are added as static EventChannel<T> fields and consumed via .AsObservable().ObserveOn(RxSchedulers.MainThreadScheduler).SubscribeAsync. — [v2rayN/ServiceLib/Events/AppEvents.cs](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Events/AppEvents.cs) [v2rayN/ServiceLib/Events/EventChannel.cs](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Events/EventChannel.cs) [v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs#L257-315](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs#L257-L315)
- New user-facing strings go into ServiceLib/Resx/ResUI.resx (plus satellites); new global option lists (e.g. fingerprints, networks, mux protocols, subconvert endpoints, geo sources) are centralized as public static lists in Global.cs. — [v2rayN/ServiceLib/Resx/ResUI.resx](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Resx/ResUI.resx) [v2rayN/ServiceLib/Global.cs#L330-419](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Global.cs#L330-L419)
- UpdateService's download-source indirection (ConstItem.GeoSourceUrl/SrsSourceUrl/SubConvertUrl with Global.* defaults) is a designed extension seam for alternate geo/rule-set/subconvert providers, including the Russia and Iran preset sources. — [v2rayN/ServiceLib/Services/UpdateService.cs#L369-413](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Services/UpdateService.cs#L369-L413) [v2rayN/ServiceLib/Global.cs#L185-211](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Global.cs#L185-L211)

## Contributor handoff

All logic changes belong in ServiceLib; the two shells should stay thin views over shared ViewModels. Read the order below before editing: Global.cs constants, ConfigHandler, CoreConfigContextBuilder, the relevant CoreConfig service, FmtHandler, and the target ViewModel. Build requires .NET 10 SDK (global.json) and a recursive submodule checkout for the Desktop project; test via 'dotnet test v2rayN/ServiceLib.Tests'. Respect the ConfigVersion migration chain, the {0} argument-template convention, embedded Sample resource names, and the LoadCore ordering (main config -> stop -> remove tun -> start -> wait for pre-socks port -> start pre-service).
- Read-first files for any change: v2rayN/ServiceLib/Global.cs (all constants/tag names/URL templates), ServiceLib/Handler/ConfigHandler.cs (config + profile persistence), ServiceLib/Manager/AppManager.cs (singleton state + SQLite query helpers), and ServiceLib/Handler/Builder/CoreConfigContextBuilder.cs (node resolution pipeline). — [v2rayN/ServiceLib/Global.cs](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Global.cs) [v2rayN/ServiceLib/Handler/ConfigHandler.cs](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Handler/ConfigHandler.cs) [v2rayN/ServiceLib/Manager/AppManager.cs](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Manager/AppManager.cs) [v2rayN/ServiceLib/Handler/Builder/CoreConfigContextBuilder.cs](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Handler/Builder/CoreConfigContextBuilder.cs)
- Conventions: managers are 'public sealed class X { private static readonly Lazy<X> _instance = new(() => new()); public static X Instance => _instance.Value; }'; handlers are static classes; string checks use IsNullOrEmpty/IsNotEmpty/TrimEx extension methods; user-facing text must come from ResUI.* generated from resx. — [v2rayN/ServiceLib/Manager/AppManager.cs#L7-11](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Manager/AppManager.cs#L7-L11) [v2rayN/ServiceLib/Handler/Fmt/FmtHandler.cs#L41-42](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Handler/Fmt/FmtHandler.cs#L41-L42) [v2rayN/ServiceLib/Common/Extension.cs](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Common/Extension.cs)
- ReactiveUI conventions: ViewModels derive from MyReactiveObject, commands via ReactiveCommand.CreateFromTask, properties via [Reactive] partial properties from ReactiveUI.SourceGenerators, and UI-marshaling via RxSchedulers.MainThreadScheduler.Schedule or Signal.FromAsync(...).SubscribeOn(...).ToTask(). — [v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs#L71-84](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs#L71-L84) [v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs#L99-236](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs#L99-L236) [v2rayN/Directory.Packages.props](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/Directory.Packages.props)
- Never persist mutable config references into CoreConfigContext — it is an immutable record snapshot so the generated config and the process launch mode (e.g. sudo/TUN decision in ShouldRunAsSudo) agree even if settings change mid-reload; clone via JsonUtils.DeepCopy before mutating. — [v2rayN/ServiceLib/Manager/CoreManager.cs#L292-303](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Manager/CoreManager.cs#L292-L303) [v2rayN/ServiceLib/Handler/Builder/CoreConfigContextBuilder.cs#L100-135](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Handler/Builder/CoreConfigContextBuilder.cs#L100-L135)
- ProfileItem carries legacy flat fields plus JSON 'extra' blobs accessed via GetProtocolExtra/SetProtocolExtra/GetTransportExtra/SetTransportExtra; new fields must respect the ConfigVersion migration ladder (v2->v3 moved protocol extras, v3->v4 moved transport extras) — add a new migration step rather than reparsing inline. — [v2rayN/ServiceLib/Manager/AppManager.cs#L337-475](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Manager/AppManager.cs#L337-L475) [v2rayN/ServiceLib/Models/Entities/ProfileItem.cs](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Models/Entities/ProfileItem.cs) [v2rayN/ServiceLib/Models/Entities/ProtocolExtraItem.cs](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Models/Entities/ProtocolExtraItem.cs)
- Build commands: 'dotnet build v2rayN/v2rayN.slnx' or per-project; WPF project targets net10.0-windows10.0.19041.0 and needs Windows or EnableWindowsTargeting; Desktop needs 'git submodule update --init --recursive' first or GlobalHotKeys.csproj is missing; tests: 'dotnet test v2rayN/ServiceLib.Tests -c Release'. — [v2rayN/v2rayN/v2rayN.csproj#L5](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/v2rayN/v2rayN.csproj#L5) [.github/workflows/build.yml](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/.github/workflows/build.yml) [.gitmodules](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/.gitmodules) [.github/workflows/test.yml#L45](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/.github/workflows/test.yml#L45)
- When changing share-URI parse/export, update both directions (Fmt Resolve + ToUri) and add a factory in FmtHandlerTests.ShareProfileFactories — the suite asserts parity with Global.ProtocolShares so a one-sided change fails CI. — [v2rayN/ServiceLib.Tests/Fmt/FmtHandlerTests.cs#L10-43](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib.Tests/Fmt/FmtHandlerTests.cs#L10-L43) [v2rayN/ServiceLib/Handler/Fmt/FmtHandler.cs](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Handler/Fmt/FmtHandler.cs)
- Safe edit boundaries: new ViewModels/services in ServiceLib; per-shell code limited to Views, Converters, platform utils (WindowsUtils, MacAppUtils) and Managers like HotkeyManager/WindowDialog; do not add UI dependencies to ServiceLib (it must stay runnable by the test project on Linux CI). (inferred) — [v2rayN/v2rayN/Manager/HotkeyManager.cs](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/v2rayN/Manager/HotkeyManager.cs) [v2rayN/ServiceLib/ServiceLib.csproj](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/ServiceLib.csproj) [.github/workflows/test.yml#L33](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/.github/workflows/test.yml#L33)
- Known traps: generated config filenames are constants (config.json, configPre.json, configTest{0}.json) under binConfigs — TaskManager deletes files matching 'Test' older than 1h there; ports are derived as socksPort + (int)EInboundProtocol; sing-box updates are version-locked at <=1.14.x. — [v2rayN/ServiceLib/Global.cs#L13-15](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Global.cs#L13-L15) [v2rayN/ServiceLib/Manager/TaskManager.cs#L61-63](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Manager/TaskManager.cs#L61-L63) [v2rayN/ServiceLib/Manager/AppManager.cs#L165-169](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Manager/AppManager.cs#L165-L169) [v2rayN/ServiceLib/Manager/CoreInfoManager.cs#L232](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Manager/CoreInfoManager.cs#L232)
- Core exe argument strings are string.Format templates where {0} is the config path (AbsolutePath flag controls quoting via GetBinConfigPath); keep the format-string contract when adding cores and provide env vars via CoreInfo.Environment (e.g. XRAY_LOCATION_ASSET). — [v2rayN/ServiceLib/Manager/CoreManager.cs#L334-350](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Manager/CoreManager.cs#L334-L350) [v2rayN/ServiceLib/Manager/CoreInfoManager.cs#L149-171](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Manager/CoreInfoManager.cs#L149-L171)
- Logging goes through Logging.SaveLog (NLog, guiLogs/) gated by config.GuiItem.EnableLog set in InitComponents; updateFunc callbacks carry (notify:bool, msg) where notify=true means surface a snackbar via NoticeManager.Enqueue. — [v2rayN/ServiceLib/Manager/AppManager.cs#L98-113](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Manager/AppManager.cs#L98-L113) [v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs#L349-357](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs#L349-L357) [v2rayN/ServiceLib/Common/Logging.cs](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/Common/Logging.cs)
- Do not call ViewModel constructors expecting side effects in design mode: MainWindowViewModel.Init short-circuits on DesignMode, and design-time data lives in v2rayN.Desktop/DesignData; both shells share the same ServiceLib ViewModels so VM changes must remain toolkit-agnostic (use WindowDialog/Interactions, not dialogs). — [v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs#L319-326](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs#L319-L326) [v2rayN/v2rayN.Desktop/DesignData](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/v2rayN.Desktop/DesignData) [v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs#L5-8](https://github.com/2dust/v2rayN/blob/28139853a8087d81e1ac7ffaf7013b937958f5d2/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs#L5-L8)

Source repository: https://github.com/2dust/v2rayN
Full evidence corpus: https://github.com/Codevetter/codevetter/tree/main/benchmarks/repo-unpacks/v2rayn.json

## Public product links

- [CodeVetter](https://codevetter.com/)
- [Download](https://codevetter.com/download)
- [Documentation](https://codevetter.com/docs/)
- [Source](https://github.com/Codevetter/codevetter)
