Problem Statement
用户通过 talos up 可以 SSH 连接到 K8s sandbox pod,但连接是临时的——CLI 进程退出后 WebSocket relay 随即销毁。VS Code Remote-SSH 扩展需要一个持久可用的 SSH 连接机制。当前文档建议手动 kubectl port-forward,但这要求用户有集群访问权限,在 ECS/远程 k3s 场景下不可行。
Solution
新增 talosd ssh-proxy 子命令,作为 SSH ProxyCommand 使用。每次 SSH 或 VS Code 发起连接时,自动建立 WebSocket relay 到 sandbox pod,无需后台常驻进程。
用户执行 talos up 创建/唤醒 sandbox 后,SSH config 中会写入 ProxyCommand 指令。之后:
- 终端
ssh tt-default 直接连入
- VS Code Remote-SSH 选择
tt-default host 即可连接
- Sandbox 休眠后重连会自动唤醒
User Stories
- As a developer, I want to run
talos up and then connect via VS Code Remote-SSH, so that I get full IDE experience in my sandbox
- As a developer, I want
ssh tt-{project} to work directly from terminal without running talos up again, so that I can quickly reconnect
- As a developer, I want the SSH relay to be established on-demand without a background daemon, so that I don't need to manage long-running processes
- As a developer, I want VS Code to automatically reconnect when my sandbox wakes from sleep, so that I don't lose my workflow
- As a developer, I want meaningful error messages in VS Code output panel when connection fails, so that I can troubleshoot issues
- As a developer, I want to open multiple VS Code windows to the same sandbox simultaneously, so that I can work on different files in parallel
- As a developer, I want
talos up to still drop me into an SSH session immediately, so that my existing workflow is preserved
- As a developer, I want no changes required on the server side for this feature, so that deployment is minimal
- As a developer, I want the SSH config to use a stable alias (not a random port), so that VS Code can reliably discover and cache the host
- As a developer, I want the ProxyCommand binary path to be resolved automatically, so that the SSH config works regardless of how talosd was installed
Implementation Decisions
SSH ProxyCommand 模式
SSH config 格式改为:
Host tt-{project}
User coder
StrictHostKeyChecking no
UserKnownHostsFile /dev/null
IdentityFile ~/.ssh/id_ed25519
ProxyCommand {resolvedBinaryPath} ssh-proxy --project {project}
每次 SSH/VS Code 连接时,SSH 客户端自动 spawn talosd ssh-proxy 子进程,通过 stdin/stdout 传输 SSH 协议数据,子进程内部建立 WebSocket relay 到 portal。无需本地 TCP server,无需后台守护进程。
新增 ssh-proxy 子命令
cli/src/commands/ssh-proxy.ts — 核心流程:
- 读 config(token, serverUrl),无 token 则 stderr 报错退出
GET /api/sandboxes?project={name} 解析 sandbox
- 若 sandbox 不存在 → stderr 提示先
talos up,exit 1
- 若 sleeping →
POST /api/sandboxes/{id}/wake(已有同步接口,等待 ready 后返回)
- 建立 WebSocket 到
/api/sandboxes/{id}/ssh?token=...
process.stdin → WebSocket,WebSocket → process.stdout
- stderr 用于状态信息(VS Code 在输出面板显示)
- WebSocket 连接前缓冲 stdin 数据(同现有 relay 模式)
- 若 WS 被 4004(sleeping)拒绝,自动 retry 一次 wake + reconnect
- 处理 SIGTERM/SIGINT 优雅退出
stdin raw mode 注意事项
ProxyCommand 模式下 SSH 客户端作为父进程,stdin 不是 TTY。process.stdin.setRawMode() 可能抛错。需要 try-catch 或判断 process.stdin.isTTY。实际上 ProxyCommand 模式下不需要 setRawMode——SSH 协议数据已经是二进制帧,直接 pipe stdin 即可。
二进制路径解析
resolveTalosBinaryPath() 函数确定 talosd 的绝对路径:
- 优先
which talosd(生产环境全局安装)
- 回退
process.argv[1] 解析为绝对路径
- 开发模式回退
npx tsx {scriptPath}
路径在 talos up 时解析并写入 SSH config,后续 SSH/VS Code 直接使用。
重构 updateSshConfig
去掉 port 参数,改写 ProxyCommand 格式。Host tt-{project} 块的替换逻辑保持不变(正则匹配 + 替换或追加)。
简化 up 命令
- 删除
establishSshRelay() 调用、let port / cleanup 变量、finally { cleanup() } 块
updateSshConfig(project) 无需 port 参数
sshIntoSandbox(project) 改为 spawn("ssh", ["tt-{project}"]) 用 host alias
- 保留
ensureSshKey()、uploadSshKey() 不变
服务端无需修改
GET /api/sandboxes?project= 已有
POST /api/sandboxes/:id/wake 已有且同步等待 ready
- WebSocket relay 已支持多连接,sleeping 时返回 4004
保留旧函数
establishSshRelay() 保留在代码中但不再被 up 调用,作为 fallback 或未来用途。
Testing Decisions
测试缝隙(Test Seams)
主要在 CLI 层面测试,使用最高可用缝隙:
- SSH config 生成:单元测试
updateSshConfig() — 验证 ProxyCommand 格式正确、已有条目替换、新增条目追加
- 二进制路径解析:单元测试
resolveTalosBinaryPath() — mock execSync 验证优先级链
- ssh-proxy 命令集成:通过 spawn
talosd ssh-proxy 验证 stdin/stdout pipe 行为(mock WebSocket server)
- 端到端:
talos up → ssh tt-default → 验证连接;VS Code Remote-SSH 连接验证
Good Test Criteria
- 只测外部行为(SSH config 输出格式、连接成功/失败),不测内部实现
- SSH config 测试验证:正确的 Host alias、ProxyCommand 包含完整路径、User/IdentityFile 字段正确
Out of Scope
- SSH multiplexing (ControlMaster) 配置优化 — 当前 SSH 默认行为已足够
- Workspace image 修改 — 已满足 VS Code Server 的 glibc/shell/curl 要求
- code-server(浏览器 IDE)支持 — 未来可扩展
- DevContainer spec 支持 — 未来可扩展
- Portal / Sandbox Manager 服务端修改
- Windows 支持(当前仅 macOS/Linux)
Further Notes
DevPod 参考
DevPod (loft-sh) 采用类似方案:SSH 作为主要集成方式,通过 ProxyCommand 按需建立连接。不同之处在于 DevPod 直接管理 Docker 容器,而我们有 K8s sandbox 这一层抽象,需要通过 WebSocket relay 穿透 Portal → Sandbox Manager → Pod。
VS Code Server 安装
首次连接时 VS Code Remote-SSH 会自动在 sandbox 内安装 vscode-server(约 300MB)。Workspace image 已包含 prerequisites(Ubuntu 22.04, glibc 2.35, bash, curl, tar)。安装后的 vscode-server 存储在 /home/coder/.vscode-server,通过 PVC 持久化。
Sleeping Sandbox 恢复时间
Sandbox 从 sleeping 恢复需要约 30-60 秒(pod 重建 + env 注入)。ssh-proxy 会在 stderr 输出 "Sandbox is sleeping, waking..." 进度,VS Code 输出面板可见。
Problem Statement
用户通过
talos up可以 SSH 连接到 K8s sandbox pod,但连接是临时的——CLI 进程退出后 WebSocket relay 随即销毁。VS Code Remote-SSH 扩展需要一个持久可用的 SSH 连接机制。当前文档建议手动kubectl port-forward,但这要求用户有集群访问权限,在 ECS/远程 k3s 场景下不可行。Solution
新增
talosd ssh-proxy子命令,作为 SSHProxyCommand使用。每次 SSH 或 VS Code 发起连接时,自动建立 WebSocket relay 到 sandbox pod,无需后台常驻进程。用户执行
talos up创建/唤醒 sandbox 后,SSH config 中会写入 ProxyCommand 指令。之后:ssh tt-default直接连入tt-defaulthost 即可连接User Stories
talos upand then connect via VS Code Remote-SSH, so that I get full IDE experience in my sandboxssh tt-{project}to work directly from terminal without runningtalos upagain, so that I can quickly reconnecttalos upto still drop me into an SSH session immediately, so that my existing workflow is preservedImplementation Decisions
SSH ProxyCommand 模式
SSH config 格式改为:
每次 SSH/VS Code 连接时,SSH 客户端自动 spawn
talosd ssh-proxy子进程,通过 stdin/stdout 传输 SSH 协议数据,子进程内部建立 WebSocket relay 到 portal。无需本地 TCP server,无需后台守护进程。新增
ssh-proxy子命令cli/src/commands/ssh-proxy.ts— 核心流程:GET /api/sandboxes?project={name}解析 sandboxtalos up,exit 1POST /api/sandboxes/{id}/wake(已有同步接口,等待 ready 后返回)/api/sandboxes/{id}/ssh?token=...process.stdin→ WebSocket,WebSocket →process.stdoutstdin raw mode 注意事项
ProxyCommand 模式下 SSH 客户端作为父进程,stdin 不是 TTY。
process.stdin.setRawMode()可能抛错。需要 try-catch 或判断process.stdin.isTTY。实际上 ProxyCommand 模式下不需要 setRawMode——SSH 协议数据已经是二进制帧,直接 pipe stdin 即可。二进制路径解析
resolveTalosBinaryPath()函数确定talosd的绝对路径:which talosd(生产环境全局安装)process.argv[1]解析为绝对路径npx tsx {scriptPath}路径在
talos up时解析并写入 SSH config,后续 SSH/VS Code 直接使用。重构
updateSshConfig去掉 port 参数,改写 ProxyCommand 格式。
Host tt-{project}块的替换逻辑保持不变(正则匹配 + 替换或追加)。简化
up命令establishSshRelay()调用、let port/cleanup变量、finally { cleanup() }块updateSshConfig(project)无需 port 参数sshIntoSandbox(project)改为spawn("ssh", ["tt-{project}"])用 host aliasensureSshKey()、uploadSshKey()不变服务端无需修改
GET /api/sandboxes?project=已有POST /api/sandboxes/:id/wake已有且同步等待 ready保留旧函数
establishSshRelay()保留在代码中但不再被up调用,作为 fallback 或未来用途。Testing Decisions
测试缝隙(Test Seams)
主要在 CLI 层面测试,使用最高可用缝隙:
updateSshConfig()— 验证 ProxyCommand 格式正确、已有条目替换、新增条目追加resolveTalosBinaryPath()— mockexecSync验证优先级链talosd ssh-proxy验证 stdin/stdout pipe 行为(mock WebSocket server)talos up→ssh tt-default→ 验证连接;VS Code Remote-SSH 连接验证Good Test Criteria
Out of Scope
Further Notes
DevPod 参考
DevPod (loft-sh) 采用类似方案:SSH 作为主要集成方式,通过 ProxyCommand 按需建立连接。不同之处在于 DevPod 直接管理 Docker 容器,而我们有 K8s sandbox 这一层抽象,需要通过 WebSocket relay 穿透 Portal → Sandbox Manager → Pod。
VS Code Server 安装
首次连接时 VS Code Remote-SSH 会自动在 sandbox 内安装 vscode-server(约 300MB)。Workspace image 已包含 prerequisites(Ubuntu 22.04, glibc 2.35, bash, curl, tar)。安装后的 vscode-server 存储在
/home/coder/.vscode-server,通过 PVC 持久化。Sleeping Sandbox 恢复时间
Sandbox 从 sleeping 恢复需要约 30-60 秒(pod 重建 + env 注入)。ssh-proxy 会在 stderr 输出 "Sandbox is sleeping, waking..." 进度,VS Code 输出面板可见。