2dust/v2rayN
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.
- 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.
- 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.
- 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.
- 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).
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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).
- v2rayN/GlobalHotKeys is a git submodule (github.com/2dust/GlobalHotKeys) referenced by v2rayN.Desktop.csproj for global hotkeys; workflows checkout with submodules: 'recursive'.
- 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.