在 macOS 的檔案系統深處,微信的聊天記錄住在一串很長的地址裡:~/Library/Containers/com.tencent.xinWeChat/Data/Documents/xwechat_files。前半段是系統配給每個 App 的容器路徑,後半段的 xwechat_files 是微信自己的倉庫命名,小寫字母、底線、開頭掛一個 x。這種命名法不對一般使用者開放,屬於工程師之間的溝通格式,安靜待在平常不會點開的資料夾深處。八月十三日,少數派 Matrix 刊出一篇教學,作者 wclebb 示範怎麼把這座倉庫搬到外接硬碟,全程不動微信 App 本體,也不動它的官方簽名。這套做法值得從設計的角度重看一遍,因為它處理的是所有系統設計的共通題:邊界畫在哪裡,以及穿越邊界時要出示什麼身分。
小容量階序與不斷長大的倉庫
Mac mini 的入門款只給 256 GB 儲存空間。這個數字坐在蘋果定價階序的最底層,購買時省下的預算,後來都以空間管理的工作償還。另一頭,微信的 xwechat_files 是座只進不出的倉庫,圖片、影片與各類檔案全數落在本地,聊天越久,佔用越大。兩者相加,把倉庫搬出去就成了剛性需求。
常見的做法是把資料夾換成符號連結,指向外接硬碟上的新位置,但這條路會被系統擋下。另一條是用 codesign 重新簽署微信,讓它取得外接硬碟的存取權,有效,代價是 App 的簽名狀態從此偏離原廠設定,每次更新後可能都得重做一次。作者的方案不走這兩條:建立一個獨立的 APFS 卷宗,掛載到 xwechat_files 這個地址上。App 不動,簽名不動,動的只有檔案系統的空間配置。
路牌與地基:兩種搬法的設計差異
符號連結本質上是一張路牌。它告訴系統:你要找的東西在別處,請到外接硬碟去。問題在於,Mac App Store 版微信住在 App Sandbox 裡,這套安全機制的設計就是逐一檢查被存取的路徑。系統會解析符號連結背後的真實目標,一旦發現目標落在授權範圍之外,存取隨即被拒。路牌立得再端正也沒有用,因為警衛核對的是地址本身。
掛載走的是另一條思路。macOS 把整個 APFS 卷宗接到 xwechat_files 這個目錄上,微信存取的路徑一個字都沒變,路徑底下對應的儲存實體已經換成外接硬碟。地址沒變,地基換了。Sandbox 核對的是路徑與授權的對應關係,並不追問這個地址底下的地質構造,這正是方案能夠成立的前提。
同一個需求,符號連結在命名層動手,掛載在實體層動手。前者改寫了 App 看到的路徑內容,後者保持路徑完整,只替換路徑指向的儲存空間。對一套以路徑為授權依據的安全系統而言,兩者得到的待遇自然天差地遠。
掛載點的深度:結構與內容的分界
方案裡有一條關鍵約束,來自作者的實測:卷宗不能掛在容器的 Data 或 Data/Documents 這兩層,一掛上去,微信的容器完整性檢查就被觸發,App 直接崩潰。只有再往下一層,掛在 Data/Documents/xwechat_files,一切才正常運作。
這條規則透露了蘋果容器設計的階序感。容器的上層目錄屬於結構,它們定義這個 App 在系統裡的形狀,由完整性檢查看守;最深處的葉節點屬於內容,內容允許替換,結構不容抽換。系統容許你重新裝修某個房間,卻不容許你抽換整層樓板。掛載點必須落在 xwechat_files 這一層,等於沿著結構與內容的分界線,把幹預的深度壓到最低。整套方案裡最漂亮的比例判斷就在這裡:一個侵入性的操作,被放在系統容忍度最高的位置。
蘋果對資料放在哪、誰能讀取這類邊界的計較是一貫的,先前觀察的蘋果 Home Hub 家庭介面設計也是同一種思路,七吋螢幕上的小工具排版之前,隱私的界線早已在底層劃好。
簽名是原廠封條
codesign 重新簽署之所以有效,是因為它直接改寫了 App 的授權身分,但封條一經更換,性質就變了。官方簽名是蘋果從 App Store 一路背書下來的信任鏈,重新簽署等於撕掉原廠封條、貼上自己的,往後每次更新,微信都會帶著原廠封條回來,操作於是得再來一次。
作者堅持保留官方簽名,這個取捨本身就是一種設計態度。簽名系統存在的目的,是讓任何改動變得可偵測;與其設法繞過防篡改機制,更省力的路徑是讓方案從頭到尾不碰被封條覆蓋的東西。這個道理與我們先前拆解過的低畫質作為一種可信度設計相通:信任的線索一旦被動過,重建的成本遠高於一開始就不去動它。
表單上的留白:APFS 的空間觀
方案的起點在磁碟工具程式:選中外接硬碟上的 APFS 容器,按「加入卷宗」。名稱填 WeChatData,格式選不區分大小寫的 APFS,保留大小留空,配額留空。這張表單本身就在傳達 APFS 的空間哲學,同一容器裡的卷宗共用可用空間,不必像傳統分割區那樣預先砌死一條容量界線。那兩個刻意留空的欄位是設計過的空白,留白在這裡就是設定值。
傳統分割區像水泥隔牆,容量劃定之後改動困難;APFS 卷宗像活動隔間,空間隨需求流動。微信的倉庫要用多少就長多少,硬碟上的其他資料也不必為了它預留一塊用不到的空地。
fstab 與 UUID:開機時的舞臺指示
自動掛載要動到 /etc/fstab,一份純文字設定檔,內容是某個卷宗該出現在哪個地址。這是 Unix 世界流傳數十年的老設計,沒有圖形介面,沒有動畫,只有路徑與識別碼的對照,卻是檔案系統每次開機時照著執行的舞臺指示。
細節在命名。設備識別碼會變,今天叫 disk7s2 的硬碟,重新接上後可能換了號碼;系統因此為卷宗配發 UUID,一組跟著卷宗走、不隨插槽變動的身分字串。寫進 fstab 的應該是 UUID,這個選擇背後是兩種命名的差別:座位號碼入場就換,身分證字號跟著人走。
順序也是設計。微信開啟前,卷宗必須先完成掛載,房客進門之前,房間要先布置好。整套方案的時間安排,都建立在這個先後關係上。
拔掉硬碟之後
作者在文前置放了適用條件:這套方案適合外接硬碟長期接著 Mac 的使用者。經常拔除硬碟、帶著 Mac 出門的人,硬碟不在時微信看到的是一個不存在的目錄,定期匯出聊天記錄反而更穩妥。設計一套空間方案時,缺席狀態與在場狀態同樣需要被設計,這段提醒是整篇教學裡最誠實的一段。
同樣誠實的還有那句警語:遷移改變的只有資料的位置,份數沒有增加。外接硬碟損壞時,資料照樣會丟,該留的備份仍要另行保留。位置與冗餬是兩件事,做好其中一件,並不會自動完成另一件。
順著系統紋理的解法
回頭看整個方案,可貴之處在於每一步都用系統原生的機制:APFS 卷宗、掛載點、fstab、UUID。與安全系統之間沒有衝突,只有對其畫界邏輯的準確閱讀,然後把操作放進邊界願意容忍的深度。好的系統介入大概都是這樣,先理解對方畫界的方式,再決定自己從哪一層進去。
對使用者,收穫是一臺 256 GB 的 Mac mini 繼續服役,微信的倉庫搬到容量寬裕的外接硬碟上。對設計觀察者,這篇教學順帶示範了一件事:耐用的解法,往往屬於力量溫和、卻與系統語法最合拍的那一種。