Repository navigation
EN Code Structure
dongle edited this page Oct 18, 2026
·
6 revisions
Repository layout for contributors and downstream developers. For conventions see AGENTS.md in the repo root. FlowWeaver originates from SniShaper and shares some technical foundations (sing-tun data plane, uTLS/ECH handling), but the codebase evolves independently.
| Layer | Technology |
|---|---|
| Backend | Go 1.27+, Wails v3 bindings, module name flowweaver
|
| Frontend | React 19 + TypeScript + MUI + Vite, @iconify/react icons (Material Symbols / MDI, bundled offline) |
| Core | Mihomo-derived core process (built with the with_gvisor tag; TUN depends on gVisor); subscription-node dialing/latency tests reuse the mihomo adapter as a library |
| Packaging | Windows MSIX (PFX-signed), 12-target cross-platform CLI matrix |
FlowWeaver/
├── main.go # Entry point: Wails app assembly + tray setup
├── app/ # Go backend application layer
│ ├── app_api.go # App struct and all frontend-bound APIs
│ ├── app_subscription.go # Subscription APIs (import/switch/node selection) + node/service latency probes
│ ├── app_fragment.go # TLS fragmentation config API (get/set/reset)
│ ├── app_system.go # System proxy toggle (registry writes)
│ ├── autostart_*.go # Autostart (Windows: Task Scheduler)
│ ├── app_tray*.go # Tray: build, session watch, reshow
│ └── app_update*.go # Version check, download and self-update install
├── core/ # Core process management
│ ├── core_client.go # Core process launch, RPC client (token-authenticated)
│ ├── core_api.go # In-core RPC service (Ping/Shutdown require token match)
│ ├── core_runtime.go # Core runtime assembly (TUN, certificates, rules, subscription)
│ └── core_admin_*.go # Admin elevation
├── engine and shared feature packages proxy/ and {subscription,singtun,netiface,
│ certmanager,cfpool,dohresolver,rules,tlsfrag,sysproxy,evolution}/ # Moved to snishaper-core
├── pkg/ # Local feature packages (two)
│ ├── sysproxy/ # Thin facade over core's sysproxy.Manager
│ └── platform/ # Platform adapters (e.g. DNS restore)
├── internal/rulesguard/ # Data-guard tests for rules/config.json
├── cli/ # Headless CLI (build tag: headless)
│ ├── main.go # Entry + command dispatch
│ ├── catalog.go # Command catalog (single source of truth for help and TUI)
│ └── tui.go # Interactive panel
├── frontend/src/ # React SPA, 11 pages
│ # Dashboard, Subscription, Proxies, Rules, Routing,
│ # DNS, Evolution, Logs, Settings, About, Welcome
├── common/ # Cross-platform helpers (open file/URL, paths, crash logs)
├── rules/config.json # Site-group rules
├── config/settings.json # App settings: ports, TUN (stack/adapter name), theme & appearance mode, TLS fragmentation, Cloudflare IPs, etc.
└── Platform.md / build.md # Platform support and build matrix docs
Frontend (React HashRouter)
│ Wails bindings: EventsOn subscriptions + async Go method calls
▼
App API (app/app_api.go)
│
├──► snishaper-core/subscription Clash subscription parsing → nodes/groups/rules
├──► snishaper-core/proxy Proxy server (mode dispatch, multi-protocol dialing, subscription UDP relay)
├──► core/ Core process (TUN, gVisor stack, auto-routing, DNS hijack)
└──► snishaper-core/{certmanager,dohresolver,cfpool,gfwlist} Certificates / DoH / CF IP pool / GFWList
- Frontend state sync: the backend emits
app:state/app:state_changedevents viaemitFrontendState(); the frontend subscribes and refreshes. - Subscription activation chain:
sub use→ subscription rules load through the core'sproxy/rules_merge.goand hide the built-in rules → node selections persist to the subscription store → the core'sproxy/subscription_dialer.godials the selected node (TCP/UDP). Nodes carrying their raw Clash config go through the core'ssubscription/mihomo.govia mihomoadapter.ParseProxy(protocol/transport support identical to the Clash core; node latency tests are the core'sURLTest); nodes without raw config fall back to the hand-rolled dialers. - TLS fragmentation settings fall back across three levels: per-rule
frag> globalfragmentsection insettings.json>tlsfrag.DefaultTLSRF*presets;NumRecords=1withNumSegments>=2means TCP-only fragmentation (bytes untouched, cut point moved before the SNI). - WireGuard tunnels run on a userspace IP stack — no kernel NIC, no routing table changes; TUN UDP traffic goes through
singtun's UDP relay via the currently selected subscription node. - TUN data plane only sniffs domains for routing rebuild (reads the first packet for SNI when fake-ip reverse lookup fails) and never rewrites the ClientHello — the TLS transcript hash covers the entire handshake, and modifying bytes in transit causes bad record MAC errors. SNI masquerading happens only at the TLS endpoint (MITM outbound replay).
- Core RPC isolation: the core and its client authenticate with a one-shot token (
Ping/Shutdownboth verify it), so overlapping instances cannot kill a freshly started core.
The proxy engine comes from github.com/SnishaperTeam/snishaper-core (pinned to v0.4.0 in go.mod, no replace) instead of being forked into this repo:
- Imported directly here:
proxy,certmanager,cfpool,dohresolver,netiface,singtun,subscription,sysproxy,evolution,config,log -
tlsfrag(core'sproxy/proxy.go,proxy/tls_fragment.go) andgfwlist(core'sproxy/auto_routing.go) are used inside core and are not imported here -
pkg/sysproxyis only a thin facade: it holds one coresysproxy.Managerand forwards the original package-level helpers, so call sites are unchanged. Core'sManagerkeeps the original proxy snapshot and status cache per instance, and the sudo user is injected throughsysproxy.WithRunAsUser - Product differences are injected rather than forked:
config.Brand{Name: core.ProductName}(core.ProductName = "FlowWeaver"),proxy.WithProductName(core.ProductName), andsubscription.WithProductName(core.ProductName)at bothsubscription.NewStorecall sites - Core builds the subscription download User-Agent from
config.Brand(clash-verge/v2.0 <product>). Core v0.3.0 and earlier hardcodedFlowWeaverthere and logged every sniffed SNI at info level; since v0.4.0sysproxyandevolutionalso live in the library (each app used to carry a byte-identical copy), so this repo requires^v0.4.0 -
netifaceis per-instance: create withnetiface.NewSelector()and refresh with(*netiface.Selector).Invalidate()after TUN start/stop, replacing the old package-globalnetiface.InvalidateCache()
- Full build:
.\build_windows.ps1 -All -Silent(per-platform:-Platform windows -Arch x64 -Type cli,gui; backend/frontend only: the 3-tuple-Build windows,gui,backend, where the scope is the third element —-Build backendalone is invalid. Linux/macOS:./build.sh; 12-target matrix in build.md) - Go build check:
go build -tags with_gvisor ./... - Go tests:
go test -tags with_gvisor ./...(flowweaver/app,flowweaver/common,flowweaver/core,flowweaver/internal/rulesguard) - Go static check:
go vet -tags with_gvisor ./... - Frontend type check (authoritative):
cd frontend && ./node_modules/.bin/tsc --noEmit - Headless CLI: see the build section of the CLI Reference
- Commit messages in English, conventional-commit style (
feat:/fix:/refactor:...). - New UI copy must be added to the zh / en / ru i18n JSON files in the same change, with matching placeholders.
- Any change that alters a file's purpose or adds new files must update this Wiki (zh + EN pages) in the same change.
- Changes to security-sensitive areas (certificates, TUN, system proxy, core RPC token) require maintainer review.