为什么要自己写一个 SSH 客户端
ssh-gui 技术系列 · 一 · 2026-10-23
问一个运维工程师,他的服务器地址清单放在哪。答案多半不是某个工具,而是几处拼接:记事本里有一份,聊天软件的置顶消息里有一份,Wiki 的某个角落里可能还有一份,彼此都不完全可信。再问他传文件用什么、看端口转发规则用什么,答案通常是另一批软件的名字。
ssh-gui 想解决的就是这种碎片。它是一个 Windows 桌面 SSH 客户端(macOS 和 Linux 也能构建,Windows 优先),把命名主机、分组、多标签终端、SFTP 文件管理、端口转发、密钥管理放在同一个进程里。打开应用,左边是按环境分好组的主机列表,双击一台机器就进终端,不再需要记事本。
它长什么样
技术形态先交代清楚:Go 写后端,Wails v3 做桌面外壳,前端是 React 加 xterm.js。构建出来是一个单独的可执行文件,前端资源用 go:embed 直接嵌进去,Windows amd64 版本 17.6 MB,不需要安装运行时。
这不是又一个 Electron 套壳。选 Go 有个很实际的原因:SSH 这件事,Go 生态里有 golang.org/x/crypto/ssh 和 github.com/pkg/sftp,是经过 OpenSSH 世界反复捶打过的实现。Electron 的 Node 侧没有同等成熟度的选择,Tauri 则意味着要拿 Rust 从头写一层 SSH 桥。Go 端做连接管理,WebView 只负责画界面,这条路线对我们来说几乎没有犹豫。
功能上,目前能用的部分包括:
- 真实 SSH2 终端,密钥、口令、Agent 三种认证,多标签加垂直分屏;
- 断线自动重连(指数退避),重启后按上次的标签布局恢复会话;
- SFTP 双栏文件管理,上传下载带进度队列;
- 本地、远程、SOCKS5 三类端口转发,配一张拓扑图看链路;
- 跳板链(ProxyJump)实际逐跳拨号,界面上配好就能走;
- 保险库存私钥(scrypt 派生主密钥 + AES-256-GCM),known_hosts 指纹校验,指纹变化直接阻断。
还做不到的也直说:2FA 和证书认证没有,SFTP 断点续传和在线编辑没有,批量执行和会话录制还在路上。仓库里的 TODO.md 把每一项都标了状态,这份诚实比功能列表本身更重要——工具是给运维用的,最忌讳界面上有个按钮点了没反应。
三个刻意不做的事
功能清单之外,有三样东西是明确决定不做的,而且短期不会改主意。
不做云同步。 主机清单、私钥、口令留在本机。市面上的 Termius 走的是云同步路线,那意味着你的私钥躺在别人的服务器上。这不是说别人做得不安全,而是这个选择和我们的另一条底线绑在一起——
无遥测。 代码里不存在任何指向非本机地址的 HTTP 客户端调用。这个应用出网的场景只有一种:连你配置的服务器。将来会加一条静态检查把这件事锁死在 CI 里,防止哪天顺手引个依赖就把上报带进来。"无遥测"写进文档很容易,难的是让它在三年后依然成立,所以要靠机制而不是靠自觉。
不做插件体系。 听起来像缺诚意,其实是排序问题:插件 API 一旦发布就是承诺,协议层还没站稳就冻结扩展接口,等于把半成品焊死在产品里。等 SSH、SFTP、转发这些底层稳定之后再说。
给谁用
目标用户就是每天在几十台机器之间跳来跳去的人:运维、SRE、运维开发。他们不需要一个更漂亮的终端模拟器——Windows Terminal 已经够好了——他们需要的是"这几十台机器"的组织方式:哪台是生产库、哪台要经过跳板机、那台只能用某把密钥登录的机器上次是怎么连上的。
这些上下文属于个人工作流,不该放在云上,也不该散落在五份文本文件里。ssh-gui 把它们做成结构化的数据,存在本机 SQLite 里,配合保险库和指纹校验,让"连接"这个动作从翻记录变成点一下。
后面几篇文章会往深处走:第二篇讲整体架构和"秘密不出 Go 进程"的边界设计,第三篇拆终端字节流的传输链路——那是一条回环 WebSocket,背后有一串关于背压和重连的取舍,第四篇讲这个项目怎么用一份 3300 行的 HTML 原型指挥整个工程。




浙公网安备 33010602011771号