FreeLattice · Celeste reading home

Public, free to read, reuse, and adapt — Kirk Patrick Miller's notice for this edition. Not a patent grant.

GROK_NEXT_ENCRYPT_CHATS_LOCALLY_2026-10-02.txt · c10

f688437464 · words 733 · characters 88202–94166

Previous section

Exact section text · Full source · Source index

Archive pages whose extracted text matched this section: not separately matched. Unmatched pages stay on the source record with the checked page method. They are not guessed into a section.

No topic tag was earned for this page under the recorded evidence rule.

+  // 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('<span style="color:var(--success);">&#9989; Restored: ' + summary.join(', ') + ' (from v' + fromVersion + ', ' + fromDate + '). Reloading in 4 seconds...</span>');
+      // before v-encrypt-chats-locally-v0.1: setStatus('...Restored: ' + summary.join(', ') + ' (from v' + fromVersion + ', ' + fromDate + '). Reloading in 4 seconds...</span>');
+      setStatus('<span style="color:var(--success);">&#9989; Restored: ' + summary.join(', ') + ' (from v' + fromVersion + ', ' + fromDate + ').' + flRestoreKeyNote + ' Reloading in 4 seconds...</span>');
 
       // 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.