Lolly
啟動應用程式

資料傳輸 - lolly-backup 打包檔

Lolly 使用者累積的一切都存放在自己的裝置上 - 沒有帳號,沒有雲端。資料傳輸打包檔就是搬移這些資料的方式:在一台裝置上匯出,透過任何方式攜帶該檔案(USB、AirDrop、寄給自己的電子郵件、網路共用),再匯入另一台裝置。檔案本身就是傳輸方式。目標裝置可以離線或連線,兩者沒有差別,因為整個過程完全不會與任何伺服器通訊。

搬移整個安裝環境的兩個按鈕:匯出我的資料寫入一個 zip,匯入資料再讀回 signed by Lollyvector SVG9 paths~4.9k nodes11 groups56 KB自己驗證取得已簽署檔案搬移整個安裝環境的兩個按鈕:匯出我的資料寫入一個 zip,匯入資料再讀回 signed by Lollyvector SVG9 paths~4.9k nodes11 groups56 KB自己驗證取得已簽署檔案

本頁是格式規格。給終端使用者的操作說明見找回你的成果 → 把你的作品移到另一台裝置。實作程式碼在 shells/web/src/data-transfer.ts,tests/data-transfer.test.ts 固定了往返契約。

範圍。 打包檔攜帶的是使用者資料,而不是目錄工具。目錄工具與目錄資產是分開同步的,並假設目標裝置上已經存在(最壞情況下版本較舊);使用者自己製作的工具則會隨 profile.json 一起傳輸。匯入絕不會安裝或升級目錄工具。

目標

封裝格式

打包檔是一個單純的 .zip 檔。下載檔案會以其所屬的人命名 - LollyTools-<First>-<Last>-<YYYY-MM-DD>-<n>.zip(例如 LollyTools-Ada-Lovelace-2026-06-26-1.zip) - 讓下載資料夾中的備份檔保持可辨識。名字與姓氏這兩部分來自個人檔案,未設定時則省略。沒有個人檔案時會產生 LollyTools-2026-06-26-1.zip,只有名字時則產生 LollyTools-Ada-2026-06-26-1.zip。每個部分都會被清理為檔名安全的字串(保留 Unicode 文字/數字,移除空格與標點,上限 32 個字元)。<n> 是同一天、同一裝置下的序號,因此同一天重複匯出不會互相衝突,並維持順序。shells/web/src/data-transfer.ts 中的 backupFilename() 負責建立此名稱。無論檔名為何,zip 的內容都是相同的。內容如下:

路徑是否必要內容
manifest.json是格式 id、版本、數量與各部分的完整性資訊。是讀取端最先查看的內容。
profile.json有設定時使用者完整的 me 記錄:姓名、聯絡方式、大頭照參照與旗標,加上資料夾、垃圾桶、專案藍圖、使用者範本與使用者自製工具、我的最愛、隱藏的工具、語言與表情符號選擇。透過 host.profile 讀取。
sessions.json是每一個已儲存的工作階段:欄位、工具 id/版本、標籤、縮圖(data-URL)與完整輸入資料。透過 host.state 讀取。
assets.json是每個已上傳資產(圖片、字型、品牌 token、標誌、下載內容的保存副本)的中繼資料,各自指向 assets/blobs/ 下的位元組。
assets/blobs/<n>.<ext>依資產而定原始資產位元組(圖片與字型檔案)。以未壓縮方式儲存(本身已是壓縮格式)。副檔名僅供辨識參考,assets.json 中的 MIME 才是權威依據。
assets/blobs/<n>.c2pa存在時以精確的二進位位元組擷取出的 Content Credentials,由資產記錄中的 _credentialFile 參照。這些並非裝置簽署金鑰。
design-systems.json存在時這次安裝製作或新增的設計系統,以 { active, records } 形式儲存。匯入時依 id 合併;僅當目標裝置沒有自己的設計系統時,才會採用打包檔的目前選擇。
file-history.json選用附版本的資產快照、終態檔案操作報告與完整的批次清單。此歷史部分有自己的版本號;由殼層內部的 fileHistory 備份轉接器提供。
revision-history.json選用,手動備份穩定的創作 ID、保留的檢查點、縮圖與滾動式復原草稿。在支援之處由 host.state.history.backup 提供。
file-history/versions/依快照而定先前的資產位元組與擷取出的憑證,與目前的資產是否仍然存在無關。
file-history/results/依已完成的作業而定精確的輸出位元組。不會保留或包含被選取用於轉換的原始檔案。
prefs.json是使用者自有的本機偏好設定:theme、sidebarWidth,以及 ct-metrics 活動統計。
lolly.txt是打包檔的人類可讀摘要(數量、個人檔案、檔名),供未使用 Lolly 開啟 zip 的人參考。每次匯出都會重新產生,且匯入時會被辨識,因此絕不會被算作跳過的部分。它是在完整性對照表之後才寫入的,因此不包含在其中。

