MCP / IPC 安全模型
本頁說明支援transcribe MCP 工具與 echophrase transcribe CLI 指令的
本機 IPC 伺服器的安全設計 - 也就是 Echophrase 讓 AI 代理與桌面應用程式
溝通的部分。本頁寫給希望自行檢驗設計,而非只聽我們說法的讀者。以下每項
說法都已對照目前的原始碼核實,並附上檔案與行號,方便您自行查證。
本頁描述 CLI/MCP 行程與正在執行的桌面應用程式之間的傳輸方式。其餘四個
MCP 工具(
record、stop、status、transcode)完全不會用到這個伺服器 -
它們完全在 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-135,constant_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-token(src-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):
- 目前工作階段為 Premium 方案(
is_premium_tier()),且 - 設定中的 MCP 伺服器選項已明確啟用。
false(src-tauri/src/settings/mod.rs:575,註解為
「預設關閉(Pro 限定)」),且 Pro 檢查採取失敗即拒絕的原則:若尚無
應用程式控制代碼或工作階段,is_premium_tier() 會回傳 false
(src-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):record、
stop、status、transcode、transcribe。內部還有第六個子指令
worker,用來執行背景錄音行程,但從未註冊為工具
(cli/src/commands/mcp.rs:6-9、cli/README.md:150) - 代理無法呼叫任何
工具對應到它。
已核實的能力邊界:
- 無法任意讀取檔案。
transcribe接受一個wav_path字串,但 HTTP 處理常式在碰觸轉錄流程前會先驗證:必須能透過canonicalize()解析 (這也會消除任何符號連結的花招)、必須存在、必須是檔案、且副檔名 必須是.wav,否則請求會在任何檔案內容被模型讀取前,直接被拒絕為400 Bad Request(src-tauri/src/ipc/server.rs:163-186,validate_wav_path)。回應內容僅有轉錄文字,絕不包含檔案內容、 目錄列表,或任何其他檔案系統資訊。 - 無法執行程式碼或呼叫 shell。 HTTP 路由器(
src-tauri/src/ipc/server.rs) 與工具路由器(cli/src/commands/mcp.rs)都沒有任何程式路徑會啟動子行程、 將字串當作程式碼執行,或把使用者輸入傳給 shell。CLI 中唯一與外部行程 的互動,是它自己為了record/stop而產生的錄音背景工作行程, 且不接受任何來自網路的未受信任輸入。 record/stop/status/transcode從不觸及網路。 它們完全透過crate::ops(cli/src/ops.rs)對本機檔案與本機cpal音訊裝置運作, 與桌面應用程式或其 IPC 伺服器是否正在執行無關。只有transcribe會與桌面應用程式溝通,且是透過上述持有者權杖保護的本機連線進行。transcribe的請求內容是檔案路徑,而非音訊本身。 CLI 讀取權杖後, 會將{"wav_path": "<本機路徑>"}以 POST 送至http://127.0.0.1:1424/v1/transcribe(cli/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 伺服器
返回工具總覽與設定說明。