CodeVetter · Repo Unpack · Data flow
2dust/v2rayN
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.
- 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).
- 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.
- 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.
- 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.
- 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).
- 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.
- 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.
- 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).
- 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.
- 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.
- 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.
- 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.