Files
bf-infra/docs/handoff-20260828-terpene-mcp-migration-cf-tokens.md
Christoph Emsenhuber b9176914e2 Handoff 2026-08-28 (Abends): terpene W1-W7 + globale MCP-Migration + CF-Token-Diagnose
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.
2026-08-28 23:51:46 +02:00

15 KiB
Raw Blame History

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-mcp ist 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:

  1. 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 wirkt verify identisch zu nie-angelegt-gewesen).
  2. 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 auf emsenhuber.net und teilzeitladen.xyz beschränken (nicht account-weit — Lehrgeld aus 2026-08-24).
  3. In ~/.secrets/credentials.env schreiben, nicht ins Repo.
  4. 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:

  1. Backup der aktuellen ~/.config/opencode/opencode.jsonc machen (cp ... .bak-pre-mcp-ip-migration-20260828).
  2. 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:
    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.
    
    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.
  3. gitea-mcp und n8n-mcp in opencode.jsonc umstellen:
    • url auf die LAN-IP.
    • headers: {"Authorization": "Bearer {env:...}"} — env-Werte aus ~/.secrets/exports.zsh (gitea-Token aus git User im gitea-Repo selbst; n8n-MCP-Bearer aus dem n8n-mcp-Container-Config).
  4. Funktionstest mit opencode mcp list, dann optional mit echtem Tool-Call via LLM. Bei 401: falscher Bearer-Scope → Container-Log lesen (gitea-mcp mit docker 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_BEARER aus ~/.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.xyz nur in der URL, nicht in Code-Identitäten (Backend-Bindings wie HYPERDRIVE_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-Zeile host 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_TOKEN und CF_DNS_API_TOKEN mö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.md Quick-Index, TP31-Zeile: „Offen: Schritt 5–6 (Tunnel bf-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), aber status: 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.txt mcp[cli]>=1.0,<2. Commit b8fd42b.
  • Probe-Werkzeuge: mcp__shelly__* und mcp__portainer__* sind jetzt in opencode verfügbar, aber heute nicht durch eine echte LLM-Antwort validiert (Tool-Gate get_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 (siehe docs/teilprojekte.md Abschnitt 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):

  1. „420.teilzeitladen.xyz ist unsere domain. alle übrigen sind ungültig und falsch."
  2. „stelle n8n-mcp bitte global auf die passende IP um. gilt analog für alle übrigen mcps, die noch fqdn verwenden."
  3. „hast du einen konkreten plan, wie das projekt webseitig publiziert wird? stichwort caddy cloudflare."
  4. „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"
  5. „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.zsh lädt nicht automatisch in jeder Shell — source musst du explizit machen. Das bash-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.1 CF-Token-Audit (höchste Blocker-Kraft — fast alles hängt daran).
  2. 1.2 Globale MCP-Migration (gitea + n8n zuerst, weil terpene aktiv ist).
  3. 1.4 STATUS.md-Korrektur im terpene-Projekt (1 Minute).
  4. 1.3 terpene-n8n-mcp in .mcp.json (5 Minuten, sobald 1.2 durch ist).
  5. 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.md Lessons-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.