模型 / 資料集
eugene1g/agent-safehouse avatar
eugene1g/agent-safehouse

Agent Safehouse:用 sandbox-exec 把 macOS 上的 AI agent 關進可組合的政策籠子

Sandbox your local AI agents so they can read/write only what they need

2,061 個 Star94 個 ForkShellApache-2.0

秒懂

它是什麼?
它解決的是本機 coding agent 拿到 shell 之後可以任意讀寫家目錄的問題,做法是 deny-first 的 sandbox-exec 政策加上可疊加的 profile。這篇談它的機制、實際指令、以及它在哪些情況下不該被當成安全邊界。
適合誰用?
如果你在 macOS 上跑 Claude Code 這類會執行 shell 指令的 agent,而且願意維護一份 appended profile,Agent Safehouse 是目前少數把 deny-first 與可組合政策講清楚的本機沙箱方案,值得先在自己的專案上跑一次。若你的工作流程大量依賴家目錄外的掛載點、或你期待的是能抵擋定向攻擊的隔離層,它不適合,README 自己就寫明它只是 hardening layer。
可以商用嗎?
可以。Apache-2.0 是寬鬆授權:你可以使用、修改並販售以它為基礎的軟體,只需保留著作權與授權聲明。
還在維護嗎?
有在維護。儲存庫最近一次提交在 2 天前。
用什麼語言寫的?
主要是 Shell(依據 GitHub 的語言統計)。

以上回答依據專案的 GitHub 資料(最近同步於 2026年9月15日)與我們的分析,不構成法律意見。

開源專案深度解析

它要擋的是 agent 拿到 shell 之後的那雙手

本機 coding agent 的權限模型有個結構性問題:它需要讀寫你的專案,於是多數人直接讓它繼承整個使用者帳號的權限。一旦 agent 執行的指令包含檔案操作,它能碰到的不只是專案目錄,還有 ~/.ssh、~/.aws、瀏覽器設定、以及各種 .env。Agent Safehouse 針對的就是這個落差,把 agent 放進 macOS 的 sandbox-exec 裡,從 deny-all 出發,只開放完成工作所需的檔案與整合。

目標讀者是已經在用 Claude Code、codex、amp 這類工具、並且願意為此維護一份政策檔的 macOS 開發者。README 的哲學段落把立場寫得很直白,它是一層 hardening,不是對抗定向攻擊的完整安全邊界。這個自我定位很重要,因為它決定了你該期待什麼:它降低的是 agent 誤觸與 prompt injection 造成大範圍檔案存取的機率,不是把 agent 變成不可逃逸的牢籠。

deny-first 政策與 workdir 的三種渲染方式

機制核心是 sandbox-exec 搭配可組合的 policy profile。Safehouse 在政策渲染階段把變數展開成具體規則,其中 HOME_DIR 與 WORK_DIR 是兩個關鍵輸入。README 特別澄清一個容易誤解的地方:HOME_DIR 本身不會授予家目錄的遞迴讀取權,它只是讓規則能用 home-relative 的形式描述。

workdir 的暴露方式有三個輔助形式:workdir-literal、workdir-subpath、workdir-prefix。相對的輔助參數以 / 開頭,因此可重用的政策可以指向像 <workdir>/.env 這樣的檔案,而不必把絕對路徑寫死在政策裡。這是它讓 profile 能跨專案重複使用的原因。

預設行為比想像中窄。它對 /、通往 $HOME 的路徑、以及 $HOME 本身只給 metadata-only traversal,讓 runtime 能探測被明確允許的家目錄路徑;對 ~/.config 與 ~/.cache 給目錄根層級的讀取,讓工具能發現 XDG 位置;另外開放少數 always-on profile 裡明確列出的家目錄項目,例如 git 與 ssh 的 metadata、以及共享的 agent 指令資料夾。README 用一個具體例子說明這個落差:stat "$HOME" 可以成功,但 ls "$HOME" 與 cat ~/secret.txt 仍然失敗,除非有更精確的規則授權。這種「能探測但不能列舉」的設計是有意為之,代價是某些工具若靠列舉目錄來找設定檔會直接壞掉。

