让远程调试有一条能走通的路
开发环境散落在 Windows、WSL、macOS 和 Linux 上。我希望从当前终端进入另一台机器,查看现场、运行程序、交换文件,而不是每换一个平台就重新寻找一套替代方案。
tline 从一个 TCP 到 PTY 的 C 实验起步,现在以 Rust 实现为主线,Go 版本对照跟进。它保持独立的小工具形态,不要求使用某个特定终端或编辑器。
先让机器在线,再按需创建终端
Agent 主动连接 Hub 并注册。空闲时不预先打开 shell;客户端选中一个在线 Label 后,Agent 才创建独立的 console。
Label 由 Hub 的授权名单绑定,不由客户端随意声明。在线列表同时保留平台、shell 和 console 数量,目标不再只能靠 IP 或主机名辨认。
桌面 Agent 可以承载多个独立 console。连接关闭就结束对应 console;当前不是 tmux 式的持久会话,也不提供断开后的历史回放。早先“预建 session、等待 attach”的实验已经归档。
同一个入口,不强求同一种终端
Windows、Linux / WSL、macOS
Rust 版本通过 portable-pty 连接 Unix PTY 与 Windows ConPTY。Windows 启动 PowerShell,Linux / WSL 与 macOS 当前启动 Bash;跨平台原生验证和跨机器终端、文件往返已经有记录,并进入日常使用。
ESP32-C3
开发板也已作为 Agent 注册到同一入口,能查询版本、运行时间和内存。这里是设备侧的有限命令控制台,不是操作系统 shell;当前只接受一个 console,也不据此声称支持桌面版的全部能力。
串口仍然是另一条路径
设备尚未联网或需要查看启动输出时,使用独立的 sline。也可以先通过 tline 进入接着开发板的电脑,再运行 sline;这是工具组合,不是把 UART 塞进 PTY 协议。
同一套身份,传文件和目录
tsync 与 tline 共用 Hub、配置和 Label,支持本地与远端之间的文件上传、下载和递归目录拷贝。传输进度包含文件数、字节数、速度与预计剩余时间。
接收端先写临时文件,校验长度与 SHA-256 后再发布该文件;默认不覆盖已有文件。目录不是一个原子事务,中途失败时,此前完成的文件仍然保留。
这是文件拷贝,不是完整 rsync:没有增量算法、断点续传、删除远端多余文件或完整元数据保留。
已经加密,也要知道信任了谁
当前 Hub 路径使用 Noise NK 建立加密连接。客户端固定 Hub 公钥,Hub 通过 token 哈希名单确认身份并分配 Label;Hub 私钥留在中心,客户端和 Agent 各自持有访问凭据。
- Hub 是可信中介:两段连接分别加密,Hub 能看到转发的明文,不是对 Hub 隐藏内容的端到端加密。
- 准入不等于细粒度授权:获准身份可访问在线 Agent,执行与文件访问受 Agent 所在操作系统用户权限约束;当前没有按目标、命令或目录细分的权限模型。
- 兼容路径单独看:早期直接
serve/connect路径仍是明文 PoC,不因 Hub 加密就自动获得同样的保护。 - 仍是 PoC:认证与传输测试不等于完整安全审计;密钥轮换、在线撤销和完整操作审计尚未形成产品级闭环。
留下来的终端模型
平台实现变了,核心问题没有变:输入由谁接收,控制信息在哪里解释,远端退出后由谁收尾。
PTY、控制信息与生命周期
终端不只是两条字节流
终端模拟器解释输出中的控制序列;PTY / ConPTY 让远端程序处在终端环境里。普通输入、窗口尺寸变化、程序退出和终端查询响应需要各自明确的语义,不能只凭“TCP 还连着”判断交互正常。
Unix PTY 与进程归属
父进程持有 PTY master,shell 连接 slave。session、控制终端与前台进程组决定终端信号交给谁;setsid() 和控制终端设置负责建立这种关系,SIGCHLD 通知与 waitpid() 回收则属于子进程生命周期。
Windows 不能直接照搬 Unix
ConPTY 提供另一套终端接口。shell 退出、输出管道结束和网络断开不一定同时发生,因此需要分别观察并协调收尾。跨平台库减少了平台代码,但不能代替真实交互验证。
最初的 C 阅读笔记保留在 Git 历史中;当前实现以各项目源码、验证记录和可复现输出为准。