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

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

Дві кнопки, що переносять всю інсталяцію: Export my data записує один архів, Import data зчитує його назадsigned by Lollyvector SVGПеревірте саміGet the signed file12 paths~5.1k nodes12 groups60 KBДві кнопки, що переносять всю інсталяцію: Export my data записує один архів, Import data зчитує його назадsigned by Lollyvector SVGПеревірте саміGet the signed file12 paths~5.1k nodes12 groups60 KB

Ця сторінка - специфікація формату. Покроковий опис для кінцевого користувача - у Using Lolly → Moving to another device. Реалізація - shells/web/src/data-transfer.ts, а tests/data-transfer.test.ts фіксує контракт двостороннього обміну.

Обсяг. Пакет переносить дані користувача, а не інструменти. Інструменти та ресурси каталогу синхронізуються окремо, і передбачається, що вони вже присутні на цільовому пристрої (у гіршому випадку - вищої версії). Імпорт ніколи не встановлює й не оновлює інструмент.

Цілі

Конверт

Пакунок - це звичайний .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.
prefs.jsonтакЛокальні налаштування, що належать користувачу: theme, sidebarWidth та лічильник активності ct-metrics.
lolly.txtтакЧитабельний людиною підсумок пакета (кількості, профіль, назва файлу) для будь-кого, хто відкриє zip без Lolly. Перегенеровується під час кожного експорту і розпізнається при імпорті, тому ніколи не рахується як пропущена частина. Записується після карти цілісності, тому лишається поза нею.

Пакет навмисно є звичайним zip-архівом: він неушкоджено переживає будь-який транспорт, і будь-який засіб розпакування може його оглянути.

profile.json - найменша частина і та, яку читач бачить першою в застосунку: дані, які виробник заповнює один раз, разом зі згодою, що дозволяє інструментам їх використовувати.

Форма даних профілю, яка стає profile.json - ім'я, контакти, фото та згода поруч із нимиsigned by Lollyvector SVGПеревірте саміGet the signed file18 paths~2.0k nodes41 groups30 KBФорма даних профілю, яка стає profile.json - ім'я, контакти, фото та згода поруч із нимиsigned by Lollyvector SVGПеревірте саміGet the signed file18 paths~2.0k nodes41 groups30 KB

manifest.json

{
  "format": "lolly-backup",
  "formatVersion": 1,
  "minReader": 1,
  "app": "lolly",
  "exportedAt": "2026-06-22T09:30:00.000Z",
  "counts": { "profile": true, "sessions": 2, "userAssets": 4, "prefs": 3 },
  "integrity": {
    "profile.json": "sha256-…",
    "sessions.json": "sha256-…",
    "assets.json": "sha256-…",
    "assets/blobs/0.webp": "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 новіших елементів пропущено»), тож відновлення чесно повідомляє, що воно залишило поза увагою.

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

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

Індикатор сховища, що розбиває дані цього пристрою на іменовані категорії, із Saved sessions і My images, відстеженими окремо від Asset cache, тут на свіжій інсталяції, де кожна категорія ще порожняsigned by Lollyvector SVGПеревірте саміGet the signed file25 paths~4.8k nodes46 groups59 KBІндикатор сховища, що розбиває дані цього пристрою на іменовані категорії, із Saved sessions і My images, відстеженими окремо від Asset cache, тут на свіжій інсталяції, де кожна категорія ще порожняsigned by Lollyvector SVGПеревірте саміGet the signed file25 paths~4.8k nodes46 groups59 KB

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

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

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

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

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

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

Довідка