Exec host 重構計畫

目標

  • 新增 exec.host + exec.security 以在 sandboxgatewaynode 之間路由執行。
  • 預設值維持安全:除非明確啟用,否則不進行跨主機執行。
  • 將執行分割為 headless runner 服務,搭配選用的 UI(macOS 應用)透過本地 IPC。
  • 提供逐代理策略、白名單、詢問模式和節點綁定。
  • 支援可搭配或不搭配白名單運作的詢問模式
  • 跨平台:Unix socket + token 驗證(macOS/Linux/Windows 一致)。

非目標

  • 不做舊有白名單遷移或舊有 schema 支援。
  • 節點 exec 不做 PTY/串流(僅彙總輸出)。
  • 不在既有的 Bridge + Gateway 之外建立新的網路層。

決策(已鎖定)

  • 設定鍵: exec.host + exec.security(允許逐代理覆寫)。
  • 提升: 保留 /elevated 作為 gateway 完全存取的別名。
  • 詢問預設值: on-miss
  • 核准儲存: ~/.openclaw/exec-approvals.json(JSON,不做舊有遷移)。
  • Runner: headless 系統服務;UI 應用透過 Unix socket 提供核准。
  • 節點身分: 使用既有的 nodeId
  • Socket 驗證: Unix socket + token(跨平台);需要時之後分割。
  • 節點主機狀態: ~/.openclaw/node.json(節點 id + 配對 token)。
  • macOS exec host: 在 macOS 應用內執行 system.run;節點主機服務透過本地 IPC 轉發請求。
  • 不使用 XPC helper: 維持 Unix socket + token + peer 檢查。

關鍵概念

Host

  • sandbox:Docker exec(現行行為)。
  • gateway:在 gateway 主機上 exec。
  • node:透過 Bridge(system.run)在節點 runner 上 exec。

安全模式

  • deny:一律阻擋。
  • allowlist:僅允許匹配者。
  • full:允許一切(等同 elevated)。

詢問模式

  • off:從不詢問。
  • on-miss:僅在白名單未匹配時詢問。
  • always:每次都詢問。

詢問與白名單獨立;白名單可搭配 alwayson-miss 使用。

策略解析(逐 exec)

  1. 解析 exec.host(工具參數 -> 代理覆寫 -> 全域預設)。
  2. 解析 exec.securityexec.ask(相同優先順序)。
  3. 若 host 為 sandbox,以本地沙箱 exec 進行。
  4. 若 host 為 gatewaynode,在該主機上套用 security + ask 策略。

預設安全

  • 預設 exec.host = sandbox
  • gatewaynode 預設 exec.security = deny
  • 預設 exec.ask = on-miss(僅在 security 允許時生效)。
  • 若未設定節點綁定,代理可定位任何節點,但僅在策略允許時。

設定介面

工具參數

  • exec.host(選用):sandbox | gateway | node
  • exec.security(選用):deny | allowlist | full
  • exec.ask(選用):off | on-miss | always
  • exec.node(選用):host=node 時使用的節點 id/名稱。

設定鍵(全域)

  • tools.exec.host
  • tools.exec.security
  • tools.exec.ask
  • tools.exec.node(預設節點綁定)

設定鍵(逐代理)

  • agents.list[].tools.exec.host
  • agents.list[].tools.exec.security
  • agents.list[].tools.exec.ask
  • agents.list[].tools.exec.node

別名

  • /elevated on = 為代理 session 設定 tools.exec.host=gatewaytools.exec.security=full
  • /elevated off = 為代理 session 恢復先前的 exec 設定。

核准儲存(JSON)

路徑:~/.openclaw/exec-approvals.json

用途:

  • 執行主機(gateway 或節點 runner)的本地策略 + 白名單。
  • 無 UI 可用時的詢問回退。
  • UI 用戶端的 IPC 憑證。

提議的 schema(v1):

{
  "version": 1,
  "socket": {
    "path": "~/.openclaw/exec-approvals.sock",
    "token": "base64-opaque-token"
  },
  "defaults": {
    "security": "deny",
    "ask": "on-miss",
    "askFallback": "deny"
  },
  "agents": {
    "agent-id-1": {
      "security": "allowlist",
      "ask": "on-miss",
      "allowlist": [
        {
          "pattern": "~/Projects/**/bin/rg",
          "lastUsedAt": 0,
          "lastUsedCommand": "rg -n TODO",
          "lastResolvedPath": "/Users/user/Projects/.../bin/rg"
        }
      ]
    }
  }
}

說明:

  • 不支援舊有白名單格式。
  • askFallback 僅在需要 ask 且無可達 UI 時套用。
  • 檔案權限:0600

Runner 服務(headless)

角色

  • 在本地強制 exec.security + exec.ask
  • 執行系統指令並回傳輸出。
  • 發出 Bridge 事件用於 exec 生命週期(選用但建議)。

服務生命週期

  • macOS 上為 Launchd/daemon;Linux/Windows 上為系統服務。
  • 核准 JSON 為執行主機本地。
  • UI 提供本地 Unix socket;runner 依需求連接。

