本地优先意味着没有账号
ConchTerm 是一个本地桌面客户端,核心能力不需要账号、不依赖云服务、不向我们的后台上报数据。你连的是你自己的服务器,凭据存在你自己的机器上,由 macOS 提供的加密机制保护。
这不是一句口号,而是由具体的进程边界和 IPC 规则支撑的。
进程边界:渲染层碰不到秘密
ConchTerm 的架构是单向依赖的:
renderer -> preload typed facade -> IPC -> main application core
在这个模型里,渲染层(React UI)只能通过 preload 暴露的强类型 window.api 与主进程通信。密码、passphrase、私钥内容、原始 host key 都不会跨 IPC,因此它们根本没有机会进入渲染层的状态。
具体来说:
contextIsolation、nodeIntegration: false、sandbox: true。- 渲染层不能直接访问 Node、文件系统或原始 IPC。
- preload 只暴露固定的、强类型的 API,不暴露 raw
ipcRenderer。
凭据边界:Main-only
凭据的处理遵守严格的 Main-only 原则:
- 密码和 passphrase 存在主进程内存中,运行期凭据库不可序列化。
- 可选的持久化凭据通过 macOS
safeStorage加密后写入本地文件。 - 连接配置文件里只存非秘密的 profile 信息(host、端口、用户名、认证方式等),不存明文密码。
- 导入/导出连接库时,会生成一个脱敏的 bundle,不含凭据、私钥引用和绝对路径。
这意味着:即使你导出连接库分享给别人,里面也不会带出你的密码或私钥路径。
IPC 边界:每次调用都校验
每个 IPC 入口都要通过一条统一管线:
- 运行时 Zod schema 校验输入。
- 校验调用者的 sender frame。
- 校验资源所有权(owner-scoped)。
- 事件只发送给所属的窗口。
这样即使某个窗口被恶意内容影响,它也无法访问其他窗口的会话、凭据或传输任务。关闭一个标签时,对应的连接、任务、路径授权和运行期凭据会被统一清理。
本地路径边界:授权作用域
文件工作台需要访问本地文件,但本地绝对路径不会跨 IPC:
- 通过文件选择器或拖拽产生的访问被登记为一条 grant,绑定到 owner、带 TTL、可以被 retain 或撤销。
- 渲染层看到的是
grantId + relativePath,不是本地绝对路径。 - 所有本地文件操作都在 grant 作用域内解析路径,越界访问会被拒绝。
- 关闭会话时撤销该 owner 的全部 grant。
诊断边界:可排查但不泄密
诊断导出(diagnostics export)是排查问题时最有价值、也最敏感的功能。ConchTerm 的设计是:导出经过二次脱敏,且默认不包含会话级秘密。
- 日志默认走 JSON Lines,带轮转和过期清理,关键模式(password、token、key、credential、host、本地路径)会被脱敏。
- 完整 host/path 只在用户显式开启的、带自动过期的诊断模式中临时记录。
- 诊断导出包会再次脱敏本地路径、host、token、password、key 等,并不包含连接库、
known_hosts或秘密。
换句话说,你把诊断包发给别人帮忙排查,不会顺手把你的服务器地址、密码或私钥一起发出去。
这些边界是写进架构的,不是事后补的
ConchTerm 的安全模型不是上线前加的一层加密,而是从架构第 0 阶段就写进规则里的不变量:秘密不传播、路径不跨进程、IPC 全程校验、日志默认脱敏。这也是我们在 dogfood 阶段就愿意公开讨论这些边界的原因——它们是产品结构的一部分,而非营销话术。