打包檔刻意採用單純的 zip 格式:無論透過何種方式傳輸都能保持完整,且任何解壓縮工具都能檢視其內容。

profile.json 是最小的一部分,也是在應用程式中最先被讀取端看到的部分:由製作者填寫一次的詳細資料,以及讓工具得以使用這些資料的選擇性開啟設定。

變成 profile.json 的個人資料詳細資訊表單:姓名、聯絡方式和大頭照 signed by Lollyvector SVG15 paths~1.2k nodes30 groups1 image40 KB自己驗證取得已簽署檔案

manifest.json

{
  "format": "lolly-backup",
  "formatVersion": 3,
  "minReader": 1,
  "app": "lolly",
  "exportedAt": "2026-06-22T09:30:00.000Z",
  "counts": { "profile": true, "sessions": 2, "userAssets": 4, "prefs": 3, "assetVersions": 1, "fileOperations": 1 },
  "integrity": {
    "profile.json": "sha256-…",
    "sessions.json": "sha256-…",
    "assets.json": "sha256-…",
    "assets/blobs/0.bin": "sha256-…",
    "file-history.json": "sha256-…",
    "file-history/versions/0.bin": "sha256-…",
    "file-history/results/0.bin": "sha256-…",
    "prefs.json": "sha256-…"
  }
}
欄位意義
format恆為 lolly-backup。沒有此欄位的檔案會被拒絕,回報「不是 Lolly 備份檔」。
formatVersion此打包檔寫入時所採用的版面配置。只要部分項目集合或結構有任何變動就會遞增。讀取端不會以此欄位作為判斷依據。
minReader安全匯入此打包檔所需的最低讀取端版本。讀取端就是以此欄位作為判斷依據。
app產生此打包檔的應用程式 id,供診斷用途。
exportedAt打包檔建立時的 ISO 時間戳記。
counts寫入端放入了哪些內容,供顯示與合理性檢查之用。
integrity選填。將除 manifest.json 以外的每個部分,對應到其未壓縮位元組的 SRI 風格 sha256-<base64> 摘要值。

版本政策(向前相容性)

formatVersion 與 minReader 之間的區分,正是讓此格式得以演進、同時不會拋棄較舊安裝環境的關鍵:

給作者的經驗法則: 如果每個現有的讀取端,即使忽略你新增的內容,仍能表現正確,那就是新增式變動 - 遞增 formatVersion,minReader 保持不變。否則就要提高 minReader。

完整性

當 manifest.integrity 存在時,讀取端會在寫入任何內容之前,驗證所列出每個部分的 SHA-256。若不相符(「未通過完整性檢查」)或缺少某個部分(「不完整」),整個匯入程序就會中止 - 不會有部分還原的情況。這能捕捉檔案傳輸過程中可能造成的損毀(例如中斷的 AirDrop、重新編碼附件的電子郵件閘道、損壞的 USB 磁區)。

