跳至主要內容

一個保管箱身份,一個同步中樞

閱讀時間 8 分鐘 作者 NT²

保管箱不必進入共用的明文信箱才能同步。它的公開密碼學身份可以指定專屬的邊緣協調器,用來通知 replica,並指向協調器本身無法開啟的加密 frame。

一個保管箱身份,一個同步中樞

選用的雲端同步功能需要一個會合點。

筆記型電腦上傳加密變更後,手機需要有地方得知保管箱已經前進。兩份 replica 離線後重新連線時,需要在一個共同位置比較進度。裝置若已在線上,一則小型通知就能告訴它取得新的加密資料,不必等到下一次輪詢。

常見答案是共用的同步服務,後方連接共用的多租戶資料表。每個帳號都把資料列寫入同一個邏輯信箱,而應用程式碼則在每次查詢加上租戶條件。

NT² 採取更窄的做法:每個啟用雲端功能的保管箱身份,都有一個 Durable Object 作為唯一的 replica 協調中樞。 這個 object 由保管箱的公開密碼學身份,也就是保管箱金鑰 DID 來命名。它協調該身份的授權 replica,保留發布進度所需的少量狀態,並透過可休眠 WebSocket 傳送輕量的變更通知。加密的 replica frame 本身則以不透明物件形式存放在 R2。

這個中樞不是保管箱。它不持有可讀副本、不搜尋機密,也沒有解密 frame 所需的金鑰。它比較像私人車站的看板:可以告知又有一件加密貨件抵達,卻不能拆封,也不知道內容發生了什麼變更。

共用資料表的誘惑

多租戶資料表廣受採用,理由很充分。它們熟悉、可查詢,而且有效率。團隊可以把所有訊息放進同一張資料表,使用 tenant_idsequencepayloadcreated_at 等資料欄。索引能快速取得下一頁;客服查詢可以統計延遲的資料列;背景 worker 可以掃描資料表並重試失敗的傳遞。

危險之處並不是關聯式資料庫本身不安全,而是隔離變成應用層必須反覆兌現的承諾。

每次讀取都必須包含正確的租戶條件。每次更新都必須驗證擁有權。每個 join 都必須把邊界一路傳遞下去。每支維護 script、每次遷移、每個佇列 consumer、每份管理匯出與每段除錯查詢,都必須記住同一條規則。只要漏掉一次條件,一般程式錯誤就可能變成跨租戶資料揭露。

框架 helper 與資料庫 policy 可以降低這項風險,卻無法消除共用資料平面帶來的組織壓力。當許多客戶的紀錄位於同一個可查詢介面,內部工具自然會圍繞這個介面成長。維運人員想用更廣的搜尋排查事故;產品團隊要求加入彙總欄位;客服工具需要預覽。接著有人提議在加密 payload 旁儲存明文標題,只因為這樣能讓某個畫面更容易實作。

營運人員的「偷看」文化可能就這樣出現,甚至沒有人正式宣布隱私政策有所改變。schema 讓廣泛可見性變得方便,可見性也就逐漸顯得理所當然。

對保管箱而言,這不是正確的文化預設值。敏感資料不該只隔著一個可能被忘記的篩選條件,就落入另一個租戶手中;可讀欄位也不該只因為共用資料表方便,就進入共用信箱。

讓公開身份選擇房間

每個 NT² 保管箱都有一個保管箱金鑰 DID:這是用來驗證雲端請求的公開密碼學身份。這個身份不是密碼,也不會揭露保管箱內容。它為服務提供穩定的公開資料,用來辨識經授權的簽章。

同一個公開身份也能以確定性方式選出保管箱的 Durable Object。某個保管箱金鑰 DID 的請求都會解析到同一個 object;不同身份則會解析到不同 object。結果是一條簡單的隔離規則:

  • 一個保管箱身份只有一個協調位址;
  • 只有該身份通過認證的請求可以使用它;
  • 該保管箱的所有 replica 排序與即時通知都經過這個中樞;
  • 不需要由共用協調器多工處理彼此無關保管箱的可讀狀態。

使用公開身份命名 object,不會讓名稱本身成為機密;這也不是目的。授權仍需要密碼學證明,任何外部請求仍需要一般的驗證與濫用防護。這項設計帶來的是確定性路由與清楚的擁有權邊界,不是靠隱匿維持安全。

Durable Object 也自然適合把 replica 可能互相競爭的短暫時刻依序處理。它可以接受通過認證的通知,得知新的加密 frame 已完成儲存,再推進協調狀態,並告訴已連線裝置目前有更新的位置。由於同一個保管箱的請求會集中至同一個邏輯 object,這項協調不需要鎖住所有客戶的全域機制。

同樣重要的是,object 的職責維持狹窄。它不需要本機保管箱的 schema,不需要登入憑證、筆記、文件類型、標籤或附件名稱等資料欄,只需要協調加密 replica 所需的外層事實。

持久協調,不透明儲存

Durable Object 很適合有狀態的協調,但不適合永久累積每一個加密位元組。replica frame 與附件 chunk 的數量可能很多,體積也可能很大;R2 的用途正是持久保存這類物件。

責任分工是刻意設計的:

  1. 授權裝置先在本機加密保管箱變更。
  2. 裝置以不透明物件參照上傳不透明 frame。
  3. 每個保管箱的 Durable Object 記錄或觀察協調所需的進度。
  4. 中樞通知其他已連線 replica,目前有更新的位置。
  5. 授權 replica 取得缺少的 frame,再於自己的裝置上解密。

