Files
terminalX/docs/HANDOFF.md
kid aa92d0e676 初始提交:terminalX 可运行态(M0/M1/M1.5 已真机验证)
- M0: libghostty SSH 终端(渲染/输入/连接)+ 白屏修复(OutputGate) + 会话状态机/自动重连
- M1: tsnet 用户态组网 + SSH-over-tsnet(fd 桥),shell 级真机验证;R5(Go+gvisor+C+++Swift 同进程) retire
- M1.5: tmux -CC 原生 tab(MVP)
- 结构: packages/(TXCore·TXTransport), apps/TerminalX, vendor/(libghostty-spm/libssh2/mbedtls/tsnet-bridge), artifacts/
- 文档: CLAUDE.md + docs/HANDOFF.md(新会话入口)
- 环境: 认证代理→依赖 vendor 本地化;Go 在 ~/.local/go;仅模拟器/未签名
- 待续: M2 mosh, tmux 多 pane, M4 安全(host key/SE)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 10:20:46 +08:00

83 lines
8.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# terminalX 交接文档HANDOFF
> 目的:让**没有上下文的新会话**读完即可掌握项目全貌并继续工作。配合 `../CLAUDE.md`(操作规范/命令)与项目记忆(`~/.claude/projects/-Users-kid-...-terminalX/memory/`,本项目会话会自动注入)一起看。
> 最后更新2026-07-24。
---
## 0. 一句话现状
一个**原生 iPad SSH 终端**已可用并真机验证libghostty 渲染 + 键盘输入 + SSH 直连 + **SSH-over-tsnet(Tailscale 用户态)** + **tmux -CC 原生 tab** + 断线自动重连。四大核心需求libghostty/tailscale/tmux 已验证,**mosh 未做**)。仅在 iOS 模拟器验证(未上真机/未签名/未提交 git
## 1. 项目愿景与硬约束
- 目标iPad/iPhone 远程轻量运维 macOS/Linux + vibe coding。平台优先级 **iPadOS > iOS > macOS(Apple Silicon)**
- iOS/iPadOS **只远程**(沙盒禁 fork/exec不能跑本地 shellmacOS 才支持本地 PTY。
- 分发目标 App StoreM5已暂缓
- 四大核心:**libghostty**(终端引擎) · **mosh** · **Tailscale tsnet(用户态,非 NetworkExtension)** · **tmux control-mode(-CC) hook**
## 2. 关键架构决策(已定,勿推翻)
1. 终端引擎用 **libghostty**,经社区 **GhosttyKit**`Lakr233/libghostty-spm`,已 vendor 到 `vendor/libghostty-spm`)。`TerminalViewState``ObservableObject`;喂字节入口 `InMemoryTerminalSession.receive(Data)`SwiftUI 视图 `TerminalSurfaceView(context:)`
2. Tailscale 走 **tsnet 用户态**Gogomobile bind → `TsnetBridge.xcframework`**不用 NetworkExtension**(避开 VPN entitlement + 50MB 内存墙)。
3. SSH 用 **libssh2 + mbedTLS**`CSSHCore.xcframework`),支持传入外部 fd。
4. **SSH-over-tsnet = fd 桥**`tsnet.Dial()` 得 net.Conn无真实 fd`socketpair(AF_UNIX)` + 双向 io.Copy 泵 → 把另一端 fd 交给 libssh2。见 `vendor/tsnet-bridge/tsnetbridge/bridge.go: DialTCPFD`
5. 一进程只一个 Go runtime、只一次 gomobile bindC/C++ 库符号隐藏只导出 C API。
6. 多账号(未实现):多 tsnet 节点并存;每 host 绑 egress profile(Direct/tailnet)**禁止多 tailnet 并发盲探**(重叠 100.x/同名会误连)。
## 3. 环境(决定构建方式,极重要)
- **带认证的 HTTPS 代理**`HTTPS_PROXY=...@...:38128`)。`github.com` release blob 能 curl**go.dev / ghcr.io 被代理 abort****SwiftPM 不支持带认证代理** → 所有 SPM 依赖 vendor 本地化。Go 模块用 `goproxy.cn`
- **Go 1.26.5 在 `~/.local/go`**(不在默认 PATH经 aliyun 镜像 curl 装),**gomobile/gobind 在 `~/go/bin`**`go env -w GOPROXY=https://goproxy.cn GOSUMDB=off` 已固化。
- 工具链Xcode 26.6 / Swift 6.3 / xcodegen 2.46 / zig 0.15.2 / cmake 4.4 / ninja 1.13 / git。
## 4. 里程碑状态
| 里程碑 | 状态 | 真机实证 |
|---|---|---|
| **M0** SSH 直连终端 | ✅ 完成 | libghostty 渲染(SGR/CJK)、键盘输入、连接;**白屏 bug 修复**(OutputGate);会话状态机+自动重连(31→含后加共 35 单测) |
| **M1** tsnet + SSH-over-tsnet | ✅ 完成 | app 内 tsnet 加入 tailnet(IP 100.74.60.108);对端枚举;**SSH-over-tsnet 登录 Ubuntu shell**(服务端 `Last login from 100.x` 证明走 tailnet |
| **M1.5** tmux -CC 原生 tab | ✅ MVP | 直连跑 `tmux -CC attach` → 渲染 fish/tmux 两原生 tab + 活动窗口内容 |
| dial 重试打磨 | ✅ | `TsnetManager.dialFD` 6 次退避重试,解决 tsnet 首连路径预热超时 |
| **R5** Go+gvisor+C+++Swift 同进程 | ✅ retire | 全程真机无崩溃 |
| **M2** mosh | ◻️ 未开始 | libmoshios(C++) + 本地 UDP relay over tsnet |
| **M3** 生命周期深化 | ◻️ 部分 | 前后台 FSM 已做mosh 快速恢复 + tmux 兜底 待做 |
| **M4** 安全 | ◻️ 未做 | **当前 SSH 接受任意 host key**(见 SSHSession TODO)known_hosts 固定 + Secure Enclave 密钥 待做 |
| **M5** 合规提审 | ⏸️ 暂缓 | 用户指示暂缓 |
## 5. 代码地图与数据流
- `packages/TXCore/Sources/TXCore/`
- `Tmux/TmuxControlParser.swift` `TmuxEvent.swift` `TmuxIDs.swift` `TmuxOutputDecoder.swift` `TmuxLayout.swift` `TmuxControlSequence.swift`DCS 检测)—— 纯解析35 单测在 `Tests/`
- `Session/SessionMachine.swift` —— `reduce(event)->[Effect]` 会话状态机(指数退避重连/后台冻结/前台恢复)。
- `packages/TXTransport/Sources/`
- `TXTransport/Transport.swift`(协议+SSHConfig+SSHError`SSHSession.swift`libssh2`preconnectedFD` 支持外部 fd事件循环 poll+非阻塞)。
- `CSSH/`C targetlibssh2 头 + `include/module.modulemap`,供 `import CSSH`)、`CSSHCore`binaryTargetlibrary-only
- `apps/TerminalX/iOS/`
- `SSHTerminalModel.swift` —— 核心编排:`OutputGate`(surface-ready 缓冲)、`TransportHolder``TsnetManager`(tsnet 节点 up/dial+重试)、`TsnetError``TmuxRouter`(raw/gateway 字节分流)、状态机驱动 startSession(直连/tsnet 分支)、`autoConnectIfConfigured`(启动参数)。
- `ContentView.swift` —— 分支:`tmuxController != nil``TmuxTabbedView`(tab 条+活动窗口);否则 `RawTerminalView``TsnetProbeView`(纯自检)`ConnectionForm`
- `TmuxController.swift` —— tmux 网关消费解析事件、window↔`TmuxWindow`(各自终端会话+OutputGate)、`list-windows`/`list-panes`/`capture-pane` 握手、`send-keys`/`select-window`/`new-window`
- `TsnetProbe.swift` —— M1 自检视图(加入 tailnet + 对端列表)。
- `TerminalXApp.swift` —— @main
- **数据流(直连)**transport.onBytes → `TmuxRouter.feed` → (raw) `OutputGate.deliver``session.receive`→libghostty(检测到 DCS) → `TmuxController.feed`→解析→各 window。输入libghostty→session write 闭包→transport.sendtmux 模式→send-keys
- **数据流(tsnet)**`TsnetManager.ensureUp`(auth key)→`dialFD`(socketpair fd)→`SSHSession(preconnectedFD:)`
## 6. 已知问题 / TODO继续工作的入口
- **M4 安全**`SSHSession.connect()``TODO(M4)`:不校验 host key接受任意。需接 known_hosts 三元组 pin(egress,host,port) + Secure Enclave 私钥(`libssh2_userauth_publickey_frommemory`+`SecKeyCreateSignature`)。
- **tmux MVP 简化**:每 window 只显示活动 pane**多 pane 分屏未做**);历史靠 `capture-pane` 抓当前屏(非完整 scrollback命令应答 FIFO 简化匹配resize 未回传(默认 80x24应发 `refresh-client -C`)。
- **tsnet 首连**:已加 dial 重试;更优是等 peer online 再 dial读 PeersJSON online 状态)。
- **签名/真机**`CODE_SIGNING_ALLOWED=NO`,仅模拟器;上真机/TestFlight 需配置签名 + entitlementstsnet 用户态**无需** NE entitlement利好审核
- **git**:已 `git init`**尚无 commit**`artifacts/*.xcframework` 已 gitignore`make`/脚本重建或从 Release 拉)。
- **多平台**iOS target 已跑macOS target本地 PTY, `TXPTY`)未建。
## 7. 下一步建议(按价值)
1. **M2 mosh over tsnet**:接 `blinksh/mosh`(libmoshios C++,依赖 protobuf) 交叉编译成 xcframework(参考 `build-cssh.sh` 模式) → 本地 UDP relay(mosh 连 127.0.0.1:NGo 侧 `tsnet` UDP `ListenPacket`/`Dial` 转发bridge 加 UDP relay 方法,类比 `DialTCPFD`)。SSH 先起 `mosh-server` 拿 key+port。**需用户提供装了 mosh-server 的 tailnet 主机**做端到端。
2. **tmux 多 pane 分屏**`TmuxLayout` 已能解析布局树 → 把 window 内多 pane 渲染成 SwiftUI 分屏(每 pane 一 surface
3. **M4 安全**known_hosts 固定 + SE 密钥。
4. **产品化**:主机列表持久化(Keychain)、多标签(非 tmux)、软键盘运维工具栏(GhosttyKit 内建,接线即可)。
## 8. 测试资源(用户提供,**凭据勿写入仓库/勿硬编码**,每次向用户索取)
- 一台局域网 macOS(有 tmux)、一台 tailnet 内 online 的 Ubuntu VM(vohive-vm)、一个 tailnet auth keyephemeral可能过期用户会给新的。用于直连 SSH / SSH-over-tsnet / tmux / M2 mosh 端到端验证。
- tailnet 是 `tail3b5057.ts.net`已见节点vohive-vm(linux)、iphone-air、ipad-pro、macbook-air-m1、macbook-air。
## 9. 工作纪律(务必遵守)
- **绝不臆想工具结果**:调用后等真实返回再推理,绝不编造 BUILD/日志/截图。曾因连续编造工具输出(含假 BUILD SUCCEEDED、错误归因 GhosttyKit而误诊——真相全靠老实 `Read` 完整 log。此规则已写入全局 `~/.claude/CLAUDE.md`
- 每步用真实证据验证:`swift test` 看真实计数、`xcodebuild` 看真实 BUILD 结果、模拟器 `screenshot` + `Read` 看真实画面。
- 与用户用**简体中文**commit/push 只在明确要求时。
</content>