完整性檢查在設計上是盡力而為:只有在 Web Crypto 可用時(所有安全的瀏覽器情境與現代 Node)才會寫入,也只有在對照表與 Web Crypto 皆存在時才會進行驗證。沒有對照表的打包檔 - 例如完整性機制出現之前建立的打包檔 - 會照常匯入,不受影響。「無法驗證」絕不會被視為「已損毀」。

資訊清單既不會列出自己,也不會列出重新產生的 lolly.txt 說明檔。摘要值涵蓋的是資訊清單所擔保的各個部分。

匯入語意

匯入是一次合併,絕不會全部取代:

已儲存的工作階段會自動重新連結到其圖片:資產參照是以 id 保留的,橋接層會在已上傳圖片還原之後重新解析它們(無論如何都必須這麼做,因為 blob: URL 無法在重新載入後保留)。

匯入摘要會回報 { profile, sessions, userAssets, prefs, skipped, failedAssets }。failedAssets 計算的是無法還原的已上傳資產(例如裝置儲存空間已滿)。這與 skipped 不同,後者計算的是來自向前相容的較新寫入端、但此版本無法辨識的部分。使用者介面會呈現 skipped(「…‧N 個較新的項目已跳過」),讓還原結果誠實地呈現遺漏的內容。

當檔案歷史存在時,摘要還會攜帶 assetVersions、fileOperations 與 failedHistory。儲存空間耗盡或不可變 ID 衝突可能導致部分還原;介面會提示使用者保留原始備份。雲端同步在一次部分或不受支援的還原之後不會推進其已套用的修訂版本,因此該快照仍可用於重試。還原並非跨越個人檔案/工作階段/資產/歷史儲存的單一交易。

創作歷史(v3)

來自支援歷史記錄的網頁主機的手動備份,會包含帶有自己的 { version: 1, documents, revisions, recoveries } 結構的 revision-history.json。它攜帶保留下來的 ID、規範化的輸入快照、版本戳記、點陣預覽與獨立的寫入端草稿。歷史轉接器會在一次讀取交易中擷取目前的工作階段及其標頭;sessions.json 會為較舊的讀取端使用這些相同的目前快照。

還原過程會先檢查酬載的 SHA-256 與位元組數、唯一身分、文件/標頭關係、系譜、時間戳記、預覽類型與各項限制,然後才以一次交易提交該封存檔。被壓縮過的父層參照可能會缺失。既有的目前作品絕不會被靜默取代:當一份創作兩邊都有時,未保持為目前版本的一側會成為一份受保護的草稿。封存檔 384 MiB 的傳輸上限會被明確檢查,儲存限制的強制執行也不會截斷已保留的檢查點。整體備份目前仍使用記憶體內的 ZIP 實作,而非串流封存檔。

摘要新增了 revisions 與 recoveryDrafts,兩者只計算這次匯入新增的部分,此外還有 added、kept、replaced、copies 與 hidden,用來說明每份創作是如何被合併的。不具備此能力的殼層會還原一般工作階段,並將歷史部分回報為已跳過。原生檔案系統歷史目前仍不受支援,直到其轉接器提供持久的歷史交易為止。P2P 訪客狀態沒有持久的歷史記錄或復原封存檔。

個人快照同步明確不包含創作歷史。將一個快照套用到帶有歷史記錄的本機文件上,會把它先前的工作狀態保留為一份獨立的復原草稿,並使任何開啟中編輯器的寫入權杖失效。其不可變的檢查點仍保留在裝置上。這能在快照取代期間保護本機歷史,但不會合併多台裝置並行產生的歷史記錄。

歷史性的資產參照會被保留,但算繪時仍會透過目標裝置現有的資產庫來解析資產。此封存檔目前尚不保證舊有的資產位元組或舊工具算繪結果完全一致。資產版本與檔案結果的位元組仍會透過它們各自現有的獨立備份部分傳輸。

