每个 agent run 的网络流量都经由为该会话选定的 egress 模式离开。三种模式是 direct、frp 与 client-proxy(实现于 packages/egress,由 EgressState 状态机治理)。
egress 以会话为作用域:模式绑定到会话,切换模式是显式的生命周期变更,失败绝不静默降级。
egress 模式
| 模式 | 传输 | 适用场景 |
|---|---|---|
direct |
执行机器的既有路由(无隧道)。 | 有公网 IP 或网络本身可达的机器。 |
frp |
frp v0.71.1 反向隧道(TCP 代理)。 | 位于 NAT 后、需要入站可达性的已登记设备。 |
client-proxy |
SOCKS5,以选定客户端作为网络出口。 | 浏览器等应当使用某个特定客户端网络的工具。 |
模式如何选定
selectBestMode(hasPublicIp, hasFrpServer, hasRelayNode):
hasPublicIp → direct
hasFrpServer → frp
hasRelayNode → client-proxy
otherwise → direct (outbound always works; inbound via the enrollment channel)
模式也可以由配置钉死:
MAGICFROG_EGRESS_MODE=client_proxy # or "direct" (default when unset)
MAGICFROG_CLIENT_ID=<client id> # defaults to the computer id
MAGICFROG_FRPS_ADDR=127.0.0.1
MAGICFROG_FRPS_PORT=7000
MAGICFROG_FRPS_TOKEN=<token>
隧道端点在健康之后以 socks5://<frps host>:<remotePort> 发布。
失败即关闭的会话作用域
EgressState 状态机有四个状态——direct、switching、client_proxy、failed——迁移如下:
| 从 | 到 | 时机 |
|---|---|---|
direct |
switching |
请求切换 egress,以目标 client-proxy 在线且已授权为守卫。 |
switching |
client_proxy |
隧道建立且健康检查通过;浏览器会话/上下文绑定新 egress(显式生命周期变更)。 |
switching |
failed |
隧道失败、客户端离线或策略拒绝——绝不回落 direct,失败是显式的。 |
client_proxy |
switching |
显式的模式或客户端变更;旧隧道先销毁再建新隧道,旧 egress 绝不复用。 |
client_proxy |
failed |
隧道断开、grant 被吊销或 token 过期——经该代理的既有连接被终止。 |
failed 是吸收态:不存在原地恢复边。在已失败的会话上重建是显式错误——回到 direct 的唯一途径是销毁并重建 egress 会话(新的会话编号),绝不是静默的原地迁移。
浏览器会话绑定
浏览器会话在创建时绑定模式。client_proxy 会话以 --proxy-server=socks5://host:port(或 http://)启动浏览器;显式 direct 会话则不带代理参数启动——那是配置定的模式,不是回落。切换中或已失败时,浏览器拒绝在没有稳定 egress 的情况下绑定:绑定携带隧道 epoch,过期绑定被拒绝(isCurrent 守卫)。
如果隧道在会话中途死掉,浏览器保持其已失效的代理地址:Chromium 会让连接失败,它绝不会自行绕过代理直连。
切换 egress
切换是显式的生命周期变更,经 PATCH /v1/runs/{runId}/egress 驱动:
- 请求:
{ "mode": "direct" | "client_proxy", "clientId": "..." }——模式为client_proxy时clientId必填(否则400)。 - 错误是类型化且显式呈现的:
400缺少 clientId,403未授权,410客户端离线(失败即关闭,无回落),504隧道建立超时。
守卫在离开当前状态之前校验:被拒绝的请求不影响健康的会话。一旦切换提交,任何失败都落入 failed——绝不退回旧 egress。
吊销时效
吊销一个客户端的 egress grant 在 5 秒内生效:先以 SIGTERM 杀 frpc,2 秒宽限后 SIGKILL,硬性 5 秒上界——egress 机器进入 failed,每一条经该代理路由的连接都被终止。既有连接是被主动终止的,不是放着等它自己超时。