Lolly
Запустити застосунок

Передавання даних - пакет lolly-backup

Усе, що накопичує користувач Lolly, зберігається на його пристрої - без облікового запису, без хмари. Пакет передавання даних - це спосіб перенести це: експортуйте його на одній інсталяції, перенесіть файл будь-яким способом (USB, AirDrop, лист собі на пошту, мережевий спільний ресурс) і імпортуйте на іншій. Файл і є транспорт. Цільовий пристрій може бути офлайн або онлайн. Це не має значення, оскільки нічого й ніколи не звертається до сервера.

Дві кнопки, що переносять всю інсталяцію: Export my data записує один архів, Import data зчитує його назад signed by Lollyvector SVG9 paths~4.9k nodes11 groups56 KBПеревірте саміОтримати підписаний файлДві кнопки, що переносять всю інсталяцію: Export my data записує один архів, Import data зчитує його назад 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) - тож папка Downloads із резервними копіями лишається зрозумілою. Частини First і Last беруться з профілю і опускаються, якщо не задані. Без профілю виходить LollyTools-2026-06-26-1.zip, а лише з іменем - LollyTools-Ada-2026-06-26-1.zip. Кожна частина очищається до безпечного для імені файлу токена (літери й цифри Unicode зберігаються, пробіли й розділові знаки прибираються, обмеження - 32 символи). <n> - це послідовність на день і на пристрій, тож повторні експорти того самого дня не конфліктують і зберігають порядок. Ім'я формує backupFilename() у файлі shells/web/src/data-transfer.ts. Вміст zip-архіву однаковий незалежно від імені. Всередині:

ШляхОбов'язковоВміст
manifest.jsonтакІдентифікатор формату, версії, кількість і цілісність по кожній частині. Перше, на що дивиться читач.
profile.jsonякщо заданоУвесь запис me користувача: ім'я, контакти, посилання на фото та прапорці, а також папки, Кошик, шаблони проєктів, користувацькі шаблони й самостійно створені інструменти, обране, приховані інструменти, мова та вибір емодзі. Читається через host.profile.
sessions.jsonтакКожен збережений сеанс: слот, ідентифікатор/версія інструмента, мітка, мініатюра (data-URL) і повні дані введення. Читається через host.state.
assets.jsonтакМетадані для кожного завантаженого ресурсу (зображення, шрифти, токени бренду, логотипи, збережені копії завантажень), кожне вказує на свої байти під assets/blobs/.
assets/blobs/<n>.<ext>для кожного ресурсуСирі байти ресурсу (файли зображень і шрифтів). Зберігаються нестиснутими (уже стиснуті формати). Розширення - косметичне. Авторитетний джерелом є MIME в assets.json.
assets/blobs/<n>.c2paякщо єВидобуті Content Credentials як точні бінарні байти, на які посилається _credentialFile у записі ресурсу. Це не ключі підписування пристрою.
design-systems.jsonякщо єДизайн-системи, створені чи додані на цій інсталяції, у вигляді { active, records }. Зливаються за ідентифікатором під час імпорту; активний вибір пакета застосовується, лише якщо на цільовому пристрої немає власної дизайн-системи.
file-history.jsonнеобов'язковоВерсіоновані знімки ресурсів, кінцеві звіти файлових операцій і повні маніфести пакетів. Частина історії має власну версію; надається внутрішнім адаптером резервного копіювання fileHistory оболонки.
revision-history.jsonнеобов'язково, ручні резервні копіїСтабільні ідентифікатори творів, збережені контрольні точки, мініатюри й ковзні чернетки відновлення. Надається через host.state.history.backup, де це підтримується.
file-history/versions/для кожного знімкаПопередні байти ресурсу та видобуті облікові дані, незалежно від того, чи існує ще поточний ресурс.
file-history/results/для кожної завершеної операціїТочні байти результату. Жоден оригінальний файл, вибраний для конвертації, не зберігається і не включається.
prefs.jsonтакЛокальні налаштування, що належать користувачу: theme, sidebarWidth та лічильник активності ct-metrics.
lolly.txtтакЧитабельний людиною підсумок пакета (кількості, профіль, назва файлу) для будь-кого, хто відкриє zip без Lolly. Перегенеровується під час кожного експорту і розпізнається при імпорті, тому ніколи не рахується як пропущена частина. Записується після карти цілісності, тому лишається поза нею.

Пакет навмисно є звичайним 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Ідентифікатор застосунку, що створив пакет, для діагностики.
exportedAtISO-мітка часу створення пакета.
countsЩо записувач поклав у пакет, для відображення та перевірки коректності.
integrityНеобов'язково. Зіставляє кожну частину, крім manifest.json, з дайджестом у стилі SRI sha256-<base64> її нестиснутих байтів.