R2 看見的是位元組、物件參照、大小與存取活動。Durable Object 看見的是 replica 進度與連線狀態等協調事實。兩者都不會收到保管箱金鑰或明文項目欄位。

這條邊界也讓失敗行為更容易理解。WebSocket 通知不是變更的持久副本,只是提示接收端檢查是否有新的 frame。如果手機正在休眠、連線中斷或漏掉通知,加密 frame 仍然留在物件儲存中,replica 可以在下次通過認證的同步追上進度。

因此,中樞能改善延遲,卻不會變成脆弱的訊息匯流排,要求每個 event 都必須恰好送達一次。

讓安靜的保管箱使用可休眠 WebSocket

大多數保管箱在大部分時間都很安靜。使用者可能編輯幾筆紀錄、關閉應用程式,接著數小時甚至數天都沒有其他變更。如果每個已連線但閒置的 replica 都必須維持完整的伺服器程序,將會浪費資源。

可休眠 WebSocket 很適合這種模式。裝置可以維持即時的變更通知通道,而平台能在沒有工作時讓 Durable Object 休眠。流量抵達後,object 會恢復、驗證相關情境,再繼續協調。

通知應該刻意保持精簡。它可以指出進度已前進,或某個已知位置之後有新的 frame;不該包含解密後的標題、筆記預覽、登入憑證類別,或任何從內容衍生的便利欄位。

這件事的重要性不只在頻寬。即時系統往往會吸引業務邏輯。一旦即時通道開始承載已解讀的項目操作,協調器就會逐漸成為另一套可讀後端。讓訊號保持與內容無關,NT² 才能維持通知與解讀之間的區分:

  • 中樞知道加密狀態變更;
  • 通過授權且已解鎖的裝置知道變更是什麼

這已足以提供反應靈敏的同步體驗。

取捨:更多邊緣機制,換取更清楚的隔離

每個保管箱身份配置一個 Durable Object,會增加複雜度。路由必須具備確定性;認證必須把請求綁定至用來選擇 object 的同一身份;物件儲存參照、重試行為、replica 游標、連線生命週期與刪除,都需要仔細處理。營運工具也必須能在不依賴明文內容的情況下診斷協調失敗。

共用資料表搭配一組無狀態 worker,乍看可能更簡單,尤其是在早期。每個保管箱的設計選擇投入更多邊緣機制,換取一條更容易陳述與稽核的邊界:身份 A 的協調位於 A 的 object;身份 B 會抵達另一個 object;兩者都不需要可讀的保管箱資料。

這是協調層的隔離,不是神奇的實體分離。這些 object 仍在共用雲端平台上執行,網路中繼資料仍然存在,實作錯誤也仍可能發生。加密與授權依然不可或缺。依身份路由會減少刻意混合無關租戶狀態的地方,卻不能取代安全工程。

還有另一項重要限制:Durable Object 是選用的,因為雲端同步本來就是選用的。

本機保管箱不必聯絡這個中樞,就能建立、解鎖、搜尋與編輯。如果網路無法使用、object 正在休眠,或使用者從未訂閱同步,裝置上的保管箱仍會運作。本機操作不會等待雲端協調器批准。

連線恢復後,已啟用同步的 replica 可以封裝加密變更、完成認證,再從持久進度繼續。雲端把保管箱延伸到多台裝置,卻不會變成批准保管箱存在的權威。

中樞不能做的事

能力界定了架構,拒絕也同樣重要。

Durable Object 無法解密保管箱內容。 它不會收到主密碼、保管箱加密金鑰,也沒有其他由服務提供者保管、可通往明文的捷徑。把協調移入有狀態的邊緣 object,不代表信任也往同一方向移動。

不會有一個共用 Durable Object 保存許多保管箱的明文。 這種 object 只會用另一個產品名稱重建全域信箱。它會集中可讀的租戶資料、鼓勵廣泛內部查詢,並讓一次路由錯誤變成潛在的資料揭露。

通知不會包含機密預覽。 裝置先得知加密 frame 已備妥,再完成認證、取得資料並於本機解讀。中樞不需要附加標題或類別,讓通知顯得更「有幫助」。

離線使用不需要這個 object。 即時通知失效可能延遲同步,但絕不能讓使用者無法開啟已經位於裝置上的保管箱。

這些拒絕會限制客服工具與伺服器端功能。營運人員可以檢查服務健康、連線失敗、物件數量、時間與授權結果,卻不能開啟一筆紀錄,查看文字為什麼顯得異常。這項限制不是尚未完成的管理功能,而是證明協調器還沒有變成明文控制室。

私人的會合點,不是可讀的主要副本

盲目同步仍需要協調。真正有意義的選擇是:協調器要成為全域、可讀的重力中心,還是維持為加密 replica 的狹窄會合點。

NT² 依每個保管箱金鑰 DID 配置這個會合點。一個身份解析至一個 Durable Object;可休眠 WebSocket 以低成本提供變更通知;R2 保留不透明 frame;授權裝置則在本機執行加密、解密、索引與衝突解讀。

這項設計需要投入更多邊緣工程,卻讓預期邊界變得具體:保管箱的雲端中樞可以協調它的 replica,而不必成為保管箱本身。

若想了解雲端身份為何與本機存取分開,請閱讀解鎖本機保管箱,不等於登入雲端。若想了解這個中樞所協調的加密傳輸模型,請接著閱讀在邊緣進行盲目 replica 同步。如果你想要一個在連線之前、期間與之後都持續可用的保管箱,歡迎開啟 NT² Vault

最後更新 2026-08-12

相關故事