{ "name": "OpenClaw Drift Detector", "nodes": [ { "parameters": { "rule": { "interval": [ { "field": "cronExpression", "expression": "45 6 * * *" } ] }, "timezone": "Australia/Perth" }, "id": "cron", "name": "Daily 06:45 AWST", "type": "n8n-nodes-base.scheduleTrigger", "typeVersion": 1.2, "position": [ 220, 320 ] }, { "parameters": { "assignments": { "assignments": [ { "name": "repoOwner", "type": "string", "value": "Anthony" }, { "name": "repoName", "type": "string", "value": "openclaw-backups" }, { "name": "branch", "type": "string", "value": "main" } ] } }, "id": "set", "name": "Set Repo Context", "type": "n8n-nodes-base.set", "typeVersion": 3.4, "position": [ 440, 320 ] }, { "parameters": { "url": "=http://gitea.kangaroo-eel.ts.net:3000/api/v1/repos/{{$json.repoOwner}}/{{$json.repoName}}/commits?sha={{$json.branch}}&limit=2", "options": { "timeout": 10000 } }, "id": "commits", "name": "Get Last 2 Commits", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [ 680, 320 ], "retryOnFail": true, "maxTries": 3, "waitBetweenTries": 1500 }, { "parameters": { "jsCode": "const commits = Array.isArray($json) ? $json : ($json.data || []);\nif (commits.length < 2) {\n return [{json:{status:'insufficient_history', message:'Not enough commits to compare'}}];\n}\nreturn [{json:{status:'ok', head: commits[0].sha, base: commits[1].sha, headMsg: commits[0].commit?.message || '', baseMsg: commits[1].commit?.message || ''}}];" }, "id": "prep", "name": "Prepare Compare SHAs", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 920, 320 ] }, { "parameters": { "conditions": { "string": [ { "value1": "={{$json.status}}", "operation": "equals", "value2": "ok" } ] } }, "id": "if-ready", "name": "Ready to Compare?", "type": "n8n-nodes-base.if", "typeVersion": 2, "position": [ 1140, 320 ] }, { "parameters": { "url": "=http://gitea.kangaroo-eel.ts.net:3000/api/v1/repos/Anthony/openclaw-backups/compare/{{$json.base}}...{{$json.head}}", "options": { "timeout": 10000 } }, "id": "compare", "name": "Compare Commits", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [ 1360, 260 ], "retryOnFail": true, "maxTries": 3, "waitBetweenTries": 1500 }, { "parameters": { "jsCode": "const files = $json.files || [];\nconst watch = [\n 'state-backup/openclaw.json',\n 'state-backup/cron/jobs.json',\n 'state-backup/devices/paired.json',\n 'AGENTS.md','SOUL.md','USER.md','TOOLS.md','MEMORY.md','HEARTBEAT.md'\n];\nconst changed = files.map(f => f.filename).filter(f => watch.some(w => f===w));\nconst critical = changed.filter(f => f.startsWith('state-backup/'));\nreturn [{json:{drift: changed.length>0, changed, critical, changedCount: changed.length, criticalCount: critical.length}}];" }, "id": "analyze", "name": "Analyze Drift", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 1580, 260 ] }, { "parameters": { "conditions": { "boolean": [ { "value1": "={{$json.drift}}", "operation": "true" } ] } }, "id": "if-drift", "name": "Drift Detected?", "type": "n8n-nodes-base.if", "typeVersion": 2, "position": [ 1800, 260 ] }, { "parameters": { "url": "http://runtipi.kangaroo-eel.ts.net:8129/message?token=AGoV3cAUyUMDbyt", "sendBody": true, "contentType": "json", "bodyParameters": { "parameters": [ { "name": "title", "value": "\ud83e\udd80 OpenClaw Drift Detected" }, { "name": "message", "value": "={{`Changed files: ${$json.changed.join(', ')}${$json.criticalCount>0 ? '\nCritical state changed.' : ''}`}}" }, { "name": "priority", "value": "={{$json.criticalCount>0 ? 9 : 6}}" } ] } }, "id": "gotify", "name": "ntfy Drift Alert", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [ 2020, 220 ] }, { "parameters": { "chatId": "1793951355", "text": "={{`\ud83e\udd80 OpenClaw drift detected\nChanged: ${$json.changed.join(', ')}${$json.criticalCount>0 ? '\n\u26a0\ufe0f Critical state changed' : ''}`}}" }, "id": "tg", "name": "Telegram Drift Alert", "type": "n8n-nodes-base.telegram", "typeVersion": 1.2, "position": [ 2240, 220 ], "credentials": { "telegramApi": { "name": "Telegram account" } } } ], "connections": { "Daily 06:45 AWST": { "main": [ [ { "node": "Set Repo Context", "type": "main", "index": 0 } ] ] }, "Set Repo Context": { "main": [ [ { "node": "Get Last 2 Commits", "type": "main", "index": 0 } ] ] }, "Get Last 2 Commits": { "main": [ [ { "node": "Prepare Compare SHAs", "type": "main", "index": 0 } ] ] }, "Prepare Compare SHAs": { "main": [ [ { "node": "Ready to Compare?", "type": "main", "index": 0 } ] ] }, "Ready to Compare?": { "main": [ [ { "node": "Compare Commits", "type": "main", "index": 0 } ], [] ] }, "Compare Commits": { "main": [ [ { "node": "Analyze Drift", "type": "main", "index": 0 } ] ] }, "Analyze Drift": { "main": [ [ { "node": "Drift Detected?", "type": "main", "index": 0 } ] ] }, "Drift Detected?": { "main": [ [ { "node": "ntfy Drift Alert", "type": "main", "index": 0 } ], [] ] }, "ntfy Drift Alert": { "main": [ [ { "node": "Telegram Drift Alert", "type": "main", "index": 0 } ] ] } }, "settings": { "executionOrder": "v1" }, "active": false }