已儲存的版本與檔案結果(v2)

這個選用的歷史部分包含 { version: 2, assetVersions: [...], operations: [...], batches: [...] };讀取端也接受不含 batches 的較早 history-v1 型態。每個快照都會標明穩定的資產 ID 與確切版本、儲存時間、位元組長度與十六進位 SHA-256,外加一筆資產記錄,其 _file 與選用的 _credentialFile 指向二進位部分。操作會攜帶原始檔案事實、請求、報告、時間戳記與選用的結果 _file;儲存後端名稱、OPFS 控制代碼與執行租約不會隨之傳輸。只支援 history-v1 的較舊讀取端,會在匯入前拒絕新的歷史版本,而不是悄悄捨棄批次成員關係。

批次清單會在處理之前記錄每一個被選取的來源檔案,包括從未被讀取的檔案、被取消的成員、預留結果空間失敗的情形以及被中斷的工作。每個成員都有一個穩定的操作 ID、來源參照/事實、要求的輸出名稱與終態報告。一個未被讀取的來源會有聲明的事實,而非憑空捏造的摘要。匯入會驗證成員身分,並與隨附的任何操作報告核對一致性。即使個別結果已被明確移除,批次報告仍然可用,但一份收據並不代表其輸出位元組仍然被儲存著。

較舊的讀取端仍會接受 v2 封裝(minReader: 1)並還原它們熟悉的部分,將不受支援的歷史部分計為已跳過。完整的歷史復原需要一個具備 fileHistory 轉接器的殼層;這是殼層內部的一條介面,而不是面向工具的新增 HostV1 能力。真實的雙裝置還原由本機 Chromium 關卡涵蓋;已安裝的 Tauri/iOS/Android 復原驗收仍是另外的工作。

不會被傳輸的內容

儲存空間計量表會列出相同的分類。已儲存的工作階段、「我的圖片」與檔案結果與版本都會被納入打包檔中;資產快取、工具預覽以及下方的離線固定項目,皆可重新產生,因此不會被納入。

儲存空間計量表將此裝置的資料分成具名類別,其中「已儲存的工作階段」與「我的圖片」與「資產快取」分開追蹤,此處為全新安裝、每個類別皆尚未有內容的畫面 signed by Lollyvector SVG37 paths~4.9k nodes85 groups65 KB自己驗證取得已簽署檔案儲存空間計量表將此裝置的資料分成具名類別,其中「已儲存的工作階段」與「我的圖片」與「資產快取」分開追蹤,此處為全新安裝、每個類別皆尚未有內容的畫面 signed by Lollyvector SVG37 paths~4.9k nodes85 groups65 KB自己驗證取得已簽署檔案

跨殼層保證

data-transfer.ts 只透過能力橋接層(host.profile、host.state、host.assets)與共用的 localStorage 偏好設定來讀寫。同一個模組在網頁版與 Tauri 上讀寫同一種通用封裝,底層分別是 IndexedDB 或檔案系統儲存。選用的歷史部分只會在對應的轉接器可用時才會出現;不受支援的部分會在匯入時被回報為已跳過。無頭測試套件針對記憶體內的橋接驗證了通用部分,而歷史交易則另有真實瀏覽器測試。

有兩個殼層基於不同原因不在此保證範圍內:

保留的擴充點

信封格式設計上是一份 manifest 加上一組具名的部分,讓日後新型態的可攜資料能不需破壞性變更就搭上這個格式。它們會以附加部分的形式加入(新的 formatVersion,相同的 minReader),而目前的讀取器會跳過它不認得的內容。這些項目目前還沒有建置。之所以在此保留這些名稱,是為了讓格式在它們到位時仍保持一致。

對讀取器而言,任何不在這些保留名稱與上述部分之列的內容,都是未知部分:原封不動地保留,並計入 skipped。

參考