# Behavior traces — fatedier/frp

> Canonical page: https://codevetter.com/unpack/frp/behavior-traces

# Behavior traces — fatedier/frp

Scanned commit d20a232996007dfe6ab425abc0a39a3ae9a0889b (2026-09-10). Generated by codevetter codevetter 1.12.0 on 2026-09-22.

Four flows were traced end-to-end. (1) frps startup: cobra -> LoadServerConfig -> validate -> NewService wires all listeners/managers -> Run spawns per-listener goroutines. (2) frpc startup+login: cobra -> LoadClientConfigResult -> aggregator merge -> NewService -> Run -> loopLoginUntilSuccess -> controlSessionDialer.Dial -> NewControl -> ctl.Run. (3) TCP proxy request path: TCPProxy.Run listens -> user conn -> GetWorkConn -> ReqWorkConn -> client NewWorkConn -> StartWorkConn -> InWorkConn -> join to local service. (4) Admin reload: GET /api/reload -> Reload -> UpdateConfigSource -> reloadConfigFromSourcesLocked -> UpdateAllConfigurer.
- frps startup order: rootCmd.RunE -> config.LoadServerConfig (legacy INI detection -> template render -> decode -> Complete) -> security.NewUnsafeFeatures + ValidateServerConfig -> runServer -> log.InitLogger -> server.NewService -> svr.Run. — [cmd/frps/root.go#L56-127](https://github.com/fatedier/frp/blob/d20a232996007dfe6ab425abc0a39a3ae9a0889b/cmd/frps/root.go#L56-L127) [pkg/config/load.go#L295-324](https://github.com/fatedier/frp/blob/d20a232996007dfe6ab425abc0a39a3ae9a0889b/pkg/config/load.go#L295-L324)
- server.NewService order: build TLS config -> optional webServer (+metrics) -> BuildServerAuth -> construct managers (ctlManager, pxyManager, pluginManager, ResourceController with port managers) -> register HTTP plugins -> group controllers -> bind muxed TCP listener -> optional KCP/QUIC/SSH/vhost listeners -> NatHoleController. — [server/service.go#L139-363](https://github.com/fatedier/frp/blob/d20a232996007dfe6ab425abc0a39a3ae9a0889b/server/service.go#L139-L363)
- svr.Run spawns goroutines: webServer.Run, HandleListener(sshTunnelListener internal=true), HandleListener(kcp), HandleQUICListener, HandleListener(websocket), HandleListener(tls), NatHoleController.CleanWorker, sshTunnelGateway.Run, then blocks in HandleListener(main listener). — [server/service.go#L366-407](https://github.com/fatedier/frp/blob/d20a232996007dfe6ab425abc0a39a3ae9a0889b/server/service.go#L366-L407)
- Inbound conn handling: HandleListener -> handleConnection -> acceptConnection (CheckMagic v1/v2, ClientHello negotiation, read first msg) -> switch on msg.Login/msg.NewWorkConn/msg.NewVisitorConn -> RegisterControl / RegisterWorkConn / RegisterVisitorConn. — [server/service.go#L443-593](https://github.com/fatedier/frp/blob/d20a232996007dfe6ab425abc0a39a3ae9a0889b/server/service.go#L443-L593)
- Control lifecycle is generation-guarded: ControlManager.Add installs ctl as pending generation keyed by runID (replacing the old control and inheriting its runMu), Activate registers user.clientID in ClientRegistry (conflict -> error), completeLogin writes LoginResp then startLocked flips state to running and launches ctl.worker. — [server/control.go#L109-171](https://github.com/fatedier/frp/blob/d20a232996007dfe6ab425abc0a39a3ae9a0889b/server/control.go#L109-L171) [server/control.go#L175-220](https://github.com/fatedier/frp/blob/d20a232996007dfe6ab425abc0a39a3ae9a0889b/server/control.go#L175-L220) [server/control.go#L226-248](https://github.com/fatedier/frp/blob/d20a232996007dfe6ab425abc0a39a3ae9a0889b/server/control.go#L226-L248) [server/control.go#L520-527](https://github.com/fatedier/frp/blob/d20a232996007dfe6ab425abc0a39a3ae9a0889b/server/control.go#L520-L527)
- frpc startup order: runClient -> LoadClientConfigResult -> NewConfigSource.ReplaceAll -> optional NewStoreSource -> NewAggregator.Load -> Filter/Complete configurers -> ValidateAllClientConfig -> startServiceWithAggregator -> client.NewService -> svr.Run. — [cmd/frpc/sub/root.go#L122-212](https://github.com/fatedier/frp/blob/d20a232996007dfe6ab425abc0a39a3ae9a0889b/cmd/frpc/sub/root.go#L122-L212)
- client.Service.Run order: optional DNSServer override -> vnetController.Init/Run -> admin webServer -> loopLoginUntilSuccess (first login failure can exit when loginFailExit) -> keepControllerWorking reconnection loop with FastBackoff (3 fast retries/min then exponential to 20s). — [client/service.go#L226-306](https://github.com/fatedier/frp/blob/d20a232996007dfe6ab425abc0a39a3ae9a0889b/client/service.go#L226-L306)
- Client login + proxy bring-up: loopLoginUntilSuccess -> controlSessionDialer.Dial(runID) -> NewControl -> ctl.SetInWorkConnCallback -> ctl.Run -> pm.UpdateAll(proxyCfgs) sends NewProxy per proxy and vm.UpdateAll(visitorCfgs); worker starts heartbeatWorker and msgDispatcher. — [client/service.go#L308-357](https://github.com/fatedier/frp/blob/d20a232996007dfe6ab425abc0a39a3ae9a0889b/client/service.go#L308-L357) [client/control.go#L110-118](https://github.com/fatedier/frp/blob/d20a232996007dfe6ab425abc0a39a3ae9a0889b/client/control.go#L110-L118) [client/control.go#L280-292](https://github.com/fatedier/frp/blob/d20a232996007dfe6ab425abc0a39a3ae9a0889b/client/control.go#L280-L292)
- Work conn establishment on demand: server GetWorkConn drains pool else sends ReqWorkConn and waits up to UserConnTimeout; client handleReqWorkConn -> connectServer -> authenticated NewWorkConn -> server RegisterWorkConn pushes into workConnCh -> proxy.Start writes StartWorkConn back to client. — [server/control.go#L595-637](https://github.com/fatedier/frp/blob/d20a232996007dfe6ab425abc0a39a3ae9a0889b/server/control.go#L595-L637) [client/control.go#L124-162](https://github.com/fatedier/frp/blob/d20a232996007dfe6ab425abc0a39a3ae9a0889b/client/control.go#L124-L162) [server/proxy/proxy.go#L57-62](https://github.com/fatedier/frp/blob/d20a232996007dfe6ab425abc0a39a3ae9a0889b/server/proxy/proxy.go#L57-L62)
- TCP data path completion: client receives StartWorkConn -> pm.HandleWorkConn(proxyName) -> Proxy.InWorkConn -> HandleTCPWorkConnection wraps conn (limit/encrypt/compress) -> writes PROXY protocol header if configured -> dials localIP:localPort -> libio.Join for bidirectional copy. — [client/control.go#L158-162](https://github.com/fatedier/frp/blob/d20a232996007dfe6ab425abc0a39a3ae9a0889b/client/control.go#L158-L162) [client/proxy/proxy.go#L160-258](https://github.com/fatedier/frp/blob/d20a232996007dfe6ab425abc0a39a3ae9a0889b/client/proxy/proxy.go#L160-L258)
- Heartbeat: client heartbeatWorker sends authenticated msg.Ping every HeartbeatInterval with backoff and closes session when lastPong exceeds HeartbeatTimeout; server heartbeatWorker closes the Control when lastPing exceeds Transport.HeartbeatTimeout. — [client/control.go#L240-278](https://github.com/fatedier/frp/blob/d20a232996007dfe6ab425abc0a39a3ae9a0889b/client/control.go#L240-L278) [server/control.go#L639-652](https://github.com/fatedier/frp/blob/d20a232996007dfe6ab425abc0a39a3ae9a0889b/server/control.go#L639-L652)
- Admin reload trace: GET /api/reload -> apiController.Reload -> Service.UpdateConfigSource (reloadMu serialized) -> cfgSource.ReplaceAll -> reloadConfigFromSourcesLocked -> aggregator.Load -> VirtualNet requirement check -> UpdateAllConfigurer -> ctl.vm.UpdateAll + ctl.pm.UpdateAll. — [client/api_router.go#L36](https://github.com/fatedier/frp/blob/d20a232996007dfe6ab425abc0a39a3ae9a0889b/client/api_router.go#L36) [client/service.go#L385-412](https://github.com/fatedier/frp/blob/d20a232996007dfe6ab425abc0a39a3ae9a0889b/client/service.go#L385-L412) [client/service.go#L496-527](https://github.com/fatedier/frp/blob/d20a232996007dfe6ab425abc0a39a3ae9a0889b/client/service.go#L496-L527)

Every claim links to its cited files at the pinned commit. Citations identify source evidence; they do not establish runtime correctness. Independent CodeVetter scan — not reviewed by upstream maintainers.

## Public product links

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