# Data flow — fatedier/frp

> Canonical page: https://codevetter.com/unpack/frp/data-flow

# Data flow — fatedier/frp

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

Config enters as TOML/YAML/JSON (or deprecated INI), is template-rendered with environment variables, decoded into pkg/config/v1 types, completed, and validated before use. Control-plane data flows as typed msg structs (Login, NewProxy, NewWorkConn, Ping, NatHole*) over a single muxed connection, versioned by the v1/v2 wire protocol. User traffic flows: public listener on frps -> proxy -> pooled work connection -> frpc -> optional plugin -> local service, joined byte streams via libio.Join. Admin data flows through gorilla/mux routers into adminapi controllers, and reloads re-merge config via the source.Aggregator.
- Config loading: LoadFileContentWithTemplate renders Go templates exposing .Envs plus parseNumberRange helpers, then LoadConfigure tries TOML->JSON marshal, JSON detection via yaml.IsJSONBuffer, or YAML (with dot-field stripping), honoring strict mode. — [pkg/config/load.go#L84-128](https://github.com/fatedier/frp/blob/d20a232996007dfe6ab425abc0a39a3ae9a0889b/pkg/config/load.go#L84-L128) [pkg/config/load.go#L176-217](https://github.com/fatedier/frp/blob/d20a232996007dfe6ab425abc0a39a3ae9a0889b/pkg/config/load.go#L176-L217)
- Legacy INI configs are detected by a 'common' section and converted through pkg/config/legacy converters (Convert_ServerCommonConf_To_v1, Convert_ClientCommonConf_To_v1) before joining the v1 path. — [pkg/config/load.go#L65-82](https://github.com/fatedier/frp/blob/d20a232996007dfe6ab425abc0a39a3ae9a0889b/pkg/config/load.go#L65-L82) [pkg/config/load.go#L295-324](https://github.com/fatedier/frp/blob/d20a232996007dfe6ab425abc0a39a3ae9a0889b/pkg/config/load.go#L295-L324) [pkg/config/legacy](https://github.com/fatedier/frp/blob/d20a232996007dfe6ab425abc0a39a3ae9a0889b/pkg/config/legacy)
- Client proxy/visitor configs come from inline [[proxies]]/[[visitors]] plus includeConfigFiles globs (LoadAdditionalClientConfigs), dedup-checked by validateNoDuplicateNames, filtered by common.Start and enabled flags in FilterClientConfigurers. — [pkg/config/load.go#L379-402](https://github.com/fatedier/frp/blob/d20a232996007dfe6ab425abc0a39a3ae9a0889b/pkg/config/load.go#L379-L402) [pkg/config/load.go#L427-447](https://github.com/fatedier/frp/blob/d20a232996007dfe6ab425abc0a39a3ae9a0889b/pkg/config/load.go#L427-L447) [pkg/config/load.go#L465-500](https://github.com/fatedier/frp/blob/d20a232996007dfe6ab425abc0a39a3ae9a0889b/pkg/config/load.go#L465-L500) [pkg/config/load.go#L502-538](https://github.com/fatedier/frp/blob/d20a232996007dfe6ab425abc0a39a3ae9a0889b/pkg/config/load.go#L502-L538)
- Login handshake: client sends msg.Login over the control conn; server runs pluginManager.Login hooks, wraps the conn in an encrypted ReadWriter (AEAD for v2, crypto RW for v1), then RegisterControl admits and activates the Control, replying LoginResp{RunID}. — [server/service.go#L454-511](https://github.com/fatedier/frp/blob/d20a232996007dfe6ab425abc0a39a3ae9a0889b/server/service.go#L454-L511) [server/service.go#L607-621](https://github.com/fatedier/frp/blob/d20a232996007dfe6ab425abc0a39a3ae9a0889b/server/service.go#L607-L621) [server/control.go#L109-220](https://github.com/fatedier/frp/blob/d20a232996007dfe6ab425abc0a39a3ae9a0889b/server/control.go#L109-L220)
- Proxy registration: client pm.UpdateAll sends msg.NewProxy; server handleNewProxy runs NewProxy plugin hooks, builds a configurer via config.NewProxyConfigurerFromMsg, creates a Proxy through proxyFactoryRegistry, runs it (acquiring ports/vhost routes) and answers NewProxyResp. — [server/control.go#L736-755](https://github.com/fatedier/frp/blob/d20a232996007dfe6ab425abc0a39a3ae9a0889b/server/control.go#L736-L755) [pkg/config/load.go#L278-293](https://github.com/fatedier/frp/blob/d20a232996007dfe6ab425abc0a39a3ae9a0889b/pkg/config/load.go#L278-L293) [server/proxy/proxy.go#L43-47](https://github.com/fatedier/frp/blob/d20a232996007dfe6ab425abc0a39a3ae9a0889b/server/proxy/proxy.go#L43-L47) [server/proxy/tcp.go#L49-89](https://github.com/fatedier/frp/blob/d20a232996007dfe6ab425abc0a39a3ae9a0889b/server/proxy/tcp.go#L49-L89)
- Work connections are pooled: server keeps workConnCh buffered at poolCount+10, sends msg.ReqWorkConn on demand; client handleReqWorkConn dials a fresh conn, writes authenticated NewWorkConn, waits for StartWorkConn, then hands the conn to the owning proxy. — [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/control.go#L49](https://github.com/fatedier/frp/blob/d20a232996007dfe6ab425abc0a39a3ae9a0889b/server/control.go#L49)
- TCP user traffic path on frpc: BaseProxy.InWorkConn -> HandleTCPWorkConnection -> wrapWorkConn (limiter -> encryption -> compression) -> optional proxyPlugin.Handle or dial localIP:localPort -> libio.Join(localConn, remote). — [client/proxy/proxy.go#L160-167](https://github.com/fatedier/frp/blob/d20a232996007dfe6ab425abc0a39a3ae9a0889b/client/proxy/proxy.go#L160-L167) [client/proxy/proxy.go#L170-258](https://github.com/fatedier/frp/blob/d20a232996007dfe6ab425abc0a39a3ae9a0889b/client/proxy/proxy.go#L170-L258)
- HTTP(S) traffic is routed by hostname through vhost.Routers into the matching proxy; frps can share the bind port with vhost ports by muxing HTTP/HTTPS listeners off mux.Mux when BindPort == VhostHTTP(S)Port. — [server/service.go#L296-349](https://github.com/fatedier/frp/blob/d20a232996007dfe6ab425abc0a39a3ae9a0889b/server/service.go#L296-L349) [server/service.go#L229-236](https://github.com/fatedier/frp/blob/d20a232996007dfe6ab425abc0a39a3ae9a0889b/server/service.go#L229-L236) [pkg/util/vhost](https://github.com/fatedier/frp/blob/d20a232996007dfe6ab425abc0a39a3ae9a0889b/pkg/util/vhost)
- UDP traffic uses msg.UDPPacket frames; the negotiated codec (udpPacketCodec) is fixed per session via the v2 ServerHello and passed into proxy/visitor managers. — [server/service.go#L678](https://github.com/fatedier/frp/blob/d20a232996007dfe6ab425abc0a39a3ae9a0889b/server/service.go#L678) [client/control.go#L50-52](https://github.com/fatedier/frp/blob/d20a232996007dfe6ab425abc0a39a3ae9a0889b/client/control.go#L50-L52) [pkg/msg/msg.go#L186](https://github.com/fatedier/frp/blob/d20a232996007dfe6ab425abc0a39a3ae9a0889b/pkg/msg/msg.go#L186)
- Hot reload path: /api/reload -> apiController.Reload -> Service.UpdateConfigSource -> configSource.ReplaceAll -> reloadConfigFromSourcesLocked -> aggregator.Load -> Filter/Complete -> UpdateAllConfigurer -> ctl.pm/vm.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) [client/control.go#L294-298](https://github.com/fatedier/frp/blob/d20a232996007dfe6ab425abc0a39a3ae9a0889b/client/control.go#L294-L298)
- Metrics flow: server/metrics.Server records NewClient/NewProxy/CloseProxy events during Control lifecycle; pkg/metrics EnableMem/EnablePrometheus are activated when webServer.Port > 0 and exposed at /metrics. — [server/control.go#L685](https://github.com/fatedier/frp/blob/d20a232996007dfe6ab425abc0a39a3ae9a0889b/server/control.go#L685) [server/control.go#L717](https://github.com/fatedier/frp/blob/d20a232996007dfe6ab425abc0a39a3ae9a0889b/server/control.go#L717) [server/service.go#L156-159](https://github.com/fatedier/frp/blob/d20a232996007dfe6ab425abc0a39a3ae9a0889b/server/service.go#L156-L159) [server/api_router.go#L35-37](https://github.com/fatedier/frp/blob/d20a232996007dfe6ab425abc0a39a3ae9a0889b/server/api_router.go#L35-L37) [pkg/metrics/metrics.go](https://github.com/fatedier/frp/blob/d20a232996007dfe6ab425abc0a39a3ae9a0889b/pkg/metrics/metrics.go)
- NatHole orchestration: NatHoleVisitor/NatHoleClient/NatHoleReport messages are dispatched as async handlers on the server Control and routed to nathole.Controller, which analyzes NAT behavior and replies NatHoleResp to visitors. — [server/control.go#L730-732](https://github.com/fatedier/frp/blob/d20a232996007dfe6ab425abc0a39a3ae9a0889b/server/control.go#L730-L732) [pkg/nathole/controller.go](https://github.com/fatedier/frp/blob/d20a232996007dfe6ab425abc0a39a3ae9a0889b/pkg/nathole/controller.go) [pkg/msg/msg.go#L192-244](https://github.com/fatedier/frp/blob/d20a232996007dfe6ab425abc0a39a3ae9a0889b/pkg/msg/msg.go#L192-L244)

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)