Політика версій (пряма сумісність)

Розділення між formatVersion і minReader дозволяє формату зростати, не залишаючи старіші інсталяції осторонь:

Правило для авторів: якщо кожен наявний читач усе одно поводитиметься правильно, просто ігноруючи ваше доповнення, - це адитивна зміна: збільшуйте formatVersion, залишайте minReader. Інакше підвищуйте minReader.

Цілісність

Коли присутнє manifest.integrity, читач перевіряє SHA-256 кожної переліченої частини перед тим, як щось записати. Невідповідність («не пройшла перевірку цілісності») або відсутня частина («неповний») перериває весь імпорт - часткового відновлення немає. Це вловлює пошкодження, яке може внести транспорт файлу (обрізаний AirDrop, поштовий шлюз, що перекодував вкладення, поганий сектор USB).

Цілісність за задумом реалізована як найкраще можливе: вона записується лише там, де доступний Web Crypto (кожен захищений контекст браузера й сучасний Node), і перевіряється лише коли присутні і карта, і Web Crypto. Пакет без карти - наприклад, створений до появи цілісності - імпортується без змін. «Неможливо перевірити» ніколи не трактується як «пошкоджено».

Маніфест не перелічує ні себе, ні перегенерований README lolly.txt. Дайджести охоплюють ті частини, за які маніфест ручається.

Семантика імпорту

Імпорт - це злиття, ніколи не повна заміна:

Збережені сеанси автоматично пере-прив'язуються до своїх зображень: посилання на ресурси зберігаються за ідентифікатором, і міст повторно розв'язує їх після відновлення завантажених зображень (це необхідно в будь-якому разі, оскільки URL-адреси blob: не переживають перезавантаження).

Підсумок імпорту повідомляє { profile, sessions, userAssets, prefs, skipped, failedAssets }. failedAssets рахує завантажені ресурси, які не вдалося відновити (наприклад, сховище пристрою заповнене). Це відрізняється від skipped, яке рахує частини від сумісного з майбутнім новішого записувача, які ця збірка не розпізнала. Інтерфейс показує skipped («… · N новіших елементів пропущено»), тож відновлення чесно повідомляє, що воно залишило поза увагою.

Коли присутня історія файлів, підсумок також несе assetVersions, fileOperations і failedHistory. Вичерпання сховища чи конфлікти незмінних ідентифікаторів можуть спричинити часткове відновлення; інтерфейс каже користувачу зберегти вихідну резервну копію. Хмарна синхронізація не просуває свою застосовану ревізію після часткового чи непідтримуваного відновлення, тож знімок лишається доступним для повторної спроби. Відновлення - не єдина транзакція для всіх сховищ профілю, сеансів, ресурсів та історії.

Історія творів (v3)

Ручні резервні копії з веб-хоста, здатного на історію, включають revision-history.json із власною схемою { version: 1, documents, revisions, recoveries }. Вона несе збережені ідентифікатори, канонічні знімки вхідних даних, мітки версій, растрові попередні перегляди й окремі чернетки авторів. Адаптер історії захоплює поточні сесії та їхні голови в одній транзакції читання; sessions.json використовує ті самі поточні знімки для старіших читачів.

Відновлення перевіряє SHA-256 і кількість байтів корисного навантаження, унікальні ідентичності, зв'язки документ/голова, походження, часові мітки, типи попередніх переглядів і ліміти, перш ніж застосувати архів однією транзакцією. Ущільнені посилання на батьків можуть бути відсутні. Наявна поточна робота ніколи не замінюється мовчки: коли твір наявний з обох боків, сторона, не збережена як поточна, стає захищеною чернеткою. Ліміт передавання архіву в 384 МіБ перевіряється явно, а ліміти сховища застосовуються без обрізання збережених контрольних точок. Резервна копія загалом усе ще використовує реалізацію ZIP у пам'яті і не є потоковим архівом.

Підсумок додає revisions і recoveryDrafts, рахуючи лише те, що додав цей імпорт, а також added, kept, replaced, copies і hidden - як саме злився кожен твір. Оболонка без цієї можливості відновлює звичайні сесії й повідомляє частину історії як пропущену. Історія нативної файлової системи лишається непідтримуваною, доки її адаптер не надасть стійкі транзакції історії. Стан гостя P2P не має стійкої історії чи архіву відновлення.

