Lolly उपयोगकर्ता जो कुछ भी जमा करता है वह उसके डिवाइस पर रहता है - कोई अकाउंट नहीं, कोई क्लाउड नहीं। डेटा-ट्रांसफ़र बंडल इसी मूल्य को हिलाने का तरीका है: इसे एक इंस्टॉल पर एक्सपोर्ट करें, फ़ाइल को किसी भी माध्यम से ले जाएँ (USB, AirDrop, ईमेल-टू-सेल्फ़, एक नेटवर्क शेयर) और दूसरे पर इम्पोर्ट करें। फ़ाइल ही परिवहन है। लक्ष्य डिवाइस ऑफ़लाइन हो या ऑनलाइन। इससे कोई फ़र्क़ नहीं पड़ता, क्योंकि कभी किसी सर्वर से बात नहीं होती।
signed by Lollyvector SVGखुद जाँचेंGet the signed file12 paths~5.1k nodes12 groups60 KB
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 राउंड-ट्रिप कॉन्ट्रैक्ट को पिन करता है।
दायरा। एक बंडल उपयोगकर्ता डेटा ले जाता है, टूल नहीं। टूल और कैटलॉग एसेट्स अलग से सिंक होते हैं और मान लिया जाता है कि वे लक्ष्य पर पहले से मौजूद हैं (सबसे बुरी स्थिति में एक ऊँचे वर्शन पर)। इम्पोर्ट कभी कोई टूल इंस्टॉल या अपग्रेड नहीं करता।
लक्ष्य
- एक फ़ॉर्मैट, हर शेल। वेब PWA, Tauri डेस्कटॉप/मोबाइल ऐप्स और भविष्य का कोई भी शेल वही बाइट्स बनाते और पढ़ते हैं। बंडल ही कॉन्ट्रैक्ट है। हर शेल का कैपेबिलिटी ब्रिज इसके पीछे प्रति-प्लेटफ़ॉर्म अडैप्टर है।
- यात्रा में जीवित रहता है। पारगमन में बिगड़ा या कटा-छँटा बंडल इम्पोर्ट पर स्पष्ट रूप से विफल होता है, कभी आधा-अधूरा रीस्टोर नहीं करता।
- इस वर्शन से आगे टिकता है। एक पुराना ऐप भी एक नए बंडल के पहचाने गए हिस्सों को इम्पोर्ट कर सकता है। एक सचमुच ब्रेकिंग फ़ॉर्मैट स्पष्ट रूप से अस्वीकार कर दिया जाता है।
- मर्ज करना सुरक्षित है। पहले से उपयोग में मौजूद इंस्टॉल पर इम्पोर्ट करना कभी उस चीज़ को नहीं मिटाता जो बंडल में नहीं थी।
एनवेलप
एक बंडल एक सादा .zip है। डाउनलोड का नाम उस व्यक्ति के नाम पर रखा जाता है जिसका यह है - LollyTools-<First>-<Last>-<YYYY-MM-DD>-<n>.zip (उदाहरण के लिए LollyTools-Ada-Lovelace-2026-06-26-1.zip) - ताकि बैकअप्स से भरा Downloads फ़ोल्डर पठनीय बना रहे। पहला और अंतिम भाग प्रोफ़ाइल से आते हैं और सेट न होने पर छोड़ दिए जाते हैं। कोई प्रोफ़ाइल न होने पर 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 | हाँ | हर अपलोड की गई एसेट (इमेज, फ़ॉन्ट, ब्रांड टोकन) का मेटाडेटा, हर एक assets/blobs/ के तहत अपने बाइट्स की ओर इशारा करता है। |
assets/blobs/<n>.<ext> | प्रति एसेट | कच्चे एसेट बाइट्स (इमेज और फ़ॉन्ट फ़ाइलें)। असंपीड़ित संग्रहीत (पहले से संपीड़ित फ़ॉर्मैट्स)। एक्सटेंशन केवल दिखावटी है। assets.json में MIME ही प्रामाणिक है। |
prefs.json | हाँ | उपयोगकर्ता-स्वामित्व वाली स्थानीय प्राथमिकताएँ: theme, sidebarWidth और ct-metrics गतिविधि गणना। |
lolly.txt | हाँ | बंडल का एक मानव-पठनीय सारांश (गिनतियाँ, प्रोफ़ाइल, फ़ाइलनाम) उस किसी के लिए जो Lolly के बिना zip खोलता है। हर एक्सपोर्ट पर फिर से बनाया जाता है और इम्पोर्ट पर पहचाना जाता है, इसलिए यह कभी छूटे हुए भाग के रूप में नहीं गिना जाता। यह इंटीग्रिटी मैप के बाद लिखा जाता है, इसलिए यह उससे बाहर रहता है। |
बंडल जानबूझकर एक सादा zip है: यह किसी भी परिवहन में बरकरार बचता है, और कोई भी अनज़िप टूल इसका निरीक्षण कर सकता है।
profile.json सबसे छोटा भाग है और वह जिसे ऐप में एक रीडर सबसे पहले देखता है: वे विवरण जिन्हें एक निर्माता एक बार भरता है, साथ ही वह ऑप्ट-इन जो टूल को उनका उपयोग करने देता है।
signed by Lollyvector SVGखुद जाँचेंGet the signed file18 paths~2.0k nodes41 groups30 KB
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। इसके बिना फ़ाइल "not a Lolly backup" के रूप में अस्वीकार कर दी जाती है। |
formatVersion | वह लेआउट जिसके साथ यह बंडल लिखा गया था। भाग-समूह या आकारों में किसी भी बदलाव पर बढ़ाया जाता है। रीडर इस पर गेट नहीं करते। |
minReader | न्यूनतम रीडर वर्शन जो इस बंडल को सुरक्षित रूप से इम्पोर्ट करने के लिए आवश्यक है। यही वह फ़ील्ड है जिस पर रीडर गेट करते हैं। |
app | निर्माण करने वाला ऐप id, निदान के लिए। |
exportedAt | बंडल बनाए जाने का ISO टाइमस्टैम्प। |
counts | लेखक ने क्या डाला, प्रदर्शन और सैनिटी-चेकिंग के लिए। |
integrity | वैकल्पिक। manifest.json को छोड़कर हर भाग को उसके असंपीड़ित बाइट्स के SRI-शैली sha256-<base64> डाइजेस्ट से मैप करता है। |
वर्शन नीति (फ़ॉरवर्ड संगतता)
formatVersion और minReader के बीच का विभाजन ही वह चीज़ है जो फ़ॉर्मैट को पुराने इंस्टॉल्स को अनाथ किए बिना बढ़ने देती है:
- जब
manifest.minReader ≤अपने ही रीडर वर्शन से कम या बराबर हो, तभी एक रीडर बंडल इम्पोर्ट करता है। यह तभी मना करता है ("needs a newer version of the app" के साथ) जब बंडल स्पष्ट रूप से एक नए रीडर की माँग करता है। - एक additive बदलाव - एक नया वैकल्पिक भाग, या एक नया वैकल्पिक मैनिफ़ेस्ट फ़ील्ड -
formatVersionबढ़ाता है लेकिनminReaderको अपरिवर्तित छोड़ता है। पुराने ऐप्स फिर भी हर वह भाग इम्पोर्ट करते हैं जिसे वे पहचानते हैं। जिन भागों को वे नहीं पहचानते उन्हें छोड़ दिया जाता है (नीचे देखें), चुपचाप गिराया नहीं जाता। - एक breaking बदलाव - जहाँ किसी भाग का गलत इम्पोर्ट डेटा को भ्रष्ट कर देता है, या जहाँ पहले वैकल्पिक भाग अनिवार्य हो जाता है -
minReaderबढ़ाता है। पुराने ऐप्स फिर उस चीज़ को इम्पोर्ट करने के बजाय जिसे वे संभाल नहीं सकते, स्पष्ट रूप से मना कर देते हैं। - यदि भविष्य का कोई बंडल
formatVersionसेट करता है लेकिनminReaderछोड़ देता है, तो रीडर सावधानी सेformatVersionपर गेट करने में वापस आ जाते हैं (बदलाव को ब्रेकिंग मानते हुए)।
लेखकों के लिए अंगूठे का नियम: यदि हर मौजूदा रीडर आपके जोड़े को अनदेखा करके भी सही काम करता रहेगा, तो यह additive है -
formatVersionबढ़ाएँ,minReaderछोड़ दें। अन्यथाminReaderबढ़ाएँ।
इंटीग्रिटी
जब manifest.integrity मौजूद होता है, तो एक रीडर कुछ भी लिखने से पहले हर सूचीबद्ध भाग का SHA-256 सत्यापित करता है। कोई बेमेल ("failed its integrity check") या कोई गायब भाग ("incomplete") पूरे इम्पोर्ट को रद्द कर देता है - कोई आंशिक रीस्टोर नहीं होता। यह उस भ्रष्टाचार को पकड़ता है जो एक फ़ाइल परिवहन ला सकता है (एक कटा हुआ AirDrop, एक ईमेल गेटवे जिसने अटैचमेंट को फिर से एनकोड किया, एक खराब USB सेक्टर)।
इंटीग्रिटी डिज़ाइन से ही बेस्ट-एफ़र्ट है: यह केवल वहीं लिखी जाती है जहाँ Web Crypto उपलब्ध है (हर सुरक्षित ब्राउज़र संदर्भ और आधुनिक Node), और केवल तभी सत्यापित होती है जब मैप और Web Crypto दोनों मौजूद हों। बिना मैप वाला बंडल - उदाहरण के लिए इंटीग्रिटी अस्तित्व में आने से पहले का कोई बंडल - बिना बदलाव के इम्पोर्ट हो जाता है। "Cannot verify" को कभी "corrupt" के रूप में नहीं लिया जाता।
मैनिफ़ेस्ट न तो अपने आप को सूचीबद्ध करता है और न ही फिर से बनाए गए lolly.txt README को। डाइजेस्ट उन्हीं भागों को कवर करते हैं जिनकी मैनिफ़ेस्ट गारंटी देता है।
इम्पोर्ट सिमेंटिक्स
इम्पोर्ट मर्ज-ओवरराइट है, कभी सब कुछ बदलना नहीं:
- लक्ष्य पर मौजूदा डेटा जगह पर छोड़ दिया जाता है।
- कोई भी टकराने वाली कुंजी - प्रोफ़ाइल, एक सेशन स्लॉट, एक अपलोड की गई इमेज id - इम्पोर्ट की गई प्रति से बदल दी जाती है।
- जो बंडल में नहीं था उसे छुआ नहीं जाता। जो सेशन लक्ष्य के पास था लेकिन बंडल में नहीं था, वह इम्पोर्ट के बाद भी बचा रहता है।
सेव किए गए सेशन अपने-आप अपनी इमेज से फिर से जुड़ जाते हैं: एसेट संदर्भ id से रखे जाते हैं, और अपलोड की गई इमेज रीस्टोर होने के बाद ब्रिज उन्हें फिर से हल करता है (इसे वैसे भी करना ही होता है, क्योंकि blob: URLs रीलोड में नहीं बचते)।
इम्पोर्ट सारांश { profile, sessions, userAssets, prefs, skipped, failedAssets } रिपोर्ट करता है। failedAssets उन अपलोड की गई एसेट्स की गिनती करता है जिन्हें रीस्टोर नहीं किया जा सका (जैसे डिवाइस स्टोरेज भरा होना)। यह skipped से अलग है, जो एक फ़ॉरवर्ड-संगत नए लेखक के उन भागों की गिनती करता है जिन्हें इस बिल्ड ने नहीं पहचाना। UI skipped को सामने लाता है ("… · N newer items skipped"), ताकि रीस्टोर इस बारे में ईमानदार रहे कि उसने क्या पीछे छोड़ा।
क्या यात्रा नहीं करता
- कैटलॉग कैश (डाउनलोड की गई एसेट मेटाडेटा और ब्लॉब्स, टूल इंडेक्स) - लक्ष्य पर मुफ़्त में फिर से सिंक हो जाते हैं।
- टूल और ब्रांड एसेट्स - दायरे से बाहर, और माना जाता है कि लक्ष्य पर पहले से मौजूद हैं।
blob:/ ऑब्जेक्ट URLs - लोड पर ब्रिज द्वारा फिर से बनाए जाते हैं।- एक्सपोर्ट सीक्वेंस काउंटर - प्रति-दिन डाउनलोड-नामकरण काउंटर (
localStorageकुंजीlolly-export-seq) एक स्थानीय नामकरण सुविधा है। इसेPREF_KEYSसे बाहर रखा जाता है, इसलिए यह कभी किसी बंडल में सवार नहीं होता।
स्टोरेज मीटर उसी विभाजन को वस्तुओं में बाँटता है। सेव किए गए सेशन और My images एक बंडल में सवार होते हैं। एसेट कैश, टूल प्रीव्यू और नीचे के ऑफ़लाइन पिन सभी फिर से व्युत्पन्न किए जा सकते हैं, इसलिए वे पीछे रह जाते हैं।
signed by Lollyvector SVGखुद जाँचेंGet the signed file25 paths~4.8k nodes46 groups59 KB
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 कार्यान्वयन अलग होता है। हेडलेस टेस्ट पूरे राउंड-ट्रिप को एक इन-मेमोरी ब्रिज के विरुद्ध चलाता है, इसीलिए यह इन सबका प्रतिनिधित्व करता है।
दो शेल उस गारंटी से बाहर हैं, अलग-अलग वजहों से:
- वन-शॉट CLI के पास ले जाने के लिए कुछ नहीं होता - इसकी स्थिति हर बार इन्वोकेशन में इन-मेमोरी और अस्थायी होती है।
- TUI स्थिति को बनाए रखता है (
~/.lolly: सेशन, फ़ोल्डर, प्रोफ़ाइल) और इसका Profile व्यू इसका बैकअप ले सकता है, लेकिन यह अपना एक सरल आर्काइव लिखता है: हर सेशन के लिएsessions/<slot>.jsonके साथprofile.jsonऔरfolders.json, बिना किसी मेनिफ़ेस्ट, बिनाformatVersion/minReaderऔर बिना इंटीग्रिटी मैप के। यह इस फ़ॉर्मैट द्वारा इम्पोर्ट योग्य नहीं है - एक रीडर इसे "not a Lolly backup" कहकर अस्वीकार करता है - और भ्रामक रूप से यह मिलता-जुलता नाम इस्तेमाल करता है (lolly-backup-<stamp>.zip)। दोनों को एकीकृत करना एक ज्ञात कमी है।
सुरक्षित एक्सटेंशन पॉइंट
एनवेलप जानबूझकर एक मेनिफ़ेस्ट और नामित भागों का एक सेट है, ताकि नए तरह का पोर्टेबल डेटा बाद में बिना किसी ब्रेकिंग बदलाव के इस पर सवार हो सके। ये अतिरिक्त भागों के रूप में शामिल होते हैं (नया formatVersion, वही minReader), और आज का रीडर जो नहीं पहचानता उसे छोड़ देता है। ये रोडमैप पर हैं, अभी लागू नहीं हुए हैं। नाम यहाँ सुरक्षित रखे गए हैं ताकि जब ये आएँ तो फ़ॉर्मैट सुसंगत बना रहे।
tokens.json- डिज़ाइन टोकन। एक W3C DTCG डिज़ाइन-टोकन दस्तावेज़ (वह फ़ॉर्मैट जिसे Penpot इम्पोर्ट और एक्सपोर्ट करता है -$value/$type/$descriptionवाले टोकन, समूहों, सेट और थीम में व्यवस्थित)। बंडल में एक टोकन सेट उपयोगकर्ता को अपने ब्रैंड प्रिमिटिव्स को अपने सेशन के साथ इंस्टॉल के बीच ले जाने देता है। लंबी अवधि में, इंजेस्ट किया गया टोकन सेट एक फ़र्स्ट-क्लास स्रोत बन जाता है जिसके विरुद्ध टूल और पैलेट एसेट रिज़ॉल्व होते हैं।penpot/- इंजेस्ट किए गए Penpot फ़ाइलें। एक Penpot फ़ाइल (या उसके निकाले गए, Lolly-प्रासंगिक सबसेट) के लिए एक सुरक्षित डायरेक्टरी जो इम्पोर्ट होकर एक टूल के रूप में सामने आती है। बंडल इंजेस्ट की गई परिभाषा को साथ ले जाएगा, ताकि यह उपयोगकर्ता के बाक़ी डेटा के साथ यात्रा करे।
इन सुरक्षित नामों और ऊपर दिए गए भागों के अलावा कुछ भी रीडर के लिए एक अज्ञात भाग है: बिना छेड़े छोड़ दिया जाता है और skipped में गिना जाता है।
संदर्भ
- मॉड्यूल:
shells/web/src/data-transfer.ts(exportBackup,importBackup,BACKUP_FORMAT,BACKUP_FORMAT_VERSION,BACKUP_READER_VERSION-backupFilename()नामकर्ता आंतरिक है)। - कॉन्ट्रैक्ट टेस्ट:
tests/data-transfer.test.ts- राउंड-ट्रिप, मर्ज, इंटीग्रिटी, फ़ॉरवर्ड-कम्पैट और रीडर-गेट केस। - इस्तेमाल की गई ब्रिज सतह:
host.profile,host.state,host.assets- देखें Host API।