Web 控制台
Chute 在运行中的内核里通过 HTTP 提供一个管理控制台。它是唯一在所有平台上都一致的界面,所以每当有东西需要被看见而不是被配置时——一次请求的选路、一条没生效的改写、一个连接为什么被拒——本手册都会指向这里。
它不是 Chute Dashboard。Dashboard 是一个独立的 macOS 应用,去连接某个 Chute 实例;这里说的控制台由内核自己提供,用浏览器打开。
启用与进入
控制台需要在 [General] 段里配置 external-http-controller,默认关闭:
[General]
external-http-controller = 127.0.0.1:9090
external-http-ui = true
访问需要鉴权。如果你没有设置 external-http-secret,Chute 会生成一个令牌并保存在 control-token 文件里,重启后仍沿用同一个——所以实际的进入方式是让 App 把已经带好令牌的地址交给你。页面加载时会从地址里取走令牌并存进浏览器的 localStorage,一个浏览器只需登录一次。每个平台都有对应入口,见打开 Web 控制台。
Overview(总览)
服务是否在运行、已运行多久、内存占用、HTTP 与 SOCKS 监听的地址和端口,以及本次运行使用的文件路径。当你不确定某个浏览器标签页连的是哪一个实例时,这一页能认出它。
Traffic(流量)
整个引擎的累计字节数与当前速度、一张速度历史图,以及按出站适配器的分解。用它回答「到底有没有东西在动」,以及当策略组在几个出口之间选择时,实际是哪一个在扛流量。
Connections(连接)
你花时间最多的一页。两个 Tab:
- Current(当前) —— 此刻打开着的连接。
- History(历史) —— 已关闭的连接,最新在前,从本次运行的记录里读回。
每一行带有主机、端口、类型、选中的策略、命中的规则、时长和字节数。时长是从这个浏览器标签页第一次看到该连接起算的,不是连接真正建立的时间,所以只在 Current(当前) Tab 上有意义。Inspect(查看) 会就地展开该行,回答这个请求为什么去了那里:
| 字段 | 它告诉你什么 |
|---|---|
| 命中规则 / 规则来源 | 决定这条连接的那一行规则,以及它来自哪个段 |
| 选中策略 / 出站适配器 | 规则选中的策略,以及实际承载它的出站 |
| DNS 来源 | 哪个解析器作答,以及是否命中缓存 |
| 命中的改写 | 改动过这条消息的改写或 Mock 规则,用规则自己的原文——每条连接最多保留 16 条命中,超出时该行会注明「另有 N 条未显示」 |
| 关闭原因 / 备注 | 它为什么结束,以及内核想说的话 |
表格下方,展开的行会显示捕获到的请求与响应——报文头与报文体,就是它们在链路上的样子(对启用了 HTTPS 解密的主机,是解密后的)。
报文体的前提是开启流量记录
只有 Chute 捕获过,报文体才会在那里。请在复现问题之前打开记录:
- 在
[General]里写replica = true,或 - Chute Mac:菜单栏 → 流量捕获,或
- 走 API:
PUT /api/features/record-traffic,请求体{"enabled": true}。
不开也不影响连接出现,选路归因与时间点都还在——只是没有报文体。[Replica] 段会进一步限制捕获范围——那里的每个过滤项(包括 hide-crashlytics-request)都会生效——被它过滤掉的请求同样不会有报文体。
导出 HAR
Connections 页上的 Export HAR(导出 HAR) 会把当前 Tab 下载成一个 HAR 1.2 文件——最多 100 条,这是端点的默认上限(在端点上用 limit 最多可提到 300),而 History Tab 本身最多显示 200 条。HAR 是通用格式:可以用浏览器开发者工具打开(Network 面板 → 导入),也可以用 Charles、Proxyman 这类工具。
导出的内容包含这些工具期待的东西——请求与响应的头、时间线,以及开启记录后的报文体——外加每个条目一个 _kl 对象,装着 HAR 没有字段承载的信息:选中的策略、命中的规则,以及命中的改写。发送与接收的耗时来自捕获自身的时间戳,而不是推算出来的,所以它是有意义的。各端 App 也有自己的出口:Chute iOS 的 Dashboard 提供 导出为 HAR 与 全部导出为 HAR(由内核生成,格式相同);Chute Mac 的连接详情窗口有 请求与历史 按钮,会打开控制台——打开的是控制台首页,而不是那条连接。一个条目对应一条连接:在承载了多个请求的 keep-alive 连接上,导出描述的是第一条消息,并注明后续消息还有多少字节未包含。
DNS
解析器缓存及每条记录由哪个服务器作答、来自 [Host] 的条目,以及系统 hosts 文件。Clear Cache(清除缓存) 清空动态记录;这里没有逐条删除——那只存在于 API 的 DELETE /api/dns/records/:domain。
Policies(策略)
出站模式(Rule / Global / Direct,即 规则 / 全局 / 直连)以及每个策略组的当前选择。在这里改选择会立刻作用于运行中的内核——和在 App 里切换策略组是同一个动作。
Rules(规则)
这一页上有两类东西,行为并不相同。
路由规则 —— 即 [Rule] 段,按求值顺序列出。只读:它们来自配置文件。
改写与 Mock 规则 —— URL 改写、Header 改写、Body 改写、Mock,以及 MitM 主机列表。这些可以在这里增删:
- 把规则按你会写进配置文件的样子原样粘进去,然后按 Add(添加)。解析不了的行会连同解析器的抱怨一起被拒绝,而不会被存下来——一条永远匹配不到的规则没有任何症状,所以当场知道更好。
- Remove(删除) 删除一条;Clear(清空) 清空一族。
- 这些改动只存在于运行中的内核。它们不会写回配置文件,重载或重启后回到文件里写的样子。用它来试规则,试通了再把成功的那一行写进文件。
改写与 Mock 命中 —— 本次运行中命中过的每一条改写或 Mock 规则,带次数和最近一次命中的时间;这张表最多跟踪 512 条不同的规则,超出的部分会注明还有多少条命中了但没记上。这张表就是对「我的改写没反应」的回答:没出现在这里的规则从未匹配过,这和「匹配了但没有可见变化」是两个不同的问题。如何区分见我的改写为什么没反应?。
Diagnostics(诊断)
引擎正持有什么、上一次运行是怎么结束的,以及一组你可以朝它发起的探测。
- Footprint / CPU / Uptime / Live flows / Superseded flows / Engine generation(内存占用 / CPU / 运行时长 / 活跃流 / 过期代际流 / 引擎代际) —— 本次运行当前的形态。流数持平而内存占用一路上升是值得查的;在 iOS 与 tvOS 上,这也正是系统在回收扩展前盯着的东西。
- Previous exit(上次退出) ——
Clean(正常退出)、Terminated unexpectedly(异常终止)或Killed for memory(疑似因内存被终止),附带上一次运行的 id、运行时长与峰值内存。Chute「崩了」时请先读这一行:Killed for memory(疑似因内存被终止)意味着是系统回收了 Chute,而不是 Chute 自己出错,这会改变接下来该查什么。 - Refusals(拒绝计数) —— 引擎拒绝过什么、因为什么,按资源与原因计数的一张表。当一条连接被丢弃而别处都解释不了时,原因通常就在这里。
- Run a probe(运行探测) —— 可达性(ICMP,给了端口则用 TCP)、经运行中解析器的 DNS 查询、出口 IP 检测,以及某条策略的延迟测试。每项都有上限且只回答一次。未定义的策略名会被拒绝而不是去测量,所以打错字回来的就是打错字。
- Events(事件) —— 本次运行的关键时刻:大面积失败、出口变化等。
- Tailscale —— 配置了
[Tailscale]段时给出引擎实时状态;没有该段时是idle,这是正常回答。 - Download diagnostic bundle(下载诊断包) —— 一个脱敏压缩包,可以直接附在支持邮件里。内容见发送诊断包。
Config(配置)
运行中的配置,显示在一个可编辑的框里,Reload(重新加载) 会应用框里的内容。
这里显示的配置已经把机密抹掉了。 密码、控制面 secret、CA 口令、WireGuard 密钥之类都显示为
<redacted>。而 Reload(重新加载) 会原样应用框里的内容,所以照着显示的文本重载,等于把这些机密替换成字面量<redacted>写进运行中的内核——需要它们的策略会开始失败。配置文件不受影响,重新选中该配置即可恢复。请把这一页用于阅读配置,以及应用你完整键入的改动。要编辑文件本身,请用各端 App 里的编辑器。
Logs(日志)
内核日志的实时跟随,按等级着色,Pause(暂停) 可以在你阅读时暂停。详细程度由 loglevel 决定,而不是这一页——如果你要的行不在这里,请调高它并重现。
控制台能交出什么
请把「能访问控制台」等同于「能访问本次运行见过的一切」。它可以给出带有 URL、请求头和进程名的连接记录、捕获到的请求与响应报文体、配置,以及日志。这正是「不鉴权」不能是默认值的原因,也是把它绑到回环之外必须由你自己设置 secret 的原因——见 external-http-secret。
本页为英文版的翻译。如内容有出入,以英文版为准。