system-droid: en vagthund der selv tjekker og reparerer mine systemer
Mine lokale tjenester døde altid lydløst — morgenrapporten udeblev, tokens udløb, backuppen frøs. Så jeg byggede en launchd-droid, der tjekker alt hver anden time, reparerer det, der trygt kan rettes, via en Claude-agent og kun skriver til mig, når der virkelig er brug for et menneske.
TL;DR
system-droid er et lille Go-program, som launchd starter hver anden time og hver gang Mac'en vågner fra dvale. Det gennemgår en liste af kontroller på tværs af alle mine lokale tjenester, tier stille, så længe alt virker, og skriver kun til Telegram, når noget faktisk er gået i stykker. Én gang om dagen sender det en kort opsummering om, at alt er grønt, så stilhed aldrig betyder "ukendt". Og når noget går i stykker, prøver det først selv at rette det: en separat Claude-agent (Opus) opsporer årsagen og udbedrer den — og tilkalder mig kun, hvis den fejler.
◆ Усі системи в нормі · 10/10 · 2026-08-17 09:00
— OpenClaw / Resonance
◆ styletts2-ua (tts) — локальний український TTS :8123
◆ openclaw gateway (:18789) — ядро агента Resonance (launchd + порт)
◆ openclaw hooks — hooks-ендпоінт увімкнено
— Інфраструктура
◆ cli-proxy Claude auth — робочий Claude-токен (валідність, авто-оновлення)
◆ system-droid — сам вартовий (надсилає ці звіти)
◆ otel-block — корпоративна телеметрія заблокована у firewall
◆ vault git backup — git-копія Obsidian-сховища на GitHubHvorfor det findes
I løbet af de seneste måneder havde jeg umærkeligt samlet en hel flåde af lokal automatisering: en OpenClaw-agent ("Resonance"), en ukrainsk StyleTTS2-stemmeserver, en LLM-proxy foran Claude, daglige notatgeneratorer til Obsidian, en git-kopi af mit vault, en firewallregel der blokerer virksomhedstelemetri. Hver af disse ting er nyttig præcis så længe, den kører. Og de gik altid i stykker på samme måde — lydløst.
Morgenrapporten udeblev simpelthen, og jeg opdagede det først om aftenen. Planlæggeren dræbte en proces, men dens seneste kørsel så stadig "vellykket" ud. En login-token udløb, og genereringen kunne leve i ugevis på en nødløsning. Ingen af disse fejl gør opmærksom på sig selv: en tjeneste klager ikke — den forsvinder bare. Jeg havde brug for nogen, der opdagede det for mig.
Sådan virker det
Hele systemet er én binær plus en config.json med to slags kontroller: HTTP (tjenesten svarer på /health) og kommando (et script slutter med kode 0). launchd starter droiden efter planen; den kører kontrollerne parallelt og afgør, om jeg overhovedet skal forstyrres. At sætte et nyt system under opsyn er én linje i konfigurationen — uden omkompilering.
Kerneprincippet er at økonomisere med opmærksomheden. Ingen dashboards der skal åbnes, ingen "alt vel"-strøm hver anden time. Der findes præcis tre slags beskeder: den daglige opsummering om at alt er grønt; "det gik i stykker — jeg har rettet det"; og "det gik i stykker — jeg har brug for dig". Alt andet er stilhed.
Selvhelbredelse
Når en kontrol fejler, løber droiden ikke straks til mig. Først starter den en Claude-agent med et begrænset værktøjssæt og skriveadgang kun til bestemte mapper. Før hvert forsøg tages et git-øjebliksbillede af alle arbejdsmapper, så alt hvad agenten ændrer, kan rulles tilbage med én kommando — hashene står direkte i beskeden:
🔧 system droid — полагоджено автоматично, 1 спроба (2026-08-02 07:41)
Було зламано:
• news digest freshness — застарілий дайджест: 2026-07-31 (2 дні тому)
✅ Зараз усі перевірки зелені.
↩️ Відкат змін агента (git reset --hard на знімок):
• ~/Developer/system-droid @ f037cfb
• ~/Developer/obsidian-cron @ e98746cOg det er ikke legetøj: agenten har faktisk regenereret et forældet sammendrag efter et natligt netudfald, selv genindlæst et fastlåst launchd-job og selv stillet diagnosen, at et tokenproblem ikke kunne løses uden mig. I beskeden ser jeg, hvad der var i stykker, hvad agenten gjorde, og hvordan det endte.
Hvad den allerede har fanget
På syv uger har droiden forvandlet flere lydløse nedbrud — hvert af dem kunne have levet i dagevis — til beskeder, jeg så med det samme:
- En dræbt rapportgenerator. macOS-kernen skød processen ned lige efter en genopbygning af binæren — morgenrapporten ville simpelthen aldrig være udkommet, og ingen ville have bemærket det.
- En udløbet refresh-token. Proxyen holdt op med at forny sin adgangstoken, og hvert LLM-kald kom tilbage med 401. Droiden sagde det ligeud: det her løses kun med et nyt login — én kommando, to minutter.
- En pladsholder i stedet for analyse. Genereringen sluttede "vellykket", men notatet indeholdt reservetekst i stedet for AI-analyse. At tjekke indholdet, ikke exitkoden, fangede det den første dag.
- En fastfrossen vault-backup. Git-plugin'et blev ved med at committe lokalt, mens push'et til GitHub stille var dødt — sikkerhedskopien ville være frosset fast — og først være blevet opdaget på det værst tænkelige tidspunkt.
Hvad den bevidst ikke retter
Den vigtigste beslutning i dette system er ikke, hvad der skal automatiseres, men hvad der ikke skal. Nogle kontroller er markeret kun-varsling: alt hvad der rører ved autentificering, andres systemer og min egen agents sessioner. En udløbet OAuth "rettes" ikke af et script — et menneske skal genudstede den i browseren. Agenten har udtrykkeligt forbud mod at røre tokens, genstarte mine kørende tjenester eller fifle med en kontrol, til den bliver grøn.
Der er også en fysisk grænse: lægen er selv en Claude-CLI, og når nettet eller login går ned, går den ned sammen med alt andet. Det, der knækker systemet, knækker også de hænder, der reparerer det. Ærlig opførsel dér er ikke heroiske forsøg, men et klart "jeg har brug for dig" — med præcis besked om, hvad der skal gøres.
De småting, det hele hvilede på
Det, der tog mest tid, var ikke kontrollerne, men vagtens egen pålidelighed. For det første: at genopbygge en Go-binær på stedet ændrer dens signatur, og launchd cacher signaturkrav — så systemet dræber simpelthen næste planlagte kørsel. Et build ender nu altid med en genindlæsning af jobbet:
# Перезбирання бінарника на місці змінює його cdhash, а launchd кешує
# вимоги до підпису (LWCR) на момент bootstrap. Після збирання кеш
# застаріває — і наступний плановий запуск ядро вбиває (exit 9) або
# launchd відмовляється його запускати (exit 78 / EX_CONFIG).
# Тому збирання ЗАВЖДИ закінчується перезавантаженням задачі:
go build -o system-droid .
codesign --verify --strict system-droid
launchctl bootout "gui/$(id -u)/com.oleksii.system-droid" 2>/dev/null || true
launchctl bootstrap "gui/$(id -u)" ~/Library/LaunchAgents/com.oleksii.system-droid.plist
./check-self.sh # свіжий запуск має бути зеленимFor det andet: for KeepAlive-tjenester er den seneste kørsels exitkode et vildledende mål. Efter hver genstart står der SIGTERM, og en fuldt levende tjeneste ser i stykker ud. Det rigtige spørgsmål er ikke "hvordan sluttede den", men "kører den lige nu":
# У KeepAlive-демона LastExitStatus після КОЖНОГО перезапуску
# ненульовий (15/SIGTERM) — тож перевірка "останній запуск успішний"
# хибно валить цілком живий сервіс. Правильне питання інше:
# чи працює він ПРЯМО ЗАРАЗ?
info=$(launchctl list "$1" 2>/dev/null) || { echo "not loaded"; exit 1; }
pid=$(printf '%s\n' "$info" | awk -F'= ' '/"PID"/{gsub(/[ ;]/,"",$2);print $2}')
[ -n "$pid" ] && [ "$pid" != "0" ] && { echo "running (pid $pid)"; exit 0; }
echo "loaded but not running"; exit 1For det tredje: tjek præcis det objekt, systemet afhænger af. Min første tokenkontrol regnede "udstedelsesdato plus et år" — og lyste grønt, mens den rigtige token havde svaret 401 i et døgn:
# Календарна перевірка сяяла зеленим — річному токену ще жити й жити:
claude oauth token — OK: valid, 365d left
# А справжній робочий токен тим часом помер, і логи проксі це знали:
token refresh failed: {"error":"invalid_grant","error_description":"Refresh token expired"}
POST /v1/chat/completions → 401 "OAuth access token has expired. Re-authenticate."Og for det fjerde: et netudfald på en enkelt cyklus klokken tre om natten er ingen grund til at vække et menneske. Alarmen "indgriben påkrævet" sendes nu kun, hvis problemet overlever to kørsler i træk. Engangsfejl opløser sig selv; ægte fejl når stadig frem — bare to timer senere:
// Тривога "потрібне втручання" — лише якщо перевірка провалилася
// два запуски поспіль. Нічний обрив мережі, демон посеред перезапуску,
// пробудження зі сну — все це минає само до наступного циклу,
// і будити людину через таке не можна. Успішне лікування,
// навпаки, повідомляється одразу — це добра новина, а не шум.
counts := loadHealCounts() // ~/.system-droid-heal-state.json
for _, r := range stillFailing {
counts[r.Name]++
}
var persistent []Result
for _, r := range stillFailing {
if counts[r.Name] >= 2 { // поріг ескалації
persistent = append(persistent, r)
}
}
// сповіщення — лише якщо len(persistent) > 0Øjne til en anden agent
Ud over Telegram kører droiden også som MCP-server. Det betyder, at min lokale agent selv kan spørge den om systemernes tilstand: i stedet for at læse rapporter siger jeg "tjek at alt virker" — agenten kalder get_health, kører hver kontrol live og svarer. Overvågningen holdt op med at være en side, man kigger på, og blev en tjeneste, man rådfører sig med.
# Дроїд працює і як MCP-сервер — локальний агент питає його сам:
Resonance › виклич get_health у system-droid і скажи, чи все зелене
get_health →
overall: 🟢 OK — 10/10 green (2026-08-17 09:00)
🟢 openclaw gateway (:18789) — ядро агента Resonance (launchd + порт)
🟢 cli-proxy Claude auth — робочий Claude-токен (валідність, авто-оновлення)
...Opsummering
Teknisk er der ikke noget kompliceret her: Go, launchd, et par shell-scripts og en Telegram-bot. Men netop dette enkle bundt ændrede følelsen af hele automatiseringsflåden: jeg holdt op med at bære rundt på en mental liste over ting, jeg "ikke må glemme at tjekke". Det er droidens job nu.
I syv uger har den for det meste tiet. Hver besked, den alligevel sendte, var enten en god nyhed om noget allerede rettet eller en præcis henvisning til, hvor mine to minutter var nødvendige. Det er, hvad bekvemmelighed betyder: et system, der vogter andre systemer — og respekterer din stilhed.
Fandt du en fejl?
Et forkert faktum, en skæv oversættelse, noget der virker usandt i denne artikel? Skriv til mig — på dit eget sprog.