Data Transfer - o pacote lolly-backup

Tudo que um usuário do Lolly acumula vive no seu dispositivo - sem conta, sem nuvem. O pacote de transferência de dados é como esse valor se move: exporte-o em uma instalação, leve o arquivo por qualquer meio (USB, AirDrop, e-mail para si mesmo, um compartilhamento de rede) e importe-o em outra. O arquivo é o transporte. O destino pode estar offline ou online. Não faz diferença, porque nada nunca fala com um servidor.

Os dois botões que movem uma instalação inteira: Export my data grava um zip, Import data o lê de voltasigned by Lollyvector SVGVerifique você mesmoGet the signed file12 paths~5.1k nodes12 groups60 KBOs dois botões que movem uma instalação inteira: Export my data grava um zip, Import data o lê de voltasigned by Lollyvector SVGVerifique você mesmoGet the signed file12 paths~5.1k nodes12 groups60 KB

Esta página é a especificação do formato. Para o passo a passo do usuário final, veja Using Lolly → Moving to another device. A implementação está em shells/web/src/data-transfer.ts, e tests/data-transfer.test.ts fixa o contrato de ida e volta.

Escopo. Um pacote carrega dados do usuário, não ferramentas. Ferramentas e ativos do catálogo são sincronizados separadamente e presume-se que já estejam presentes no destino (no pior caso, em uma versão mais nova). Importar nunca instala ou atualiza uma ferramenta.

Objetivos

O envelope

Um pacote é um .zip simples. O download recebe o nome da pessoa a quem pertence - LollyTools-<First>-<Last>-<YYYY-MM-DD>-<n>.zip (por exemplo LollyTools-Ada-Lovelace-2026-06-26-1.zip) - para que uma pasta de Downloads cheia de backups continue legível. As partes de primeiro e último nome vêm do perfil e são omitidas quando não definidas. Sem perfil, o resultado é LollyTools-2026-06-26-1.zip, e apenas um primeiro nome dá LollyTools-Ada-2026-06-26-1.zip. Cada parte é sanitizada para um token seguro para nome de arquivo (letras/dígitos Unicode mantidos, espaços/pontuação removidos, limitado a 32 caracteres). <n> é uma sequência por dia, por dispositivo, então exportações repetidas no mesmo dia não colidem e permanecem em ordem. backupFilename() em shells/web/src/data-transfer.ts monta o nome. O conteúdo do zip é idêntico independentemente do nome. Dentro:

PathRequiredContents
manifest.jsonsimId do formato, versões, contagens e integridade por parte. A primeira coisa que um leitor examina.
profile.jsonquando definidoO registro me do usuário (nome, contato, referência de foto, flags). Lido via host.profile.
sessions.jsonsimCada sessão salva: slot, id/versão da ferramenta, rótulo, miniatura (data-URL) e dados de entrada completos. Lido via host.state.
assets.jsonsimMetadados de cada ativo enviado (imagens, fontes, tokens de marca), cada um apontando para seus bytes em assets/blobs/.
assets/blobs/<n>.<ext>por ativoOs bytes brutos do ativo (arquivos de imagem e fonte). Armazenados sem compressão (formatos já compactados). A extensão é cosmética. O MIME em assets.json é a fonte autoritativa.
prefs.jsonsimPreferências locais de propriedade do usuário: theme, sidebarWidth e a contagem de atividade ct-metrics.
lolly.txtsimUm resumo legível por humanos do pacote (contagens, perfil, nome do arquivo) para quem abrir o zip sem o Lolly. Regenerado a cada exportação e reconhecido na importação, então nunca conta como parte pulada. É escrito depois do mapa de integridade, então fica fora dele.

O pacote é um zip simples de propósito: sobrevive a qualquer transporte intacto, e qualquer ferramenta de descompactação consegue inspecioná-lo.

profile.json é a menor parte e a primeira que um leitor vê no app: os detalhes que uma produtora preenche uma vez, mais o opt-in que permite às ferramentas usá-los.

O formulário de detalhes do Profile que se torna profile.json - nome, contato, foto e o opt-in ao ladosigned by Lollyvector SVGVerifique você mesmoGet the signed file18 paths~2.0k nodes41 groups30 KBO formulário de detalhes do Profile que se torna profile.json - nome, contato, foto e o opt-in ao ladosigned by Lollyvector SVGVerifique você mesmoGet 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-…"
  }
}
FieldMeaning
formatSempre lolly-backup. Um arquivo sem isso é rejeitado como "not a Lolly backup".
formatVersionO layout com que este pacote foi escrito. Incrementado a cada mudança no conjunto de partes ou formas. Os leitores não se baseiam nele.
minReaderA versão mínima de leitor necessária para importar este pacote com segurança. É neste campo que os leitores se baseiam.
appId do app produtor, para diagnóstico.
exportedAtTimestamp ISO de quando o pacote foi criado.
countsO que o gravador colocou, para exibição e verificação de sanidade.
integrityOpcional. Mapeia cada parte, exceto manifest.json, a um digest no estilo SRI sha256-<base64> dos seus bytes não compactados.

