Skip to main content

MCP / IPC 安全模型

本页说明支持 transcribe MCP 工具与 echophrase transcribe CLI 指令的 本机 IPC 服务器的安全设计 - 也就是 Echophrase 让 AI 代理与桌面应用程序 沟通的部分。本页写给希望自行检验设计,而非只听我们说法的读者。以下每项 说法都已对照目前的源代码核实,并附上文件与行号,方便您自行查证。
本页描述 CLI/MCP 进程与正在运行的桌面应用程序之间的传输方式。其余四个 MCP 工具(recordstopstatustranscode)完全不会用到这个服务器 - 它们完全在 CLI 进程内对本机文件运作。工具总览请参阅 MCP 服务器

仅绑定本机,永不对外

IPC 服务器明确且仅绑定 127.0.0.1
src-tauri/src/ipc/mod.rs:124-126)。代码中没有任何路径会绑定 0.0.0.0 或其他界面 - 模块本身的文档注解也载明同样的限制 (src-tauri/src/ipc/server.rs:3:「仅绑定 127.0.0.1(绝不使用 0.0.0.0)」)。 这具体代表什么:这个 socket 只有在同一台机器、同一个操作系统用户工作阶段 下运行的进程才能连上(loopback 界面不会对外部主机在您的区网或互联网上 公开,而典型的操作系统层级 socket 权限,也代表共用机器上的其他操作系统 用户账号无法连接)。除了「已经以您的身份在您机器上运行的软件」之外, 没有任何东西能连到 1424 端口。相较于绑定在可路由界面上的服务器,这是实质上 更小的攻击面:没有连接端口转发设置错误、没有路由器 UPnP 漏洞、也没有可能 意外暴露的云端元数据端点。

持有者权杖:每次启动全新产生,停止即失效

每个 POST /v1/transcribe 请求都必须带有 Authorization: Bearer <token>。检查采用常数时间比对,而非单纯的 ==, 目的正是避免比对过程出现计时侧通道 (src-tauri/src/ipc/server.rs:100-135constant_time_eq)。不需验证的 GET /v1/health 路由只回报应用程序版本 - 从不透露任何关于权杖或用户的 信息,因此探测这个端口不会得到「应用程序正在运行」以外的任何情报 (src-tauri/src/ipc/server.rs:15-16, 65-71)。 权杖的生命周期:
  • 每次启动服务器都全新产生。 start() 每次被调用时都会调用 write_fresh_token(),从 rand::thread_rng() 取出 32 个随机字节并 以十六进位编码 - 不是安装时产生一次,也不会跨重启重复使用 (src-tauri/src/ipc/mod.rs:107-133, 176-194)。
  • 产生后立即以仅拥有者可读写的权限写入(Unix 上为 0o600) (src-tauri/src/ipc/mod.rs:187-191)。(目前 Windows 与 macOS 依赖的是 每位用户设置档目录的默认 ACL,而非此程序路径中明确的等效 chmod 调用 - 详见下方「限制」一节。)
  • 存放位置与 CLI 读取的相同每位用户数据目录 - <平台数据目录>/echophrase-cli/run/ipc-tokensrc-tauri/src/ipc/paths.rs:76-105)。桌面应用程序与 CLI 各自独立 解析这个路径,但刻意设计为完全一致,避免任一方依赖另一方的 crate。
  • 服务器停止时即移除,而非放着不管:stop() 在返回前会调用 clear_token_best_effort()src-tauri/src/ipc/mod.rs:161-166, 200-207),强制结束时也会运行同样的清除。这一点值得特别强调: 停止的服务器是无法验证的,而不只是无法连接。即使有东西缓存了 前一个工作阶段的有效权杖,只要应用程序(或仅是开关)关闭,该权杖 立刻失效,因为支撑它的权杖文件已不存在,且下次 start() 会产生 完全无关的新权杖。
  • CLI 自己的读取路径,会把「找不到权杖文件」与「无法连接」视为同一种 情况,回报同一则友好消息,而非原始的连接错误(cli/src/ops.rs:316-322)。

明确选择加入,并在服务器端强制运行

服务器不会因为应用程序启动就自动开始监听。spawn() 依序检查两个条件, 两者皆成立才会启动(src-tauri/src/ipc/mod.rs:67-88):
  1. 目前工作阶段为 Premium 方案(is_premium_tier()),且
  2. 设置中的 MCP 服务器选项已明确激活。
此设置默认为 falsesrc-tauri/src/settings/mod.rs:575,注解为 「默认关闭(Pro 限定)」),且 Pro 检查采取失败即拒绝的原则:若尚无 应用程序句柄或工作阶段,is_premium_tier() 会返回 falsesrc-tauri/src/ipc/mod.rs:95-101) - 用代码自己的话说,就是「宁可 不公开本机服务器」。 要判断这是真正的闸门还是只是界面上的开关,有两个细节很重要:
  • 这个闸门是在 Rust 端强制运行,而非前端。支持设置开关的 Tauri 指令 set_mcp_server_enabled,会在打开设置前于服务器端重新检查 Pro 方案, 不受前端所认为的状态影响(src-tauri/src/settings/mod.rs:840-857)。
  • 手动编辑设置档尝试绕过同样行不通:用于 YAML 导入/协调的通用设置修补 路径,在启动服务器前会再次检查 Pro 方案,正是为了封堵这条路径 (src-tauri/src/settings/mod.rs:802-814,注解:「免费方案用户 导入/编辑 yaml 打开此设置,绝不能真的打开这个端口」)。