安裝與最小可用的啟動方式

Homebrew 路徑是 brew install eugene1g/safehouse/agent-safehouse。不想用 tap 的話,README 也給了獨立腳本的做法:先 mkdir -p ~/.local/bin,再用 curl 從 releases/latest/download/safehouse.sh 下載到 ~/.local/bin/safehouse,最後 chmod +x。兩種方式都只裝一個執行檔,沒有常駐 daemon。

真正需要設計的是啟動包裝。README 建議把機器特定的設定放進 shell wrapper 加上一份本機 appended profile,而不是塞進專案設定。以 zsh 或 bash 為例,在 ~/.zshrc 或 ~/.bashrc 裡設定 SAFEHOUSE_APPEND_PROFILE 指向 $HOME/.config/agent-safehouse/local-overrides.sb,再定義一個 safe 函式呼叫 safehouse --add-dirs-ro="$HOME/server" --append-profile="$SAFEHOUSE_APPEND_PROFILE" "$@",然後用 safe-claude 包住 claude --dangerously-skip-permissions。fish 版本語法不同但結構一樣。

這裡有個值得注意的組合:包裝函式裡傳的是 --dangerously-skip-permissions。跳過 agent 自己的權限確認,前提是外層 sandbox 已經把範圍收窄。這個安排合理,但它把安全性完全押在政策檔是否正確上,沒有第二道防線。

本機政策檔的內容是 scheme 形式,例如 allow file-read* 搭配 home-literal "/.gitignore_global"、home-subpath "/Library/Application Support/CleanShot/media"、subpath "/Volumes/Shared/Engineering",再用 deny file-read* file-write* 加 workdir-literal "/.env" 把根目錄的環境檔鎖住,即使 workdir 可寫也讀不到。

append-profile 的順序就是它的覆寫語意

要移除預設的家目錄例外,README 指向 --append-profile,理由是 appended profile 最後載入,所以其中的 deny 規則可以收窄先前的預設。反過來說,這也是它唯一可靠的覆寫手段:你不能從前面插入規則去蓋掉後面的。

使用上的分工在文件裡寫得清楚:一般共享資料夾存取走 --add-dirs-ro 或 --add-dirs,--append-profile 留給機器本地的政策例外或最終的 deny/allow 覆寫。當 repo 是共享的、但每台開發機的掛載點不同時,這個切分才成立。把兩者混用會讓政策檔失去可攜性。

這個設計的張力在於,安全性的關鍵落在最後載入的那份檔案上,而那份檔案按定義不在版本控制裡。團隊可以共享 repo 的 profile,卻無法共享彼此的 local-overrides.sb,於是「這台機器實際上開了哪些路徑」變成一個只存在於本地的問題。這是可組合政策換來的代價,不是實作缺陷。

內建路徑的符號連結解析只覆蓋一半

