雲端儲存與同步設計
狀態:截至 2026-09-17,步驟 1–4 已實作;步驟 5 已有可重現的基準。持久化寄件匣/收件匣收據、變更標記、程序內的同步工作階段,以及支援 MemoryTransport 故障注入、與供應商無關的執行器,皆已在 crates/core/src/sync.rs 實作。OneDrive/Google Drive 檔案介接器、權杖更新及 CLI cloud sync 已實作,並以本機測試資料驗證。尚未驗證實際供應商帳號。行動端 AppAuth 登入與前景同步控制項已實作;原生同意流程需要平台註冊。目前授權範圍請參閱 cloud-authorization.md。本文件擴充 OneDrive/Google Drive 的需求,要求並行使用的裝置不得破壞或鎖住彼此的資料庫檔案。
決策
保留 SQLite 作為裝置本機的交易式儲存區。新增與供應商無關的同步協定,透過雲端 API 交換不可變且經驗證的加密物件。每個裝置擁有自己的資料庫、身分、密碼與本機寫入者。雲端儲存是不受信任的物件傳輸媒介,而非掛載的資料庫。
絕不可將使用中的 memories.db、journal、WAL、SHM 或鎖定檔放在雲端管理的同步目錄中。不可透過遠端檔案系統開啟資料庫。保留預設的作業系統應用程式資料位置。偵測/拒絕已知 OneDrive/Drive 根目錄是未來的設定防護措施,目前尚未強制執行;無法可靠偵測任意同步用戶端與掛載點。
flowchart LR
A[Device A local SQLite] --> QA[Durable outgoing queue]
QA --> C[OneDrive or Google Drive encrypted objects]
C --> VB[Download and verify]
VB --> B[Device B local SQLite]
B --> QB[Durable outgoing queue]
QB --> C
C --> VA[Download and verify]
VA --> A
既有行為與限制
crates/core/src/store.rs:作業系統的獨占鎖會拒絕第二個本機儲存區擁有者。SQLite 使用journal_mode=DELETE、synchronous=FULL,並以交易更新加密狀態與簽署檢查點。這可保護配合規則的本機程序;不是獨立檔案複本之間的分散式鎖。- 每次儲存都會序列化並加密完整
State;解鎖會檢查完整檢查點歷史紀錄。較大的資料庫會增加寫入成本與啟動時間。這是擴充性問題,並非必須替換 SQLite 本身的證據。本次評估尚未進行工作負載基準測試。 - 資料庫初始化會持有目的地的作業系統鎖,同時在相同目錄建立私有暫存資料庫。它會提交初始加密狀態與檢查點、關閉 SQLite、同步檔案,然後發布,且不會取代既有目的地。發布前當機會讓最終路徑仍可供重試。當機後可能留下廢棄的
.memories-init-*檔案;它們絕不會當作使用中的資料庫載入,也不會自動刪除。 Service在複製的狀態上處理修改,並在接受修改前提交。Vault::merge會先驗證候選內容,再取代目前保管庫。簽署修訂歷史紀錄已能保留並行編輯與刪除標記。- 桌面 IPC 最多接受 16 個並行請求;狀態派送會持有服務互斥鎖。行動端使用各自的服務鎖與短暫的登錄表查詢。若直接在這些臨界區段內發出雲端 HTTP 請求,會延遲其他操作,包括鎖定。這屬於阻塞,尚未證明存在循環死結。雲端工作者必須在網路 I/O 前釋放服務鎖;桌面請求處理目前使用有上限的並行連線。
- 既有同步匯出是完整保管庫、為收件者加密的封套。剛完成雲端驗證的裝置仍需通過裝置核准,才能解密或匯入保管庫。雲端登入不得取代密碼學裝置配對。
資料庫替代方案
| 選項 | 對此儲存庫的適合程度 | 決策 |
|---|---|---|
| 本機 SQLite 加上不可變雲端物件 | 重用交易、加密、稽核與衝突處理;不需伺服器 | 建議採用 |
| 以 redb 或 LMDB 作為本機儲存區 | 嵌入式交易儲存可行,但更換引擎不會提供跨裝置合併或雲端鎖定 | 目前沒有足夠理由移轉 |
| 同步資料夾中的共用資料庫或加密封存檔 | 獨立的檔案上傳可能覆寫版本;附屬鎖與日誌未協調 | 不採用於主動同步 |
| PostgreSQL 加上專用同步 API | 集中處理伺服器並行,但需要維運服務與獨立離線/加密協定 | 僅在產品需求包含伺服器時考慮 |
| Automerge 或 Yjs 等 CRDT 程式庫 | 未來協作文字編輯可能有用;無法取代持久儲存或簽署權限檢查 | 不用於憑證或原則變更 |
將 SQLite 改為 WAL 並不是雲端同步解決方案。經過測量後,WAL 未來或許有助於本機並行讀取,但它需要同一主機上的協調,也不會消除單一本機寫入者的需求。
雲端物件契約
從既有的完整保管庫封套格式開始;避免同時改變儲存、密碼學與同步粒度。只有在不可變封套路徑經過驗證後,才新增增量加密修訂/區塊。
- 每個封套只產生一次,上傳前將確切位元組持久化到本機寄件匣。使用完整密文物件的 SHA-256 作為邏輯 ID。重試重用這些位元組;每次重試重新加密會產生新物件。
- 物件與發布描述項不可變。沒有共用可寫入的
latest.json、共用鎖定檔、全域雲端序號或資料庫覆寫。任何未來的可變 head/快取都僅供參考,並可透過列舉物件復原。 - 先發布資料物件,再發布不可變的簽署描述項,綁定格式版本、不透明保管庫參照、寄件者、收件者、物件雜湊與長度,以及各裝置的前置項/序號。檔名使用密文識別碼,不使用明文標題、帳號名稱或密碼雜湊。
- 雲端列舉與傳送可能延遲或順序改變,因此描述項可能早於其相依物件出現。在所有物件都已存在且通過驗證之前,保持待處理狀態。相依物件缺少時,絕不可部分匯入或推進其已套用狀態。
- 提交前驗證長度、密文雜湊、簽章、收件者、保管庫範圍、目前授權,以及既有歷史紀錄不變條件。供應商 ETag 與雜湊是傳輸中繼資料,不是密碼學授權依據。
- 重播物件與供應商重複檔案不會造成問題:依已驗證的邏輯物件 ID 去除重複,而非依檔名。缺少雲端檔案絕不表示應用程式層級刪除;刪除由簽署的刪除標記表示。
雲端供應商可以刪除或扣留物件。本機已知 head/檢查點可偵測部分回復舊狀態的情況,但沒有可信任新檢查點的新裝置,無法證明自己擁有最新狀態。此設計提供完整性與明確衝突,無法保證面對惡意供應商時的可用性或最新程度。
交易與工作者生命週期
本機寫入者負責應用程式狀態、檢查點更新、寄件匣/收件匣狀態與同步確認。在網路活動前後使用兩個短暫臨界區段:
- 本機準備:授權、擷取一致匯出,並提交確切的加密上傳工作及世代/工作階段綁定。使用者編輯與其變更/匯出標記必須以原子方式提交,避免當機遺失工作。
- 釋放鎖後傳輸:獨立工作者執行 OAuth 更新、上傳、下載、分頁與重試,不持有服務互斥鎖或資料庫寫入交易。限制並行數、回應大小、重試次數,以及連線/讀取期限。遵守供應商節流與
Retry-After。 - 本機提交:重新取得寫入者,驗證授權與工作階段世代,合併至目前狀態而非舊擷取副本,再一併提交狀態、檢查點、已套用 ID 與收件匣狀態。只有發現的工作已持久化,才能推進供應商列舉游標。
鎖定/登出會取消工作並使延遲結果失效。已失效的工作階段不得完成新的解密/匯入。取消無法收回供應商已接受的密文上傳。未完成工作應可續傳;上傳成功代表雲端持久儲存,不代表另一裝置已確認。
OAuth 權杖保持加密且僅存於裝置本機,不包含在雲端物件、裝置核准套件、匯出備份、搜尋與 MCP 中。更新權杖輪替必須先持久化,後續操作才能使用新權杖;每個帳號的並行更新依序執行,HTTP 交換期間不持有服務鎖。絕不可為工作者新增公開的權杖讀取操作。
供應商特定行為
| 供應商 | 應用程式儲存 | 上傳/重試需求 |
|---|---|---|
| OneDrive | Microsoft Graph 應用程式資料夾,Files.ReadWrite.AppFolder |
使用不可變物件名稱與上傳工作階段衝突行為 fail;物件已存在時,下載/驗證它,不要取代。只有端點文件明確支援時才使用條件式請求。 |
| Google Drive | appDataFolder、drive.appdata 範圍 |
預先產生檔案 ID,並與工作一同持久化,以安全重試結果不明的建立操作;不可視檔名為唯一值。衝突後驗證既有 ID/內容。 |
兩種介接器都必須實作分頁、可續傳傳輸、有上限的下載、回應遺失復原與供應商特定錯誤處理。不可假設 Drive 支援檔案系統重新命名/鎖定,或與 Graph 相同的前置條件語意。重新導向的下載 URL 與可續傳上傳 URL 需要明確的憑證轉送規則,避免將 OAuth bearer 權杖送至任意主機。
初始應用程式資料夾設計服務於使用者已配對、連接相同供應商帳號與應用程式的裝置。Google 應用程式資料儲存不支援不同帳號之間分享檔案。跨使用者保管庫分享需要明確的傳輸/分享設計;不得悄悄擴大 OAuth 範圍。若同時啟用 OneDrive 與 Google Drive,它們是獨立複本,不是彼此同步的資料夾。分別追蹤待處理與成功的傳輸。
驗證實際帳號前,需要 OAuth 用戶端註冊與供應商同意設定。CLI 授權應使用瀏覽器 OAuth 搭配 PKCE、state 驗證與 loopback 回呼。行動端需要其支援的原生授權流程與生命週期處理;不可將桌面回呼假設原封不動套用至行動端。
衝突、復原與保留
- 保留兩份並行的憑證/卡片修訂。使用前要求明確選擇或解決衝突;不可挑選最新的時鐘時間戳記,也不可合併密碼字串。重用既有原則分叉與舊 epoch 隔離規則。
- 檢查碼/簽章失敗會拒絕傳入物件,並保留先前本機狀態。狀態中應區分損毀輸入、缺少相依物件、登入過期、配額與內容衝突,不要全部回報為鎖定錯誤。回應位元組與雜湊不符時,仍可透過退避重試。雜湊已驗證但無效的物件具有獨立拒絕收據;絕不算作已成功套用的相依物件。拒絕會阻止回報同步成功,並持續依供應商顯示在雲端狀態中。中斷供應商連線會清除其拒絕收據,讓後續連線能重新掃描。混合拒絕與成功匯入的舊收據會清除一次;既有簽署封包收據仍會阻止重複匯入。
- 目錄探索的收件匣上限為 4096 個項目,另有 16 個保留的相依物件插槽。已驗證描述項會依雜湊直接排程缺少的物件,優先於一般下載。未被參照的封套緩衝區可在資源壓力下捨棄,並在其描述項抵達時重新取得。不完整的目錄頁絕不會將游標推進至未納入的 ID 之後。
- 每次狀態提交都會移除目前保管庫存取權已撤銷之收件者的排隊匯出。工作者會在物件操作之間驗證工作階段,包括供應商 ID 配置之後。Google 檔案 ID 會在首次上傳前儲存於加密寄件匣,因此確認遺失與重新啟動都會重用相同的不可變供應商物件。
- 初期保留舊雲端物件。安全垃圾回收需要各裝置確認、離線裝置原則、撤銷處理與復原保留原則。僅憑列舉中缺少物件與經過時間並不足夠。
- 保留獨立加密備份。多裝置同步也會傳播有效刪除,無法取代時間點復原。
- 結構描述移轉前,新增考量復原的移轉路徑:本機鎖定、保留已驗證備份、以交易移轉、驗證,再啟用。移轉失敗必須讓先前儲存區仍可使用。
實作順序
- 完成。有上限的寫入者請求(
CloudSyncEnqueue/CloudSyncWork/CloudSyncAck),以及加密State內的持久化寄件匣/收件匣收據。SQLite 加密、journal_mode=DELETE與完整狀態儲存維持不變。沒有 WAL 或資料庫引擎移轉。 - 完成,僅限本機傳輸。與供應商無關的不可變物件執行器(
sync::execute),支援MemoryTransport故障注入。測試涵蓋確切位元組重試、提交後重新開啟、重複 put 驗證、描述項先於資料、損毀物件、鎖定/登出失效,以及兩個已配對裝置。此測試傳輸不使用實際雲端帳號。 - 完成,僅限本機測試資料。權杖更新、OneDrive/Google Drive 不可變物件介接器、daemon
CloudSyncRun(HTTP 位於寫入者鎖之外),以及memories cloud sync。未授權實際 Microsoft/Google 帳號。 - 已實作,實際原生同意流程待驗證。行動端登入使用 AppAuth;前景同步共用核心執行器。移至背景會鎖定所有 handle 並取消延遲結果。Widget 與原生 FFI 回歸測試涵蓋取消;實際供應商/實際裝置同意流程仍是發布關卡。刻意未啟用背景排程。
- 已有基準。請參閱效能,了解使用可拋棄資料庫執行的 release 基準測試,涵蓋資料庫大小、儲存/解鎖/搜尋延遲與同步流量。若有需要,將本機完整狀態 blob 移轉為加密的各保管庫/修訂/金鑰資料列與交易式收據,再引入增量雲端物件。保留簽署 ID、修訂歷史紀錄、密文隱私與備份復原。
驗證證據與剩餘關卡
在 Windows 上,crates/core/tests/storage_resilience.rs 測試可拋棄儲存區與子程序。本次評估通過三個情境:
- 當另一程序擁有儲存區時,獨立程序會遭拒;終止擁有者會釋放作業系統鎖,原始加密狀態可重新開啟。
- 子程序啟動 SQLite 寫入交易,強制將髒頁寫入回復日誌,並在提交前遭終止。
Store::unlock驗證原始狀態/檢查點,且PRAGMA integrity_check傳回ok。 - 兩個已授權裝置獨立編輯相同資產。兩種合併順序都會收斂,重播具冪等性,兩個 head 都保留,而缺少父項的傳入快照會遭拒,且不會修改良好狀態。
刻意忽略的輔助程式由父測試明確啟動。執行:
cargo test -p memories-core --test storage_resilience --locked -j 1 -- --test-threads=1
cargo test -p memories-core --lib store::tests --locked -j 1 -- --test-threads=1
store.rs 中的初始化回歸測試會在暫存完成、發布之前終止子程序,然後重新建立/解鎖最終儲存區。它們也驗證目的地鎖持續持有,且既有或新出現的目的地絕不遭覆寫。OAuth 測試涵蓋鎖定後延遲結果失效。
這些是本機程序當機與模型測試,無法證明能抵禦實際斷電、磁碟/控制器故障、雲端用戶端檔案取代或實際供應商行為。磁碟空間不足、大型資料庫、發布時斷電,以及雲端重試/發布邊界仍需要測試。本次評估未授權或寫入任何實際雲端帳號。
參考資料
於 2026-09-09 查閱的官方文件: