Der Schmerz der OpenClaw Maintenance
OpenClaw ist ein Produkt des Vibe Coding. Man sagt, die Qualität des Code sei — hmm — optimierungsfähig bis fragwürdig. Ich kann das nicht wirklich beurteilen — ich bin kein (ausreichend guter) Entwickler.
Was mir aber recht schnell klar wurde, nachdem ich seit Anfang 2026 mit OpenClaw experimentiere, dass selbst ein einfaches Update ziemlich herausfordernd sein kann. Während ich eines im Februar noch selbst durchführen und anschließend diverse Unstimmigkeiten beseitigen konnte, ging OpenClaw beim Wechsel von v2026.4.12 auf v2026.7.1–2 komplett auf die Bretter.
Mit Hilfe von Claude konnte ich das zwar alles beheben, aber es war trotzdem extrem zeitaufwändig. Ich habe nämlich den “klassischen” Claude Chat benutzt, was dann zu ziemlichen Copy ‘n Paste-Orgien führte: Problem (Text oder Screenshot) und mit Bitte um Lösung prompten, Claudes Lösungsvorschlag sichten und ins Terminal pasten, Ergebnis und Logs wieder zurück zu Claude.
Nach 2h mehr oder minder konstantem Rumgewerkel fasste Claude dann das Ergebnis wie folgt zusammen:
- Gateway sauber auf 2026.7.1-2, über npm/stable statt dem alten Git-Checkout
- Alle Workspace-Daten erhalten
- Command Owner gesetzt
- pdf-tools und mcp-hass wieder aktiv
- iMessage komplett von BlueBubbles auf das native imsg-Plugin migriert – DM und Gruppe laufen
Das gesamte Vorgehen, das Claude wählte, um die Probleme nach der Migration von .4.12 auf .7.1–2 zu testen, zu analysieren, zu fixen und erneut zu testen war dabei extrem beeindruckend. Nach ich diesen Erfahrungsbericht dann auf Mastodon postete, meinte dann jemand, dass er Wartungsaufgaben durch ChatGPT Codex durchführen lässt.
Da eines meiner Teams sehr gute Erfahrungen mit Codex gemacht hat, dachte ich mir, dass nun der Zeitpunkt gekommen ist, an dem ich mir hierzu mal selbst Hands-on-Erfahrungen hole — allerdings nicht mit Codex, sondern mit Claude Code.
OpenClaw Maintenance mit Claude Code
Hintergrund
Mein OpenClaw läuft auf einem dedizierten Mac Studio mit einer eigenen UserID und normalen Nutzerrechten (kein admin, kein root, kein sudo), hat eine eigene Identität im Netz (Max), eine eigene Mailadresse und kann nur auf seine eigene Daten schreibend zugreifen. Max kann nur lesend auf sehr wenige meiner Daten zugreifen, die ich hierfür extra selektiert habe (bspw. eine Obsidian Knowledge Base). Kommunizieren kann Max via Telegram, iMessage und Email. Ansonsten kann er ins Web gehen via Brave Search, WebFetch und Brave Browser. Auf extra für diesen Zweck selektierte Entitäten der Hausautomation kann er via Home Assistant MCP zugreifen, den Status auslesen und bei einigen wenigen den Status ändern (bspw. einige Schalter schalten oder auch die Temperatur von Klimaanlage oder Whirlpool einstellen).
Die Aufgabe
Mein Ziel bei Max war und ist es, einen OpenClaw-Playground zu haben, bei dem nichts kaputt gehen kann, wenn ich damit experimentiere oder eine Prompt Injection das Ding Amok laufen lässt. Und wenn doch etwas kaputt geht — oder sogar alles — dann soll das schlichtweg nichts ausmachen, weil meine Daten getrennt sind und ich die Maschine plattmachen und wieder komplett neu aufbauen kann. Selbst eine potentielle Komplettexfiltration der Daten, die Max lesen kann, sollte kein Problem sein.
Ein weiteres Ziel ist Max’ Autarkie: Der gesamte Software-Stack, den der User “Max” benötigt, um OpenClaw mit Ollama und allen zugehörigen Skills und Tools laufen zu lassen, soll komplett in Max’ User-Kontext laufen und sich dort maintainen lassen.
Dieses Ziel habe ich als Aufgabe formuliert an Claude Code gegeben.
Und was soll ich sagen? Es hat grandios gut funktioniert: Claude Code zunächst unter einem Admin laufen gelassen, um die Abhängigkeiten zu Tools analysieren zu lassen, die Max nicht selbst maintainen kann (kein sudo) und soll (Impact auf andere User). Dann diese Abhängigkeiten aufgelöst: Claude Code unter Max laufen lassen und alles in seinem User-Kontext beschafft und installiert, was er benötigt.
Weil das so extrem gut funktioniert hat, habe ich anschließend noch mehrere Optimierung an OpenClaw vorgenommen: Performance optimiert, E‑Mail von Apple Mail MCP auf Himalaya umgestellt, Finetuning an iMessage- und Telegram-Gruppen, etc.
Abschließend habe ich Claude Code gebeten, seine gesamte Arbeit in einer strukturierten MD-Datei zu dokumentieren. Und da ich bei jedem Schritt dabei war, den er gemacht hat, kann ich sagen: Exzellente Arbeit! Sowohl die Analyse, die Arbeitsplanung, die Durchführung, das Testen und auch die abschließende Dokumentation: Einfach nur Wow.
Um einen Eindruck von Claudes Fähigkeiten zu bekommen — und ich bin sicher nur an der Oberfläche gekratzt zu haben — folgt hier die Dokumentation, die er erstellt hat.
OpenClaw / Mac Studio — Änderungsprotokoll
Dokumentation aller Änderungen, die Claude am OpenClaw-Setup auf dem Mac Studio (, User macstudio*****max****) vorgenommen hat. Stand: 29. Juli 2026.
1 Node.js / nvm — Autonomie von max****
Ziel: OpenClaw und alle Abhängigkeiten sollen vollständig unter dem Nicht-Admin-User max**** laufen, unabhängig vom Admin-Account b****** und dessen Homebrew-Installation (/opt/homebrew, für max**** nicht beschreibbar).
- nvm installiert (offizielles Install-Skript), Node 22.23.1 als Default gesetzt
~/.npmrcbereinigt (prefix=...-Zeile entfernt, die mit nvm kollidierte)openclawundmcporterunter nvm neu installiert (parallel zur bestehenden Homebrew-Node-Installation)- uv installiert (
~/.local/bin, kein Homebrew nötig) — für spätere Python-Abhängigkeiten ohne Admin-Rechte
2 OpenClaw-Gateway-Service
- Service-PATH (
~/.openclaw/service-env/ai.openclaw.gateway.env) manuell um~/.local/binergänzt, da der reguläregateway install-Reinstall die PATH nicht korrekt aus der interaktiven Shell übernimmt (betrifft z.B. Erkennung vonhimalaya, siehe Abschnitt 8) openclaw doctor --repairmehrfach ausgeführt (Plugin-Registry-Reparatur nach nvm-Umstieg, Telegram-groupAllowFrom-Korrektur)- Tool-Sichtbarkeit
tools.sessions.visibilityvon Defaulttreeaufagentgesetzt — notwendig, damit isolierte Cron-Sessions (z.B. der Memory-Sync-Job) auf andere Sessions desselben Agents zugreifen können (sessions_history)
3 Apple Mail MCP (apple-mail-mcp) — Repariert, dann komplett abgelöst
3.1 Ursprüngliche Reparatur (mittlerweile obsolet, siehe Abschnitt 7)
- Python-venv des Skills war leer/defekt (Python 3.9,
ModuleNotFoundError: mcp) → mituv+ Python 3.12 neu aufgebaut - Falsche venv zuerst repariert (
plugin/venvstatt der tatsächlich vonmcporter.jsonreferenzierten Top-Level-venv) → korrigiert - macOS-Automation-Berechtigungen (AppleEvents) für zwei Prozess-Identitäten erteilt (Terminal, Homebrew-Node 22.23.1) — jeweils per physischer Bestätigung am Bildschirm
- Syntax-Bug in
SKILL.md: Beispiele nutzten ungültigetool(arg: "value")-Syntax stattkey=value→ korrigiert (verursachtezsh:1: number expected) - Zeilenumbruch-Bug:
escape_applescript()incore.pywandelte echte Zeilenumbrüche in den literalen Text\num (AppleScript kennt kein\n-Escape) → neue Funktionescape_applescript_multiline()ergänzt, nutzt AppleScripts& return &-Verkettung - Zitat-Bug: Mail.app verpackt per
content-Property gesetzten Text automatisch in ein<blockquote>(Darstellung als Zitat mit blauem Balken auf iOS) — als Workaround auf Zwischenablage-Paste umgestellt (compose_emailgeneriert automatisch simples HTML via_build_html_from_text(), wenn keinbody_htmlübergeben wird)
3.2 Warum trotzdem abgelöst
Die Paste-basierte Lösung hängt an macOS Accessibility/UI-Automatisierung (System Events), die sich als grundsätzlich instabil erwies (Fenster-Erkennung fiel wiederholt und unvorhersehbar aus, u.a. mit einem realen Mail-Verlust bei einem unbeaufsichtigten Cron-Lauf zur Folge). Auf Wunsch des Nutzers komplett durch eine protokoll-basierte Lösung ersetzt (Abschnitt 7).
4 Cron-Jobs — Diverse Fixes
| Job | Fix |
|---|---|
| Täglicher Memory-Log Eintrag | (a) tools.sessions.visibility global auf agent gesetzt (Grundproblem: isolierte Cron-Session konnte andere Sessions nicht lesen → sessions_history lief ins Leere, Job meldete fälschlich MEMORY_SKIP). (b) Prompt umgeschrieben: feste Session-Keys statt unzuverlässiger sessions_list-Suche; alle 4 relevanten Kanäle (Telegram/iMessage × direkt/Gruppe) statt nur einem. © Explizite Regel ergänzt: MEMORY_SKIP-Antwort und tatsächliches Schreiben schließen sich gegenseitig aus (behebt Fälle, in denen der Job schrieb, aber trotzdem “Skip” meldete). |
| Morgens — News | “Kein \n”-Formatierungs-Notbremse entfernt (durch den Zeilenumbruch-Fix in Abschnitt 3 nicht mehr nötig), echte Absatzstruktur erlaubt. Später auf Himalaya umgestellt (Abschnitt 7). |
| Donnerstagsnachmittags — Wochenende | Suche auf nichtkommerzielle Veranstaltungskalender eingeschränkt. Timeout-Fehler behoben (payload.timeoutSeconds von Default 600s auf 1200s erhöht, da Recherche + Versand bei diesem Job regelmäßig >600s brauchten). Später auf Himalaya umgestellt. |
| Donnerstagsnachmittags — J** | Timeout ebenfalls auf 1200s erhöht (gleicher Grund). |
5 Tailscale — Fernzugriff für die iOS-App
- Tailscale (App Store, unter
max**** eigener Apple-ID/Tailscale-Identität, getrennt vonb****) installiert und eingerichtet - Gateway umgestellt:
gateway.bind: loopback(kein LAN-Zugriff mehr) +gateway.tailscale.mode: serve(nur innerhalb des eigenen Tailnets erreichbar, kein öffentliches Funnel) - Erreichbar unter
wss://macstudio*****.*.*.* - Für
b***** iPhone: separates, eigenes Tailscale-Konto empfohlen, nur der Mac-Studio-Host gezielt geteilt (statt volles Tailnet-Mitglied) - Device-Pairing der iOS-App mehrfach neu aufgesetzt (Node-Rolle freigegeben für Kamera/Fotos-Zugriff)
Offen/ungelöst: Bild- und Text-Versand aus der iOS-App blieb sporadisch in der lokalen Warteschlange hängen (“In Warteschlange”, nie beim Gateway angekommen). Ursache nicht abschließend geklärt — es zeigte sich zusätzlich eine instabile Accessibility-Erkennung von Mail-Fenstern durch macOS selbst (siehe Abschnitt 3), möglicherweise ein verwandtes systemseitiges Phänomen. Nutzer hat das Thema vorerst zurückgestellt (Telegram funktioniert zuverlässig für Bilder).
6 Gruppen-Verhalten (Telegram & iMessage)
- Problem: Max antwortete in der gemeinsamen Gruppe (Bodo & Y****) auf jede Nachricht, auch wenn nur Y**** direkt angesprochen wurde
requireMention: truefür die Telegram-Gruppe UND für alle iMessage-Gruppen (channels.imessage.groups.*.requireMention) gesetzt- Globales Trigger-Wort ergänzt:
messages.groupChat.mentionPatterns: ["\\bmax\\b"]— Max reagiert jetzt nur bei@max****, direkter Antwort auf seine eigene Nachricht, oder dem Wort “Max” im Text. Gilt für beide (Bodo und Y****) gleichermaßen.
7 Mail-Migration: Apple Mail MCP → Himalaya CLI
Vollständige Ablösung der AppleScript/Mail.app-Automatisierung durch direktes SMTP/IMAP.
- Himalaya CLI installiert (
curl ... | PREFIX=~/.local sh, kein Homebrew nötig), Version 2.0.0 - Skill in OpenClaw aktiviert (
skills.entries.himalaya.enabled: true), lag zuvor deaktiviert unterskills.entries.<name>.enabled(unabhängig von Binary-Verfügbarkeit) ~/.config/himalaya/config.tomlangelegt (iCloud SMTPsmtp.mail.me.com:587, IMAPimap.mail.me.com:993)- Sicheres Credential-Handling: App-spezifisches Passwort liegt in der macOS-Keychain (
security add-generic-password ... -s himalaya-icloud), Config referenziert es nur perpassword.command— kein Klartext-Passwort in Dateien - Bundled
SKILL.mdhatte veraltete Befehlssyntax (z.B.folderstattmailbox,message writestattmessage compose --send) — korrigiert auf die tatsächliche v2.0‑Syntax - End-to-end getestet: Senden (SMTP) und Empfangen/Lesen (IMAP) funktionieren zuverlässig, deutlich schneller und stabiler als die alte Mail.app-Automatisierung
- Cron-Jobs Morgens — News und Donnerstagsnachmittags — Wochenende auf
himalaya message compose ... --send --save "Sent Messages"umgestellt; explizite Anweisung, Erfolg nur bei tatsächlicher Erfolgsmeldung zu melden
7.1 Vollständige Bereinigung alter Referenzen
apple-mail-mcp-Skill deaktiviert (skills.entries.apple-mail-mcp.enabled: false)apple-mail-mcp-Eintrag aus beiden aktivenmcporter.json-Configs entferntAGENTS.md: Mail-Instruktion auf Himalaya umgeschrieben,mcporter/apple-mail-mcpfür Mail explizit verbotenMCP-SYNTAX.md: Enthielt noch die generell falsche mcporter-Syntax (Doppelpunkt-Stil, Ursache des ursprünglichenzsh-Bugs) — korrigiert aufkey=value; Mail-Beispiel durch Himalaya-Verweis ersetztMEMORY.md: Komplette alte apple-mail-mcp-Befehlsreferenz durch Himalaya-Referenz ersetzt- Sieben veraltete, seit Monaten ungenutzte Einzel-Skripte gelöscht (
send_event_email.py/.sh,email_reader.py,tmp_send_email.py,email_venues_2026_04_30.py,daily/newsletter_email.py,tmp/send_email.py)
8 Inbox-Schutz gegen Prompt Injection (Himalaya-Äquivalent zur Mail.app-Regel)
Ersetzt/ergänzt die bestehende Mail.app-Regel (client-seitig, nur aktiv wenn Mail.app läuft) durch einen Himalaya-nativen, deterministischen Mechanismus.
- Neues Skript:
~/.openclaw/workspace/scripts/inbox_allowlist_filter.py- Prüft ausschließlich den Absender (
From-Header überenvelope list --json), liest niemals den Mail-Body - Verschiebt alle Nachrichten von nicht erlaubten Absendern nach “Deleted Messages”
- Allowlist: …
- Prüft ausschließlich den Absender (
- Als Cron-Job vom Typ “command” eingerichtet (
Inbox Allowlist Filter, alle 10 Minuten) — bewusst kein LLM-Turn, rein deterministisch, dadurch von Natur aus immun gegen Prompt-Injection-Versuche im Mail-Inhalt - Benachrichtigungen deaktiviert (würde sonst alle 10 Minuten auch “nichts gefunden” melden); Verlauf über
openclaw cron runseinsehbar - Getestet mit simulierten Absendern außerhalb der Allowlist (inkl. eines Prompt-Injection-Textbeispiels) — korrekt erkannt und in den Papierkorb verschoben
9 Ollama / Performance
keep_alive: "24h"für das aktive Modell (qwen3.6:35b-a3b-q4_K_M) gesetzt — Standard war 5 Minuten, danach musste das Modell bei jeder Anfrage neu geladen werden (gemessen: 7,2s Kaltstart vs. 132ms warm)temperature: 0.7,top_p: 0.9gesetzt (Standard wartemperature=1,top_p=0.95) — Milderung des gelegentlichen Einstreuens chinesischer Schriftzeichen (bekanntes Qwen-Phänomen, keine 100%ige Garantie)- Geprüft, aber unverändert gelassen (Nutzer-Entscheidung):
OLLAMA_NUM_PARALLEL=1,OLLAMA_KV_CACHE_TYPE(Standard) — beides bräuchte einen Ollama-Neustart mit geänderten Server-Umgebungsvariablen web_search-Tool wieder aktiviert (tools.web.search.provider: "brave") — Brave-API-Key war bereits als Plugin-Credential vorhanden, aber nicht mit demweb_search-Tool verknüpft
10 Aktueller Stand (Zusammenfassung)
- Mail: läuft vollständig über Himalaya/SMTP/IMAP, keine Mail.app-Automatisierung mehr für Senden/Empfangen durch Max; Passwort sicher in der Keychain
- Cron-Jobs: alle mail-versendenden Jobs auf Himalaya umgestellt, Timeouts und Formatierung korrigiert, Memory-Sync-Job liest zuverlässig alle 4 Kanäle
- Fernzugriff: Tailscale eingerichtet und aktiv; iOS-App-Nachrichtenversand mit Bildern bleibt ein offener, nicht abschließend gelöster Punkt
- Gruppen: Max reagiert nur noch auf direkte Ansprache (Name/Mention/Reply), nicht mehr auf jede Nachricht
- Sicherheit: Absender-Allowlist-Filter als eigenständige, KI-freie Schutzschicht gegen Prompt Injection über Mail aktiv
- Performance: Modell bleibt 24h warm geladen, Sampling leicht konservativer eingestellt