Особиста синхронізація знімків явно виключає історію творів. Застосування знімка до локального документа з історією зберігає його попередній робочий стан як окрему чернетку відновлення й анулює токен запису будь-якого відкритого редактора. Його незмінні контрольні точки лишаються на пристрої. Це захищає локальну історію під час заміни знімком; це не зливає паралельні історії пристроїв.

Історичні посилання на ресурси зберігаються, тоді як рендеринг усе ще розв'язує ресурси через наявну бібліотеку призначення. Цей архів поки не гарантує точні старі байти ресурсу чи старі рендери інструмента. Байти версій ресурсів і результатів файлів і далі подорожують через свою наявну окрему частину резервної копії.

Збережені версії та результати файлів (v2)

Необов'язкова частина історії містить { version: 2, assetVersions: [...], operations: [...], batches: [...] }; читачі також приймають ранішу форму history-v1 без пакетів. Кожен знімок визначає стабільний ідентифікатор ресурсу й точну версію, час збереження, довжину в байтах і hex SHA-256, а також запис ресурсу, чиї _file та необов'язковий _credentialFile вказують на бінарні частини. Операції несуть вихідні факти файлу, запит, звіт, часові мітки й необов'язковий результат _file; назви бекендів сховища, дескриптори OPFS та оренди виконання не подорожують. Старіші читачі, що розуміють лише history-v1, відхиляють нову версію історії перед імпортом, замість того щоб мовчки відкидати належність до пакета.

Маніфести пакетів записують кожне обране джерело перед обробкою, зокрема файли, які так і не прочитано, скасованих учасників, невдачі резервування місця під результат і перервану роботу. Кожен учасник має стабільний ідентифікатор операції, посилання/факти джерела, запитану назву виводу й кінцевий звіт. Непрочитане джерело має заявлені факти, а не вигаданий дайджест. Імпорт перевіряє ідентичність учасника та узгодженість з будь-яким перенесеним звітом операції. Звіти пакета лишаються доступними, коли окремі результати явно видалено, але розписка не означає, що байти її виводу все ще зберігаються.

Старіші читачі й далі приймають конверт v2 (minReader: 1) і відновлюють знайомі частини, рахуючи непідтримувані частини історії як пропущені. Повне відновлення історії вимагає оболонки з адаптером fileHistory; це внутрішній шов оболонки, а не нова можливість HostV1, звернена до інструментів. Справжнє відновлення між двома пристроями покриває локальний шлюз Chromium; приймання відновлення у встановлених Tauri/iOS/Android лишається окремим.

Що не переноситься

Індикатор сховища деталізує той самий розподіл. Збережені сеанси, My images та File results & versions потрапляють у пакет. Кеш ресурсів, попередні перегляди інструментів та офлайн-закріплення нижче них усі відновлювані заново, тому лишаються осторонь.

Індикатор сховища, що розбиває дані цього пристрою на іменовані категорії, із Saved sessions і My images, відстеженими окремо від Asset cache, тут на свіжій інсталяції, де кожна категорія ще порожня signed by Lollyvector SVG37 paths~4.9k nodes85 groups65 KBПеревірте саміОтримати підписаний файлІндикатор сховища, що розбиває дані цього пристрою на іменовані категорії, із Saved sessions і My images, відстеженими окремо від Asset cache, тут на свіжій інсталяції, де кожна категорія ще порожня signed by Lollyvector SVG37 paths~4.9k nodes85 groups65 KBПеревірте саміОтримати підписаний файл

Гарантія міжоболонкової сумісності

data-transfer.ts читає та записує виключно через міст можливостей (host.profile, host.state, host.assets) і спільні налаштування localStorage. Той самий модуль читає та записує спільний конверт у вебі та Tauri, через сховище IndexedDB чи файлової системи. Необов'язкові частини історії з'являються лише там, де доступний відповідний адаптер; непідтримувана частина повідомляється як пропущена при імпорті. Безголовий набір тестів перевіряє спільні частини проти моста в пам'яті, тоді як транзакції історії також мають тести в реальному браузері.

Дві оболонки виходять за межі цієї гарантії, з різних причин:

Зарезервовані точки розширення

Конверт за задумом - це маніфест плюс набір іменованих частин, щоб нові види портованих даних могли долучитися пізніше без критичних змін. Вони вбудовуються як додаткові частини (нова formatVersion, той самий minReader), і сьогоднішній зчитувач пропускає те, чого не розпізнає. Це ще не реалізовано. Назви зарезервовано тут, щоб формат лишався узгодженим, коли вони з'являться.

Усе поза цими зарезервованими назвами та частинами вище є для зчитувача невідомою частиною: залишається незміненим і враховується в skipped.

Довідка