macOS 的系統路徑常有符號連結,例如 /etc 指向 /private/etc 底下的位置。Safehouse 的內建 profiles/* 模組會引用這類相容路徑,例如 /etc、/private/etc/resolv.conf、/private/etc/localtime。政策渲染時,它會從 allow file-read* 規則裡解析內建絕對路徑,當撰寫的路徑是符號連結時,為真實目標路徑產生對應的授權。這樣主機特定的系統檔能繼續運作,而不必把來源 profile 放寬成對 /private/etc 的遞迴存取。

README 明確標出目前範圍:只涵蓋內建的絕對 literal 與 subpath 讀取授權。使用者自己提供的路徑授權另外正規化,可寫入的與 metadata-only 的內建規則目前不會被這個機制自動展開。

這個限制的實際後果是:如果你在 appended profile 裡授權一個符號連結路徑,不要預期它會自動被解析成真實目標。內建與使用者提供的路徑走兩套邏輯,這個不對稱在除錯時很容易被誤判成 sandbox-exec 本身的問題。

git worktree 只在啟動那一刻快照

當選定的 workdir 本身就是一個 Git worktree root 時,Safehouse 會在啟動時自動偵測。該 worktree 會取得它需要的共享 Git metadata 存取權,條件是它的 common dir 位於選定的 workdir 之外;同一個 repo 其他既有的 linked worktree 也會變成預設可讀,方便跨 tree 檢視。

關鍵限制在下一句:這個快照不會為已經在執行的 process 更新。所以如果你習慣把 worktree 建在像 ~/worktrees 這樣穩定的父目錄下,README 建議直接用 --add-dirs-ro 把那個根目錄加進去,而不是依賴自動偵測。

這是典型的靜態政策與動態工作流程之間的衝突。自動偵測涵蓋的是啟動當下存在的那一組 worktree,之後新建的不在內。對於一天開好幾個 worktree 的人來說,明確加父目錄比依賴偵測可靠。

Linux 使用者不該繞路,以及與 bubblewrap 的差異

Safehouse 是為 macOS 量身打造的,底層是 sandbox-exec。README 直接列出 Linux 的原生替代方案,包括 vetto、bubblewrap、firejail、nono.sh、sandlock 與 isolated-agent。這個清單本身就是一個判斷:作者不建議在 Linux 上用 macOS 的機制硬套。

以 bubblewrap 為例,兩者的取徑差在隔離單位的定義。bubblewrap 用的是 unprivileged user namespaces,它構造的是一個新的 mount 與 namespace 視圖,檔案系統的可見性是靠重新組裝出來的;Agent Safehouse 不重建檔案系統視圖,它是在既有的檔案系統上,用 sandbox-exec 的 deny-first 規則逐條開放路徑。前者比較容易做到「這個 process 根本看不到某個目錄」,後者則是在同一個可見的世界裡決定哪些操作被允許。

這個差異決定了政策的心智模型。Safehouse 的政策是一份允許清單,你要為 agent 的每個需求加一條;bubblewrap 的啟動參數更像是在描述一個新的環境。對已經熟悉 sandbox-exec profile 語法的人,Safehouse 的可組合 profile 與 workdir 輔助形式能省下重複撰寫;對不想學 scheme 形式政策的人,這個門檻是實實在在的。

維護成本與 Apache-2.0 的邊界

維護成本主要不在升級,而在政策檔本身。每次 agent 工具新增一種設定檔位置、或你的專案多一個需要存取的共享目錄,你都要在 appended profile 裡補規則,而那份檔案不在版本控制裡,也就沒有 review 流程。從 release 節奏看,v0.11.0 到 v0.11.1 相隔約一週,v0.11.1 到 v0.12.0 約兩個月,屬於持續維護中的專案,但這只說明它有在動,不代表升級無痛。

升級時要驗證的是內建路徑解析與 worktree 偵測這兩塊,因為它們的行為取決於政策渲染階段的邏輯,而 README 對這兩者的範圍描述都帶有「目前」這樣的限定詞。

授權是 Apache-2.0。這對內部使用與商業環境通常不構成障礙,但授權不會為你的政策檔正確性背書。README 自己把定位寫成 hardening layer,那麼因政策寫得太寬而導致的存取,責任在使用者這一側。如果你需要的是可審計的隔離保證,這個專案提供的東西和你需要的不一樣。

編輯結論

如果你在 macOS 上跑 Claude Code 這類會執行 shell 指令的 agent,而且願意維護一份 appended profile,Agent Safehouse 是目前少數把 deny-first 與可組合政策講清楚的本機沙箱方案,值得先在自己的專案上跑一次。若你的工作流程大量依賴家目錄外的掛載點、或你期待的是能抵擋定向攻擊的隔離層,它不適合,README 自己就寫明它只是 hardening layer。採用前先確認三件事:你的 agent 在預設政策下是否仍能完成日常工作、你需要的例外能否只用 --add-dirs-ro 與 --append-profile 表達、以及你建立 worktree 的位置是否落在啟動時快照得到的範圍內。

官方來源

  1. eugene1g/agent-safehouse on GitHub
  2. License: Apache-2.0
  3. Project website
  4. README
  5. Releases
社群筆記

社群筆記