Política de versão (compatibilidade futura)

A separação entre formatVersion e minReader é o que permite ao formato crescer sem deixar instalações antigas órfãs:

Regra prática para autores: se todo leitor existente ainda se comportaria corretamente ao ignorar sua adição, ela é aditiva - incremente formatVersion, deixe minReader. Caso contrário, eleve minReader.

Integridade

Quando manifest.integrity está presente, um leitor verifica o SHA-256 de cada parte listada antes de escrever qualquer coisa. Uma divergência ("failed its integrity check") ou uma parte ausente ("incomplete") aborta toda a importação - não há restauração parcial. Isso captura a corrupção que um transporte de arquivo pode introduzir (um AirDrop truncado, um gateway de e-mail que recodificou o anexo, um setor de USB ruim).

A integridade é best-effort por design: só é escrita onde a Web Crypto está disponível (todo contexto seguro de navegador e Node moderno), e só é verificada quando tanto o mapa quanto a Web Crypto estão presentes. Um pacote sem o mapa - por exemplo, um de antes de a integridade existir - é importado sem alteração. "Não é possível verificar" nunca é tratado como "corrompido".

O manifesto não lista nem a si mesmo nem o README lolly.txt regenerado. Os digests cobrem as partes que o manifesto atesta.

Semântica de importação

A importação é mesclar e sobrescrever, nunca substituir tudo:

Sessões salvas se reconectam automaticamente às suas imagens: as referências de ativos são mantidas por id, e a ponte as resolve novamente depois que as imagens enviadas são restauradas (ela precisa fazer isso de qualquer forma, porque URLs blob: não sobrevivem a uma recarga).

O resumo de importação reporta { profile, sessions, userAssets, prefs, skipped, failedAssets }. failedAssets conta os ativos enviados que não puderam ser restaurados (armazenamento do dispositivo cheio, por exemplo). É distinto de skipped, que conta partes de um gravador mais novo e compatível para trás que esta build não reconheceu. A interface exibe skipped ("… · N newer items skipped"), então a restauração é honesta sobre o que deixou para trás.

O que não viaja

O medidor de armazenamento detalha essa mesma divisão. Sessões salvas e Minhas imagens viajam em um pacote. O cache de ativos, as prévias de ferramentas e os pins offline abaixo deles são todos re-deriváveis, então ficam de fora.

O medidor de armazenamento dividindo os dados deste dispositivo em categorias nomeadas, com Saved sessions e My images rastreados separadamente do Asset cache, aqui em uma instalação nova onde toda categoria ainda está vaziasigned by Lollyvector SVGVerifique você mesmoGet the signed file25 paths~4.8k nodes46 groups59 KBO medidor de armazenamento dividindo os dados deste dispositivo em categorias nomeadas, com Saved sessions e My images rastreados separadamente do Asset cache, aqui em uma instalação nova onde toda categoria ainda está vaziasigned by Lollyvector SVGVerifique você mesmoGet the signed file25 paths~4.8k nodes46 groups59 KB

Garantia entre shells

data-transfer.ts le e grava exclusivamente através da bridge de capacidades (host.profile, host.state, host.assets) e das preferências compartilhadas em localStorage. Como a bridge é a única costura, o mesmo módulo produz um pacote byte a byte idêntico em cada shell, mesmo com o armazenamento subjacente diferindo - IndexedDB na web, o sistema de arquivos no Tauri. Os shells do Tauri reutilizam esse módulo sem alterações. Só a implementação de host.state deles é diferente. O teste headless exercita o round-trip completo contra uma bridge em memória, e é por isso que ele representa todos eles.

Dois shells ficam fora dessa garantia, por motivos diferentes:

Pontos de extensão reservados

O envelope é um manifesto mais um conjunto de partes nomeadas por design, para que novos tipos de dados portáveis possam usá-lo depois sem uma mudança que quebre compatibilidade. Eles entram como partes aditivas (novo formatVersion, mesmo minReader), e o leitor de hoje ignora o que não reconhece. Isso está no roadmap, ainda não implementado. Os nomes são reservados aqui para que o formato permaneça coerente quando chegarem.

Qualquer coisa fora desses nomes reservados e das partes acima é, para um leitor, uma parte desconhecida: deixada intocada e contada em skipped.

Referência