Persistent Context Modification Warnings

Hi,

I love the virtual switches to expose some config custom logic, e.g. selecting if my auto AC Ignore logic to connect when ESS demands it is active.

To use it, I needed to active a persistent context store. All good and well, but the official procedure results in the scary red modified warnings. I know one day I will need support on something entirely unrelated and the conversation will demand I undo whatever caused the modified flag.

I feel like a persistent context store is a pretty standard and innocuous feature. Could I request this be part of the standard Node-RED image’s config?

I usually create a memory only context store as the default, and a file system as an option on all other Node-RED instances.

Regards,
Deon

Use the above to make a file that can store the context permanently without causing the red issue, it is allowed with no problems. 1st inject to make the file, 2nd to check it is there.

Your cut and paste has errors and special characters in it.
I proper copy from the export option in nodered should render fine when tagged with the flows markup and display via mermaid.

How do I paste part of a flow like just a group, or selected nodes?

Just define it with your mouse then choose export selected, or just this flow.

[{"id":"e99119303f2e614d","type":"group","z":"8d5542876c246ef6","name":"Persistence/context","style":{"stroke":"#ff0000","label":true},"nodes":["b3e72b5b88a040b2","44773b671f82c051","d1a39f9bb57004e6","b7c0cfa5f8e04d0b","468d462f933c47e5","ad8db70e82eb4f45","mod_persist_fail","fn_show_modal","fn_set_fail_flag","fn_acknowledge_modal","d379ad022c393034","105b2f270ed429e5","bceefcfe39af28d1","btn_persist_health","fn_persist_health","debug_persist_health","074b6241262c30ad","btn_context_master","fn_prepare_context_modal","tmpl_context_modal","sw_context_choice","fn_clear_global_ram","fn_clear_global_all","fn_clear_flow_ram","fn_clear_flow_all","fn_clear_all_all","e1784102c9339bf6","c68f992a45f44e70","d4490bd64fcf45ae","e029c599f28949fc","ac1d5c676b5c4273","88238466a4e49759"],"x":14,"y":799,"w":872,"h":822},{"id":"b3e72b5b88a040b2","type":"inject","z":"8d5542876c246ef6","d":true,"g":"e99119303f2e614d","name":"make file \"settings-user.js\"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":"","topic":"","payload":"// settings-user.js for persistent file contextmodule.exports = {  contextStorage: {    default: { module: \"memory\" },    file:    { module: \"localfilesystem\" }  }};","payloadType":"str","x":170,"y":840,"wires":[["44773b671f82c051"]]},{"id":"44773b671f82c051","type":"file","z":"8d5542876c246ef6","d":true,"g":"e99119303f2e614d","name":"to /data/home/nodered/.node-red/settings-user.js","filename":"/data/home/nodered/.node-red/settings-user.js","appendNewline":false,"createDir":false,"overwriteFile":"true","encoding":"utf8","x":490,"y":840,"wires":[["d1a39f9bb57004e6"]]},{"id":"d1a39f9bb57004e6","type":"debug","z":"8d5542876c246ef6","d":true,"g":"e99119303f2e614d","name":"Write status","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":770,"y":840,"wires":[]},{"id":"b7c0cfa5f8e04d0b","type":"inject","z":"8d5542876c246ef6","d":true,"g":"e99119303f2e614d","name":"List /data/home/nodered/.node-red/","props":[{"p":"payload","v":"/data/home/nodered/.node-red/","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":200,"y":900,"wires":[["468d462f933c47e5"]]},{"id":"468d462f933c47e5","type":"exec","z":"8d5542876c246ef6","d":true,"g":"e99119303f2e614d","command":"ls -lh","addpay":true,"append":"","useSpawn":"false","timer":"","oldrc":false,"name":"ls folder","x":420,"y":900,"wires":[["ad8db70e82eb4f45"],[],[]]},{"id":"ad8db70e82eb4f45","type":"debug","z":"8d5542876c246ef6","d":true,"g":"e99119303f2e614d","name":"Folder/File List","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","x":650,"y":900,"wires":[]},{"id":"mod_persist_fail","type":"ui_template","z":"8d5542876c246ef6","g":"e99119303f2e614d","group":"a985e96a8135e229","name":"Persistence Fail Modal","order":8,"width":"0","height":"0","format":"<script>\n(function(scope){\n    function showModal(msg) {\n        if (!window.PERSIST_FAIL_MODAL) {\n            var modal = document.createElement('div');\n            modal.id = 'persist-fail-modal';\n            modal.style = 'position:fixed;top:0;left:0;width:100vw;height:100vh;background:rgba(24,10,10,0.92);z-index:99999;display:flex;align-items:center;justify-content:center;';\n            modal.innerHTML = `\n                <div style=\"background:#b71c1c;color:#fff;padding:32px 28px 28px 28px;border-radius:14px;box-shadow:0 4px 30px #000a;width:320px;max-width:90vw;text-align:center;font-size:1.12em;\">\n                    <div style='font-size:2.2em;margin-bottom:8px;'>⚠️</div>\n                    <b>Persistence Error</b><br>\n                    Node-RED file context <b>NOT working</b>.<br>\n                    Settings/logs <u>will not survive reboot</u>!<br>\n                    <span style='font-size:0.95em;opacity:0.7;'>Check SD card, storage, or settings-user.js</span>\n                    <br><br>\n                    <button id='acknowledge-persist-fail' style=\"margin-top:6px;background:#fff;color:#b71c1c;padding:8px 16px;font-weight:bold;border:none;border-radius:7px;cursor:pointer;font-size:1em;\">Acknowledge</button>\n                </div>`;\n            document.body.appendChild(modal);\n            window.PERSIST_FAIL_MODAL = modal;\n            document.getElementById('acknowledge-persist-fail').onclick = function(){\n                scope.send({payload:'ack'});\n                modal.remove();\n                window.PERSIST_FAIL_MODAL = null;\n            };\n        }\n    }\n    scope.$watch('msg', function(msg){\n        if(msg && msg.payload===true){\n            showModal(msg);\n        }\n    });\n})(scope);\n</script>","storeOutMessages":false,"fwdInMessages":false,"resendOnRefresh":true,"templateScope":"local","className":"","x":440,"y":1020,"wires":[["fn_acknowledge_modal"]]},{"id":"fn_show_modal","type":"function","z":"8d5542876c246ef6","g":"e99119303f2e614d","name":"Show Modal if Fail","func":"// pass = true means fail; set by health check\n// Only trigger modal if flag is set and not acknowledged\nif(msg.pass===false || msg.pass===undefined){\n    var fail = flow.get('PERSIST_FAIL', 'file');\n    if(fail){\n        msg.payload = true; // triggers modal\n        return msg;\n    }\n}\nreturn null;","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":210,"y":1020,"wires":[["mod_persist_fail","105b2f270ed429e5"]]},{"id":"fn_set_fail_flag","type":"function","z":"8d5542876c246ef6","g":"e99119303f2e614d","name":"Set Fail Flag on Health Check","func":"// Call this from health check node: pass = false -> fail\nflow.set('PERSIST_FAIL', !msg.pass, 'file');\nreturn msg;","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":180,"y":960,"wires":[["fn_show_modal"]]},{"id":"fn_acknowledge_modal","type":"function","z":"8d5542876c246ef6","g":"e99119303f2e614d","name":"Acknowledge Button","func":"// Clear fail flag when user acknowledges\nif(msg && msg.payload==='ack'){\n    flow.set('PERSIST_FAIL', false, 'file');\n}\nreturn null;","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":680,"y":1020,"wires":[[]]},{"id":"d379ad022c393034","type":"inject","z":"8d5542876c246ef6","g":"e99119303f2e614d","name":"fail","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"true","payloadType":"bool","x":210,"y":1080,"wires":[["mod_persist_fail","105b2f270ed429e5"]]},{"id":"105b2f270ed429e5","type":"ui_template","z":"8d5542876c246ef6","g":"e99119303f2e614d","group":"2ce15f4144b5de0a","name":"Persistence Fail Modal","order":0,"width":"0","height":"0","format":"<script>\n(function(scope){\n    function showModal(msg) {\n        if (!window.PERSIST_FAIL_MODAL) {\n            var modal = document.createElement('div');\n            modal.id = 'persist-fail-modal';\n            modal.style = 'position:fixed;top:0;left:0;width:100vw;height:100vh;background:rgba(24,10,10,0.92);z-index:99999;display:flex;align-items:center;justify-content:center;';\n            modal.innerHTML = `\n                <div style=\"background:#b71c1c;color:#fff;padding:32px 28px 28px 28px;border-radius:14px;box-shadow:0 4px 30px #000a;width:320px;max-width:90vw;text-align:center;font-size:1.12em;\">\n                    <div style='font-size:2.2em;margin-bottom:8px;'>⚠️</div>\n                    <b>Persistence Error</b><br>\n                    Node-RED file context <b>NOT working</b>.<br>\n                    Settings/logs <u>will not survive reboot</u>!<br>\n                    <span style='font-size:0.95em;opacity:0.7;'>Check SD card, storage, or settings-user.js</span>\n                    <br><br>\n                    <button id='acknowledge-persist-fail' style=\"margin-top:6px;background:#fff;color:#b71c1c;padding:8px 16px;font-weight:bold;border:none;border-radius:7px;cursor:pointer;font-size:1em;\">Acknowledge</button>\n                </div>`;\n            document.body.appendChild(modal);\n            window.PERSIST_FAIL_MODAL = modal;\n            document.getElementById('acknowledge-persist-fail').onclick = function(){\n                scope.send({payload:'ack'});\n                modal.remove();\n                window.PERSIST_FAIL_MODAL = null;\n            };\n        }\n    }\n    scope.$watch('msg', function(msg){\n        if(msg && msg.payload===true){\n            showModal(msg);\n        }\n    });\n})(scope);\n</script>","storeOutMessages":false,"fwdInMessages":false,"resendOnRefresh":true,"templateScope":"local","className":"","x":440,"y":1060,"wires":[["fn_acknowledge_modal"]]},{"id":"bceefcfe39af28d1","type":"inject","z":"8d5542876c246ef6","g":"e99119303f2e614d","name":"pass","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"false","payloadType":"bool","x":210,"y":1120,"wires":[["mod_persist_fail","105b2f270ed429e5"]]},{"id":"btn_persist_health","type":"ui_button","z":"8d5542876c246ef6","g":"e99119303f2e614d","name":"Check Persistence Health","group":"uigroup_maint","order":3,"width":"3","height":"1","passthru":false,"label":"Check Persistence Health","tooltip":"Checks if file context is working","color":"#333333","bgcolor":"#D9D9D9","className":"","icon":"verified_user","payload":"","payloadType":"str","topic":"","topicType":"str","x":170,"y":1160,"wires":[["fn_persist_health"]]},{"id":"fn_persist_health","type":"function","z":"8d5542876c246ef6","g":"e99119303f2e614d","name":"Save test value to FILE context","func":"// Write a test value to file context and check after 1 sec\nconst k = '_persist_test';\nconst v = 'OK-' + Math.floor(Math.random()*1e6);\nflow.set(k, v);\nflow.set(k, v, 'file');\nnode.status({fill:'yellow',shape:'dot',text:'Checking...'});\nsetTimeout(function(){\n    var got = flow.get(k,'file');\n    var pass = (got === v);\n    node.status(pass ? {fill:'green',shape:'dot',text:'File context OK'} : {fill:'red',shape:'ring',text:'FAIL'});\n    msg.payload = pass ? `Persistence OK: ${got}` : `FAIL: Got \"${got}\"`;\n    msg.pass = pass;\n    node.send(msg);\n},1000);\nreturn null;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":430,"y":1160,"wires":[["debug_persist_health","074b6241262c30ad"]]},{"id":"debug_persist_health","type":"debug","z":"8d5542876c246ef6","g":"e99119303f2e614d","name":"Persistence Health Result","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","x":710,"y":1160,"wires":[]},{"id":"074b6241262c30ad","type":"ui_toast","z":"8d5542876c246ef6","g":"e99119303f2e614d","position":"top right","displayTime":"5","highlight":"","sendall":true,"outputs":0,"ok":"OK","cancel":"","raw":false,"className":"","topic":"payload","name":"","x":710,"y":1200,"wires":[]},{"id":"btn_context_master","type":"ui_button","z":"8d5542876c246ef6","g":"e99119303f2e614d","name":"Clear Context Data","group":"uigroup_maint","order":4,"width":"3","height":"1","passthru":false,"label":"Clear Context Data","tooltip":"","color":"#333333","bgcolor":"#D9D9D9","className":"","icon":"delete_sweep","payload":"","payloadType":"str","topic":"context_menu","topicType":"str","x":150,"y":1260,"wires":[["fn_prepare_context_modal"]]},{"id":"fn_prepare_context_modal","type":"function","z":"8d5542876c246ef6","g":"e99119303f2e614d","name":"Prepare Context Modal","func":"msg.topic = 'Clear Context Data';\nmsg.payload = 'Choose what to clear:';\n// Buttons shown in the modal\nmsg.buttons = [\n    { id:'flow_ram', label:'Flow (RAM only)' },\n    { id:'global_ram', label:'Global (RAM only)' },\n    { id:'flow_all', label:'Flow (RAM + Persistent)' },\n    { id:'global_all', label:'Global (RAM + Persistent)' },\n    { id:'all_all', label:'ALL (RAM + Persistent)' }\n];\nreturn msg;","outputs":1,"noerr":0,"x":370,"y":1260,"wires":[["tmpl_context_modal"]]},{"id":"tmpl_context_modal","type":"ui_template","z":"8d5542876c246ef6","g":"e99119303f2e614d","group":"a985e96a8135e229","name":"clear Context Modal","order":4,"width":0,"height":0,"format":"<div style=\"display:none\"></div>\n<script>\n(function(scope){\n    // Reset dialog tracking on refresh\n    scope.lastDialogActive = false;\n\n    function openModal(msg){\n        if (!msg || !Array.isArray(msg.buttons) || scope.lastDialogActive) return;\n\n        // Ensure enough buttons for layout (5 expected)\n        if (msg.buttons.length < 5) return;\n\n        // Update labels for persistent (file) options\n        msg.buttons[2].label = \"FLOW (RAM + FILE)\";\n        msg.buttons[3].label = \"GLOBAL (RAM + FILE)\";\n        msg.buttons[4].label = \"ALL (RAM + FILE)\";\n\n        var icons = {\n            flow_ram: \"memory\",\n            global_ram: \"layers\",\n            flow_all: \"sticky_note_2\",\n            global_all: \"folder\",\n            all_all: \"delete_forever\"\n        };\n\n        function makeButton(b){\n            var icon = icons[b.id] || \"help\";\n            return `\n            <md-button class=\"md-raised md-primary\"\n                style=\"margin:7px auto; width:90%; border-radius:14px; height:40px; min-height:40px; position:relative; overflow:hidden; text-align:center;\"\n                ng-click=\"choose('${b.id}')\">\n                <span style=\"display:block; width:100%; height:100%; text-align:center; line-height:40px; position:relative; pointer-events:none;\">\n                    <md-icon class=\"material-icons\"\n                        style=\"position:absolute; left:14px; top:50%; transform:translateY(-50%); line-height:1;\">\n                        ${icon}\n                    </md-icon>\n                    <span style=\"display:inline-block; vertical-align:middle; line-height:normal; font-size:13px; font-weight:500;\">\n                        ${b.label}\n                    </span>\n                </span>\n            </md-button>`;\n        }\n\n        var ramHeader = '<div style=\"width:100%; text-align:center; font-size:14px; font-weight:600; color:#8cd7ff; margin-top:10px; margin-bottom:4px;\">RAM MEMORY</div>';\n        var persistentHeader = '<div style=\"width:100%; text-align:center; font-size:14px; font-weight:600; color:#ffda8c; margin-top:10px; margin-bottom:4px;\">FILE MEMORY</div>';\n        var ramButtons = makeButton(msg.buttons[0]) + makeButton(msg.buttons[1]);\n        var persistentButtons = makeButton(msg.buttons[2]) + makeButton(msg.buttons[3]) + makeButton(msg.buttons[4]);\n        var divider = '<div style=\"width:90%; margin:9px auto; border-top:2px solid #666;\"></div>';\n        var cancelButton =\n            `<md-button class=\"md-raised md-warn\"\n                style=\"margin:12px auto 6px auto; width:90%; height:40px; min-height:40px; border-radius:14px; position:relative; overflow:hidden; text-align:center;\"\n                ng-click=\"cancel()\">\n                <span style=\"display:block; width:100%; height:100%; text-align:center; line-height:40px; position:relative; pointer-events:none;\">\n                    <md-icon class=\"material-icons\" style=\"position:absolute; left:14px; top:50%; transform:translateY(-50%); line-height:1;\">cancel</md-icon>\n                    <span style=\"display:inline-block; vertical-align:middle; line-height:normal; font-size:13px; font-weight:500;\">CANCEL</span>\n                </span>\n            </md-button>`;\n\n        // Define modal dialog\n        var dialog = {\n            template: `\n                <md-dialog aria-label=\"Clear Context\"\n                    style=\"min-width:330px; max-width:370px; padding:0; border-radius:12px; overflow:hidden; max-height:90vh;\">\n                  <md-dialog-content\n                       style=\"padding:16px 16px 6px 16px; font-size:17px; font-weight:600; display:flex; justify-content:center; text-align:center; color:#fff;\">\n                       ${(msg.payload || \"Choose what to clear:\")}\n                  </md-dialog-content>\n                  <md-dialog-actions layout=\"column\" layout-align=\"center center\"\n                       style=\"padding-bottom:10px; max-height:none; overflow:hidden;\">\n                       ${ramHeader}\n                       ${ramButtons}\n                       ${divider}\n                       ${persistentHeader}\n                       ${persistentButtons}\n                       ${cancelButton}\n                  </md-dialog-actions>\n                </md-dialog>`,\n            controller: function($scope, $mdDialog){\n                $scope.choose = function(id){\n                    scope.lastDialogActive = false;\n                    $mdDialog.hide(id);\n                };\n                $scope.cancel = function(){\n                    scope.lastDialogActive = false;\n                    $mdDialog.cancel(\"cancel\");\n                };\n            },\n            clickOutsideToClose: true,\n            multiple: false\n        };\n\n        // Keyboard handling (Enter/Esc)\n        var handler = function(e){\n            if (e.key === \"Enter\"){\n                document.removeEventListener('keydown', handler);\n                scope.lastDialogActive = false;\n                $mdDialog.hide(msg.buttons[0].id);\n            }\n            else if(e.key === \"Escape\"){\n                document.removeEventListener('keydown', handler);\n                scope.lastDialogActive = false;\n                $mdDialog.cancel(\"cancel\");\n            }\n        };\n        document.addEventListener('keydown', handler);\n\n        // Open modal\n        scope.lastDialogActive = true;\n        var inj = angular.element(document.body).injector();\n        var $mdDialog = inj.get('$mdDialog');\n        $mdDialog.show(dialog).then(\n            function(result){ scope.lastDialogActive = false; scope.send({topic:result}); },\n            function(){ scope.lastDialogActive = false; scope.send({topic:\"cancel\"}); }\n        );\n    }\n\n    // Listen for incoming messages\n    scope.$watch('msg', openModal);\n\n})(scope);\n</script>","storeOutMessages":false,"fwdInMessages":false,"resendOnRefresh":false,"templateScope":"local","className":"","x":600,"y":1260,"wires":[["sw_context_choice"]]},{"id":"sw_context_choice","type":"switch","z":"8d5542876c246ef6","g":"e99119303f2e614d","name":"Which operation?","property":"topic","propertyType":"msg","rules":[{"t":"eq","v":"flow_ram","vt":"str"},{"t":"eq","v":"global_ram","vt":"str"},{"t":"eq","v":"flow_all","vt":"str"},{"t":"eq","v":"global_all","vt":"str"},{"t":"eq","v":"all_all","vt":"str"}],"checkall":"true","repair":false,"outputs":5,"x":190,"y":1360,"wires":[["fn_clear_flow_ram"],["fn_clear_global_ram"],["fn_clear_flow_all"],["fn_clear_global_all"],["fn_clear_all_all","e1784102c9339bf6"]]},{"id":"fn_clear_global_ram","type":"function","z":"8d5542876c246ef6","g":"e99119303f2e614d","name":"Global (RAM only)","func":"for (let key in global.keys()) {\n    global.set(key, undefined);\n}\nreturn null;","outputs":0,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":430,"y":1360,"wires":[]},{"id":"fn_clear_global_all","type":"exec","z":"8d5542876c246ef6","g":"e99119303f2e614d","command":"rm -f /data/home/nodered/.node-red/context/global_*.json","addpay":false,"append":"","useSpawn":"false","timer":"","winHide":false,"name":"Global (RAM + persistent)","x":650,"y":1380,"wires":[[],[],[]]},{"id":"fn_clear_flow_ram","type":"function","z":"8d5542876c246ef6","g":"e99119303f2e614d","name":"Flow (RAM only)","func":"for (let key in flow.keys()) {\n    flow.set(key, undefined);\n}\nreturn null;","outputs":0,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":420,"y":1320,"wires":[]},{"id":"fn_clear_flow_all","type":"exec","z":"8d5542876c246ef6","g":"e99119303f2e614d","command":"rm -f /data/home/nodered/.node-red/context/flow_*.json","addpay":false,"append":"","useSpawn":"false","timer":"","winHide":false,"name":"Flow (RAM + persistent)","x":650,"y":1320,"wires":[[],[],[]]},{"id":"fn_clear_all_all","type":"exec","z":"8d5542876c246ef6","g":"e99119303f2e614d","command":"rm -f /data/home/nodered/.node-red/context/*.json","addpay":false,"append":"","useSpawn":"false","timer":"","winHide":false,"name":"ALL (RAM + persistent)","x":650,"y":1440,"wires":[[],[],[]]},{"id":"e1784102c9339bf6","type":"debug","z":"8d5542876c246ef6","g":"e99119303f2e614d","name":"debug 14","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":400,"y":1400,"wires":[]},{"id":"c68f992a45f44e70","type":"exec","z":"8d5542876c246ef6","g":"e99119303f2e614d","command":"ls -lh /data/home/nodered/.node-red/context/","addpay":false,"append":"","useSpawn":false,"timer":"","oldrc":false,"name":"Quick test: ls context folder","x":220,"y":1520,"wires":[["d4490bd64fcf45ae"],["e029c599f28949fc"],["ac1d5c676b5c4273"]]},{"id":"d4490bd64fcf45ae","type":"debug","z":"8d5542876c246ef6","g":"e99119303f2e614d","name":"CTX ls stdout","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":620,"y":1500,"wires":[]},{"id":"e029c599f28949fc","type":"debug","z":"8d5542876c246ef6","g":"e99119303f2e614d","name":"CTX ls stderr","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":620,"y":1540,"wires":[]},{"id":"ac1d5c676b5c4273","type":"debug","z":"8d5542876c246ef6","g":"e99119303f2e614d","name":"CTX ls rc","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":600,"y":1580,"wires":[]},{"id":"88238466a4e49759","type":"inject","z":"8d5542876c246ef6","g":"e99119303f2e614d","name":"RUN: ls context folder","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"str","x":180,"y":1460,"wires":[["c68f992a45f44e70"]]},{"id":"a985e96a8135e229","type":"ui_group","name":"connectivity","tab":"uitab_tools","order":2,"disp":true,"width":"5","collapse":true,"className":""},{"id":"2ce15f4144b5de0a","type":"ui_group","name":"Power Flow","tab":"dashboard_tab","order":1,"disp":true,"width":"10","collapse":true,"className":""},{"id":"uigroup_maint","type":"ui_group","name":"Maintenance","tab":"uitab_tools","order":1,"disp":true,"width":"6","collapse":false,"className":""},{"id":"uitab_tools","type":"ui_tab","name":"Tools","icon":"settings_backup_restore","disabled":false,"hidden":false},{"id":"dashboard_tab","type":"ui_tab","name":"Main","icon":"developer_dashboard","order":1,"disabled":false,"hidden":false},{"id":"e877c81c01565429","type":"global-config","env":[],"modules":{"node-red-dashboard":"3.6.6"}}]

Any questions please ask.

To be honest, I think this is far riskier than enabling the file system context store.

Hence my request to Victron, please enable a file context store without screaming “Modified!”

This is not modified, anything that node-red can do is not in the root files, node red is limited not to modify the OS. the risks are only with SSH.