+ // v-encrypt-chats-locally-v0.1: the unlock key never travels in a backup. Before this, every + // localStorage key went into the file, fl_device_encryption_key too, so anyone holding a backup + // could open the API keys saved in it (the comment on _FL_DEVICE_KEY_STORAGE already promised + // "NOT exportable in backups"). Chats in IndexedDB still go in as readable text, as before. + var FL_BACKUP_NEVER_EXPORT = ['fl_device_encryption_key']; + // Saved keys sealed with a device's own unlock key. On restore they never replace a key this + // device already has (an old blob from another device would only read as nothing here). + var FL_BACKUP_SEALED_KEYS = ['fl_apiKey_enc', 'fl_apiKey_provider', 'fl_ghToken_enc', 'fl_hfToken_enc', 'fl_ghSyncToken']; function getAllLocalStorage() { var data = {}; try { for (var i = 0; i < localStorage.length; i++) { var key = localStorage.key(i); + // before v-encrypt-chats-locally-v0.1: data[key] = localStorage.getItem(key); (for every key) + if (FL_BACKUP_NEVER_EXPORT.indexOf(key) >= 0) continue; data[key] = localStorage.getItem(key); } } catch(e) {} @@ -33136,6 +33518,8 @@ const FlBackup = (function() { var idbData = []; for (var i = 0; i < IDB_NAMES.length; i++) { var dbData = await getAllIDBData(IDB_NAMES[i]); + // v-encrypt-chats-locally-v0.1: backups stay restorable on any device, so sealed chats are opened into the file. + if (typeof FLChatVault !== 'undefined') dbData = await FLChatVault.openDump(dbData); if (dbData && Object.keys(dbData.stores).length > 0) { idbData.push(dbData); } @@ -33265,17 +33649,26 @@ const FlBackup = (function() { } var summary = []; + var flRestoreKeyNote = ''; // v-encrypt-chats-locally-v0.1 // Restore localStorage if (backup.localStorage && typeof backup.localStorage === 'object') { var lsCount = 0; + // v-encrypt-chats-locally-v0.1: an unlock key in an older backup is never written back + // (this device keeps its own), and saved sealed keys never replace ones this device has. + var lsKeptHere = 0; Object.keys(backup.localStorage).forEach(function(key) { try { + if (FL_BACKUP_NEVER_EXPORT.indexOf(key) >= 0) { lsKeptHere++; return; } + if (FL_BACKUP_SEALED_KEYS.indexOf(key) >= 0 && localStorage.getItem(key) != null) { lsKeptHere++; return; } + // before v-encrypt-chats-locally-v0.1: localStorage.setItem(key, backup.localStorage[key]); (for every key) localStorage.setItem(key, backup.localStorage[key]); lsCount++; } catch(e) {} }); summary.push(lsCount + ' settings'); + if (lsKeptHere > 0) summary.push(lsKeptHere + ' key settings kept as they were on this device'); + flRestoreKeyNote = ' Saved API keys open only on the device that sealed them; on another device, enter them again.'; } // Restore IndexedDB @@ -33306,7 +33699,8 @@ const FlBackup = (function() { try { SoulCeremony.fire('emerald', 'Welcome home. Everything is restored.'); } catch(e2) {} } - setStatus('✅ Restored: ' + summary.join(', ') + ' (from v' + fromVersion + ', ' + fromDate + '). Reloading in 4 seconds...'); + // before v-encrypt-chats-locally-v0.1: setStatus('...Restored: ' + summary.join(', ') + ' (from v' + fromVersion + ', ' + fromDate + '). Reloading in 4 seconds...'); + setStatus('✅ Restored: ' + summary.join(', ') + ' (from v' + fromVersion + ', ' + fromDate + ').' + flRestoreKeyNote + ' Reloading in 4 seconds...'); // Reload after a short delay setTimeout(function() { @@ -36463,7 +36857,10 @@ window.FLSearch = (function() { var os = tx.objectStore(storeConfig.store); var getAll = os.getAll(); getAll.onsuccess = function() { - var items = getAll.result || []; + // v-encrypt-chats-locally-v0.1: open sealed chat records first; plain records pass through. + var _flRawItems = getAll.result || []; + var _flOpened = (typeof FLChatVault !== 'undefined') ? FLChatVault.openAny(storeConfig.db, storeConfig.store, _flRawItems) : Promise.resolve(_flRawItems); + _flOpened.then(function(items) { var matches = []; for (var i = 0; i < items.length; i++) { // Exclude current conversation to prevent echoing user's own words back @@ -36482,6 +36879,7 @@ window.FLSearch = (function() { } db.close(); resolve(matches); + }, function() { db.close(); resolve([]); }); }; getAll.onerror = function() { db.close(); resolve([]); }; }; @@ -47028,6 +47426,16 @@ const MemoryIndex = (function() { }); } + // v-encrypt-chats-locally-v0.1: this index keeps its own full copy of every chat, + // so it is sealed too. The helpers above stay as written. + var _flMiGetRaw = miGet, _flMiPutRaw = miPut, _flMiGetAllRaw = miGetAll; + if (typeof FLChatVault !== 'undefined') { + miGet = function (key) { return _flMiGetRaw(key).then(function (r) { return FLChatVault.openFor('FreeLatticeMemory/conversations', r); }); }; + miPut = function (value) { return FLChatVault.sealFor('FreeLatticeMemory/conversations', value).then(_flMiPutRaw); }; + miGetAll = function () { return _flMiGetAllRaw().then(function (rs) { return FLChatVault.openAll('FreeLatticeMemory/conversations', rs); }); }; + FLChatVault.registerStore('FreeLatticeMemory/conversations', function () { return miDb; }, MI_STORE); + } + // --- Keyword Extraction (TF-IDF inspired) --- function extractKeywords(text) { if (!text) return []; =====END FL DIFF===== End of file.