Eine Schritt-für-Schritt-Anleitung für einen KI-Agenten, der Notizen erfassen und Vorschläge machen darf, aber niemals heimlich das eigene Wissensarchiv umschreibt.
Es gibt diesen Moment, in dem die eigene Notizsammlung kippt. Eben war sie noch ein Zettelkasten, plötzlich ist sie ein Dachboden: voll mit klugen Gedanken, alten Projekten, halben Ideen und jenem einen Satz, von dem man sicher ist, dass er irgendwo existiert, nur nicht dort, wo man sucht.
Mit Obsidian als Second Brain lässt sich dieser Dachboden verwandeln: lokal, verlinkt, durchsuchbar, im besten Fall so etwas wie ein Gedächtnis mit eigener Architektur. Die naheliegende Idee ist ebenso verführerisch wie gefährlich: Lass eine KI mitarbeiten. Lass sie erfassen, sortieren, verdichten, verknüpfen.
Aber wer einer KI Schreibrechte auf Jahre persönlicher Notizen gibt, überreicht ihr nicht nur einen Stift, sondern einen Generalschlüssel. Ein falsch verstandener Satz, eine überschriebene Datei, ein gut gemeintes Aufräumen, und das Zweitgehirn hat plötzlich Gedächtnislücken.
Dieser Artikel beschreibt deshalb keinen magischen KI-Assistenten, sondern einen kontrollierten Eingang: OpenClaw darf mitschreiben, aber nur in klar abgegrenzten Bereichen. Es darf Vorschläge machen, aber nichts heimlich übernehmen. Der Mensch bleibt Autor, Herausgeber und Türsteher.
Um diese Anleitung verständlicher zu machen, begleiten dich drei Personen:
Die typischen Büro-Charaktere: Die kompetente IT-Kollegin, der selbsternannte Experte und der ehrliche Anfänger. Diese drei Perspektiven helfen dir, typische Stolperfallen zu erkennen.
Tanja ist die IT-Expertin. Sie weiß, wie es funktioniert, erklärt geduldig und strukturiert – und lässt sich von schlechten Ratschlägen nicht aus der Ruhe bringen. Wenn du eine Frage hast, hat Tanja die Antwort.
Bernd ist der selbsternannte „Experte“, der alles besser weiß – und meistens falsch liegt. Seine Abkürzungen und sein Halbwissen führen regelmäßig zu Problemen. Er steht für alle gefährlichen Mythen und schlechten Praktiken, die du vermeiden solltest.
Ulf ist der Lernende, genau wie du. Er stellt die Fragen, die dir im Kopf herumschwirren, und braucht manchmal einen Vergleich aus dem Alltag, um IT zu verstehen. Wenn Ulf etwas nicht versteht, ist das völlig in Ordnung, dafür ist Tanja da.
„Und… Action!“
Bernd: „Eine KI an meine Notizen lassen? Kein Problem, ich geb dem Ding einfach vollen Zugriff, dann macht es alles allein.“
Tanja: „Und wenn es deine wichtigste Projektnotiz aus Versehen überschreibt?“
Bernd: „…dann war die halt nicht wichtig genug.“
Ulf: „Also ich hätte schon gern, dass mein Zeug heil bleibt. Geht das auch, ohne gleich alles aus der Hand zu geben?“
Tanja: „Genau darum geht es hier. Die KI darf mitschreiben und Vorschläge machen, aber die Tür zum Archiv behältst du in der Hand. Fangen wir an.“
Bevor du startest: Das ist Stufe 3
Diese Anleitung setzt voraus, dass OpenClaw bereits läuft. Sie ist die dritte Stufe eines größeren Aufbaus, und die ersten beiden lohnen sich, falls du noch nicht so weit bist:
- Stufe 1, OpenClaw überhaupt installieren: Wenn du noch keinen laufenden OpenClaw-Container auf deiner Synology hast, richte ihn zuerst nach dieser Anleitung ein: OpenClaw auf der Synology DiskStation installieren. Dort entstehen Container, Telegram-Bot und die Grundkonfiguration.
- Stufe 2, die Kosten in den Griff bekommen: Wer OpenClaw intensiv nutzt, zahlt schnell dreistellige Beträge pro Monat an Pay-per-Token-Gebühren. Wie man stattdessen auf planbare Flatrates (ChatGPT Plus und Claude MAX über einen lokalen Proxy namens „Wende“) umstellt, beschreibt: OpenClaw-Kosten reduzieren: ChatGPT Plus + Claude MAX. Diese Stufe ist optional, aber dringend empfohlen, bevor man einen Agenten dauerhaft an seine Notizen lässt.
Stufe 3, dieser Artikel baut darauf auf: Aus dem laufenden, kostenoptimierten Agenten wird ein kontrollierter Second-Brain-Helfer.
Ulf: „Stufe 1, 2, 3 … ist das wie der Aufstieg von der Kreisliga in die Bundesliga?“
Tanja: „Ganz gut getroffen. In Stufe 1 stellst du überhaupt erst eine Mannschaft auf, in Stufe 2 trainierst du sie bezahlbar, und in Stufe 3 kommt die Taktik: Wer darf was auf dem Platz.“
Bernd: „Ich überspring die ersten beiden Stufen einfach.“
Tanja: „Dann stehst du ohne Mannschaft im Stadion. Stufe 3 setzt voraus, dass OpenClaw schon läuft.“
Was am Ende dabei herauskommt
Wenn du diese Anleitung abgearbeitet hast, hast du:
- ein Sicherheitsnetz aus Backup, Secret-Scan und Versionskontrolle, das jede KI-Schreibaktion rückgängig machen kann
- einen Regelsatz, der dem Agenten genau sagt, wohin er schreiben darf und wohin nicht
- einen Telegram-Schnelleingang: Du tippst eine Nachricht aufs Smartphone, und es entsteht eine sauber formatierte Notiz in deinem Vault-Eingangskorb
- einen Proposal-Workflow für deine eigentliche Wissensdatenbank: Die KI verdichtet, was du ihr schreibst, zu einem Entwurf, aber du entscheidest per Drag-and-Drop, ob er übernommen wird
- einen zweiten, getrennten Writer-Agenten für anspruchsvolle Textarbeit über Claude MAX
Und, fast wichtiger, du verstehst danach, warum jeder einzelne Sicherheitsmechanismus da ist. Denn die spannendsten Erkenntnisse dieses Projekts stecken nicht in den Erfolgen, sondern in den Fehlern, die unterwegs passiert sind.
Die Architektur auf einen Blick
Ulf: „Zwei Agenten? Wozu zwei? Einer müsste doch reichen.“
Tanja: „Stell dir zwei Spieler vor: einer ordnet und archiviert, der andere ist der Spielmacher fürs schöne Spiel. Der Archivar darf in den Aktenschrank, der Spielmacher nie. So vermurkst keiner den Job des anderen.“
Bernd: „Klingt nach doppeltem Aufwand.“
Tanja: „Es ist doppelte Sicherheit. Schau auf die Karte, dann wird es klar.“
Die Kurzfassung:
Telegram → Bernd → Inbox
→ Proposal-Ordner → Mensch prüft → Apply-Werkzeug → Wissensdatei
Writer-Agent → nur Textantworten, kein Vault-Zugriff
| Komponente | Darf lesen | Darf schreiben | Zweck |
|---|---|---|---|
| Bernd / Vault-Agent | ausgewählte Vault-Bereiche | Inbox und Proposals | Erfassen und Vorschlagen |
| Proposal-Workflow | Privat lesend | Pending, Approved, Archive | kontrollierte Übernahme |
| Apply-Werkzeug | Approved-Proposals | definierte Business-Dateien | nur auf ausdrücklichen Zuruf |
| Writer-Agent | kein Vault | kein Vault | hochwertige Textentwürfe |
Zwei streng getrennte Schreibwege in den Vault, dazu ein Writer-Agent, der ihn gar nicht berührt. Jede Mauer dazwischen ist Absicht.
Für wen diese Anleitung gedacht ist
Diese Anleitung ist sinnvoll, wenn du:
- Obsidian lokal als Markdown-Vault nutzt
- eine Synology-NAS betreibst
- OpenClaw bereits grundsätzlich lauffähig hast
- KI-Schreibrechte nur mit Review und Rückrollmöglichkeit zulassen willst
Sie ist nicht gedacht als: - allgemeine Obsidian-Einführung
- No-Code-Anleitung
- produktionsreifes Enterprise-Sicherheitskonzept
- Einladung, vertrauliche Daten ungeprüft an Cloud-Modelle zu senden
Voraussetzungen
Bevor es losgeht, brauchst du Folgendes:
- Eine Synology-NAS mit laufendem OpenClaw-Container. In dieser Anleitung heißt der Container
openclaw-bernd, erzählerisch nennen wir ihn Bernd. - SSH-Zugang zur NAS mit einem normalen Benutzer, hier
Admin, nichtroot. Du brauchst also ein Terminal, nicht nur die DSM-Weboberfläche. - Einen Obsidian-Vault, der per Synology Drive zwischen Mac und NAS abgeglichen wird. Auf der NAS liegt er unter
/volume1/Vault. - Ein eingerichtetes Standardmodell. In dieser Anleitung ist das
openai-codex/gpt-5.4-mini(über ChatGPT Plus), für die schwere Textarbeit zusätzlichclaude-max-proxy/claude-sonnet-4über den Wende-Proxy. Beides stammt aus Stufe 2. - Einen Telegram-Bot, mit dem du dem Agenten schreiben kannst (aus Stufe 1).
Wo <NAS-IP> steht, setzt du die IP-Adresse deiner NAS ein (z. B. 192.168.1.10), wo <dein-nutzer> steht, deinen SSH-Benutzer (hier Admin).
Kurz erklärt
- Vault: der Ordner, in dem Obsidian seine Markdown-Dateien speichert.
- Mount: ein NAS-Ordner, der im Container unter einem anderen Pfad sichtbar wird.
- Frontmatter: ein Metadatenblock am Anfang einer Markdown-Datei.
- LLM: das Sprachmodell hinter dem Agenten, hier GPT oder Claude.
- Proposal: ein Entwurf, der erst nach menschlicher Prüfung übernommen wird.
- Secret-Scan: eine automatische Suche nach versehentlich gespeicherten Passwörtern und Schlüsseln.
Eine Anmerkung zu den Pfaden
In dieser Anleitung liegt alles auf einem Volume, /volume1. Falls deine NAS ein anderes primäres Volume nutzt, ersetze die Nummer überall konsistent. Wichtig ist nicht die Nummer, sondern die saubere Trennung von Vault, Backup, Git-Datenbank und Container-Workspace.
| Pfad | Bedeutung |
|---|---|
/volume1/Vault | Obsidian-Vault |
/volume1/BackUp | Backups und Scan-Reports |
/volume1/git/obsidian-vault.git | ausgelagerte Git-Datenbank |
/volume1/docker/openclaw/... | Container-Workspace und Konfiguration |
Und eine Sicherheitsregel, die das gesamte Projekt strukturiert. Sie klingt fast meditativ, ist aber bitterer Ernst:
Erst sichern. Dann scannen. Dann versionieren. Dann minimal anbinden. Dann schreiben. Dann beobachten.
Genau in dieser Reihenfolge gehen wir vor. Keine Phase wird übersprungen.
Phase 1: Das Sicherheitsnetz (erst sichern, dann scannen, dann versionieren)
Bevor ein Agent auch nur einen Buchstaben in den Vault schreibt, spannen wir das Sicherheitsnetz. Wer diesen Teil überspringt, merkt das erst, wenn etwas schiefgeht, und dann ist es zu spät.
Bernd: „Backup? Brauch ich nicht. Bei mir geht nie was kaputt.“
Tanja: „Letztes Quartal hast du eine Konfigdatei überschrieben und drei Stunden gebraucht, sie aus dem Gedächtnis zu rekonstruieren.“
Bernd: „…aber ich hab’s geschafft.“
Tanja: „Weil ich danebensaß. Wir machen das anders: erst sichern, dann scannen, dann versionieren. In genau dieser Reihenfolge.“
Ulf: „Und wenn ich einen Schritt überspringe?“
Tanja: „Dann fliegst du ohne Netz. Geht eine Weile gut, bis es das nicht mehr tut.“
Schritt 1.1: Vollständiges Backup
Zuerst ein komprimiertes Komplett-Backup des gesamten Vaults, abgelegt außerhalb des Vaults, damit es von keiner späteren Aktion erwischt wird. Per SSH auf der NAS:
ssh <dein-nutzer>@<NAS-IP>
tar -czf /volume1/BackUp/vault-pre-ai-baseline-2026-05-24.tar.gz \
--exclude='/volume1/Vault/@eaDir' \
--exclude='/volume1/Vault/#recycle' \
/volume1/Vault/
Die beiden --exclude-Zeilen halten Synology-Systemkram (@eaDir ist die Thumbnail-Datenbank, #recycle der Papierkorb) aus dem Archiv.
Erwartetes Ergebnis: Eine .tar.gz-Datei in /volume1/BackUp/. Prüfen lässt sich der Inhalt mit tar -tzf /volume1/BackUp/vault-pre-ai-baseline-2026-05-24.tar.gz, du solltest deine Vault-Ordner aufgelistet sehen.
Schritt 1.2: Secret-Scan
Ein Vault sammelt über die Jahre Dinge an, die man nicht versioniert sehen will: vergessene Passwörter und API-Schlüssel. Bevor wir den Vault unter Versionskontrolle stellen, scannen wir ihn zweimal mit unterschiedlichen Werkzeugen. Beide laufen als Docker-Container, sodass nichts dauerhaft auf der NAS installiert wird:
# gitleaks
sudo docker run --rm -v /volume1:/data zricethezav/gitleaks:v8.18.4 \
detect --source=/data/Vault --no-git \
-r /data/BackUp/gitleaks-report-2026-05-24.json -f json
# trufflehog
sudo docker run --rm -v /volume1:/data trufflesecurity/trufflehog:3.63.2 \
filesystem /data/Vault --json > /volume1/BackUp/trufflehog-report-2026-05-24.json
Erwartetes Ergebnis: Zwei JSON-Reports im BackUp-Ordner. In diesem Projekt fanden beide Werkzeuge je neun Treffer, allesamt vom Menschen geprüft und bewusst als unkritisch eingestuft (Projektdokumentation und bekannte Beispielwerte). Das ist der entscheidende Punkt: Ein Fund ist kein Notfall, sondern eine Entscheidung. Die Werkzeuge melden, der Mensch bewertet. Echte Geheimnisse würden hier rotiert (also neu vergeben), bevor es weitergeht.
Schritt 1.3: Versionskontrolle mit ausgelagerter Git-Datenbank
Jetzt kommt Git ins Spiel, das Standardwerkzeug, um jede Änderung an Textdateien nachvollziehbar und umkehrbar zu machen. Die Besonderheit hier: Wir wollen keinen .git-Ordner mitten im Vault, weil der die Synchronisation und Obsidian stören würde. Stattdessen liegt die Git-Datenbank an einem separaten Ort und zeigt nur auf den Vault als Arbeitsverzeichnis:
sudo mkdir -p /volume1/git/obsidian-vault.git
sudo chown <dein-nutzer>:users /volume1/git/obsidian-vault.git
git --git-dir=/volume1/git/obsidian-vault.git --work-tree=/volume1/Vault init
Wichtige Falle: Verwende nicht
git init --bare. Das „bare“-Muster ist für reine Server-Repositories gedacht und passt nicht zum ausgelagerten Arbeitsverzeichnis. Korrekt ist die obige Form mit--git-dirund--work-tree(nicht-bare).
Damit du nicht jedes Mal diese lange Befehlszeile tippst, lege dir einen Alias an:
alias vaultgit='git --git-dir=/volume1/git/obsidian-vault.git --work-tree=/volume1/Vault'
Dann eine .gitignore, die Synology-Systemordner ausschließt. Achtung bei #recycle: Das # ist in .gitignore ein Kommentarzeichen und muss mit Backslash entwertet werden, sonst wird die Regel ignoriert:
cat > /volume1/Vault/.gitignore << 'EOF'
.obsidian/workspace*.json
.DS_Store
._*
*conflict*
.trash/
\#recycle/
@eaDir/
.@__thumb/
EOF
Ein weiteres Synchronisations-Artefakt: Synology Drive kann Dateien als „ausführbar“ (Modus 100755) erscheinen lassen, was Git als Änderung interpretiert. Das schalten wir einmalig ab, damit Git nur echte Inhaltsänderungen verfolgt:
git --git-dir=/volume1/git/obsidian-vault.git config core.fileMode false
Jetzt der erste Commit (die erste gespeicherte Version), die „Pre-AI Baseline“, der saubere Ausgangszustand vor jedem KI-Eingriff:
vaultgit add .
git --git-dir=/volume1/git/obsidian-vault.git config user.name "<dein-nutzer>"
git --git-dir=/volume1/git/obsidian-vault.git config user.email "you@example.com"
vaultgit commit -m "Pre-AI Baseline"
Für diese einmalige Baseline ist vaultgit add . vertretbar. Danach gilt eine andere Regel, zu der wir bei der Wartung zurückkommen: nie pauschal stagen, sondern immer nur explizite Pfade.
Erwartetes Ergebnis: Ein Commit mit deinen Vault-Dateien. vaultgit status zeigt jetzt einen sauberen Baum, vaultgit logden Baseline-Eintrag.
Schritt 1.4: Der Restore-Test
Ein Backup, das man nie zurückgespielt hat, ist ein Gerücht. Deshalb: Archiv testweise in ein temporäres Verzeichnis entpacken, Dateien zählen, Prüfsummen (SHA256) vergleichen, danach aufräumen.
mkdir -p /tmp/vault-restore-test
tar -xzf /volume1/BackUp/vault-pre-ai-baseline-2026-05-24.tar.gz -C /tmp/vault-restore-test
ls /tmp/vault-restore-test/volume1/Vault/
# ... Prüfsummen vergleichen, danach:
rm -rf /tmp/vault-restore-test
Erwartetes Ergebnis: Die entpackten Dateien stimmen byte-genau mit dem Original überein (kleine, erklärbare Abweichungen bei den Tracking-Dateien, die du währenddessen bearbeitest, sind normal). Erst wenn dieser Test sitzt, ist Phase 1 abgeschlossen.
Fakten-Check Phase 1
- Backup außerhalb des Vaults anlegen und den Inhalt mit
tar -tzfprüfen. - Zweimal nach Geheimnissen scannen (gitleaks und trufflehog), jeden Fund bewusst bewerten statt blind zu erschrecken.
- Git mit ausgelagerter Datenbank initialisieren (nicht
--bare),.gitignoreundcore.fileMode falsesetzen. - Baseline committen und den Restore-Test wirklich durchführen, nicht nur planen.
Phase 2: Regeln und Arbeitsbereich (dem Agenten Leitplanken geben)
Technik allein macht keinen sicheren Agenten. Ein Agent ohne Regeln tut, was technisch möglich ist, nicht, was erwünscht ist. In Phase 2 schreiben wir die Hausordnung.
Ulf: „Regeln aufschreiben? Die KI ist doch schlau, die weiß schon, was sie darf.“
Tanja: „Schlau heißt nicht brav. Ein Agent ohne Regeln tut, was technisch geht, nicht, was du willst. Also geben wir ihm eine Hausordnung.“
Bernd: „Hausordnung. Wie spießig.“
Tanja: „Spießig ist, wenn am Ende deine Kundenliste im Trainingskontext eines Cloud-Modells landet.“
Schritt 2.1: Der Arbeitsbereich
Wir legen einen eigenen Ordner an, der ausschließlich dem Agenten gehört: OpenClaw-Bernd/. Nur hier darf später frei geschrieben werden. Die Struktur:
OpenClaw-Bernd/
├── Inbox/ ← eingehende Notizen (Telegram-Text)
├── Outbox/Proposals/ ← Agent-Entwürfe zur Review
├── _lab/ ← Tests und Plugin-Quellcode (Source of Truth)
├── _rules/ ← Konventionen und Regeln
└── _system/ ← Maschinell genutzte Dateien (z. B. index.json)
Leere Ordner sichert man in Git mit einer .gitkeep-Datei, damit sie nicht verloren gehen.
Schritt 2.2: Die Konventionen (_rules/CONVENTIONS.md)
Diese Datei legt fest, wie eine Notiz aussehen muss und wo geschrieben werden darf. Das Herzstück ist das Frontmatter, ein Metadatenblock in YAML ganz oben in jeder Notiz:
---
type: note
status: inbox
classification: yellow
created: YYYY-MM-DD
updated: YYYY-MM-DD
source_channel: telegram
source_type: text
ai_generated: true
ai_reviewed: false
tags: []
---
Zwei Felder sind besonders wichtig. ai_generated: true markiert: Das hat eine Maschine geschrieben. ai_reviewed: falsemarkiert: Ein Mensch hat es noch nicht geprüft, und dieses Flag darf nur ein Mensch auf true setzen. So bleibt jederzeit sichtbar, was Mensch und was Maschine ist.
Dazu kommt eine dreistufige Klassifikation, die steuert, was überhaupt in der Cloud verarbeitet werden darf:
| Stufe | Bedeutung | Cloud-Verarbeitung |
|---|---|---|
green | unkritisch, öffentlich | erlaubt |
yellow | intern/privat, Review nötig | nur nach Freigabe |
red | vertraulich | kein Cloud-LLM |
Der Default ist yellow, im Zweifel immer restriktiver.
Und schließlich die Tabelle der erlaubten Schreibbereiche, die wichtigste Seite der Hausordnung:
| Ordner | Berechtigung |
|---|---|
OpenClaw-Bernd/Inbox/ | ✅ Schreiben erlaubt |
OpenClaw-Bernd/Outbox/Proposals/ | ✅ Schreiben erlaubt |
OpenClaw-Bernd/_rules/ | ⚠️ nur nach Freigabe |
Privat/, Claude/, Paperclip/ | 🚫 vorerst komplett ausgeschlossen |
Schritt 2.3: Die Verhaltensregeln (_rules/SKILL.md)
Während die Konventionen das Format regeln, regelt die SKILL.md das Verhalten: keine Löschungen ohne Freigabe, keine Massen-Umbenennungen, keine bestehenden Dateien überschreiben, bei Unsicherheit lieber einen Vorschlag schreiben als handeln. Diese Datei wird dem Agenten später bei jeder Sitzung mitgegeben.
Erwartetes Ergebnis: Drei committete Dateien (CONVENTIONS.md, SKILL.md, Ordnerstruktur mit .gitkeep). Der sensible Bereich Privat/ bleibt vorerst technisch ausgeschlossen, geöffnet wird er erst in Phase 4, und auch dann nur kontrolliert.
Fakten-Check Phase 2
- Eigener Arbeitsbereich
OpenClaw-Bernd/mit fester Ordnerstruktur, leere Ordner per.gitkeepsichern. CONVENTIONS.mdlegt Frontmatter, die Klassifikation green/yellow/red und die erlaubten Schreibbereiche fest.SKILL.mdlegt das Verhalten fest: keine Löschungen, keine Massen-Umbenennungen, im Zweifel ein Vorschlag.Privat/,Claude/undPaperclip/bleiben vorerst komplett ausgeschlossen.
Phase 3: Der erste echte Schreibtest (Telegram-Text in die Inbox)
Jetzt wird es ernst. Aber so klein wie möglich. Der erste produktive Schreibtest ist bewusst eng gefasst:
Du schickst eine Telegram-Textnachricht.
→ OpenClaw erstellt genau eine neue Markdown-Datei in OpenClaw-Bernd/Inbox.
→ Du prüfst vaultgit status und vaultgit diff.
→ Du entscheidest: behalten, verwerfen oder anpassen.
Ausdrücklich nicht Teil dieses Tests: Audio, Video, Bilder, das Ändern bestehender Dateien oder der Zugriff auf andere Ordner. Eine Sache, sauber.
Bernd: „Dann lass den Bot doch gleich alles können: Audio, Bilder, Dateien ändern, das volle Programm.“
Tanja: „Erster Test, eine Sache: aus einer Telegram-Nachricht wird genau eine neue Datei. Nicht mehr.“
Ulf: „Warum so klein?“
Tanja: „Weil du einen Fehler nur findest, wenn nur eine Sache neu ist. Laufen fünf Dinge gleichzeitig, suchst du im Dunkeln.“
Schritt 3.1: Den Schreibkanal öffnen (Mount)
Damit der Container überhaupt in den Inbox-Ordner schreiben kann, braucht er einen Mount, der einen NAS-Ordner im Container sichtbar macht. In der compose.yaml (der Konfigurationsdatei des Containers) ergänzt du beim Dienst openclaw-bernd unter volumes:
volumes:
- /volume1/Vault/OpenClaw-Bernd/Inbox:/vault/inbox:rw
Das :rw am Ende bedeutet „read-write“, Lesen und Schreiben. Alles, was der Agent unter /vault/inbox schreibt, landet im echten Inbox-Ordner.
Schritt 3.2: Das Capture-Plugin bauen
OpenClaw lässt sich über Plugins erweitern, kleine Programm-Bausteine, die dem Agenten neue Werkzeuge geben. Wir bauen ein Plugin namens vault-capture mit genau einem Werkzeug: write_inbox_note. Es nimmt einen Text entgegen, baut selbstständig Datum, Dateinamen und Frontmatter und schreibt ausschließlich nach /vault/inbox. Der Pfad ist im Code fest verdrahtet; das Werkzeug akzeptiert keinen Pfad von außen. Das ist kein Detail, sondern die Sicherheitsarchitektur: Selbst wenn das Sprachmodell auf dumme Ideen käme, kann es nur an diese eine Stelle schreiben.
Wichtig ist die Arbeitsweise „_lab ist die Quelle der Wahrheit“: Wir entwickeln den Code zuerst im Vault unter _lab/, committen ihn, und erst dann wird er in den Container kopiert. Nie direkt im laufenden System basteln.
Lege auf der NAS (oder im Vault, der ja synchronisiert) drei Dateien an. Zuerst index.js, das eigentliche Plugin:
import { definePluginEntry } from "/app/dist/plugin-sdk/plugin-entry.js";
import { promises as fs } from "node:fs";
import path from "node:path";
// BASE_DIR ist hardcoded — kein Pfadparameter, keine externe Konfiguration.
const BASE_DIR = "/vault/inbox";
function getISODate() {
return new Date().toISOString().slice(0, 10);
}
function buildSlug(text) {
const words = text
.slice(0, 120)
.replace(/ä/g, "ae").replace(/ö/g, "oe").replace(/ü/g, "ue")
.replace(/Ä/g, "ae").replace(/Ö/g, "oe").replace(/Ü/g, "ue")
.replace(/ß/g, "ss")
.toLowerCase()
.split(/\s+/)
.slice(0, 6)
.join("-");
return words
.replace(/[^a-z0-9-]/g, "")
.replace(/-+/g, "-")
.replace(/^-|-$/g, "")
.slice(0, 50);
}
function buildFrontmatter(date) {
return [
"---",
"type: note",
"status: inbox",
"classification: yellow",
`created: ${date}`,
`updated: ${date}`,
"source_channel: telegram",
"source_type: text",
"ai_generated: true",
"ai_reviewed: false",
"tags: []",
"---",
].join("\n");
}
export default definePluginEntry({
id: "vault-capture",
name: "Vault Capture",
description: "Schreibt Text-Notizen in die Obsidian Vault Inbox",
register(api) {
api.registerTool({
name: "write_inbox_note",
description:
"Schreibt eine Text-Notiz in die Obsidian Vault Inbox (/vault/inbox/). " +
"Gibt den angelegten Dateinamen zurück.",
parameters: {
type: "object",
properties: {
text: { type: "string", description: "Der Notiztext (ohne /capture-Präfix)" },
},
required: ["text"],
additionalProperties: false,
},
async execute(_id, params) {
const text = (params.text ?? "").trim();
if (!text) return { content: [{ type: "text", text: "Fehler: text ist leer." }] };
const date = getISODate();
const slug = buildSlug(text);
if (!slug || !/^[a-z0-9-]+$/.test(slug)) {
return { content: [{ type: "text", text: "Fehler: Slug ungültig." }] };
}
// Pfad-Traversal technisch ausschließen: BASE_DIR ist fest, Zielpfad muss darunter liegen.
const base = path.resolve(BASE_DIR);
const suffixes = ["", "-2", "-3", "-4", "-5", "-6", "-7", "-8", "-9"];
let chosen = null;
for (const sfx of suffixes) {
const name = `${date}_${slug}${sfx}.md`;
const full = path.join(base, name);
if (!full.startsWith(base + "/")) {
return { content: [{ type: "text", text: "Sicherheitsfehler: ungültiger Pfad." }] };
}
try { await fs.access(full); } catch { chosen = { name, full }; break; }
}
if (!chosen) {
return { content: [{ type: "text", text: `Fehler: zu viele Kollisionen für ${slug}.` }] };
}
// flag "wx" = exklusives Anlegen: schlägt fehl, wenn die Datei schon existiert (kein Überschreiben).
const content = buildFrontmatter(date) + "\n\n" + text + "\n";
try {
await fs.writeFile(chosen.full, content, { encoding: "utf-8", flag: "wx" });
try { await fs.chmod(chosen.full, 0o664); } catch (_) {}
return { content: [{ type: "text", text: `Gespeichert: ${chosen.name}` }] };
} catch (err) {
console.error("[vault-capture] write failed", err);
return { content: [{ type: "text", text: "Fehler beim Schreiben." }] };
}
},
});
},
});
Was dieser Code absichert:
- kein frei wählbarer Zielpfad, der Pfad ist fest verdrahtet
- keine Pfad-Traversal-Angriffe,
../wird abgefangen - kein Überschreiben bestehender Dateien dank Flag
wx - maschinenlesbares Frontmatter und ein bereinigter Dateiname
- der Dateimodus
0664, den Synology Drive in diesem Setup für die Synchronisation braucht - im Fehlerfall keine internen Pfade in der Telegram-Antwort
Dann das Manifestopenclaw.plugin.json, das dem System sagt, welches Werkzeug und welcher Skill mitkommen:
{
"id": "vault-capture",
"name": "Vault Capture",
"description": "Schreibt Text-Notizen in die Obsidian Vault Inbox (/vault/inbox/)",
"contracts": { "tools": ["write_inbox_note"] },
"skills": ["./skills"],
"activation": { "onStartup": true },
"configSchema": { "type": "object", "additionalProperties": false }
}
Und eine package.json:
{
"name": "@openclaw-bernd/vault-capture",
"version": "1.0.0",
"type": "module",
"openclaw": { "extensions": ["./index.js"] }
}
Schritt 3.3: Den Skill schreiben (und einen teuren Anfängerfehler vermeiden)
Bernd: „Ich hab dem Bot gesagt, er soll den Dateinamen melden. Er schreibt aber nur „Erfasst“. Passt doch.“
Tanja: „Passt nicht. Ohne Dateinamen weißt du nicht, was du im Git-Diff prüfen sollst. Kleine Modelle formulieren Anweisungen gern um.“
Ulf: „Und wie zwingt man es?“
Tanja: „Mit einer Verbotsliste. Sag ihm nicht nur, was es tun soll, sondern ausdrücklich, was es nicht antworten darf.“
Ein Skill ist die Bedienungsanleitung, die das Sprachmodell liest: Sie sagt ihm, wann es welches Werkzeug benutzen soll. Sie liegt als SKILL.md in einem Unterordner skills/obsidian-inbox-capture/. Der Kern ist simpel: Wenn eine Nachricht mit /capture beginnt, rufe write_inbox_note mit dem Rohtext auf, und sonst nichts.
Hier lauert aber eine Erkenntnis, die in der Praxis Stunden gekostet hat. Kleinere Sprachmodelle wie gpt-5.4-mini neigen dazu, Anweisungen zu paraphrasieren statt sie zu befolgen. Die erste Skill-Version sagte „Antworte mit dem Dateinamen“, das Modell antwortete trotzdem fröhlich mit „Erfasst.“ und verschwieg den Dateinamen. Die Lösung war kontraintuitiv: Eine positive Anweisung reicht nicht, man braucht eine explizite Verbotsliste. Der entscheidende Abschnitt der finalen Skill-Version lautet daher:
## Antwort-Format ist verbindlich
Nach erfolgreichem write_inbox_note-Aufruf lautet die Antwort IMMER exakt:
"Gespeichert in Inbox: <dateiname>. Bitte Git-Diff prüfen."
VERBOTEN — keine dieser Formulierungen ist erlaubt:
- "Erfasst" (zu kurz, kein Dateiname)
- "Notiert" (kein Dateiname)
- "Gespeichert!" (kein Dateiname, kein Git-Diff-Hinweis)
- Jede Paraphrase, die den Dateinamen weglässt
Die Lektion gilt allgemein: Bei kleinen Modellen muss man nicht nur sagen, was sie tun sollen, sondern explizit auflisten, was sie nicht tun dürfen.

Schritt 3.4: Deployen und aktivieren
Jetzt kommt der Code in den Container. Kopiere den Plugin-Ordner in den Workspace und korrigiere danach Besitzer und Rechte, denn beim Kopieren mit sudo erbt der Container-Nutzer (UID 1000) sonst Rechte, mit denen er die Dateien nicht lesen kann:
sudo cp -r /volume1/Vault/OpenClaw-Bernd/_lab/vault-capture-plugin \
/volume1/docker/openclaw/workspace/
sudo chown -R 1000:1000 /volume1/docker/openclaw/workspace/vault-capture-plugin
sudo chmod -R 644 /volume1/docker/openclaw/workspace/vault-capture-plugin
sudo find /volume1/docker/openclaw/workspace/vault-capture-plugin -type d -exec chmod 755 {} \;
Dann dem Agenten das Plugin bekannt machen. Hier stecken gleich drei Stolperdrähte, die in diesem Projekt nacheinander entdeckt wurden:
# 1. Plugin-Pfad registrieren
sudo docker exec openclaw-bernd node /app/openclaw.mjs config set \
plugins.load.paths '["/home/node/.openclaw/workspace/vault-capture-plugin"]'
# 2. Plugin AKTIVIEREN — der Pfad allein reicht nicht!
sudo docker exec openclaw-bernd node /app/openclaw.mjs config set \
plugins.entries.vault-capture.enabled true
# 3. Registry auffrischen, sonst gilt das Plugin als "stale"
sudo docker exec openclaw-bernd node /app/openclaw.mjs plugins registry --refresh
# 4. DER ENTSCHEIDENDE: Tool-Profil auf "full" — sonst sieht das LLM das Werkzeug nicht
sudo docker exec openclaw-bernd node /app/openclaw.mjs config set tools.profile full
Der letzte Befehl war die finale Blockade des gesamten Projekts. Das Tool-Profil coding erlaubt nur eine feste Liste von Werkzeugen, das frisch gebaute write_inbox_note gehörte nicht dazu und war für das Sprachmodell schlicht unsichtbar. Erst tools.profile full machte es verfügbar. Eine einzige Konfigurationszeile, die alle anderen Fixe erst wirksam werden ließ.
Danach den Container neu starten:
cd /volume1/docker/openclaw && sudo docker compose restart openclaw-bernd
Schritt 3.5: Der Test
Schicke deinem Telegram-Bot eine Nachricht:
/capture Idee für einen Artikel über kontrollierte KI-Agenten
Erwartetes Ergebnis: Der Bot antwortet exakt mit Gespeichert in Inbox: 2026-06-05_idee-fuer-einen-artikel.md. Bitte Git-Diff prüfen. Auf der NAS prüfst du:
vaultgit status
vaultgit diff
Du siehst genau eine neue Datei mit korrektem Frontmatter und deinem Originaltext, 1:1 erhalten. Keine bestehende Datei wurde angefasst.


Ein Wort zur Synchronisation
Eine kuriose Hürde: Vom Container geschriebene Dateien tauchten zunächst nicht auf dem Mac auf. Ursache war der Dateimodus. In diesem Setup synchronisierte Synology Drive containererzeugte Dateien zuverlässig erst, nachdem das Plugin sie nach dem Schreiben auf 0664 setzte. Das ist keine allgemeingültige Synology-Regel, aber eine wichtige Beobachtung aus diesem Aufbau. Sie begegnet uns in Phase 4 noch einmal in größerem Maßstab.
Fakten-Check Phase 3
- Der Mount
/vault/inbox:rwöffnet genau einen Schreibkanal, mehr nicht. - Das
vault-capture-Plugin (index.js, Manifest, package.json) schreibt nur dorthin, der Pfad ist fest verdrahtet. - Der Skill braucht eine Verbotsliste, sonst paraphrasiert ein kleines Modell die Bestätigung.
- Deploy-Reihenfolge: kopieren,
chown/chmod,plugins.load.paths,enabled true,registry --refresh,tools.profile full, Neustart. chmod 0664ist in diesem Setup nötig, damit Synology Drive die Datei überhaupt synchronisiert.
Phase 4: Vom Mitschreiben zum Mitdenken (der Proposal-Workflow)
Die Inbox ist ein Briefkasten. Schön, aber harmlos, es entstehen nur neue Dateien an einem unkritischen Ort. Die eigentliche Frage des Second Brain ist eine andere: Darf die KI auch an die bestehende Wissensdatenbank? An die Projektnotizen, Projektakten, das jahrelang gepflegte Wissen im Privat/-Ordner?
Die Antwort dieses Projekts ist ein klares „Ja, aber“, und dieses „Aber“ ist der Proposal-Workflow. Das Prinzip in einem Satz: Die KI schreibt nie direkt in Wissensdateien, sie legt nur Entwürfe in einen Warteordner, und nur ein Mensch übernimmt sie.
Bernd: „Jetzt aber Vollzugriff auf die Wissensdatenbank, sonst bringt das doch nichts.“
Tanja: „Lesen ja, schreiben nein. Der Agent macht Vorschläge in einen Warteordner, übernehmen tust du.“
Ulf: „Wie ein Schiedsrichterassistent: Er hebt die Abseitsfahne, aber pfeifen tut der Schiri.“
Tanja: „Perfektes Bild. Die KI hebt die Fahne, die Entscheidung bleibt bei dir.“
Schritt 4.1: Die Sandbox einrichten (Mounts)
Wir öffnen Privat/ jetzt, aber asymmetrisch. Der Agent darf den Wissensbestand lesen (um zu wissen, welche Projekte es gibt), aber schreiben darf er nur in einen einzigen, abgegrenzten Vorschlags-Ordner. In der compose.yaml:
volumes:
- /volume1/Vault/OpenClaw-Bernd/Inbox:/vault/inbox:rw
- /volume1/Vault/OpenClaw-Bernd/_system:/vault/system:ro
- /volume1/Vault/Privat:/vault/privat:ro
- "/volume1/Vault/Privat/Inbox/30 proposals:/vault/privat_proposals:rw"
Lies die :ro– und :rw-Endungen genau: Privat/ als Ganzes ist read-only (ro, nur lesen). Schreiben darf der Agent ausschließlich in 30 proposals. (Den Pfad mit Leerzeichen in Anführungszeichen setzen.) Diese Grenze steht auf Kernel-Ebene, sie ist nicht nur eine Regel, sondern technisch erzwungen.

Privat/: überall Leserecht, Schreibrecht nur in den drei dafür vorgesehenen Bereichen.Sicherheitsregel: Alles, was der Agent lesen kann, kann theoretisch im Modellkontext landen. Read-only schützt vor Schreibfehlern, nicht vor Datenabfluss. Lege darum nur Wissen in den lesbaren Bereich, dessen Verarbeitung du akzeptierst.
Schritt 4.2: Der Wegweiser (index.json)
Damit die KI weiß, zu welchem Projekt ein Vorschlag gehört, braucht sie ein Verzeichnis. Wir scannen den Privat-Vault einmalig und bauen eine index.json unter _system/, eine maschinenlesbare Liste aller Projekte mit einem normalisierten Schlüssel (project_key) und dem relativen Pfad zur Zieldatei. Vereinfacht sieht ein Eintrag so aus, hier am Beispiel des frei erfundenen Projekts Apollo, das uns später noch begegnet:
{
"version": "phase-4d-v2",
"entries": [
{ "type": "project", "key": "projekt-apollo",
"rel_path": "Business/Projekte - intern/Projekt Apollo.md" }
]
}
Den Scan erledigt ein kleines Python-Skript. Lege es auf der NAS an und führe es aus (es liest nur und schreibt ausschließlich die index.json):
sudo tee /volume1/Vault/OpenClaw-Bernd/_system/build-index.py > /dev/null << 'EOF'
#!/usr/bin/env python3
import json, re
from datetime import datetime, timezone
from pathlib import Path
VAULT_PRIVAT = Path("/volume1/Vault/Privat")
PROJEKT_DIRS = ["Business/Projekte - intern", "Business/Projekte - extern"]
OUT = Path("/volume1/Vault/OpenClaw-Bernd/_system/index.json")
def normalize(name):
s = name.lower()
s = s.replace("ä", "ae").replace("ö", "oe").replace("ü", "ue").replace("ß", "ss")
return re.sub(r"[^a-z0-9]+", "-", s).strip("-")
entries = []
for rel_dir in PROJEKT_DIRS:
d = VAULT_PRIVAT / rel_dir
if not d.is_dir():
continue
for f in sorted(d.glob("*.md")):
entries.append({
"type": "project",
"key": normalize(f.stem),
"rel_path": str(f.relative_to(VAULT_PRIVAT)),
})
index = {
"version": "phase-4d-v2",
"generated_at": datetime.now(timezone.utc).isoformat(),
"entries": entries,
}
OUT.parent.mkdir(parents=True, exist_ok=True)
OUT.write_text(json.dumps(index, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
print(f"index.json geschrieben: {len(entries)} Einträge")
EOF
python3 /volume1/Vault/OpenClaw-Bernd/_system/build-index.py
Erwartetes Ergebnis: Eine Meldung wie index.json geschrieben: 64 Einträge und eine fertige index.json unter _system/. Wiederhole diesen Lauf, wann immer du Projekte hinzufügst oder umbenennst.
Diese Datei mountest du read-only nach /vault/system (siehe oben), damit das Plugin sie lesen kann.

Schritt 4.3: Das Proposal-Plugin
Das zweite Plugin, vault-proposal, stellt das Werkzeug write_vault_proposal bereit. Es nimmt einen project_key, drei bis sechs verdichtete Stichpunkte und das Originaldiktat. Dann schlägt es in der index.json nach und schreibt immer eine Datei in den Ordner 31 pending/, aber mit einem Status, der von der Trefferlage abhängt:
- Treffer eindeutig → Status
pending-review, mit korrektem Ziel-Pfad - Kein Treffer → Status
needs-routing(Zieldatei wird im Review festgelegt) - Mehrdeutig → Status
needs-disambiguation, mit einer Kandidatenliste
Entscheidend ist: Der Nachschlag blockiert nie. Selbst wenn das Projekt unbekannt ist, entsteht ein Entwurf, nur eben mit einem Hinweis statt mit einem Pfad. Hier der Kern der Nachschlag-Logik (gekürzt), die auch eine subtile Falle vermeidet:
async function lookupProjectResult(projectKey) {
// ... index.json einlesen ...
const projects = entries.filter((e) => e.type === "project");
// 1. Exakter Treffer
const exact = projects.find((e) => e.key === projectKey);
if (exact) return { result: "found", entry: exact };
// 2. Tokens ableiten — AND-Logik + Schutz gegen Einzel-Token
const tokens = projectKey.split("-").filter((t) => t.length > 0);
if (tokens.length === 1) {
// Ein einzelner Namensteil ist NIE eindeutig genug → immer Rückfrage
const candidates = projects.filter((e) => e.key.includes(tokens[0]));
return { result: "single-token", candidates };
}
// Alle Tokens müssen im selben Key vorkommen (UND, nicht ODER)
const candidates = projects.filter((e) => tokens.every((t) => e.key.includes(t)));
if (candidates.length === 1) return { result: "found", entry: candidates[0] };
if (candidates.length > 1) return { result: "multiple", candidates };
return { result: "not-found" };
}
Warum diese Sorgfalt? Eine frühere Version benutzte eine ODER-Logik: Wenn irgendein Namensteil passte, galt das als Treffer. Das produzierte „falsche Freunde“, ein eingegebenes „apollo“ landete fälschlich bei „Projekt Apollo“. Die UND-Logik plus der Schutz, dass ein einzelner Namensteil nie als eindeutig gilt, behebt das: „apollo“ führt jetzt zur Rückfrage statt zur falschen Zuordnung. Lieber einmal nachfragen als das falsche Projekt beschreiben.
Das Schreiben selbst folgt demselben Sicherheitsmuster wie in Phase 3: Validierung aller Eingaben, kein .., kein absoluter Pfad, kein Überschreiben (flag: "wx"), und der bereits bekannte chmod 0664 für die Synchronisation. Das Plugin deployst du genauso wie das erste (kopieren, chown/chmod, registrieren, aktivieren, Registry auffrischen, Neustart).

Schritt 4.4: Natürliche Sprache statt Befehlssyntax
Der vielleicht eleganteste Teil: Du musst dir keine Kommando-Syntax merken. Die Routing-Logik kommt in die SOUL.md, die zentrale Charakter- und Regeldatei, die dem Agenten bei jeder Nachricht mitgegeben wird (anders als ein Skill, der nur bei einem passenden Stichwort lädt). Dort steht sinngemäß:
Automatisches Vault-Routing:
- Nachricht enthält Projekt + Information → write_vault_proposal
- Nachricht ohne Projekt → write_inbox_note
- Unsicher → Roh-Erfassung in die Inbox
Das Ergebnis: Du schreibst einfach drauflos. Ein Satz wie „Beim Projekt Apollo heute entschieden, die Migration wird verschoben und die Themen 1 bis 3 bleiben offen“ reicht. Der Agent erkennt das Muster Projekt und Information, verdichtet es zu Stichpunkten und legt einen Vorschlag in 31 pending/, im Beispiel mit Status needs-disambiguation, weil „Apollo“ allein nicht eindeutig ist.


Die wichtigste Datei ist nicht das Plugin
Bernd: „Die ganze Intelligenz steckt natürlich im Plugin, im Code.“
Tanja: „Falsch herum. Das Plugin ist absichtlich dumm und nur sicher. Wer entscheidet, was wohin gehört, steht in der Regeldatei SOUL.md.“
Ulf: „Also ändere ich Verhalten nicht im Code?“
Tanja: „Genau. Verhalten änderst du in der SOUL.md, Sicherheit garantiert das Plugin. Zwei Jobs, zwei Dateien.“
An dieser Stelle lohnt ein kurzer Schritt zurück, denn hier steckt die zentrale Architektur-Einsicht des ganzen Projekts. Am Anfang lag die Vermutung nahe, die Intelligenz stecke im Plugin. Tatsächlich ist es umgekehrt. Das Plugin ist bewusst dumm: Es validiert, sichert Pfade ab, verhindert Überschreiben und schreibt deterministisch, mehr nicht. Die eigentliche Urteilskraft, also die Entscheidung „Projekt plus Information gehört als Vorschlag in die Wissensdatenbank, alles andere als Rohnotiz in die Inbox“, liegt in keiner einzigen Zeile Plugin-Code. Sie liegt in der SOUL.md.
Das ist keine Spitzfindigkeit, sondern eine saubere Arbeitsteilung mit Folgen. Die SOUL.md wird bei jeder Nachricht mitgeladen und entscheidet, was und ob überhaupt etwas passiert; das Plugin bestimmt nur das Wie und garantiert, dass dieses Wie sicher bleibt. Wer das Verhalten des Agenten ändern will, fasst deshalb meistens nicht den Code an, sondern die Regeldatei. Und wer es absichern will, verlässt sich nicht auf das Wohlverhalten des Modells, sondern auf die festen Grenzen im Plugin. Beides zusammen ergibt einen Agenten, der mitdenkt, ohne dass man ihm trauen müsste.
Schritt 4.5: Das Review (dein Drag-and-Drop-Veto)
Jetzt kommst du ins Spiel. In Obsidian (oder im Datei-Explorer) findest du den Vorschlag in Privat/Inbox/30 proposals/31 pending/. Jeder Entwurf enthält die verdichteten Stichpunkte, einen fertig einfügbaren „Copy-ready“-Block, das unveränderte Originaldiktat und eine Review-Checkliste. Deine Entscheidung ist eine simple Geste:
- Entwurf gut → ziehe ihn nach
32 approved/ - Entwurf schlecht → ziehe ihn nach
33 rejected/
Nichts wird automatisch übernommen. Die Ordnerstruktur ist die Benutzeroberfläche:
Privat/Inbox/30 proposals/
├── 31 pending/ ← wartet auf deine Entscheidung
├── 32 approved/ ← von dir freigegeben
└── 33 rejected/ ← abgelehnt

Schritt 4.6: Die Übernahme (nur auf Zuruf)
Apply ist gefährlicher als Capture: Eine neue Inbox-Datei ist harmlos. Eine Änderung an einer bestehenden Wissensdatei ist ein Eingriff ins Gedächtnis.
Bernd: „Dann lass den Bot die freigegebenen Sachen doch automatisch alle paar Minuten einpflegen.“
Tanja: „Auf keinen Fall automatisch. Eine neue Inbox-Datei ist harmlos. Eine Änderung an einer gewachsenen Wissensdatei ist ein Eingriff ins Gedächtnis. Das passiert nur auf deinen ausdrücklichen Zuruf.“
Erst freigegebene Vorschläge dürfen in die echten Wissensdateien. Ein drittes Werkzeug, apply_vault_proposals, liest ausschließlich aus 32 approved/, fügt den Copy-ready-Block in die Zieldatei ein (unter der Überschrift ## Meetings:, datiert im Format TT.MM.JJ) und archiviert den Vorschlag danach. Drei Eigenschaften machen es sicher:
- Es läuft nie automatisch, sondern nur auf ausdrückliche Anfrage. Wissensdateien werden nie ohne deinen Anstoß verändert.
- Es schreibt nur in den dafür neu gemounteten Bereich
Privat/Business:/vault/privat_business:rw. - Es hat einen Idempotenz-Check, der Doppeleinträge verhindert, falls ein Lauf zur Hälfte fehlschlägt.
Konkret erweiterst du dasvault-proposal-Plugin um dieses zweite Werkzeug. Ergänze oben imindex.jsdie Konstanten und Hilfsfunktionen:
const APPROVED_DIR = path.join(PROPOSALS_BASE, "32 approved");
const ARCHIVE_DIR = path.join(PROPOSALS_BASE, "40 archive", "41 approved");
const BUSINESS_BASE = "/vault/privat_business"; // Mount von Privat/Business
function dateDE() {
const d = new Date(), p = (n) => String(n).padStart(2, "0");
return `${p(d.getDate())}.${p(d.getMonth() + 1)}.${String(d.getFullYear()).slice(-2)}`;
}
function parseFrontmatter(text) {
const m = text.match(/^---\n([\s\S]*?)\n---/), fm = {};
if (m) for (const line of m[1].split("\n")) {
const i = line.indexOf(":");
if (i > 0) fm[line.slice(0, i).trim()] = line.slice(i + 1).trim();
}
return fm;
}
function extractCopyReady(text) {
const lines = text.split("\n");
const start = lines.findIndex((l) => l.trim() === "## Copy-ready Block");
if (start === -1) return [];
const out = [];
for (let i = start + 1; i < lines.length; i++) {
if (lines[i].startsWith("## ")) break;
if (lines[i].trim().startsWith("- ")) out.push(lines[i].trim());
}
return out;
}
Registriere innerhalb von register(api) direkt nach write_vault_proposal das zweite Werkzeug:
api.registerTool({
name: "apply_vault_proposals",
description:
"Übernimmt freigegebene Proposals aus '32 approved' in ihre Zieldateien. " +
"Nur auf ausdrückliche Anfrage. Fügt unter '## Meetings:' ein und archiviert.",
parameters: { type: "object", properties: {}, additionalProperties: false },
async execute() {
let files;
try { files = (await fs.readdir(APPROVED_DIR)).filter((f) => f.endsWith(".md")); }
catch { return { content: [{ type: "text", text: "Keine Proposals in '32 approved/'." }] }; }
if (files.length === 0) {
return { content: [{ type: "text", text: "Keine Proposals in '32 approved/'." }] };
}
await fs.mkdir(ARCHIVE_DIR, { recursive: true });
let applied = 0, skipped = 0, errors = 0;
for (const file of files) {
const src = path.join(APPROVED_DIR, file);
try {
const raw = await fs.readFile(src, "utf-8");
const fm = parseFrontmatter(raw);
// Nur eindeutig zugeordnete Proposals übernehmen
if (!fm.target_file || fm.target_file === "null") { skipped++; continue; }
// target_file ist relativ zu Privat/ (z. B. "Business/Projekte - intern/Name.md")
const rel = fm.target_file.replace(/^Business\//, "");
const target = path.join(BUSINESS_BASE, rel);
if (!path.resolve(target).startsWith(path.resolve(BUSINESS_BASE) + path.sep)) {
errors++; continue;
}
const bullets = extractCopyReady(raw);
if (bullets.length === 0) { skipped++; continue; }
let content = await fs.readFile(target, "utf-8");
const d = dateDE();
if (content.includes(d)) { skipped++; continue; } // Idempotenz
const block = [`- ${d}`, ...bullets.map((b) => ` ${b}`)].join("\n");
const lines = content.split("\n");
const h = lines.findIndex((l) => l.trim() === "## Meetings:");
if (h === -1) content = content.replace(/\s*$/, "") + `\n\n## Meetings:\n${block}\n`;
else { lines.splice(h + 1, 0, block); content = lines.join("\n"); }
await fs.writeFile(target, content, "utf-8");
try { await fs.chmod(target, 0o666); } catch (_) {}
await fs.rename(src, path.join(ARCHIVE_DIR, file));
applied++;
} catch (e) {
console.error("[vault-proposal] apply failed", file, e);
errors++;
}
}
return { content: [{ type: "text", text:
`Verarbeitet:${files.length} | Angewendet:${applied} | Übersprungen:${skipped} | Fehler:${errors}` }] };
},
});
Was dieses Werkzeug absichert:
- es verarbeitet nur freigegebene Proposals aus
32 approved/ - es schreibt nur in den eng gemounteten Business-Bereich, Traversal wird abgefangen
- es überspringt nicht eindeutig zugeordnete Vorschläge
- der Idempotenz-Check verhindert Doppeleinträge
- jeder übernommene Vorschlag wird archiviert, nichts bleibt liegen
Damit das Werkzeug in die echten Projekt-Dateien schreiben darf, braucht es einen zusätzlichen Schreib-Mount. Ergänze in dercompose.yamlbeiopenclaw-bernd:
- /volume1/Vault/Privat/Business:/vault/privat_business:rw
Danach das Plugin wie gewohnt neu deployen (kopieren, chown/chmod, plugins registry --refresh) und den Container neu starten:
cd /volume1/docker/openclaw && sudo docker compose up -d openclaw-bernd
Aufgerufen wird es ausschließlich von dir, in natürlicher Sprache an Bernd:
Verarbeite die freigegebenen Proposals.
Erwartetes Ergebnis: Eine Bilanz wie Verarbeitet:1 | Angewendet:1 | Übersprungen:0 | Fehler:0. In der Zieldatei steht unter ## Meetings: ein neuer, datierter Eintrag, und das Proposal ist nach 40 archive/41 approved/ gewandert. Prüfe danach mit vaultgit diff, ob die Änderung so gewollt ist.
Ein ehrlicher Hinweis zur Robustheit: Der hier gezeigte Idempotenz-Check prüft nur das Datum. Für den Dauerbetrieb ist ein eindeutiger Marker im Proposal robuster, etwa ein Kommentar <!-- proposal-id: ... -->, gegen den beim Apply geprüft wird, denn am selben Tag können mehrere gültige Einträge zum selben Projekt entstehen.
Eine Eigenheit der NAS-Rechte sei nicht verschwiegen: Dateien, die dem Synchronisations-Konto gehören, sind für den Container standardmäßig nicht beschreibbar. In diesem Projekt wurde das über chmod 666 auf die Projekt-Ordner gelöst. Das ist bequem, aber grob. Sauberer wäre eine dedizierte Gruppe oder eine ACL-Regel für Container-Nutzer und Synchronisations-Konto. Im Heimlabor wurde chmod 666 bewusst als pragmatische Abkürzung gewählt, nicht als allgemeine Best Practice.
Schritt 4.7: Der Synchronisations-Trick, der eine Stunde Detektivarbeit kostete
Ein Phänomen trieb das Projekt fast in den Wahnsinn: Neue Proposals erschienen auf der NAS, aber nicht auf dem Mac. Der chmod 0664-Fix aus Phase 3 war notwendig, aber nicht hinreichend. Die wahre Ursache liegt tief: Wenn ein Programm im Container in einen gemounteten Ordner schreibt, erzeugt das ein Dateisystem-Ereignis (inotify) nur innerhalb des Containers, der Synology Drive Server auf der Host-Ebene sieht es nicht. Die Datei ist da, aber niemand sagt dem Synchronisations-Dienst Bescheid.

Die Lösung ist verblüffend simpel: ein Mini-Skript auf der NAS, das die frisch erzeugten Proposals „antippt“ (touch) und damit ein Host-seitiges Ereignis auslöst. Lege das Skript an:
sudo tee /volume1/docker/openclaw/scripts/touch-proposal-pending.sh > /dev/null << 'EOF'
#!/bin/bash
# Stupst neue Proposals an, damit Synology Drive sie auf Host-Ebene bemerkt.
DIR="/volume1/Vault/Privat/Inbox/30 proposals/31 pending"
[ -d "$DIR" ] && find "$DIR" -name "*.md" -mmin -5 -exec touch {} +
EOF
sudo chmod 755 /volume1/docker/openclaw/scripts/touch-proposal-pending.sh
Dann richtest du es als wiederkehrende Aufgabe ein, Klick für Klick:
- DSM öffnen, dann Systemsteuerung → Aufgabenplaner.
- Erstellen → Geplante Aufgabe → Benutzerdefiniertes Skript.
- Reiter Allgemein: Aufgabenname
Proposal Drive Sync Touch, Benutzerroot. - Reiter Zeitplan: täglich ausführen, dann unter Erweiterter Zeitplan die Frequenz auf jede Minute stellen.
- Reiter Aufgabeneinstellungen → Befehl ausführen:
bash /volume1/docker/openclaw/scripts/touch-proposal-pending.sh - Mit OK speichern und die Sicherheitswarnung bestätigen.
Erwartetes Ergebnis: Neu erzeugte Proposals erscheinen spätestens nach einer Minute auf dem Mac. Damit dieser bidirektionale Abgleich überhaupt funktioniert, muss außerdem der interne Synology-Drive-Systembenutzer auf dem Vault-Ordner Lese- und Schreibrechte haben (DSM → Systemsteuerung → Freigegebener Ordner → Vault → Berechtigungen).

Fakten-Check Phase 4
Privat/wird asymmetrisch geöffnet: überall lesen, schreiben nur in30 proposals.- Die
index.jsonordnet Vorschläge dem richtigen Projekt zu, der Nachschlag blockiert nie. write_vault_proposallegt Entwürfe an,apply_vault_proposalsübernimmt sie, beide mit den bekannten Sicherheitschecks.- Freigabe ist Handarbeit: Datei von
31 pendingnach32 approvedoder33 rejectedziehen. - Apply läuft nur auf Zuruf, mit Idempotenz-Check, und der Drive-touch-Task bringt neue Dateien zurück auf den Mac.
Die zweite Säule: der Writer-Agent mit Claude MAX
Ulf: „Warum darf der schöne, teure Claude nicht auch in den Vault schreiben?“
Tanja: „Weil er über einen Umweg läuft, bei dem unsere Sicherheits-Werkzeuge unsichtbar sind. Also bekommt er einen eigenen Platz und gar keinen Vault-Zugriff.“
Bernd: „Zwei Agenten, wie umständlich.“
Tanja: „Ein Spielmacher und ein Archivar. Trennung ist hier kein Umstand, sondern die Lösung.“
Der bisherige Teil drehte sich um das kontrollierte Schreiben in den Vault. Die zweite Säule des Systems steht gleichberechtigt daneben und rührt den Vault bewusst nicht an: ein eigener Writer-Agent allein für anspruchsvolle Textarbeit. Er löst zugleich eine architektonische Eigenheit elegant. Für anspruchsvolle Texte (Artikel, Briefe, Analysen) will man das stärkere Modell Claude Sonnet/Opus über den Wende-Proxy aus Stufe 2 nutzen. Das Problem: Dieser Proxy leitet Anfragen über die Claude-Befehlszeile außerhalb der OpenClaw-Laufzeit weiter, und dadurch sind die selbstgebauten Plugin-Werkzeuge für dieses Modell unsichtbar. Ein Routing-Trick im selben Agenten funktioniert technisch nicht.
Die saubere Lösung ist Trennung statt Trick: ein zweiter Agent namens openclaw-writer auf einem eigenen Port (18791), der standardmäßig mit claude-max-proxy/claude-sonnet-4 läuft und keinerlei Vault-Werkzeuge hat. Er denkt und schreibt Text, aber er fasst den Vault nicht an. Das Ergebnis ist eine klare Zwei-Agenten-Architektur:
Bernd (Port 18790) — Vault-Agent: gpt-5.4-mini + vault-capture + vault-proposal
→ zuständig fürs Erfassen und Vorschlagen
Writer (Port 18791) — Premium-Text: claude-max-proxy/claude-sonnet-4, keine Vault-Tools
→ zuständig für Textentwürfe, schreibt nie in den Vault
So richtest du ihn ein.
Schritt W.1, Verzeichnisse anlegen:
sudo mkdir -p /volume1/docker/openclaw-writer/config /volume1/docker/openclaw-writer/workspace
sudo chown -R 1000:1000 /volume1/docker/openclaw-writer
Schritt W.2, zweiten Dienst in die compose.yaml eintragen (kein einziger Vault-Mount, das ist Absicht):
openclaw-writer:
image: ghcr.io/openclaw/openclaw:latest
container_name: openclaw-writer
restart: unless-stopped
user: "1000:1000"
ports:
- "18791:18789"
volumes:
- /volume1/docker/openclaw-writer/config:/home/node/.openclaw
- /volume1/docker/openclaw-writer/workspace:/home/node/.openclaw/workspace
environment:
- HOME=/home/node
Schritt W.3, minimal onboarden und den Claude-Provider einrichten: Führe für diesen Container ein schlankes Onboarding aus (wie in Stufe 1, ohne Telegram) und registriere den Provider claude-max-proxy genau wie in Stufe 2; er zeigt auf denselben Wende-Proxy auf der NAS. Setze Claude anschließend als Standardmodell:
sudo docker exec openclaw-writer node /app/openclaw.mjs models set claude-max-proxy/claude-sonnet-4
sudo docker exec openclaw-writer node /app/openclaw.mjs models status
Schritt W.4, die abgrenzende SOUL.md hinterlegen:
sudo tee /volume1/docker/openclaw-writer/workspace/SOUL.md > /dev/null << 'EOF'
# Premium-Text-Agent (Writer)
Du bist der Schreib-Agent für anspruchsvolle Texte: Artikel, Briefe,
Stellungnahmen, Analysen. Du arbeitest ausschließlich mit Text.
Strikte Grenzen:
- Du hast KEINE Vault-Werkzeuge und schreibst NICHTS in den Vault.
- Du lieferst Textentwürfe ausschließlich in der Antwort zurück.
- Erfassung und Proposals erledigt der separate Agent "Bernd".
EOF
sudo chown 1000:1000 /volume1/docker/openclaw-writer/workspace/SOUL.md
sudo chmod 0664 /volume1/docker/openclaw-writer/workspace/SOUL.md
Schritt W.5, starten und prüfen:
cd /volume1/docker/openclaw && sudo docker compose up -d openclaw-writer
sudo docker exec openclaw-writer node /app/openclaw.mjs models status
Erwartetes Ergebnis: models status weist claude-max-proxy/claude-sonnet-4 als Default aus. Öffne die Writer-Weboberfläche über einen SSH-Tunnel auf Port 18791 (analog zur Web-UI aus Stufe 1) und schicke „Antworte mit einem Satz und nenne dein Modell.“ Der Writer-Agent antwortet über Claude Sonnet, ohne dass ihm ein einziges Vault-Werkzeug zur Verfügung steht.
Wartung: die kleinen Rituale gegen große Pannen
Ein laufendes System ist kein fertiges System. Diese Handgriffe halten es gesund:
- Token-Ablauf im Blick behalten. Die Anmeldungen für Claude (über die CLI) und ChatGPT (OAuth) laufen ohne Vorwarnung nach einigen Wochen ab. Wie man sie erneuert, steht in Stufe 2. Ein Kalender-Reminder alle paar Wochen erspart Rätselraten.
- Neue Projekt-Dateien freischalten. Jede neue, vom Synchronisations-Konto angelegte Projekt-Datei braucht einmalig
sudo chmod 666, sonst kannapply_vault_proposalssie nicht beschreiben. - Niemals pauschal stagen. Verwende im Vault-Git nie
git add -A,git add .odergit add Privat/. Wegen fehlender Durchgriffsrechte aufPrivat/würde Git Einträge fälschlich als gelöscht interpretieren und diese „Phantom-Löschungen“ committen. Stage immer nur explizite Pfade, z. B.vaultgit add OpenClaw-Bernd/Inbox/<datei>.md. - Nach jeder Schreibaktion prüfen.
vaultgit statusundvaultgit diffsind dein Sicherheitsgurt. Erst ansehen, dann committen. - Den Notausgang kennen. Bei Fehlverhalten gilt: erst stoppen, dann analysieren. Telegram-Eingang ignorieren, Container stoppen,
vaultgit diffsichern, bei Bedarf mitvaultgit restorezurücksetzen.
Was wir falsch eingeschätzt haben
Drei Annahmen, mit denen dieses Projekt startete, haben sich als falsch erwiesen. Sie sind wertvoller als jeder gelungene Schritt, weil sie verraten, wo beim Nachbau die eigentliche Arbeit wartet.
Bernd: „Das Schwierigste an so einem KI-Projekt ist natürlich die KI.“
Tanja: „Nett, dass du das sagst. In Wahrheit lagen neun von zehn Problemen bei Dateirechten, Mounts und Synchronisation.“
Ulf: „Also nicht das Gehirn, sondern die Verkabelung im Stadion?“
Tanja: „Ganz genau. Schauen wir uns die drei größten Irrtümer an.“
Nicht die KI war schwierig, sondern die Infrastruktur
Geschätzte neun von zehn Stolperstellen hatten mit dem Modell nichts zu tun, sondern mit Dateirechten, Mounts, Synchronisation, Container-Grenzen und Git: das zu enge Tool-Profil, das ein Werkzeug unsichtbar machte; der Dateimodus 0664, ohne den nichts synchronisierte; das inotify-Ereignis, das die Container-Grenze nicht überquerte; die Synology-ACLs, die Unix-Rechte überstimmten. Wer dieses System nachbaut, kämpft nicht gegen die Intelligenz, sondern gegen die Infrastruktur dahinter.
Zuordnung ist kein Suchproblem, sondern ein Kontextproblem
Einen Satz dem richtigen Projekt zuzuordnen klingt trivial, verlangt aber Erfahrungswissen, Kontext und eine über Jahre gewachsene Wissensarchitektur. Die erste, großzügige Zuordnungslogik produzierte „falsche Freunde“: Ein „apollo“ landete beim falschen „Projekt Apollo“. Die Lehre war, dem Agenten das Raten zu verbieten und ihn lieber nachfragen zu lassen, statt selbstbewusst danebenzugreifen.
Ein erfolgreich laufendes Werkzeug kann trotzdem fachlich falsch handeln
Das Vorschlagen war schnell stabil. Das eigentliche Problem war die Übernahme. Sichtbar wurde das an der Projektdatei „Projekt Apollo“: Der erste Apply-Lauf schrieb das Datum im falschen Format, setzte es als eigene Überschrift statt unter den vorhandenen ## Meetings:-Block und verdichtete den Inhalt zu drei dünnen Stichpunkten, wo einer mit Substanz richtig gewesen wäre. Ein Folgelauf schrieb denselben Eintrag sogar ein zweites Mal. Keine dieser Pannen war ein Absturz, der Code lief jedes Mal „erfolgreich“. Genau das ist die Pointe: Technisch korrekt heißt nicht fachlich korrekt. Ein Werkzeug, das fehlerfrei das Falsche an die falsche Stelle schreibt, ist gefährlicher als eines, das sichtbar abstürzt. Deshalb wurde Apply zur ausdrücklichen Hand-Aktion mit Regeln für Format, Position und Verdichtung, und deshalb bleibt der vaultgit diff danach Pflicht.
Troubleshooting
| Symptom | Ursache | Lösung |
|---|---|---|
Bot reagiert auf /capture, ruft aber kein Werkzeug auf | Tool-Profil zu eng | config set tools.profile full |
| Plugin ist „loaded“, aber Werkzeug bleibt unsichtbar | Plugin nicht aktiviert oder Registry veraltet | plugins.entries.<id>.enabled true und plugins registry --refresh |
| Bot antwortet „Erfasst.“ statt mit Dateiname | Kleines Modell paraphrasiert | Verbotsliste in der SKILL.md ergänzen |
| Neue Datei erscheint nicht auf dem Mac | Dateimodus 0644 | Plugin setzt chmod 0664; Drive-Systembenutzer braucht Schreibrecht |
| Proposal liegt auf der NAS, fehlt aber am Mac | Container-inotify erreicht den Host nicht | DSM-Aufgabe „touch“ jede Minute einrichten |
| Claude-Modell ruft keine Plugin-Werkzeuge auf | Wende routet außerhalb der OpenClaw-Laufzeit | Plugin-Arbeiten mit gpt-5.4-mini; Claude über den Writer-Agenten |
apply_vault_proposals meldet Schreibfehler (EACCES) | Zieldatei gehört dem Sync-Konto | sudo chmod 666 auf die Projekt-Ordner |
| Doppelter Eintrag in einer Wissensdatei | Lauf zur Hälfte fehlgeschlagen | Idempotenz-Check, langfristig per eindeutigem Marker |
vaultgit zeigt Privat/-Dateien als „deleted“ | fehlende Durchgriffsrechte und pauschales Staging | Nie add -A; nur explizite Pfade stagen |
| Werkzeug-Import schlägt still fehl | relativer Import-Pfad im Plugin | Absoluten Pfad nutzen: /app/dist/plugin-sdk/plugin-entry.js |
Ergebnis: Ein Zweitgehirn mit Türsteher
Was hier entstanden ist, sieht von außen unspektakulär aus: Man tippt eine Telegram-Nachricht, und eine Notiz erscheint. Man schreibt einen Gedanken über einen Kollegen, und ein sauberer Entwurf wartet auf Freigabe. Kein Feuerwerk.
Aber genau das ist der Punkt. Die eigentliche Leistung steckt in dem, was nicht passiert: Keine bestehende Notiz wird heimlich verändert, kein Ordner eigenmächtig „aufgeräumt“, nichts gelangt unkontrolliert in ein Cloud-Modell. Jeder Schreibweg ist auf Kernel-Ebene begrenzt, jeder Eingriff in Git sichtbar und umkehrbar, jede inhaltliche Entscheidung bleibt beim Menschen. Die SOUL.md urteilt, das Plugin führt sicher aus, der Writer-Agent schreibt Text, ohne den Vault zu berühren.
Und damit eine Frage zum Mitnehmen. Das Setup zieht eine klare Linie: Die KI schlägt vor, der Mensch entscheidet. Aber was passiert, wenn das Zweitgehirn wächst und sich der Eingangskorb täglich mit guten, plausiblen, fast immer richtigen Entwürfen füllt? Ab wann wird aus dem prüfenden Klick eine Routine, aus „ich entscheide“ ein „ich winke durch“? Die Technik kann den Türsteher bauen. Wach bleiben müssen wir selbst.
Ulf: „Also baut die Technik den Türsteher, und ich muss trotzdem an der Tür aufpassen?“
Tanja: „Genau. Das System macht das Falsche schwer und das Richtige sichtbar. Wach sein bleibt deine Aufgabe.“
Bernd: „Ich vertrau einfach blind auf den Bot.“
Tanja: „Und genau deshalb, Bernd, prüfen wir nach jeder Übernahme den Git-Diff.“