UI 整合(macOS 應用)

IPC

  • Unix socket 在 ~/.openclaw/exec-approvals.sock(0600)。
  • Token 儲存在 exec-approvals.json(0600)。
  • Peer 檢查:僅限相同 UID。
  • 挑戰/回應:nonce + HMAC(token, request-hash) 防止重播。
  • 短 TTL(如 10s)+ 最大 payload + 速率限制。

詢問流程(macOS 應用 exec host)

  1. 節點服務從 gateway 接收 system.run
  2. 節點服務連接至本地 socket 並傳送提示/exec 請求。
  3. 應用驗證 peer + token + HMAC + TTL,然後依需要顯示對話框。
  4. 應用在 UI 上下文中執行指令並回傳輸出。
  5. 節點服務將輸出回傳給 gateway。

UI 不存在時:

  • 套用 askFallbackdeny|allowlist|full)。

流程圖(SCI)

Agent -> Gateway -> Bridge -> Node Service (TS)
                         |  IPC (UDS + token + HMAC + TTL)
                         v
                     Mac App (UI + TCC + system.run)

節點身分 + 綁定

  • 使用 Bridge 配對中既有的 nodeId
  • 綁定模型:
    • tools.exec.node 將代理限制在特定節點。
    • 若未設定,代理可選擇任何節點(策略仍強制預設值)。
  • 節點選擇解析:
    • nodeId 精確匹配
    • displayName(正規化)
    • remoteIp
    • nodeId 前綴(>= 6 字元)

事件

誰看到事件

  • 系統事件為逐 session,在下一次 prompt 時呈現給代理。
  • 儲存在 gateway 的記憶體內佇列(enqueueSystemEvent)。

事件文字

  • Exec started (node=<id>, id=<runId>)
  • Exec finished (node=<id>, id=<runId>, code=<code>) + 選用的輸出尾部
  • Exec denied (node=<id>, id=<runId>, <reason>)

傳輸

方案 A(建議):

  • Runner 傳送 Bridge event frame exec.started / exec.finished
  • Gateway 的 handleBridgeEvent 將這些對應至 enqueueSystemEvent

方案 B:

  • Gateway 的 exec 工具直接處理生命週期(僅同步)。

Exec 流程

Sandbox host

  • 既有的 exec 行為(Docker 或未沙箱化時為主機)。
  • PTY 僅在非沙箱模式下支援。

Gateway host

  • Gateway 行程在自己的機器上執行。
  • 強制本地 exec-approvals.json(security/ask/allowlist)。

Node host

  • Gateway 以 system.run 呼叫 node.invoke
  • Runner 強制本地核准。
  • Runner 回傳彙總的 stdout/stderr。
  • 選用的 Bridge 事件用於開始/完成/拒絕。

輸出上限

  • 合併的 stdout+stderr 上限為 200k;事件保留尾部 20k
  • 截斷時附加清楚的後綴(如 "… (truncated)")。

斜線指令

  • /exec host=<sandbox|gateway|node> security=<deny|allowlist|full> ask=<off|on-miss|always> node=<id>
  • 逐代理、逐 session 覆寫;除非透過設定儲存否則不持久。
  • /elevated on|off|ask|full 仍為 host=gateway security=full 的捷徑(full 跳過核准)。

跨平台方案

  • Runner 服務是可攜的執行目標。
  • UI 為選用;不存在時套用 askFallback
  • Windows/Linux 支援相同的核准 JSON + socket 協定。

實作階段

第 1 階段:設定 + exec 路由

  • 新增 exec.hostexec.securityexec.askexec.node 的設定 schema。
  • 更新工具串接以遵循 exec.host
  • 新增 /exec 斜線指令並保留 /elevated 別名。

第 2 階段:核准儲存 + gateway 強制

  • 實作 exec-approvals.json 讀寫器。
  • gateway host 強制白名單 + 詢問模式。
  • 新增輸出上限。

第 3 階段:節點 runner 強制

  • 更新節點 runner 以強制白名單 + 詢問。
  • 新增至 macOS 應用 UI 的 Unix socket 提示橋接。
  • 串接 askFallback

第 4 階段:事件

  • 新增節點 -> gateway Bridge 事件用於 exec 生命週期。
  • 對應至 enqueueSystemEvent 供代理 prompt。

第 5 階段:UI 完善

  • Mac 應用:白名單編輯器、逐代理切換器、詢問策略 UI。
  • 節點綁定控制(選用)。

測試計畫

  • 單元測試:白名單比對(glob + 不區分大小寫)。
  • 單元測試:策略解析優先順序(工具參數 -> 代理覆寫 -> 全域)。
  • 整合測試:節點 runner 拒絕/允許/詢問流程。
  • Bridge 事件測試:節點事件 -> 系統事件路由。

開放風險

  • UI 不可用:確保 askFallback 被遵循。
  • 長時間執行的指令:依賴逾時 + 輸出上限。
  • 多節點歧義:除非有節點綁定或明確的節點參數否則報錯。

相關文件