实时切换此设置会立即启动或停止服务器(不需重启应用程序),关闭时也会 如上所述移除持有者权杖。

五个工具能做什么、不能做什么

MCP 服务器(echophrase mcp)仅公开五个工具,通过 #[tool_router] 定义在固定方法集合上(cli/src/commands/mcp.rs:93-156):recordstopstatustranscodetranscribe。内部还有第六个子指令 worker,用来运行背景录音进程,但从未注册为工具 (cli/src/commands/mcp.rs:6-9cli/README.md:150) - 代理无法调用任何 工具对应到它。 已核实的能力边界:
  • 无法任意读取文件。 transcribe 接受一个 wav_path 字符串,但 HTTP 处理常式在碰触转录流程前会先验证:必须能通过 canonicalize() 解析 (这也会消除任何符号链接的花招)、必须存在、必须是文件、且扩展名 必须是 .wav,否则请求会在任何文件内容被模型读取前,直接被拒绝为 400 Bad Requestsrc-tauri/src/ipc/server.rs:163-186validate_wav_path)。回应内容仅有转录文字,绝不包含文件内容、 目录列表,或任何其他文件系统信息。
  • 无法运行代码或调用 shell。 HTTP 路由器(src-tauri/src/ipc/server.rs) 与工具路由器(cli/src/commands/mcp.rs)都没有任何程序路径会启动子进程、 将字符串当作代码运行,或把用户输入传给 shell。CLI 中唯一与外部进程 的交互,是它自己为了 record/stop 而产生的录音背景工作进程, 且不接受任何来自网络的未受信任输入。
  • record/stop/status/transcode 从不触及网络。 它们完全通过 crate::opscli/src/ops.rs)对本机文件与本机 cpal 音频设备运作, 与桌面应用程序或其 IPC 服务器是否正在运行无关。只有 transcribe 会与桌面应用程序沟通,且是通过上述持有者权杖保护的本机连接进行。
  • transcribe 的请求内容是文件路径,而非音频本身。 CLI 读取权杖后, 会将 {"wav_path": "<本機路徑>"} 以 POST 送至 http://127.0.0.1:1424/v1/transcribecli/src/ops.rs:315-329)。 桌面进程会自行从磁盘读取 WAV 字节;音频内容在整个往返过程中都 不会经由 socket 串行化传输,来回只有一个路径字符串与一段转录文字。

音频与转录结果会流向哪里

转录作业在已经运行中的桌面应用程序进程内进行,使用它已加载的后端 (Candle、ONNX/parakeet-rs)在本机 GPU/CPU 上推理 - 与桌面界面本身的 听写功能使用完全相同的程序路径。转录后端的任何实作中都没有 HTTP 用户端 (src-tauri/src/transcription/{mod,candle_whisper,onnx_backend,parakeet}.rs 除了文档注解中的网址外,不含任何网络调用),因此在这个流程中,音频、 路径或最终文字都不会发送到任何 Echophrase 服务器。这里涉及的唯一网络 流量,就是同一台机器上 CLI/MCP 进程与桌面应用程序之间的 loopback HTTP 请求。 这与产品更广泛的设计原则一致(与 MCP 无关):语音处理在本机完成, Echophrase 的服务器端仅用于验证与订阅管理,绝不处理音频。

诚实面对限制

  • 权杖文件的保护程度,仅等同于您的操作系统账号。 它的权限比特 (Unix 上为 0600)能阻挡共用机器上其他用户读取,但任何以 的身份运行的进程 - 行为异常的浏览器扩充功能、其他应用程序中受感染的 依赖项、恶意软件,或您已授予广泛文件系统访问权的另一个 AI 代理 - 都能读取您终端机能读取的同一个文件。这与您的操作系统用户账号属于 相同的信任边界,而非更强的边界。若您不信任以您身份运行的任意本机 进程,那么也不该信任这个权杖文件对它们保密 - 而这其实是几乎所有采用 「本机权杖 over loopback」设计的开发工具都有的共通事实,并非 Echophrase 特有。
  • 我们未验证 Windows/macOS 的文件 ACL。 0o600 权限设置调用是 #[cfg(unix)] 条件式编译(src-tauri/src/ipc/mod.rs:187-191);在 Windows 与 macOS 上,权杖文件继承的是您每位用户数据目录的默认 操作系统权限,我们在此并未独立审核。
  • 硬崩溃可能让过期的「running」状态文件残留。 用来回报服务器 「为何」停止服务的状态档(not_pro / disabled / running), 只会在正常关闭时清除;若进程硬崩溃,可能留下显示 "running" 的 状态,即使进程实际上已不存在。这不会造成安全性后果 - 真正把关请求的 是权杖文件,已死亡的进程无论如何都无法回应请求 - 但既然我们说过会 诚实揭露落差而非只讲保证,这一点值得一提 (src-tauri/src/ipc/mod.rs:252-265)。
  • 本页描述的是我们于 2026-07-30 读取的代码。 撰写本文时 IPC 模块正在积极开发中(正在进行的变更,是为了让 CLI 错误消息更友好而 添加状态档);本页在设计层级的说法(仅本机、每次启动全新权杖、 停止即移除、Pro 限定、失败即拒绝)是我们预期会保持稳定的部分, 但请将具体行号视为当下快照,而非永久保证。

MCP 服务器

返回工具总览与设置说明。