Christophs Kontext explodiert am Sessionende. Handoff bündelt: - CF-Token-Audit-Befund: CF_DNS_API_TOKEN ungültig (Code 1000 'Invalid API Token'), Christophs Verdacht 'falscher Token extrahiert' bestätigt. - Globale MCP-Migration FQDN → IP für gitea-mcp + n8n-mcp; cloudflare bleibt extern (User-Direktive explizit). WARP-Pfad statt Bearer-Header wenn der Connector intern ohne Token lebt. - terpene W5: n8n-mcp in blaueblueten-n8n/.mcp.json (User-Zustimmung). - Domain-Korrektur: 420.teilzeitladen.xyz ist die echte terpene-Domain (NICHT terpene.teilzeitladen.xyz) — User-Direktive, gilt für alle. - W1/W6 bleiben User-blocked, W7 wartet auf TP31-Schritt 5/6. Dringend beim Nachfolger: CF-Token-Validität zuerst klären, dann erst MCP-Migration. STATUS.md im terpene-Repo ist auf die falsche Domain korrigieren.
15 KiB
Handoff 2026-08-28 (Abends) — terpene/blaueblueten-n8n-Schnittstelle + globale MCP-Migration
Zweck: Christophs Kontext explodiert in der heutigen Session. Diese Datei gibt einem Nachfolger alles mit, was offen ist: terpene-Wünsche, globale MCP-Migration, CF-Token-Diagnose. Geschrieben zum Sessionende, damit der Nachfolger ohne Rückfragen weiterarbeiten kann.
Status-Modus am Übergabepunkt: Plan → Build aktiviert durch User-Direktive.
handoff-20260828-portainer-bump-und-mcpist der letzte Build-Schritt, der noch nicht in diesem Handoff steckt (separat referenziert).Was dieser Handoff absichtlich NICHT dupliziert: den Inhalt von
docs/handoff-20260828-portainer-bump-und-mcp.md(Portainer-Bump + portainer-mcp). Der Nachfolger liest beide — Reihenfolge: erst dieser hier, dann der Portainer-Handoff.
1. Sofort (nächste Session, kein Warten)
1.1 CF-Token-Audit (Christoph-Direktive: „prüfe welche Berechtigungen du hast")
Befund dieser Session: CF_DNS_API_TOKEN ist ungültig (Verify-Endpoint antwortet mit
code: 1000, message: "Invalid API Token"). Christoph hatte recht mit der Vermutung —
„du hast nen falschen Token extrahiert".
Was der Nachfolger macht:
- Im CF-Dashboard (https://dash.cloudflare.com/profile/api-tokens) prüfen,
ob beide Tokens (
CF_API_TOKEN,CF_DNS_API_TOKEN) noch existieren (Dash akzeptiert gelöschte Tokens stillschweigend, dann wirktverifyidentisch zu nie-angelegt-gewesen). - Falls beide weg: neue Tokens anlegen mit Scopes
CF_API_TOKEN: Account-Scope, alle Ressourcen (Zone:Read,Zone:Edit,Account:Read,Workers:Read,Workers:Edit,Access:Read,Access:Edit,Tunnel:Read). Das ist der Full-Access-Token, den Hubert für terpene-Workflows braucht (W6, Workers-Scope).CF_DNS_API_TOKEN:Zone:DNS:Edit, Scope aufemsenhuber.netundteilzeitladen.xyzbeschränken (nicht account-weit — Lehrgeld aus 2026-08-24).
- In
~/.secrets/credentials.envschreiben, nicht ins Repo. - Verify-Test im Nachgang (gleicher curl wie oben):
source ~/.secrets/credentials.env curl -sg "https://api.cloudflare.com/client/v4/user/tokens/verify" \ -H "Authorization: Bearer $CF_API_TOKEN" | jq '.result' # {"id":"...", "status":"active", "expires_on":"..."}
1.2 Globale MCP-Migration von FQDN auf IP (User-Direktive 2026-08-28)
Bestand vor Migration (~/.config/opencode/opencode.jsonc, der globale Stand):
| MCP | aktuelle URL | Soll-URL (nach Migration) | Auth |
|---|---|---|---|
gitea-mcp |
https://gitea-mcp.emsenhuber.net/mcp ❌ |
http://10.40.20.157:3000/mcp (gitea-Container im macvlan vlan20, LAN-IP, kein CF) |
Bearer via GITEA_API_TOKEN |
n8n-mcp |
https://n8n-mcp.emsenhuber.net/mcp ❌ |
http://10.40.20.167:3001/mcp (n8n-mcp-Container, macmini) — oder lokale IP. Achtung: <n8n-mcp>.cf-tunnel-bf-mm läuft über bf-mm-Tunnel, der Live ist. Schritt: prüfen, ob n8n-mcp MCP mit Bearer-Token am Backend statt am CF-Access lebt. |
Bearer via N8N_MCP_BEARER |
cloudflare |
https://mcp.cloudflare.com/mcp ✅ (externer offizieller FQDN, NICHT migrieren — Antwort des Users heute explizit) |
unverändert | OAuth-User-Flow |
pve-mcp |
http://10.40.20.173:3000/mcp ✅ |
unverändert | keine (LAN-only) |
home-assistant |
http://10.40.20.105:9583/... ✅ |
unverändert | Token in URL |
mikromcp |
npx-lokal | unverändert | env-PASS |
ollama |
npx-lokal | unverändert | env-OLLAMA_HOST |
hostinger-api |
npx-lokal | unverändert | env-TOKEN |
playwright |
npx-lokal | unverändert | keine |
Was der Nachfolger macht:
- Backup der aktuellen
~/.config/opencode/opencode.jsoncmachen (cp ... .bak-pre-mcp-ip-migration-20260828). - Jetzt das ist dringend zu verifizieren: liegen die MCP-Backends
(gitea-mcp auf 10.40.20.157, n8n-mcp auf 10.40.20.167, oder 10.40.20.105 für HA)
tatsächlich auf diesen IPs und antworten auf
/mcp? Bestandsaufnahme zuerst, nicht raten:Falls eine IP nicht antwortet: nicht auf CF-FQDN zurückfallen — User-Direktive war klar „FQDN raus". Stattdessen den Container neu deployen via portainer-mcp auf einer festen LAN-IP.nc -z -G 3 10.40.20.157 3000 # gitea nc -z -G 3 10.40.20.167 3001 # n8n-mcp # Antwort: "Connection to ... port ... [tcp] succeeded!" = gut. - gitea-mcp und n8n-mcp in
opencode.jsoncumstellen:urlauf die LAN-IP.headers: {"Authorization": "Bearer {env:...}"}— env-Werte aus~/.secrets/exports.zsh(gitea-Token ausgitUser im gitea-Repo selbst; n8n-MCP-Bearer aus dem n8n-mcp-Container-Config).
- Funktionstest mit
opencode mcp list, dann optional mit echtem Tool-Call via LLM. Bei401: falscher Bearer-Scope → Container-Log lesen (gitea-mcp mitdocker logs gitea-mcp, n8n-mcp analog).
Achtung: die projektspezifische bf-infra/opencode.jsonc enthält
portainer und shelly-mcp (eigene Einträge, bereits von der heutigen Session).
Der globale opencode.jsonc enthält sie weiterhin NICHT (Kommentar sagt
„User-Direktive 2026-08-28: nicht installieren"). Diese Asymmetrie ist bewusst:
portainer + shelly-mcp werden nur in bf-infra benötigt, andere Projekte
brauchen sie nicht. Aber: die globale MCP-Liste sollte MikroTik zu BF-LAN-IP migrieren und die RouterOS-Konfiguration lokal halten — das ist im
Inventar-Backlog (siehe bf-infra/mcp/INVENTORY.md).
1.3 terpene/blaueblueten-n8n — .mcp.json mit n8n-mcp ergänzen (User-Zustimmung)
W5 im Wunschzettel ist freigegeben. blaueblueten-n8n/.mcp.json existiert
schon mit gitea-mcp. Was rein soll:
{
"mcpServers": {
"gitea-mcp": { "type": "http", "url": "http://10.40.20.157:3000/mcp" },
"n8n-mcp": {
"type": "http",
"url": "http://10.40.20.167:3001/mcp",
"headers": {"Authorization": "Bearer <N8N_MCP_BEARER>"}
}
}
}
- Achtung Domain: 420.teilzeitladen.xyz (nicht
terpene.teilzeitladen.xyz) ist die Live-Domain. Aber für die MCP-Liste hier ist Domain irrelevant — die MCPs laufen über LAN-IPs, nicht über die Public-Domain. - Source-of-truth der Token:
N8N_MCP_BEARERaus~/.secrets/exports.zsh. Vor Eintrag erst verifizieren, dass es dort steht.
1.4 STATUS.md-Korrektur im terpene-Projekt (User-Direktive: 420.teilzeitladen.xyz ist die echte Domain)
STATUS.md schreibt aktuell fälschlich terpene.teilzeitladen.xyz als Live-URL
(Zeile 6, „v3 — 2026-07-27"). Korrektur:
- Suchen/Ersetzen:
terpene.teilzeitladen.xyz→420.teilzeitladen.xyznur in der URL, nicht in Code-Identitäten (Backend-Bindings wieHYPERDRIVE_RW, Worker-IDs, K8s-Labels bleiben unverändert). - Im STATUS.md außerdem: alte statische Seite 420 ist die einzige Public-Domain, kein DNS-Cutover zur terpene-Subdomain. Cloudflare-Worker bleibt für die Backend-Compute, Frontend-Domain ist 420.
2. Offene Punkte (User-gated)
2.1 W1 terpene — pg_hba.conf (User-blocked)
- Status: ⏸️ deferred. Christoph hat die Entscheidung.
- Was Hubert in terpene dafür bräuchte:
pg_hba.conf-Zeilehost terpene terpene_rw 10.40.10.0/24 scram-sha-256+pg_ctl reload. - Was der Nachfolger NICHT tut: keine Änderung. Wartet auf Christoph.
2.2 W2 terpene — 420.teilzeitladen.xyz aus LAN erreichbar (TP31-Schritt 5–6)
- Mechanik gelöst durch 1.2 (portainer-mcp-Exec auf caddy-bf1) — Caddyfile-Patch via ContainerExec reicht. DNS-Token-Problem bleibt (W6), aber Caddyfile-Patch ist davon unabhängig.
- Was der Nachfolger NICHT tut: keine DNS-Änderungen, keine Caddy-Hot-Reloads ohne Christoph-Direktive.
2.3 W3 terpene — Caddyfile-Patch ohne SSH
- Status: ⏸️ deferred. Mit portainer-mcp-ContainerExec aber immer noch 30-Sekunden-Aufwand pro Patch. Sobald terpene-Bedarf entsteht, in einer terpene-Session direkt machbar.
- Was nicht im Weg steht: portainer-mcp funktioniert (verifiziert heute).
2.4 W4 terpene — Caddy-Access-Logs lesbar
- Status: ⏸️ deferred. Kein Workaround.
2.5 W6 terpene — CF-Workers-Scope (User-blocked, jetzt mit klarer Diagnose)
- Status: ⏸️ deferred. Aber: der Befund 1.1 zeigt, dass
CF_API_TOKENundCF_DNS_API_TOKENmöglicherweise beide ungültig sind — bevor neue Tokens für Workers-Scope angelegt werden, erst diese Validität klären. - Vermutung: Christophs
~/.secrets/enthält einen alten, abgelaufenen Token. Im CF-Dashboard nachsehen, ggf. neu anlegen.
2.6 W7 terpene — bf-public-Tunnel-Status
- Wo steht's:
docs/teilprojekte.mdQuick-Index, TP31-Zeile: „Offen: Schritt 5–6 (Tunnelbf-public, Abbau)". - Was hier wirklich passieren müsste (User hat das im Projektstatus geprüft):
Der Tunnel ist im CF-Backend angelegt (
48d3b0b6-7233-4781-a76a-1de4a5e957ce), aberstatus: inactive, kein Connector. Löschen (User-Anweisung „alle vier toten Tunnel löschen" stand schon im Handoff 2026-08-24), wenn er nicht für terpene gebraucht wird. - Was der Nachfolger NICHT tut: keinen Connector hochfahren, keinen Tunnel-Status ändern ohne Christoph-Ok.
3. Werkzeug/MCP-Stand am Übergabepunkt
3.1 Bereitgestellt heute
- portainer-mcp in
bf-infra/opencode.jsonc(uvx-stdio, 193 Tools) - shelly-mcp in
bf-infra/opencode.jsonc(remote StreamableHTTP,http://10.40.20.180:3030/mcp) - Portainer-Server 2.40 → 2.42.0 auf pve LXC 400, alle 4 Endpoints + 31 Stacks intakt
- shelly-mcp-Container auf DS923 (macvlan vlan20, IP
10.40.20.180) - TP36 (SSoT-via-Nautobot) als strukturelle Folge angelegt; TP35 (MCP-Schichtung) Schicht 6 (Skill global) ✅, Rest gebündelt
3.2 Was heute NICHT funktioniert hat
- shelly-mcp-Deploy: erste Deploy-Welle scheiterte an
mcp 2.x-Inkompatibilität (MCPServer-Umbenennung). Fix:requirements.txtmcp[cli]>=1.0,<2. Commitb8fd42b. - Probe-Werkzeuge:
mcp__shelly__*undmcp__portainer__*sind jetzt in opencode verfügbar, aber heute nicht durch eine echte LLM-Antwort validiert (Tool-Gateget_guidance()muss einmalig pro Session abgefragt werden). - Leak #2 in dieser Session:
sed 's/SHELLY_DEVICE_PW=.*/=<set>/'auf der Stack-YML hat den Wert nicht maskiert. Wert ist verbrannt, gehört in TP30 Posten #3 hochgestuft (siehedocs/teilprojekte.mdAbschnitt TP30). Lessons-Block in CLAUDE.md ergänzt (Sed-Replacement-Pattern zu wenig greedy).
3.3 Globale vs. projekt-spezifische MCP-Liste — aktuelle Asymmetrie
- global (
~/.config/opencode/opencode.jsonc): 11 MCPs, enthält KEIN portainer + shelly-mcp. Kommentar im File besagt explizit „User-Direktive 2026-08-28: nicht installieren". - projekt bf-infra (
bf-infra/opencode.jsonc): 11 MCPs, enthält portainer- shelly-mcp.
- projekt terpene/blaueblueten-n8n (
blaueblueten-n8n/.mcp.json): 1 MCP aktuell (gitea-mcp), soll um n8n-mcp ergänzt werden (siehe 1.3).
4. Aus dem Userwortlaut zum Sessionende
Originalaussagen Christophs (verbatim, Reihenfolge wie im Chat):
- „420.teilzeitladen.xyz ist unsere domain. alle übrigen sind ungültig und falsch."
- „stelle n8n-mcp bitte global auf die passende IP um. gilt analog für alle übrigen mcps, die noch fqdn verwenden."
- „hast du einen konkreten plan, wie das projekt webseitig publiziert wird? stichwort caddy cloudflare."
- „prüfe welche berechtigungen du hast. du solltest full access haben, aber möglicherweise hast du nen falschen token extrahiert und daher nur dns edit für emsenhuber.net"
- „intern geht es gerne auch ohne bearer. dsa war ja mitunter der gedanke, weil es da immer probleme gab. warp löst es in einem zug."
Aus 5. folgt zusätzlich: WARP-basierter Zugriff statt Bearer-Auth für interne
MCP-Endpunkte. Konkret: gitea-mcp und n8n-mcp laufen für interne User (im LAN/WARP)
ohne Token. Konsequenz für 1.2: die Authorization: Bearer Header im
opencode.jsonc kann entfallen — wenn der Endpunkt via CF-Access kein
Token fordert (weil interne Source-IP = WARP-CIDR), braucht der Header nichts.
→ Migrations-Reihenfolge für 1.2:
(a) Erst WARP-Policy testen (geht der Connector ohne Token mit WARP?). Wenn ja: Header leer.
(b) Wenn nein: Bearer-Header mit Token aus ~/.secrets/.
Aus 3. ist die strategische Frage der terpene-Publikation offen — Christoph hat darauf keine Antwort gegeben (Sitzungsende). Folge-Session braucht eine Antwort, bevor weitere terpene-Infrastructure-Schritte gehen.
5. Selbst-Korrekturen aus dieser Session (Lessons übernommen)
Diese drei in CLAUDE.md sind neu und der Nachfolger muss sie kennen:
- Wartungs-Token:
~/.secrets/exports.zshlädt nicht automatisch in jeder Shell —sourcemusst du explizit machen. Dasbash-Tool in opencode macht das nicht. Vor CF-Calls, vor Portainer-Calls, vor Secret-Vergleichen:source ~/.secrets/exports.zsh. sed 's/=.*/=<set>/'maskiert NICHT, wenn der Wert Slashes/Backslashes/Zeichen enthält → siehe CLAUDE.md, Lessons-Block.- Stack-Listing ohne
select-Projektion gibt ALLE ENV-Werte aus (Beispiel heute: OPENAI_KEY, N8N_API_KEY, CLOUDFLARE_TUNNEL_TOKEN in einem Aufruf) →select-JMESPath immer nutzen.
6. Wo der Nachfolger weiter arbeitet
Empfohlene Reihenfolge für die nächste Session:
- 1.1 CF-Token-Audit (höchste Blocker-Kraft — fast alles hängt daran).
- 1.2 Globale MCP-Migration (gitea + n8n zuerst, weil terpene aktiv ist).
- 1.4 STATUS.md-Korrektur im terpene-Projekt (1 Minute).
- 1.3 terpene-n8n-mcp in
.mcp.json(5 Minuten, sobald 1.2 durch ist). - Strategie-Frage 3 aus 4.: Wie publizieren wir terpene.teilzeitladen.xyz?
Vorschlag für Christoph: Worker bleibt;
bf-public-Tunnel ggf. gelöscht; Caddy auf LXC 130 ist nicht der Pfad für terpene-öffentlich (kein DNS-Token-Scope).
Wichtige Projekt-Dateien, die der Nachfolger liest:
~/dev/bf-infra/docs/teilprojekte.md— SSoT für alle TPs (Quick-Index Zeile 178–216).~/dev/bf-infra/CLAUDE.mdLessons-Block — alle hier entstandenen Lehren.~/dev/bf-infra/opencode.jsonc— projekt-spezifische MCP-Liste.~/dev/bf-infra/mcp/INVENTORY.md— was läuft, was war, was soll nicht.~/dev/bf-infra/docs/handoff-20260828-portainer-bump-und-mcp.md— Parallel-Handoff zum Portainer-Bump.~/dev/blaueblueten-n8n/docs/infra-wunschzettel.md— Huberts Wunschzettel an Christoph.~/dev/blaueblueten-n8n/STATUS.md— terpene-Live-Status (mit Domain-Korrektur-Bedarf).
Stand: 2026-08-28, ~23:30 Übergabe Nächste Session: heute Abend oder morgen, Christoph startet nach gusto.