隐私与安全
文档编写中
部分内容可能尚不完整。
ThinkTerm 不收集任何关于你、你的机器或你如何使用它的信息。没有遥测、没有统计分析、没有追踪器,项目方也没有接收这些数据的服务器。除了一次更新检查之外,ThinkTerm 通过网络发出的每一样东西,都是你自己发起的。
不经你操作就会发出的
只有更新检查。桌面应用运行期间,会向 GitHub 的 release API 询问是否有新版本:启动后大约十秒检查一次(前提是记录里的上一次检查已经早于检查频率),之后每过同样长的时间再检查一次。频率是 设置 › 软件更新 里的 检查频率 —— 每 12 小时、每天 或 每周 —— 在你用这一行之前则是 check_for_updates_interval_seconds,默认十二小时。命令行工具、TUI 和会话 server 从不自行检查。设置 › 软件更新 里的 检查更新 在你按下时发出同样的请求。
这个请求是匿名的。除非你自己设置了 THINKTERM_UPDATE_TOKEN,否则它不带任何凭据 —— 那个变量只在访问私有 fork 时才有用。ThinkTerm 自己的代码从不读取 GITHUB_TOKEN,所以你为别的工具导出的 token,不会被一次你没有发起的检查发送出去。安装脚本会读取它,而更新会运行那个脚本,见下文。GitHub 看到这个请求,就像看到任何其他请求一样;制作 ThinkTerm 的人看不到。
设置 › 软件更新 里的 自动检查更新 开关可以关掉它;在你用过这个开关之前,在配置里写 check_for_updates = false 也能关闭。改动在一分钟内生效。这两项见 配置,更新流程的其余部分见 更新。
你使用某项功能时才会发出的
| 你做的事 | 发往哪里 |
|---|---|
| 通过 SSH、Mosh 或 ThinkTerm Connect 连接 | 你连接的那台主机 |
连接一台开着 检测操作系统 或 识别远端程序 的主机(新主机两项都是开的) | 那台主机,走同一条连接:检查它的发行版;或者把 ThinkTerm 的 shell 集成复制到那里的 ~/.config/thinkterm/,并在登录 shell 的启动文件里加一行来加载它,此后这个 shell 每开始一条命令,都会把命令行报告给 ThinkTerm |
| 打开一篇显示网络图片的笔记 | 图片地址指向的那台服务器 |
在设置里按下 检查更新,或运行 thinkterm update --check | GitHub 的 release API,和后台检查是同一个匿名请求 |
| 连接一台没装 ThinkTerm 的 ThinkTerm Connect 主机,并同意安装 | 这台机器连 GitHub(只在你同意之后),查找发布版并取一份带签名的安装脚本,通过同一条 SSH 连接发给那台主机;然后那台主机以 SSH 用户的身份,从 GitHub 下载发布文件到 ~/.local |
| 同意更新远端 server | 和安装一样,发生在你对那唯一一个问题回答 yes 之后,这个问题会说明那台主机上将有什么变化。在此之前什么都不会查。重启 server 是另一个问题 |
运行 thinkterm update,或按下 安装更新 | GitHub:发布版 API,用来找到最新的发布版;安装脚本;以及发布归档 —— GitHub 会把归档下载重定向到它自己的存储,GitHub 慢时也可能由镜像提供(见下文) |
| 开启网页访问 | 打开你所生成链接的浏览器 |
| 打开内置 tailnet | Tailscale 的控制服务器,或你在 Control server 里设置的那台,用来加入并保持在 tailnet 里;到 tailnet 里机器的连接都经过它,也可能由 Tailscale 的中继服务器转发 |
为网页访问开启 连接其他机器,并在页面里打开一台机器 | 你指定的那台机器,由 server 通过 SSH 连接。如果那里需要安装或更新 ThinkTerm,server 会尽可能把自己的程序经这条连接复制过去,否则由那台主机从 GitHub 下载安装脚本和发布文件 |
安装更新 和 thinkterm update 会在你的环境里运行安装脚本。脚本在设置了 GITHUB_TOKEN 时会读取它并随请求发送,但只有在必须自己下载归档时才会这样;ThinkTerm 已经下载并核对过归档时,脚本不会再下载。只要 GitHub 记录了这个归档的校验和,ThinkTerm 就会这么做。
本页凡是说发布归档(在 Windows 上是安装程序)来自 GitHub 的地方,它也可能来自镜像:由别人运营、转发 GitHub 下载的主机。下载来源 为默认的 自动 时,以及在安装脚本里,只有 GitHub 慢或连不上时才会去试镜像。镜像能看到你的地址和你下载的是哪个版本,和 GitHub 一样。安装更新 和 thinkterm update 运行的查找发布版和安装脚本始终来自 GitHub;token 从不发给镜像,设置了 token 时根本不用镜像;而且只有 GitHub 记录了校验和、能核对镜像发来的内容时,才会用镜像。设置 › 软件更新 里的 直连 GitHub,或者脚本的 --mirror direct,会让所有下载都只走 GitHub。ThinkTerm 为远端 server 安装或更新时,会自己取得安装脚本和发布信息,再通过 SSH 发给那台主机。下载来源 为 自动 时,它们也可能来自镜像,但必须带有能用 ThinkTerm 内置密钥验证通过的签名;验证不通过的脚本会换成随 ThinkTerm 附带的那一份,未经验证的东西不会在那台主机上运行。主机自己下载发布归档,来源是 GitHub 或镜像,规则同上。有系统代理或环境里指定的代理时,更新请求会经过它。
笔记里的网络图片默认会加载,而加载一张图片会让那台服务器看到你的地址,和任何网页一样。note_remote_images_enabled = false 可以禁止它。
插件是你自己安装的程序,可以像其他任何程序一样使用网络。比如 ThinkTerm 源码里的 Stocks 插件,就会从 Yahoo Finance 获取报价。下载包里不含任何插件,见 插件。
第一次打开应用时出现的设置页不会给这张清单增加任何内容。它会在这台电脑上查找 Herdr 会话、WezTerm 配置,以及你的代码编辑器、编程 agent 和 cmux 最近用过的文件夹,并且不会把其中任何东西发往别处。它只读这些程序已经写下的内容:Herdr 的会话文件,加上(Windows 除外)通过 Herdr 自己的本地 socket 询问哪些会话在运行;WezTerm 的配置文件;各编辑器的最近文件夹列表;以及 Claude Code、Codex、Pi 和 cmux 各自记下的所用文件夹。对 agent 的会话文件,它只取文件夹和日期,不取说过的话。它不会进入这些文件夹去看里面有什么,因为那可能让 macOS 在你还没选择导入之前就弹出访问文档或桌面的请求。
保存在本机的
ThinkTerm 把它的状态保存在你的用户账户里:Space、Project 和 Thread,SSH 主机簿,片段,最近使用的命令,各项设置。它们在各个系统上分别放在哪里,见 配置。笔记 Vault 是你自己选的文件夹,里面是普通的 Markdown 文件。
终端输出保存在内存里,由应用或会话 server 持有。有一项设置会把其中一部分写到磁盘上,而且在桌面应用所在的机器上默认是开的:设置 › 通用 › 重新打开时恢复窗格内容。ThinkTerm 的终端按正常流程停下时 —— 连同终端一起退出应用、关机或注销、会话 server 停止 —— 每个本地 pane 的主屏幕和回滚历史(最多 10,000 行)会保存到终端状态目录的 kept-output 里,好在下次放回新 shell 的上方。布局一回来,记录就删除;如果那个 Thread 没再打开,15 天后也会删除;关掉这项设置会删除全部记录。写入之前,看起来像密码、token 或私钥的文字会换成 •,地址里带有这类内容的链接会被丢掉。这只是模式匹配,不可能认出所有秘密,所以请把 pane 里显示过的内容当作明文留在了磁盘上。在没有桌面应用的机器上,比如远程主机,要在那里运行 thinkterm cli restore-output on 才会保存任何东西;restore-output off 把它关上。见 配置。
设置 › 工作区 › 系统通知 默认开启。ThinkTerm 在后台时,一个 Thread 完成、开始等你、或因错误停下,它都会交给系统一条通知,内容是 Thread 的名字、这个状态,以及那里运行的程序自己说的话。此后系统会像对待其他应用的通知一样显示和保留它,可能包括锁屏。如果不希望 Thread 名字或程序上报的内容出现在那里,就关掉这个开关,或者取消你不想被通知的场合:开关下面的 完成、等你 和 出错 是分开选的。
在 macOS 和 Linux 上,最要紧的那些文件以只有你能读的 0600 权限写入:
secret.key,加密已保存 SSH 密码的密钥- 片段文件
- 你导出的备份里的文件
- 网页监听的 token 文件,以及它的证书和私钥
web_machines.json,网页添加的机器以及为它们保存的密码重新打开时恢复窗格内容保存的记录,放在只有你能打开的文件夹里tailnet.json,内置 tailnet 的设置,其中的认证密钥和节点密钥用同一个secret.key加密- ThinkTerm 新建的笔记,除非所在文件夹给了它自己的权限
这挡得住这台机器上的其他账户,挡不住任何以你的身份运行的东西 —— 同步客户端、备份任务、插件 —— 它们读一个 0600 文件和 ThinkTerm 一样容易。这对保存的密码意味着什么,见 远程。
thinkterm tailnet up --serve-ssh 会让 tailnet 里的机器连到这台机器自己的 sshd,而 sshd 看到的来源是 127.0.0.1 或 ::1,所以任何信任本地连接的规则也会作用于它们。除此之外没有别的东西会打开它;见 远程。
Windows 上没有对应的权限位可设,这些文件继承所在文件夹的权限;对默认的用户配置文件来说,就是你本人、SYSTEM 和 Administrators 组。为了让凭据不进入漫游配置文件(在某些域里,Windows 会在注销时把它复制到配置文件服务器),SSH 主机簿、它的密钥和 web_machines.json 放在 %LOCALAPPDATA%\ThinkTerm\ssh\,网页监听的证书放在 %LOCALAPPDATA%\thinkterm\web-tls\。
网页端
在你打开之前,网页访问是关闭的。打开之后,页面、脚本和字体都由你自己的机器提供;页面不会向任何第三方发出请求,也不会收集任何使用情况。
浏览器凭你生成的 token 进入,token 是链接的一部分。页面一加载就会把 token 从地址栏里拿走,放进这个标签页的 sessionStorage,标签页关闭时浏览器会将其丢弃。拿到这条链接的人,就在那台机器上拥有一个以你身份运行的 shell,所以它和 SSH 密钥一样是凭据。
只有配置了 token_file,服务端才会把 token 写入磁盘;否则它们只在内存里,重启后全部失效。对每个 token,它写下的是 token 的 SHA-256 摘要,而不是 token 本身 —— 所以文件被复制走也放不进任何人 —— 以及它的名称、创建时间、过期时间、最后使用时间,和对最后使用它的浏览器的粗略描述,比如 "iPhone · Safari"。不会记录任何设备的地址,服务端日志里记的是哪个 token 被放行,而不是谁用了它。
只有在监听绑定到非回环地址、而你又没有提供自己的证书时,才会生成自签名证书。证书里写着这台机器的主机名和它的地址,任何连到这个端口的人都能看到 —— 证书本来就是做这个的。
页面会把你对它的选择 —— 语言、主题、字号、配色方案、当时显示的 Space、面板宽度、命令面板最近的选择,以及它应当重新打开的其他机器(在 thinkterm.machines 下)—— 保存在浏览器的 localStorage 里,留在那台设备上。清除这个站点的数据就会忘掉它们。谁能访问这个端口、如何核对证书,见 网页访问。
页面还可以通过 server 打开其他机器。这默认是关闭的:设置 › 网页 里的 连接其他机器 会对所有监听器开启,没有桌面的 server 则在 web_servers 的某个监听器上设置 relay = true。关闭它,已经打开的机器会在几秒内断开。开启后,发起连接的是 server,通过 SSH,用它自己的密钥、它的 SSH agent 里的密钥和它自己的 ~/.ssh/config。桌面端主机簿里保存的主机也会列出来,保存了密码的主机会直接用那个密码登录,不再询问。所以页面能到达这台 server 的 SSH 配置所能到达的任何地方,还能自己添加任意主机和端口。token 本来就给了 server 上的一个 shell,从那里也能用同样的密钥,因此这并不增加额外的访问权限;但开启时链接一旦泄露,能到达的就不只是 server,还有那些机器。主机提出的问题 —— 密码、是否信任它的主机密钥、是否在那里安装 ThinkTerm —— 会在页面里问你,并经由 server 回答;你要求保存的密码或密钥口令,会加密保存在 server 上的 web_machines.json 里,和桌面端保存的密钥放在一起。在页面的新主机表单里填写的密码也以同样方式保存,你同意在某台主机上安装 ThinkTerm 的回答也会被记住。这个连接不是端到端的:SSH 会话终止于 server 而不是浏览器,所以浏览器和主机之间传递的内容都要经过它。其余见 网页访问。
macOS 上的权限提示
macOS 会把一个程序做的事记在启动它的那个应用头上。ThinkTerm 启动你的 shell,你的 shell 启动你运行的一切,所以一条碰到受保护内容的命令 —— 比如 find 遍历到存放通讯录的文件夹,或者某个打开摄像头的工具 —— 弹出的提示上写的会是 ThinkTerm。提示的原文也是这么说的:An application launched via ThinkTerm would like to access…。ThinkTerm 本身没有任何去读取你的通讯录、摄像头、位置之类的代码。
放在后台会话 server 里的本地终端,是由那个 server 启动的,而不是由应用启动的,它会以自己的名义 thinkterm-mux-server 申请权限。一个你已经允许过 ThinkTerm 访问的文件夹,可能还需要为 server 再允许一次。ThinkTerm 如何告诉你被拒绝的是哪一个,见 配置。
在 macOS 上,设置 › 权限 能在某个程序提出请求之前,就让你看到这些程序已经拥有什么:完全磁盘访问、屏幕与系统录音、麦克风 和 摄像头,各自显示为已允许、没有、被拒绝、还没问过或查不到,按你的终端归谁名下(应用还是会话 server)来读取。查看本身从不会让 macOS 弹出提示。要特别小心的是完全磁盘访问:一旦允许,终端里的任何程序都可以读写其他应用的数据,macOS 不会再问。见 配置。
程序打印出的链接与粘贴内容
终端里的程序可以打印一个链接,让任何地址藏在任何文字后面;你粘贴的文本也可能带有换行,内容一到就把命令执行了。ThinkTerm 对这两种情况,都会先问再做。
点击链接时,网页,也就是 http 或 https 地址,会直接在浏览器里打开。其他任何地址 —— 文件、mailto:、ssh://、某个应用自己的 scheme,或者根本没有 scheme 的文字 —— 会先弹出一个小菜单,把地址显示出来,让你选 用其他应用打开 或 取消。地址里含有看不见的字符,比如零宽空格,或能让文字方向反转的控制字符,则根本不会打开:菜单会把这些字符按码点写出来,并显示 没有打开:链接不安全。ThinkTerm 交给系统打开工具的东西,不管是链接还是路径,只要以短横线开头就会被拒绝,因为 open 和 xdg-open 会把它当成选项;名字这样的文件可以写成 ./-name 或完整路径。ThinkTerm 的日志只记录“打开过一个链接”,不记录它的地址。
设置 › 终端 › 粘贴带换行的内容前先问 默认是关的。打开后,带换行的粘贴会先问 仍然粘贴 还是 取消,但只针对没有要求括号粘贴(bracketed paste)的程序,也就是内容一到就逐行执行的那种。zsh 这类把整段当一次粘贴的 shell 不会被问,判断不出来的 pane 也不问。
插件
插件以你的身份、用你的权限运行。新插件在你允许之前保持关闭 —— 在设置或网页的设置里点 允许,或运行 thinkterm plugin enable —— 如果插件被移到别的目录,会再问一次。允许之后,ThinkTerm 也只在侧栏里显示它,但程序本身外面没有沙盒:它能读你的文件、使用网络、运行其他程序;在通过 SSH 访问的终端旁边,它还能请 ThinkTerm 在那台主机上做同样的事。安装插件时,像安装任何其他程序一样对待它。详见 插件。
报告安全问题
请私下报告,使用 GitHub 的私密漏洞报告功能:打开仓库的 Security 标签页,选择 Report a vulnerability,或者直接前往 github.com/RoversX/ThinkTerm/security/advisories/new。请不要为此开公开的 issue、discussion 或 pull request。
尽量附上以下内容:
- 版本号(
thinkterm -V的输出)和你的操作系统 - 涉及的部分 —— 桌面应用、会话 server、网页访问、SSH 或 Mosh 连接、插件 server
- 复现步骤,或者概念验证
- 攻击者能做到什么,以及事先需要具备什么
你应该会在一周内收到回复。复杂问题的修复可能需要更久,期间会告诉你进展。修复发布时,安全公告会随之公开并致谢你,除非你不希望署名;在那之前,请不要公开细节。
只支持最新的发布版,修复会进入下一个版本。报告前请确认问题在最新版上仍然存在。
什么算在范围内
仓库里的全部代码都在范围内,包括 ThinkTerm 从 WezTerm 继承的代码。如果某个问题也影响 WezTerm,请注明,以便同时向上游报告。
以下不算 ThinkTerm 的漏洞:
- 已安装的插件能做的事。它是你自己安装的程序,以你的身份运行。
- 已经持有有效浏览器 token、能读取你的 ThinkTerm 数据,或者能以你的身份登录的人所能获得的访问。token 和保护已保存密码的密钥都是凭据。
- ThinkTerm 只是负责运行的那些程序本身的问题,比如你的 shell、
ssh或mosh。
其他来源的构建
以上所说,针对的是 ThinkTerm 的源码,以及它自己的发布流程在 GitHub 上发布的构建。从其他地方获得的构建,可能已被改成另一种行为。
