Client YAML 多 SOCKS5 入口实施计划
Client YAML 多 SOCKS5 入口实施计划
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: 让 tunnelmesh-client 通过一个进程和一个 WebSocket 会话,从 client.yaml 启动多个本地 SOCKS5 入口。
Architecture: 扩展 client.tunnels 配置模型,新增 protocol: socks5 及其认证、监听和远程校验字段;新增 client run 命令,在一个进程内按配置创建多个 forward,并共享同一个 WebSocket session opener。保留现有 forward socks5 命令,不破坏兼容性。
Tech Stack: Go、Cobra、Viper、TunnelMesh Client、Vitest 不涉及。
Spec: docs/user-guide/client.md、docs/operations/config-examples.md、internal/cli/root.go、internal/config/config.go。
Global Constraints
- 不改变现有
forward socks5命令行为。 - 不改变 Agent/Server 协议。
client.tunnels中同一时刻可以配置多个socks5条目。- 所有 SOCKS5 入口必须共享同一个 Client WebSocket 会话。
- 配置校验必须拒绝重复监听地址、空 Agent ID、非法认证模式和非 loopback 监听未显式允许的情况。
- 生产默认只监听 loopback。
Task 1: 扩展配置模型和校验
Files:
- Modify:
internal/config/config.go - Modify:
internal/config/config_test.go
Interfaces:
TunnelConfig新增字段:Protocol stringListenAddr stringAgentID stringAuthMode stringAllowRemote boolAuthURL string
-
保留现有
TargetHost、TargetPort字段用于tcp/udp/http。 - 步骤 1:写失败测试
新增测试:
func TestLoadClientYAMLWithMultipleSOCKS5Tunnels(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, "client.yaml")
yaml := `mode: local
client:
server_url: wss://example.com/ws/client
token: test-token
tunnels:
- name: socks-a
protocol: socks5
listen: 127.0.0.1:10866
agent_id: agent-a
- name: socks-b
protocol: socks5
listen: 127.0.0.1:10867
agent_id: agent-b
`
if err := os.WriteFile(path, []byte(yaml), 0o600); err != nil { t.Fatal(err) }
cfg, err := config.Load(context.Background(), config.ConfigOptions{ConfigFile: path})
if err != nil { t.Fatal(err) }
if len(cfg.Client.Tunnels) != 2 { t.Fatalf("tunnels = %d, want 2", len(cfg.Client.Tunnels)) }
if cfg.Client.Tunnels[0].Protocol != "socks5" || cfg.Client.Tunnels[0].ListenAddr != "127.0.0.1:10866" { t.Fatalf("first tunnel = %+v", cfg.Client.Tunnels[0]) }
if cfg.Client.Tunnels[1].Protocol != "socks5" || cfg.Client.Tunnels[1].ListenAddr != "127.0.0.1:10867" { t.Fatalf("second tunnel = %+v", cfg.Client.Tunnels[1]) }
}
- 步骤 2:运行测试确认失败
go test ./internal/config -run TestLoadClientYAMLWithMultipleSOCKS5Tunnels -count=1
预期失败:Protocol 或 ListenAddr 字段不存在,或 YAML 解析失败。
- 步骤 3:实现最小配置模型
在 TunnelConfig 中新增上述字段,并在 Validate 中加入:
protocol必须是tcp、udp、http、socks5之一。socks5必须提供listen和agent_id。- 非默认认证只能是
none或password。 - 非本机监听必须显式
allow_remote: true。 -
监听地址不能重复。
- 步骤 4:运行测试确认通过
go test ./internal/config -run TestLoadClientYAMLWithMultipleSOCKS5Tunnels -count=1
Task 2: 新增 client run 命令
Files:
- Modify:
internal/cli/root.go - Modify:
internal/cli/client_runtime_test.go
Interfaces:
- 新增命令:
tunnelmesh-client run - 输入:
--config client.yaml -
行为:读取
client.tunnels,在一个 WebSocket 会话内启动所有入口。 - 步骤 1:写失败测试
新增测试:
func TestClientRunStartsMultipleSOCKS5Tunnels(t *testing.T) {
// 使用 fake WebSocket session 和 fake listener
// 断言一个进程内同时监听 10866 和 10867
// 断言两个入口使用同一个 session opener
}
- 步骤 2:运行测试确认失败
go test ./internal/cli -run TestClientRunStartsMultipleSOCKS5Tunnels -count=1
预期失败:run 命令不存在。
- 步骤 3:实现最小 run 命令
实现逻辑:
- 加载配置。
- 建立 Client WebSocket。
- 遍历
cfg.Client.Tunnels。 - 对
socks5创建client.NewSOCKS5Forward。 - 所有 forward 共享同一个
client.NewSessionOpener(session)。 - 启动全部 forward 后阻塞等待,任一 forward 退出则关闭其他 forward 并返回错误。
- 步骤 4:运行测试确认通过
go test ./internal/cli -run TestClientRunStartsMultipleSOCKS5Tunnels -count=1
Task 3: 更新文档和本机服务
Files:
- Modify:
docs/user-guide/client.md - Modify:
docs/operations/config-examples.md - Modify:
/Users/z-yuchangjun/.config/tunnelmesh/client.yaml - Modify:
/Users/z-yuchangjun/Library/LaunchAgents/com.tunnelmesh.client.plist
Interfaces:
- YAML 示例展示两个 SOCKS5 入口。
-
LaunchAgent 只启动一个
tunnelmesh-client run --config ...进程。 - 步骤 1:更新文档
在文档中加入:
client:
tunnels:
- name: socks-a
protocol: socks5
listen: 127.0.0.1:10866
agent_id: agent-a
- name: socks-b
protocol: socks5
listen: 127.0.0.1:10867
agent_id: agent-b
- 步骤 2:更新本机配置
把本机 client.yaml 改为两个 SOCKS5 tunnel。
- 步骤 3:更新 LaunchAgent
ProgramArguments 改为:
/Users/z-yuchangjun/.local/bin/tunnelmesh-client
--config /Users/z-yuchangjun/.config/tunnelmesh/client.yaml
run
删除临时的 tunnelmesh-client-socks5-multi 脚本。
- 步骤 4:重启并验证
launchctl kickstart -k gui/$(id -u)/com.tunnelmesh.client
lsof -nP -iTCP:10866 -sTCP:LISTEN
lsof -nP -iTCP:10867 -sTCP:LISTEN
ps aux | rg '[t]unnelmesh-client'
预期:
- 10866、10867 都在监听。
- 只有一个
tunnelmesh-client主进程。
验证命令
go test ./internal/config -run TestLoadClientYAMLWithMultipleSOCKS5Tunnels -count=1
go test ./internal/cli -run TestClientRunStartsMultipleSOCKS5Tunnels -count=1
go test ./... -count=1
go vet ./...
git diff --check
回滚
- 代码回滚恢复
internal/config/config.go、internal/cli/root.go和测试。 - 本机回滚恢复
client.yaml和com.tunnelmesh.client.plist。 - 不涉及数据库或协议迁移。