总体架构
WG Free Mesh 由控制台、后端、数据库、Go 客户端、EMQX 和 Docker gateway 组成。
Browser Console
-> Gateway
-> Frontend static files
-> FastAPI backend
-> SQLAlchemy repository
-> SQLite / PostgreSQL
-> EMQX management API
-> MCP server
-> EMQX MQTT ports
wfm-agent
-> Backend bind API
-> EMQX MQTT
-> wg / awg / wg-quick / awg-quick组件职责
前端控制台
前端负责页面组织、用户输入、接口调用、状态展示和交互反馈。它不复制后端业务规则,不自行判断在线状态、同步状态或下载产物状态。
FastAPI 后端
后端是业务规则中心。配置、端点、Mesh 对、端口转发、快照、MCP、客户端绑定、MQTT 授权和系统状态都以数据库中的应用数据为准。
数据库
数据库是唯一业务真相。当前支持 SQLite 和 PostgreSQL,访问统一通过 SQLAlchemy。表结构变化必须进入 Alembic 迁移。
EMQX
EMQX 只负责 MQTT 通信。节点账号、密码、授权规则和连接状态由后端根据数据库同步到 EMQX。EMQX 离线时,后端仍应能启动并落库,待 EMQX 恢复后再同步。
Go 客户端
客户端由 wfm-agent 和 wfmctl 组成。wfm-agent 作为系统服务运行,接收配置和控制命令。wfmctl 提供安装、绑定、状态、日志、启停和卸载能力。
Docker gateway
Docker 部署通过 gateway 提供统一 Web 入口。生产环境的 HTTPS 建议由 Nginx、Caddy 或外部网关终止,WFM 容器内部保持清晰的 HTTP 和 MQTT 边界。
数据流
常见写入路径:
- 前端调用后端 API。
- 后端校验请求并写入数据库。
- 后端发布 SSE 事件刷新页面。
- 如需客户端动作,后端通过 MQTT 下发命令。
- 客户端 ACK 后,后端更新运行态和控制日志。
常见读取路径:
- 前端请求配置、端点或系统状态。
- 后端从数据库读取并生成投影。
- 前端只展示投影,不重新实现业务推导。
控制面与数据面
WFM 管理的是 WireGuard / AmneziaWG 的配置和控制面,不转发真实隧道流量。
控制面包括:
- 浏览器访问控制台。
- 控制台调用后端 REST API。
- 控制台订阅 SSE 实时事件。
- 后端通过 EMQX 向动态客户端下发控制命令。
- 客户端通过 MQTT 上报心跳、ACK、运行状态和日志。
- MCP 客户端通过
/mcp调用只读或写入工具。
数据面包括:
- 节点之间的 WireGuard / AmneziaWG UDP 隧道。
- 用户业务流量在隧道内转发。
- 端口转发规则在目标端点本机通过生命周期命令实现。
后端不代理 WireGuard 流量,也不位于 Mesh 数据路径上。后端离线后,已运行的隧道通常仍按客户端本机配置继续运行,但控制台无法继续下发新配置或控制命令。
运行入口
当前有三类主要入口:
| 入口 | 使用者 | 说明 |
|---|---|---|
| Web/API/SSE/MCP gateway | 浏览器、AI 客户端、外部 HTTP 调用方 | Docker 部署时统一从 gateway 暴露。 |
| MQTT public listener | wfm-agent | 客户端连接 EMQX,端口和 TLS 由部署与设置共同决定。 |
| WireGuard / AmneziaWG UDP | 真实 Mesh 节点 | 节点之间直连,不经过 WFM 后端。 |
生产环境建议把 Web/API/SSE/MCP 放到同一个 HTTPS 反向代理入口。MQTT 可以由 gateway 暴露独立端口,是否启用 TLS 取决于客户端 MQTT 初始化设置。
客户端通道
动态端点上线需要两条通道:
- 一次性 HTTP 绑定通道:
wfmctl bind使用绑定 token 调用/api/client/bind。 - 长期 MQTT 控制通道:
wfm-agent使用绑定返回的 MQTT 凭据连接 EMQX。
绑定成功后,后端把 MQTT 凭据写入数据库并同步到 EMQX。客户端本地 profile 只保存连接后端和 MQTT 所需的数据,不保存配置协议真相。每次控制、探测和配置下发时,后端都会在 payload 中携带当次协议和配置内容。
实时刷新通道
控制台实时刷新使用 SSE,而不是 WebSocket。设计原因:
- 控制台主要需要服务端到浏览器的刷新信号。
- 浏览器写操作已经走 REST API。
- SSE 天然支持断线重连和简单文本帧。
- 后端可以把事件视为“刷新提示”,不要求前端从事件中还原完整业务状态。
前端接收事件后按 scope 和事件类型重新拉取对应 REST 投影。例如配置概览变化后拉取配置概览,端点控制日志变化后拉取日志。
EMQX 同步模型
EMQX 是运行时投影,不是业务真相。
后端负责:
- 生成节点 MQTT username/password/client_id。
- 写入数据库。
- 调用 EMQX 管理 API 创建或更新用户。
- 提供 EMQX AuthZ HTTP 回调。
- 后端启动、EMQX 恢复、快照恢复后按数据库重建 EMQX 节点账号。
EMQX 临时不可用时,后端仍应启动并允许非 MQTT 功能工作。涉及客户端控制的功能应明确报错或显示不可用,而不是让整个系统失败。
快照模型
快照是应用级快照,不是数据库文件拷贝。
快照包含除管理员密码外的应用数据,包括:
- 配置、端点、Mesh 对、端口转发。
- 系统设置。
- MCP token 和审计记录。
- 客户端 MQTT 凭据。
- 快照元数据和 WireGuard 目录数据。
快照恢复后,不信任历史在线态。后端会清空运行态,按快照中的客户端凭据重建 EMQX 用户,并通过 detect 重新确认端点状态。
失败降级
| 故障 | 期望行为 |
|---|---|
| EMQX 不可用 | 后端可启动;MQTT 控制不可用;数据库写入仍以数据库为准。 |
| 客户端离线 | 控制命令不能成功;页面显示离线或掉线;已有隧道状态以客户端重新上报为准。 |
| SSE 断开 | 前端自动重连;必要时重新拉取系统状态。 |
| 数据库不可用 | 后端启动失败或等待重试后失败;不能伪造业务状态。 |
| gateway 不可用 | 外部无法访问 Web/API/SSE/MCP;容器内部服务不代表公网可用。 |
设计取舍
- 后端优先保持可启动,不能因为 EMQX 临时不可用导致主程序不可用。
- 快照是应用级数据,不绑定 SQLite 文件或 PostgreSQL dump。
- MCP 与 Web API 共享同一套业务规则,不绕过服务层。
- Docker 是当前推荐部署方式,因为它能统一处理后端、前端、EMQX、证书和 gateway。
