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 伺服器

返回工具總覽與設定說明。