Der Branch enthaelt jetzt origin/main (0680cca, TP25 Fund E / TP27) und die Korrekturc8b2a15aus claude/nervous-williams-acf000. Inhaltliche Korrektur an meinen eigenen Dateien - Ich hatte die wifi-security-Drift (ft-over-ds=no auf sec-main) dem 7.24.1-Upgrade zugeschrieben. Falsch: gc-transaktion.py status fuehrt TP25-fund-e-ft-over-ds als OFFEN -- der Hebel wurde 08:09 bewusst gesetzt, der Core erst 14:36 gehoben. Sie gehoert einer laufenden TP25-Session, braucht keine Entscheidung und darf nicht gruen gemacht werden. Korrigiert im §5-Prompt und im §2/§3-Handoff. - Merkregel in CLAUDE.md: vor jeder Drift-Bewertung gc-transaktion.py status lesen. Dazu die Abgrenzung Transaktion (befristeter Versuch) vs. Soll/Template (Dauerzustand) -- die sechs AP-Drifts aus TP16.3 §3.4 sind Dauerzustand und gehoeren deshalb ins Template, nicht in eine Transaktion. - Notiert: gc-transaktion.py findet den Token aus einem Worktree heraus nicht (210-nautobot.yml liegt nur im Haupt-Checkout); die Transaktionsliste kommt trotzdem, nur die Compliance-Spalte fehlt. Konflikt in teilprojekte.md (ROS-7.24.1-Zeile) zusammengefuehrt statt eine Seite zu verwerfen: TP27-Erledigung samt Zeitstempeln aus main plus die npk-Folge fuer TP16.3 aus diesem Branch. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
49 KiB
BF-Netzwerk — Projekt-Kontext (auto-read)
Standort BF: VLAN-segmentiertes Heimnetz auf MikroTik/RouterOS. Ziel: CAKE/QoS gegen Bufferbloat, DNS/DHCP-Ablösung, WLAN via CAPsMAN auf dem Core (TP28, 2026-08-17 — der Standalone-Entscheid von 2026-07-22 ist revidiert), Firewall Least-Privilege (TP5 deny-by-default scharf seit 2026-08-18), Multi-Site VPN.
Pflichtlektüre pro Sitzung (Reihenfolge)
docs/teilprojekte.md(Quick-Index-Block) — kanonischer Stand, Scope, Abhängigkeiten, offene TPs. 1 Read reicht.docs/handoff-*.md(neueste) — wo die letzte Session aufgehört hat.- Bei Live-Writes oder Inventur:
docs/cf-tunnel-mapping.md,docs/teilprojekte-archiv.md. - 🔴 WLAN/Fund E — HEBEL 1 GESCHEITERT + TP27 durchgezogen:
docs/handoff-20260831-tp27-upgrade-und-hebel1-befund.md—ft-over-ds=nohat nicht gegriffen (124reauthenticatingnach dem Eingriff, zwei erlebte Ausfälle um 13:16 und 14:23). TransaktionTP25-fund-e-ft-over-dsbleibt offen, Compliance steht weiter absichtlich auf 134/135. Nächste Entscheidung: Hebel 2 (ft=no) oderwpa2-psk,wpa3-psk— vorher prüfen, ob 7.24.1 das Verhalten schon verändert hat. Vorstufen: Eingriff · Befund. - ap3-Bring-up 2026-08-30:
docs/handoff-20260830-ap3.md— sechs Funde zu Werks-MikroTiks, CAPsMAN und Golden-Config-Lücken. - Inventur-Befund 2026-08-27:
docs/handoff-20260827-inventur-und-architektur.md— Master-Struktur, Asymmetrie, Drift.
Was du sofort wissen musst (Inventur 2026-08-27)
bf-infraist aktiver Master mit Gitea-Remote:origin=https://gitea.emsenhuber.net/admin/bf-infra.git(live verifiziert 2026-08-30). ⚠️ Die frühere Zeile „lokal, kein Gitea-Remote → verloren bei Festplattencrash" ist überholt — sie stammt aus der Inventur vom 2026-08-27 und war damals richtig; das Remote existiert seit spätestens 2026-08-28 (origin/main=b917691). Die Backup-Lücke ist damit keine Struktur-, sondern eine Disziplinfrage:git push. Am 2026-08-30 lagen vier Commits nur lokal. 🔴 Das Remote ist nur so viel wert wie der letzte Push — der Backup-Guard (~/dev/.bin/backup-guard.sh, TP35 Schicht 5) bricht Commits ab, solange kein Backup markiert ist; das ist die Erinnerung ans Pushen, kein Hindernis. Nicht mit--no-verifyumgehen. 🔴 Merkregel: diese Zeile hat eine Session dazu gebracht, „kein Remote" zu behaupten, stattgit remote -vzu tippen — vor jeder Aussage über Remotes/Backups live prüfen.admin/bf-network-r6(Gitea) ist Archiv, letzte aktive Commits 2026-08-25 → read-only-Header.admin/bf-golden-config(Gitea) ist Bot-Master (Nautobot 2×/Tag), nicht manuell.admin/blaueblueten-n8n(Gitea) ist Master für terpene-Repo.inventory/ist tot (Pipeline seit 2025-06 stale). Live-Daten aus MikroMCP/CF/Portainer.mcp/INVENTORY.mdist BF-Netz-SSoT;~/.claude.json/opencode.jsoncsind globalere Werkzeugkästen.- terpene.teilzeitladen.xyz: Worker läuft auf Mac mini (
10.40.20.167:8890), kein Caddy-Block, kein CF-Tunnel — Backfill via DB-Migration blockiert (Handoff 2026-08-27). - shelly-mcp / portainer-mcp bewusst NICHT als MCP-Client (User-Direktive 2026-08-28): shelly-mcp-Container auf DS923 wird via Portainer-API (Skript-Aufrufe,
mcp/portainer/.envals Token-Quelle) deployt, nicht via MCP-Protokoll. Inventar-Eintrag spiegelt das.mcp-host-policy.mdist SSoT für die Host-/IP-Konvention. - Mac mini
10.40.20.167ist der n8n-Host (n8n:5678, n8n-redis:6379, n8n-mcp:3001) — NICHT der zentrale MCP-Host. Performance-Überlegung: MCP-Clients würden auf dem macmini (User-Workstation) laufen und die Box zusätzlich belasten; deshalb liegt der MCP-Host auf der DS923 (nas-bf, Portainer Endpoint 14). gitea-mcp läuft aktuell noch auf dem macmini (historisch, eigene Container-Definition dort) — gehört bei Gelegenheit auf die DS923 umgezogen (separater Punkt, nicht Teil dieser Session). Container-Definitionen für n8n leben auf dem macmini (Docker Desktop); FQDN→Backend-Mapping imcaddy/Caddyfile.
TP-Management (2026-08-26)
Wenn während einer Session ein neues Teilprojekt, eine Abhängigkeit oder ein Querschnittsthema sichtbar wird, das bisher in keinem TP abgebildet ist:
- Vorschlagen und mit dir abstimmen (nicht eigenmächtig anlegen).
- Auf dein Okay hin anlegen: Quick-Index-Zeile +
### TP<n>-Abschnitt mit Status🔜, Scope, Abhängigkeiten, Abgrenzung. Nächste freie Nummer vergeben (höchste vergebene + 1; Lücken aus verworfenen TPs bleiben frei). - Ist-Stand-Block mitziehen (Datum, betroffene Zeilen).
Abgeschlossene TPs stehen seit 2026-08-23 in docs/teilprojekte-archiv.md (Volltext, unverändert, mit Inhaltsverzeichnis). Ein TP wandert erst ins Archiv, wenn nichts mehr daran zu tun ist — und wieder zurück, sobald doch etwas dazukommt. Ein archiviertes TP ist ein geschlossenes Ticket, kein versiegeltes: bei neuer Arbeit an derselben Baustelle wandert der Volltext zurück nach teilprojekte.md (Zeiger bleibt im Archiv), kein Zwillings-TP mit neuer Nummer (Christoph-Direktive 2026-08-30, erster Fall TP28/ap3) — und der Ist-Stand-Block wird bei jeder Änderung mitgezogen, nicht nur der betroffene TP-Abschnitt.
VLAN-Modell (kanonisch)
| VLAN | Name | Subnetz | Zweck |
|---|---|---|---|
| 10 | MAIN | 10.40.10.0/24 | Trusted (Handy/Laptop) |
| 20 | INFRA | 10.40.20.0/24 | NAS/VMs/Docker/HA |
| 30 | IOT | 10.40.30.0/24 | Smart-Home |
| 40 | MEDIA | 10.40.40.0/24 | Sonos/AppleTV/TV/Cast |
| 50 | UNTRUSTED | 10.40.50.0/24 | Konsole/Low-Trust |
| 90 | GUEST | 10.40.90.0/24 | nur Internet |
| 99 | MGMT | 10.40.99.0/24 | Switch/AP/Hypervisor-Mgmt |
| 100 | WAN | (Arris-Transfer) | zum Arris-Kabelmodem (Liwest, Public-IP) |
GW je VLAN = .1 (crs326-core L3) · Mgmt VLAN99: core .99.1, branch .99.2, ap1 .99.11, ap2 .99.12
Hardware & Zugang
| Gerät | IP | Zugang |
|---|---|---|
| — | ❌ außer Betrieb (2026-08-08) — durch crs326-core (L3-Router + Core-Switch, Technik UG) ersetzt; physisch abgelöst. (Telnet-Runner switch/ = nur noch historisch.) |
|
| — | ❌ abgebaut 2026-07-23 (TP15 Branch-Tausch) — durch crs326-branch in Technik OG ersetzt, physisch entfernt |
|
| crs326-branch (CRS326, Technik OG) | 10.40.99.2 (VLAN99) · Bootstrap Rev4 2026-08-08 | RouterOS 7.24.1 · admin/~/.secrets (CRS326_BRANCH_*) · REST http://10.40.99.2/rest · MikroMCP crs326-branch ✅ live (2026-08-16 verifiziert) · TP15 ✅: ersetzt sw2 (L2-Switch), volle Portbelegung (ether1 Uplink-Trunk, 2/8 NAS, 3 Drucker, 4 Dock, 5/6 Sonos, 7 macmini, 24 ap2) · sw-id RGZJ-UK0T, MAC 04:F4:1C:B4:15:58 |
| crs326-core (CRS326, Technik UG) | 10.40.99.1 (VLAN99-SVI = L3-GW; same-subnet-Fallback 10.40.10.1) · Identity crs326-core |
RouterOS 7.24.1 · admin/~/.secrets (CRS326_CORE_*) · MikroMCP routerId crs326-core ✅ live (2026-08-16 verifiziert) · TP15/TP21.1 ✅ 2026-08-08: PRODUKTIVER L3-Router (ersetzt ap1) — Routing/DHCP (7 Server + 54 Leases)/DNS (DoH zum CF-Gateway mit verify-doh-cert=yes, TP23 Stufe 1 ✅ 2026-08-18; servers=1.1.1.1 nur noch Bootstrap) · NTP-Server für den Standort/NAT/WG-Hub (fm+dw live); WAN vlan100=Liwest 90.146.33.177 · sw-id 4W04-URHT, MAC 04:F4:1C:B4:17:76 |
| hAP ax² #1 (ap1) | 10.40.99.11 (VLAN99) · Identity ap1 |
RouterOS 7.24.1 · admin/~/.secrets (ROUTER_AP1_*) · REST http://10.40.99.11/rest · MikroMCP ap1 ✅ live · TP28 ✅ 2026-08-17: reiner CAP (Manager = crs326-core) · 🔴 FUNKSTILL seit 2026-08-30: keine Provisioning-Regel am Manager, weil ap3 daneben steht und denselben Kanal 5180 fährt. Gerät bleibt erreichbar. Beim Umzug ans Sideboard → 5260/Ce (Band B, DFS) und Uplink auf core ether3 — Uplink ether1 (PoE-Port, poe-out=off) an crs326-core ether15; SSIDs/Kanäle kommen vom Manager, ch36/ch1 · MAC D0:EA:11:4D:40:E0 · Config-Referenz routeros/.secrets/ap1-prov-base.rsc |
| hAP ax² #2 (ap2) | 10.40.99.12 (VLAN99) · Identity ap2 |
RouterOS 7.24.1 · admin/~/.secrets (ROUTER_AP2_*) · REST http://10.40.99.12/rest · MikroMCP ap2 · TP28 ✅ 2026-08-17: reiner CAP — Uplink ether1 (PoE) an crs326-branch ether24; ch44/ch11 (TP25: ch149 war unter ETSI leistungsbegrenzt) · MAC D0:EA:11:4D:40:3A · Config routeros/.secrets/ap2-prov-base.rsc |
| cAP ax (ap3) | 10.40.99.13 (VLAN99) · Identity ap3 |
✅ LIVE seit 2026-08-30 (TP28-Nachtrag) · cAPGi-5HaxD2HaxD, RouterOS 7.24.1 + RouterBOOT 7.24.1 (seit 2026-08-31 ist die ganze Flotte auf 7.24.1, TP27-Angleichung erledigt) · reiner CAP, Manager crs326-core · Uplink ether1 → core ether21 (Trunk) · Kanäle 5180/ax/Ce/I (5 GHz) + 2437/ax (ch6) nach TP25-Plan · 🔴 nur 2 Ethernet-Ports (ether1 PoE-in, ether2 PoE-out, kein Bridge-Port) → ap.extra_ports=[] · device-mode=basic (scheduler/fetch/romon erlaubt, anders als ap1/ap2) · Dateien routeros/tp28-ap3-phase{1,2}-*.rsc + -service-accounts.rsc + -werksreste.rsc |
| 🔑 Einheitliches Admin-PW (2026-08-08) | alle 4 MikroTik (core/branch/ap1/ap2) | user admin, gleiches PW (~/.secrets/credentials.env → ROUTER_*_PASS/CRS326_*_PASS; „Überlebensversicherung", User-Vorgabe). Werks-Defaults ap1/ap2 damit obsolet. |
| NAS (nas-bf, DS923) | 10.40.20.10 | SSH adm.christoph@10.40.20.10 (kein PW nötig) · Docker (Portainer endpointId=14) |
| platform-pg (LXC 105, pve) | 10.40.20.20 | SSH via pve (pct exec 105 --) · TP20 ✅ 2026-07-16: PG16, DB nautobot, Creds postgres/.secrets |
| Nautobot (Portainer-Stack, NAS) | 10.40.20.30 | Portainer ep14, Stack Id 203 · TP21 ✅ 2026-07-16: ghcr.io/nautobot/nautobot:3.1.7, DB=platform-pg, Creds in docs/nas-vlan20/stacks/210-nautobot.yml (gitignored) · TP22 ✅ 2026-08-13: Golden-Config-App + Device-Adoption DURCH — alle 4 MikroTik adoptiert, Deploy-Pipeline scharf (Image 3.1.7-gc9), 68/68 Compliance-Checks grün / 0 Drifts (2026-08-16) |
| — | ❌ entfernt 2026-07-16 (User hat den Container gelöscht — overkill für 1 Switch auf der Abschussliste, s. TP2.1/TP15) | |
| — | ❌ entfernt 2026-07-16 — mit dem Controller gelöscht. ✅ Aufräumen erledigt/gegenstandslos (geprüft 2026-08-16): nicht mehr in mcpServers registriert (nur noch als „disabled" in ein paar Projekt-Scopes = harmloser Merkposten), und kein omada-Hostname mehr im CF-Ingress |
|
| pve-mcp | 10.40.20.173 | StreamableHTTP http://10.40.20.173:3000/ (Root-Pfad, NICHT /sse) · NAS-Stack ep14 (=nas-bf .20.10, NICHT LXC .126!) id 202 (vlan20-only, TP14.1 2026-07-10) · mcp-pve v0.6.1 (Upgrade 2026-07-10: Single-Session-Bug 0.4.8 „Server already initialized" gefixt, TP14.2a) · ~/.claude.json ✓ |
| Proxmox (pve) | 10.40.20.100 | ssh root@10.40.20.100 (SSH-Key ✓) · TP14 2026-07-13: Host-Mgmt VLAN1→VLAN20 (vmbr0.20 + Default-GW-Flip); .41.100 bleibt interim ohne Route |
| Docker-LXC (auf pve) | 10.40.20.126 | Portainer CE 2.40.0 (https://10.40.20.126:9443) · TP14.2 2026-07-10: LXC 400 net0 tag=20/.20.126 (iface heißt jetzt veth20, NICHT eth0); Docker-Netz vlan20=ipvlan parent veth20; adguard_bf1 .20.16, adguard_fm2 .20.17 |
| Mac mini | 10.40.20.167 (VLAN20) | ssh macmini · ⚠️ die früher hier stehende 10.40.41.167 war stale (VLAN1) — live korrigiert 2026-08-16 (ARP reachable auf vlan20) |
| Arris-Kabelmodem | (Bridge-Mode, keine Mgmt-IP im BF-LAN) | Liwest-Edge · speist crs326-core vlan100 (VLAN100-Transfer, WAN-IP 90.146.33.177) — seit Core-Cutover 2026-08-08, davor via sw1 gi16 → ap1 · FritzBox 6660 seit 2026-07-01 komplett ausgebaut (Liwest-Tausch, kein Zurück); Telefonie nicht weitergeführt |
Tooling
- MikroTik (core/branch/ap1/ap2): MikroMCP-Server live →
mcp__mikromcp__*-Tools (routerIdcrs326-core/crs326-branch/ap1/ap2). Reads immer safe; Writes mitdryRun:truepreviewen, dannapply_plan. Passwort setzen viamanage_user set-password(/user set*ist per Deny-Policy blockiert). ⚠️ Reboot geht auf ROS 7.24 gar nicht ueber MikroMCP: dasreboot-Tool sendetdelay, die REST-API kennt den Parameter nicht (unknown parameter delay), undrun_commandist fuer/system reboot*gesperrt — Weg istSSHPASS="$PW" sshpass -e ssh admin@<ip> "/system reboot"(PW aus~/.secrets, im Output maskieren). 🔴 Niemalssshpass -p— das schreibt das Passwort in die Kommandozeile, wo es inpsund jedem argv-Trace steht; genau so ist am 2026-08-22 das Core-Passwort in ein Sitzungstranskript geraten und musste rotiert werden.-ehält es in der Umgebung des Kindprozesses. ✅ Alle sechs Skripte im Repo sind am 2026-08-31 umgestellt (nautobot/gc-transaktion.py,routeros/import-core-rsc.pyund die vier inzwischen nachrouteros/archiv-tp15-cutover/archivierten Cutover-Skripte) — gemessen mitps-Stichproben plus Negativkontrolle, nicht nur gelesen. Neue Skripte erben die Regel nicht von selbst: wer einen sshpass-Aufruf schreibt, bautenv=dict(os.environ, SSHPASS=pw)mit ein.bridge/vlan/setnur auf[find … and !dynamic]. 🔴 Zweiter Leak-Weg, gleiche Klasse:list_wifi_interfacesgibtsecurity.passphraseALLER Profile im Klartext zurück — am 2026-08-24 hat ein Read, der nur diecap-wifiN-Zuordnung finden sollte, damit fünf WLAN-PSKs (main/iot/media/untrusted/guest) in ein Transkript geschrieben. Kein Werkzeugfehler — das falsche Werkzeug für die Frage. Für Kanäle/interface wifi channel print detail, für die CAP-Zuordnung… print where master=yesmit Feldauswahl. Der Scan selbst (/interface wifi scan, nur am Manager) ist unbedenklich. Merkregel dahinter: vor jedem Read fragen, was das Tool AUSSER dem Gesuchten noch ausgibt. ALLNET-Switch (sw1): ❌ Gerät ausgebaut 2026-08-08 → der Telnet-Runner unterswitch/ist Archiv, nicht mehr ausführbar (kein Endpunkt). Sieheswitch/README.md. Nicht mehr als Werkzeug anbieten.Omada-Switch (sw2): ❌ abgebaut 2026-07-23 (TP15, durchcrs326-branchersetzt). Controller + omada-mcp komplett gelöscht — Omada spielt keine Rolle mehr, nie wieder aufgreifen (User-Entscheid 2026-07-22). Aufräum-Reste ✅ geprüft 2026-08-16 (s. Tabelle oben).- Nautobot Golden Config (= der einzige legitime Config-Weg für Netzgeräte):
nautobot/gc-configplan.py(Token =NAUTOBOT_SUPERUSER_API_TOKENausdocs/nas-vlan20/stacks/210-nautobot.yml, gitignored).--job <name>legt manuelle Pläne an (Dry-Run default),--deploy-ids …deployt gestaffelt,--refresh-compliancefährt Backup → Intended → Compliance in der einzig richtigen Reihenfolge. ⚠️POST /api/plugins/golden-config/config-plan/ist405— das ViewSet ist read-only. ConfigPlans entstehen nur über den JobGenerate Config Plans(plan_type=manual+commands-TextVar). - Docker-Management: Portainer API
https://10.40.20.126:9443, Key inmcp/portainer/.env. Ablösung durch Komodo (native MCP) geplant.
Kritische Gotchas
- 📜
ALLNET-Gotchas (1 Telnet-VTY,— gegenstandslos seit 2026-08-08 (Gerät ausgebaut). Nur noch historisch relevant, s.savenicht vergessen)switch/README.md. - 📝 WLAN-Logs sind seit 2026-08-30 persistent — und der Topic heißt
wireless, nichtwifi. Auch auf dem neuenwifi-qcom-Stack tragen Roaming-/Connect-Eventswireless;infobzw.wireless,debug. Ein geratenestopics=wifiwäre wirkungslos geblieben. Zwei Regeln auf dem Core (nicht auf den CAPs — unter CAPsMAN führt der Manager die Events) schickenwirelessundcapsin denselben Kollektor wie TP5 (10.40.20.18:514, Tagesdateien unter/opt/tp5-syslog/log/10.40.20.1/). ⚠️ RouterOS UND-verknüpft Topics innerhalb EINER Regel (topics=info,!firewall= info UND NICHT firewall) —topics=wireless,capshätte nichts gematcht, es braucht zwei Regeln. Vorher lief alles nur in einen ~75-Zeilen-Ringpuffer, der von einer Stunde Arbeit überschrieben war; ein seit Wochen gemeldetes Client-Problem war deshalb rückwirkend nicht diagnostizierbar./system loggingsteht jetzt unter der 27. ComplianceRulesystem-logging— vorher unter keiner. Merkregel: bevor man ein sporadisches Problem sucht, erst prüfen, ob es überhaupt irgendwo mitgeschrieben wird. - 🧮 Ein Log-Burst über VIELE MACs ist Infrastruktur, über EINE MAC ein Client (Fund 2026-08-31).
Am 31.08. sahen 13 ×
caps,info … joined, 4 ×activity timeoutund zwei Massenburstsassociation rejected, no cached PMK(74 Zeilen, 38 verschiedene MACs) wie eine CAPWAP- Instabilität aus, die das ganze Netz trifft. Es waren die Reboots des TP27-Upgrades. Jedes Ereignis fiel auf einen Neustart. 🔴 Vor jeder Client-Diagnose erstgrep " caps,"und/system resource get uptimelesen — sonst sucht man einen Gerätefehler, der ein geplanter Reboot war. Der Umkehrschluss gilt auch: 93 von 93reauthenticatingvon einer MAC auf einer SSID sind ein echter Client-/SSID-Befund. - 🗺️ Die
cap-wifiN-Karte verschiebt sich bei JEDEM CAP-Rejoin, nicht nur beim Provisionieren. Am 2026-08-31 wanderte sie zweimal an einem Tag (08:09 Re-Provisionierung, 14:36 Core-Reboot); eine im Handoff festgehaltene Tabelle war nach sechs Stunden falsch. ⇒ Interface-Namen in Logzeilen sind ohne einen zeitnahen Karten-Snapshot NICHT zuschreibbar. Vor jeder Auswertung/interface wifi radio print terse proplist=cap,radio-mac,interfaceam Manager lesen — und wenn zwischen Ereignis und Auswertung ein Rejoin liegt, die Zuordnung als unbekannt behandeln statt zu raten. - 🔑 CAPsMAN verteilt KEINE Login-Passwörter — nur WLAN-PSKs. Zwei verschiedene Dinge heißen
„Passwort": die WPA-Passphrase (
/interface wifi security) liegt am Manager und wird an die CAPs verteilt ✅; der Geräte-Login (/user) ist eine lokale Benutzer-DB auf jedem Gerät ❌. Beleg: ap1 trägtadmin,ha,nautobotlokal, mit gerätelokalenlast-logged-in-Zeitstempeln. Das einheitliche Admin-PW ist eine von Hand angewendete Konvention, keine Automatik — und logisch kann es nicht anders sein: ein Gerät wird erst zum CAP, nachdem sich jemand eingeloggt hat. - 🔴 Ein neuer CAP braucht
configuration.manager=capsmanauf JEDEM Master-Radio./interface wifi cap set enabled=yesallein reicht nicht — ohne das Feld bietet der CAP seine Radios dem Manager gar nicht erst an. Symptom (2026-08-30, ap3):remote-cap printzeigtstate=Ok, aber/interface wifi radio printam Manager listet für dieses Gerät nichts, und weil die Provisioning-Regel dann nicht matcht, sucht man den Fehler in derradio-mac. Im Werkszustand ist das Feld leer (=local). - 🔌 Werks-MikroTik am Trunk:
ether1ist WAN, und das sieht aus wie ein Netzproblem. Beim cAP ax (und den hAPs) stehtether1per defconf in der Interface-ListeWAN. Folge: Discovery aus (kein MNDP → in/ip neighborunsichtbar), Input-TCP verworfen (22/80/8291/8728 zu), ICMP aber erlaubt — es pingt, was sich wie „erreichbar" liest. Kein Registry-Eintrag und kein MikroMCP hilft, denn Port 80 ist zu. Der Weg rein ist die LAN-Seite (ether2oder das Werks-WLAN); dort/interface list member remove [find where interface=ether1]— das ist der Fix. - 🔀
pvidist INGRESS,tagged/untaggedist EGRESS. Ein Port mitpvid=99, der in der tagged-Liste von VLAN99 steht, nimmt untagged an und antwortet getaggt. Symptom (2026-08-30): ein DHCP-Lease hing ewig aufofferedund ARP auffailed— der DISCOVER kam an, der ACK nie. Für ein Werksgerät gehört der Port untagged in die VLAN-Zeile. - 🕳️ Ein neues Gerät ohne
secrets_grouperzeugt eine LÜCKE, keine Fehlermeldung. Ohne Secrets Group scheitert der Golden-Config-Backup-Job still; die intended config wird trotzdem sauber gerendert, aber ohne Backup entstehen keine Compliance-Einträge — und die Gesamtzahl bleibt unauffällig (bei uns 104/104), weil ein Gerät ohne Ergebnisse auch keine Drift meldet. 🔴 Merkregel: eine gleichbleibende Compliance-Zahl nach dem Hinzufügen eines Geräts ist ein BEFUND, kein Erfolg. Erst130/130bewies, dass ap3 wirklich geprüft wird. Zweite Voraussetzung: die dedizierten Service-Accountsnautobot/ha(TP-PWD) existieren auf einem Werksgerät nicht. - 🔢 Firewall-Regelnummern aus
print wheresind Handles, keine Positionen. Nach einemplace-beforezeigte/ip firewall filter print terse where comment~"..."die Nummer 60, währenddropauf 59 stand — die Regel lag trotzdem davor. Beweis ist der Zähler: eine Regel hinter demdropkann nicht zählen. Wer nach der Zahl geht statt nachprint stats, „repariert" eine funktionierende Regel kaputt. - 🔌
crs326-corehat KEIN PoE-out (/interface ethernet poeexistiert dort gar nicht, CRS326-24G-2S+). Ein AP an einem Core-Port braucht eigene Stromversorgung. Der cAP ax bringt dafür Netzteil UND Gigabit-PoE-Injector im Karton mit (Produktseite, 2026-08-30) — die TP25-Notiz „kein Netzteil im Lieferumfang üblich" gilt für dieses Modell nicht. Symptom, wenn man das übersieht: der Core-Port steht aufno-linkund man sucht den Fehler im VLAN-Tagging. - 🧩 Die AP-Flotte ist seit ap3 heterogen — Literal-Listen im GC-Template sind dadurch gefährlich
geworden. Der AP-Zweig hatte
ether3/ether4/ether5als Bridge-Ports hartkodiert; das war die stille Annahme „AP == hAP ax² mit 5 Ports". Der cAP ax hat zwei. Seit 2026-08-30 kommt die Liste ausap.extra_ports(ConfigContextap-common, Default = hAP-ax²-Bestand; ap3 überschreibt sie perlocal_config_context_datamit[]). ⚠️ Nautobot merged Dicts tief, Listen ERSETZEND — für diesen Fall genau richtig, beizones/radioswäre es eine Falle. Merkregel: bevor ein zweites Modell derselben Rolle dazukommt, die Literal-Blöcke im Template daraufhin lesen, welche Zeile eigentlich „Gerätetyp" meint und nicht „Rolle". - 🔵 Container auf MikroTik — korrigiert 2026-08-25, die alte Pauschalregel war nie nachgemessen.
Es hiess hier „hAP ax² hat 128 MB Flash, kein USB → Container NICHT auf dem Router". Live gelesen:
ap1/ap2 sind
arm64mit 95 MB FREIEM Flash und 690 MB freiem RAM — das reicht für einen kleinen Container (USB ist laut MikroTik-Doku nur für schnellere Extraktion, nicht Pflicht). Faustregel: ~70 MB nutzbar je AP, weil RouterOS Reserve für sein eigenes.npkbraucht. 🔴crs326-coredagegen NIE:arm32-bit, 2,9 MB frei von 16 MB. Der Core ist der Netz-SPOF, aber kein Anwendungs-Host — das ist eine Hardware-Tatsache, keine Politik. 🔑 Und die TP16.1-Aussage „Connector als RouterOS-Container ❌ nicht möglich" gilt nur für den WARP-Connector, weil der ein TUN-Device braucht (RouterOS reicht keines durch).cloudflaredbraucht kein TUN — Userspace-Proxy. Die Schlussfolgerung überträgt sich nicht. Merkregel: bei einer „strukturellen" Einschränkung erst prüfen, ob sie ein Default ist oder ein Gesetz — und ob sie dem konkreten Dienst überhaupt gilt. - Schwere Container laufen weiterhin auf NAS/pve, nicht am Router (Caddy, AdGuard: die eine Config ändert sich staendig, die andere schreibt dauernd Logs auf NAND).
- VLAN20-Docker-Hosts: Host-DNS nach dem Umzug prüfen (2026-07-10, TP14.2a): NAS + pve-LXC zeigten
/etc/resolv.confnoch auf totes.41.254→docker pullghcr scheitert (EAI_AGAIN, aber Egress ok!). Fix =.20.1: LXCpct set 400 --nameserver 10.40.20.1; Synology-Falle: DSM-„Allgemein"-DNS ≠ wasdockerdliest — Interface zieht DNS per DHCP → Fix an der Netzwerkschnittstelle „DNS manuell". Laufende Container frieren den127.0.0.11-Upstream zum Erstellzeitpunkt ein (Recreate nötig). Merkregel:docker-pull-hängt+dns.lookup EAI_AGAINaberTCP zu ghcr-IP:443 ok= toter Resolver, nicht Egress. - 📜
ALLNET VLAN-DB (TP1)— historisch. Die VLAN-Semantik gilt weiter (40=MEDIA, 50=UNTRUSTED), die Portangaben (GE1/GE2/GE8) beziehen sich auf ein ausgebautes Gerät. - ap2 hängt an
crs326-branch ether24(Trunk, native VLAN1 + tagged 10-99); branch-Uplinkether1→crs326-core ether8(korrigiert 2026-08-16 — die frühere Angabe „→ sw1 GE8" stammt aus der Zeit vor dem Core-Tausch; MAC-hart belegt in TP22 Schritt 5). - Mesh = NEIN (beide APs haben wired Trunk-Uplink → Loop-Risiko).
- Beide APs = CAPs, Manager ist
crs326-core(TP28 ✅ 2026-08-17). Der Entscheid „kein CAPsMAN" von 2026-07-22 ist revidiert: er galt der Konstellation ap1=Manager+AP, in der ein Manager seine eigenen Radios nicht provisionieren kann — mit dem Core als Manager sind beide APs symmetrisch. Der Core braucht dafür keinwifi-qcom,capsmansteckt im Basispaket.- 🔴
/interface wifi scangeht am CAP NICHT — nur am Manager (Fund 2026-08-23). Das Gerät antwortetfailure: not allowed (/interface/wifi/scan); das ist weder device-mode noch eine Policy-Lücke (Gegentest:/tool fetchsagt auf demselben Gerät explizit „not allowed by device-mode", und die Gruppefullhattest+sniff). Ein CAP besitzt sein Radio nicht. Richtig ist/interface wifi scan cap-wifiN duration=…am Core — dort läuft es einwandfrei. ⚠️ Diecap-wifiN-Zuordnung vorher überradio-macbestimmen, sie ist nicht stabil. - 🔴 Messfalle: unter CAPsMAN führt der MANAGER die Tabellen.
/interface/wifi/registration-tableund diewireless-Logs sind auf ap1/ap2 leer, obwohl das WLAN läuft. Immer am Manager zählen. - 🔴 Die CAPs finden den Manager per Layer-2-Discovery (
discovery-interfaces=vlan99,current-caps-man-addressist eine MAC, keine IP). Deshalb müssen MNDP (udp/5678) und CAPWAP (udp/5246-5247) in derinput-Kette des Core erlaubt bleiben — sonst kommt das WLAN nach dem nächsten AP-Reboot nicht zurück, während laufende Sessions unauffällig weiterlaufen (Fund 2026-08-18, TP5 Etappe 4). - 🔁 Slave-VAPs aendern heisst zweimal arbeiten (2026-08-22).
slave-configurationsin einerprovisioning-Zeile zu setzen bewirkt am Funk nichts, solange nicht/interface wifi capsman remote-cap provision [find where identity="apX"]nachgezogen wird — und das gilt in beide Richtungen: auch beim Entfernen senden die VAPs sonst munter weiter. 🔴 Und die Abnahme nicht am Kommentar festmachen: beim Rueckbau wird der Interface-Kommentar geleert, waehrend der VAP steht. Wer aufcomment~"ssid"prueft, sieht "weg" und liegt falsch. Richtig istdatapath.vlan-idbzw. die Interface-Zahl am CAP (/interface wifi printdort). Beim Interface-Zustand ist ueberhaupt der CAP die Wahrheit: der Manager zeigt nach einer Re-Provisionierungrunning=falsefuer allecap-*, waehrend am CAP alles laeuft — das Gegenstueck zur Regel, dass die Registration-Table am Manager zu lesen ist. - Local Forwarding ist keine Einstellung mehr: im neuen
/interface/wifigibt es kein zentrales Forwarding,datapathexistiert dort nicht. - 🔴🔴
/interface wifi cap slaves-datapathMUSS auf beiden APs gesetzt sein (dp-caps-slaves, ein lokales datapath-Objekt mitbridge=bridgeund ohnevlan-id). Es ist das Einzige, was die vom Manager erzeugten Slave-VAPs lokal in die Bridge haengt: der Manager schicktdatapath.bridge=bridgemit, der CAP uebernimmt davon nur dievlan-id. Fehlt es, assoziieren Clients auf-iot/-media/-untrusted/-guestsauber und bekommen nie DHCP, waehrenddirtyvader-mainweiterlaeuft — dessen Master-Radioswifi1/wifi2sind statische Bridge-Ports. Genau so lagen IOT+MEDIA am 2026-08-18 rund 14 Stunden (Ursache: CAPsMAN-Cutover 08-17, nicht die TP5-Haertung). Abnahme fuer jeden WLAN-Umbau ist eine frische DHCP-Lease pro SSID — NICHT die Registration-Table, die zeigte waehrend des Ausfalls 25 zufriedene IOT-Clients. - 🔎 Merkregel „connect ok, kein DHCP": zuerst
torchauf dem betroffenen VLAN am Core UND den TP5-Drop-Log lesen. Sind beide leer, wird nichts verworfen — es kommt nichts an, und die Firewall ist entlastet. Dann liegt es an L2 (Bridge-Mitgliedschaft/Tagging), nicht am Regelwerk.
- 🔴
- mgmt-VLAN-Wechsel am Switch = Lockout-Risiko: VLAN muss existieren + Port muss es tragen, in einem Schritt.
- 🔥 Die Firewall ist seit 2026-08-18 deny-by-default — in BEIDEN Ketten. Ein neuer Dienst über eine VLAN-Grenze braucht ab jetzt eine explizite Regel, sonst funktioniert er einfach nicht. Diagnosewerkzeug ist die Log-Regel direkt vor dem Drop (
TP5-WOULD-DROP/TP5-INPUT-WOULD-DROP), die weiterläuft und in den Kollektor10.40.20.18:514schreibt (Dateien auf dem Docker-LXC unter/opt/tp5-syslog/log/). Erst dort nachsehen, bevor irgendwo anders gesucht wird. MAIN→INFRA/MEDIA/MGMT/IOT stehen dauerhaft offen (IOT seit TP5 E5, 2026-08-19 — Drucker + Shellys); IOT→MAIN bleibt bewusst zu, dort sitzt der Wert der Segmentierung. Ad-hoc-Zugriff aus MAIN in ein noch geschlossenes VLAN (UNTRUSTED, GUEST) läuft über die selbstverfallende Liste:/ip firewall address-list add list=jit-admin address=<deine-IP> timeout=1h. (Die früher hier genannten Beispiele „Scannen, IoT anlernen" brauchen jit-admin seit E5 nicht mehr.)- 🖨️ Merkregel aus dem E5-Drop-Log: der Drucker fällt mit udp/161 (SNMP) auf, nicht mit 631 — macOS fragt zuerst den SNMP-Status ab und bricht ab, bevor es zu IPP kommt. Eine Whitelist auf 631 hätte nichts repariert. Bei „Gerät antwortet nicht" also das Log lesen, statt den erwarteten Port zu raten.
- ⏰ Zeitquelle ist der Core (
/system ntp server, seit 2026-08-18), ausgeliefert per DHCP-Option 42 (ntp-server=in jedem der 7 Netze) und drift-überwacht (ComplianceRulesystem-ntp). Vorher lief nirgends eine Synchronisation: die anderen drei Geräte zeigten auf10.40.99.1, wo kein Server lief, und der Core selbst hatte gar keinen NTP-Client — ap2 war dadurch 17 Stunden versetzt. 🔴 Zwei Fallen dabei: (a) ein NTP-Server mituse-local-clock=nound unsynchronisiertem Client liefert Stratum 16, das jeder Client verwirft — Server einschalten allein reicht nicht; (b) wird der Server eingeschaltet, bevor sein Client synchron ist, hält er diesen Zustand fest →enabled=no/=yesnach der Synchronisation. Merkregel: bei widersprüchlichen Zeitstempeln zwischen Geräten zuerst/system ntp client get statuslesen, und zum Eingrenzen einen Client testweise auf eine öffentliche Quelle zeigen lassen. - 📡 SSDP-Relay für die macOS-Sonos-App: LXC 108
mcast-relayauf pve, zweibeinig10.40.10.21(VLAN10) +10.40.40.21(VLAN40),multicast-relaymit--noMDNS(mDNS macht der Core). Unit im Repo:mcast-relay/multicast-relay.service. Zwei Beine sind zwingend — SSDP ist link-lokal mit TTL 1, ein Relay muss Mitglied beider Broadcast-Domänen sein; Routing hilft nicht.- 🔴
--ssdpUnicastAddrgehört in ein zweibeiniges Relay NICHT — die Flagge hat den Hinweg 7 Wochen lang blockiert (drin 2026-08-18, ausgebaut 2026-08-25). Sie schreibt inmulticast-relay.pyZ. 630 die Quelle jedes SSDP-Pakets an239.255.255.250:1900auf<addr>:1901um (auch NOTIFY); beim Senden greift dann Z. 691if srcAddr == ssdpUnicastAddr and not onNetwork(srcAddr, tx['addr'], tx['netmask']): continue—10.40.10.21liegt nicht im VLAN40-Netz, also verwarf der Relay die M-SEARCH selbst, bevor sie die Player erreichte. Der Rückweg gehört stattdessen geroutet:forward TP5: MEDIA->MAINist offen, die Player antworten unicast an den echten Sucher. (Upstream nennt den Rückweg selbst# FIXME: record more than one?— es gibt nur einen globalen Merkslot.) - 🔎 Symptom-Merkregel: App sucht hunderte Male, sieht NOTIFYs, baut aber nie eine Verbindung auf = Relay, nicht Netz. Diagnose in einer Zeile — Richtungsbilanz im Journal:
grep -c "via eth1"(VLAN10→VLAN40) gegengrep -c "via eth0". 0 zu n heißt: der Hinweg fehlt. - ⚠️ Aus dem Relay heraus lässt sich der Relay nicht testen (Loop-Schutz ignoriert eigene Quelladressen) — am echten Client testen oder
journalctl -u multicast-relaymitlesen. Und am Client nicht auf Port 1900 messen: Sonos-App und Spotify halten dort je einSO_REUSEPORT-Socket, die Unicast-Antwort bekommt nur eines davon. Richtig ist eine Probe mit ephemerem Quellport (bind((ifaddr, 0))), genau wie ein echter Client sucht. - 🔴 Zweite, unabhängige Ursache desselben Falls (08-25): die Sonos-App suchte gar nicht. Nach der Relay-Reparatur lag weiter nichts an — der Mitschnitt auf der VLAN40-Leitung (
pct exec 108 -- tcpdump -i eth1 -n -s0 -A "udp and host <client>") zeigte: jedes SSDP-Paket des MacBooks kam von Chrome (ST: urn:dial-multiscreen-org:…dial:1,USER-AGENT: Chromium), kein einziges von Sonos. Die App hatte Sockets offen (*:1900,*:6969,TCP *:3400) und redete mit der Cloud, legte aber nichts auf die Leitung. Ursache war das Local-Network-Gate von macOS: es stand auf „an" und griff trotzdem nicht. Fix = Schalter unter Datenschutz & Sicherheit → Lokales Netzwerk aus, App beenden, wieder an, App starten (hart:sudo tccutil reset LocalNetwork com.sonos.macController2). ⇒ Merkregel: „die App sucht hunderte Male" ist erst dann ein Befund, wenn der Mitschnitt sagt, WER sucht. Im Handoff vom Vormittag waren die gezählten 291 Suchen Chrome + eigene Proben; die Zahl hat drei Stunden in die falsche Richtung gezeigt. - 🖥️ Das MacBook ist dual-homed in VLAN10:
en5= 10.40.10.183 (USB-Dock, Default-Route) unden0= 10.40.10.82 (WLAN). Die früher als „zweiter Mac" gelesene.183ist dasselbe Gerät — beim Messen immer beide Beine prüfen.
- 🔴
- 🏠 Home Assistant: der
http:-Block inconfiguration.yamlist WIRKUNGSLOS (Fund 2026-08-26, TP31). HA hat die http-Config nach.storage/httpmigriert ("yaml_migration_done": true); seither wird die YAML kommentarlos ignoriert — selbst ein ungueltiger Schluessel darin erzeugt keinen Config-Fehler. 🔴 Die Logzeile fuehrt aktiv in die Irre:Received X-Forwarded-For header from an untrusted proxy <ip>faellt laut Quelltext nur bei aktivemuse_x_forwarded_for— man haelt die Option fuer wirksam und die Liste fuer falsch und editiert die falsche Datei. Richtig ist der WS-Zweischritthttp/config/configure(setztpending, startet HA neu) → binnen 5 Minutenhttp/config/promote, sonst Auto-Revert aufstable. Merkregel zur Eingrenzung: ein Testskript in<config>/python_scripts/+python_script.reload— taucht der Service auf, liest HA das Verzeichnis, und die Frage ist nicht mehr welche Datei, sondern warum ein gelesener Block nicht wirkt. - 🐳 Nautobot auf der DS923 braucht einen abgeschalteten Image-Healthcheck. Der eingebackene Check ruft alle 10 s
nautobot-server health_check= einen vollen Django-Start; unter Last dauert der >2 min, die Aufrufe stapeln sich unbegrenzt → Speicher alle →fork()mit ENOMEM → DSM räumt den ganzen Stack ab (am 2026-08-17 zweimal passiert, Load bis 73 bei 4 Kernen). Im Stack stehen deshalbhealthcheck: disable: trueauf worker+scheduler,--concurrency=2undmem_limit.cpus:geht auf Synology nicht (Kernel ohne CFS-Quota, der Stack-Deploy schlägt sonst fehl). - 📜
Omada ES208G = Easy Managed— gegenstandslos (Gerät abgebaut 2026-07-23, Controller gelöscht). - L3-Router =
crs326-core(TP21.1 ✅ 2026-08-08, ersetzt ap1). Bridge vlan-filtering=yes; VLAN-SVIs.1je VLAN mit DHCP (7 Server / 54 Leases); DNS über DoH zum CF-Gateway,verify-doh-cert=yes(TP23 Stufe 1 ✅ 2026-08-18). Die alte Notiz „core hat kein CA" war zu pessimistisch: es braucht kein Bundle, sondern genau einen Root (GTS Root R4, ~1 KB) — die Kette ist Leaf → GTS WE1 → R4. Beide Firewall-Ketten enden seit 2026-08-18 mitdrop(TP5 E3/E4);TP10-seal-lanistdisabled, Mgmt-Input kommt nur noch aus VLAN10, von HA (8728), von Nautobot (22) und übercf-mesh. ap1/ap2 sind NUR noch APs — kein DHCP/DNS/NAT/Routing/WG dort. (Die frühere „TP4: ap1 ist L3-Router"-Zeile ist überholt.) - ✅✅ Edge-Cutover (2026-07-01; Router-Umzug 2026-08-08): FritzBox ausgebaut, Arris im Bridge-Mode. Liwest hat die FB6660 dauerhaft gegen ein Arris getauscht (physisch weg). WAN terminiert auf
crs326-core vlan100(VLAN100-Transfer), core zieht die öffentliche Liwest-IP90.146.33.177→ kein Double-NAT. core = Public-Edge + WG-Hub (fm/dw live), NICHT mehr ap1. Telefonie nicht weitergeführt. Runbookrouteros/wan-cutover-runbook.md(ap1-Teile historisch). - hAP
device-mode=home(ap1/ap2): scheduler + fetch blockiert (not allowed by device-mode) → AP-Provisionierung idempotent ohne Scheduler.[/ping]gibt nil in 7.23. REST je APhttp://10.40.99.11|12/rest(Creds =ROUTER_AP{1,2}in~/.claude.json). Der L3-Router crs326-core ist KEIN device-mode=home → dort gehen scheduler/fetch (z.B. DDNS-Updater). - Erstzugriff Werks-hAP am CRS-Fabric: Uplink
ether1= Werks-WAN →192.168.88.1(LAN) darüber NICHT erreichbar. Weg: Gerät auf LAN-Port (ether2) patchen ODER Core-Temp-Pfad192.168.88.2/24auf core-bridge+ srcnat masq192.168.88.0/24, dann scp+/import(danach Temp-Pfad + stale ARP.88.1entfernen). Winbox-Neighbors zeigt die APs NICHT (MNDP pro-VLAN, Mgmt auf vlan99) — kein L2-Fehler. - 🖨️ ZWEI Drucker, nicht verwechseln (geklärt 2026-08-23): der Brother MFC ist der
eigentliche Drucker und hängt auf VLAN10 (
10.40.10.6, MAC30:05:5C:78:86:20, statisch, keine DHCP-Reservierung) — er steht damit im selben VLAN wie die Macs, dieforward-Kette greift dort gar nicht. Der P750W ist der Etikettendrucker auf VLAN30 (10.40.30.5) — nur für ihn gilt der VLAN-übergreifende SNMP-Fall unten. - P750W (Brother, VLAN30) läuft auf
dirtyvader-iot(User-bestätigt 2026-08-08) — kein Access-List-Pin nötig; die alte „nur am co-located L3-GW-AP erreichbar"-Logik ist mit L3-GW auf dem Core hinfällig. Coverage-Kanten/Terrasse grenzwertig (−76..−89 dBm); TX am AT-Max (18/15 dBm); echter Fix = Platzierung. SSoTdocs/wifi-p750w-coverage-and-map.md. ⚠️ Power-Save ist KEIN Thema dieses Netzes — die früher hier stehende Merkregel „DHCP-geht-aber-Ping-nicht = ARP/Power-Save" war eine Fehldiagnose (2026-08-13 korrigiert: der Drucker war schlicht ausgeschaltet, das Ping-Symptom war WARP, s.u.). Nicht wieder einführen. - Ping auf JEDE lokale IP tot, aber Web/App/Print (TCP) geht überall = WARP-Client lokal versehentlich noch aktiv (2026-08-13 live beobachtet, BF vor Ort):
cloudflared/WARP ist QUIC-Proxy, kann kein ICMP, reicht TCP aber durch — auch wenn man physisch im BF-LAN steht, nimmt der Client bei aktivem WARP trotzdem den Tunnel-Umweg. Fix: WARP trennen, kein Router-Eingriff nötig. Vor jeder Firewall-/Mangle-/VLAN-Suche zuerst prüfen, wenn das Symptom „Ping auf ALLES tot, TCP geht" lautet — das ist ein Client-, kein Netzproblem. Strukturell schließt TP16.1 (natives WireGuard-CF-Mesh aufcrs326-corestatt cloudflared-QUIC) genau diese ICMP-Lücke. Memoryref_warp_local_icmp_trap.
Lessons for Sessions (2026-08-27, aus der Inventur-Session)
Verhaltensregeln für nachfolgende AI-Sessions — kleiner, klarer, weniger Drift:
-
Bestandsaufnahme vor Plan. Erst was ist da, wo ist der Schaden — dann Vorschläge. Nicht umgekehrt.
-
Eine große Fragenrunde statt vieler kleiner. Wenn der Plan von Annahmen abhängt, sammle die Annahmen in einer Runde, nicht in 3–4 Mikrorunden.
-
Master-Frage früh stellen. Bei jedem Repo, jeder Datei, jedem Tooling: „Was ist die SSoT, wo lebt der Master, was ist Mirror?" — bevor du eine Lösung vorschlägst.
-
Drift zuerst, Lösungen später. Drift-Tabelle posten + fragen, was richtig ist. Keine Lösungen vor dem Befund.
-
MCP-Erstellung/-Stilllegung = aktiv melden (2026-08-27, Christoph-Direktive). Wer einen neuen MCP anlegt, ändert oder abschaltet — meldet das proaktiv an den User, nicht stillschweigend im Commit. Begründung: jede AI-Session ist gleichzeitig Oberhand über Container (MCP-Hosts) und Garbage-Collector auf Infra-Ebene — wenn Drift unbemerkt entsteht, sammelt sich Müll.
-
MCPs brauchen Host-Verifikation vor Edit. Wenn der MCP verändert/editiert wird: vorher per Live-Read (nc/ps/curl) prüfen, ob der Host noch antwortet. Beispiel: 2026-08-27 —
shelly-mcpzeigte auf10.40.20.30:3030, aber kein Host antwortet. Konfig aufenabled=false, dokumentieren statt hart fehlschlagen zu lassen. (Stand 2026-08-28: shelly-mcp wird auf DS92310.40.20.180deployt —BUILD.mdliegt bereit.) -
Mac mini ist n8n-Host, nicht zentraler MCP-Host (User-Direktive 2026-08-28): MCP-Dienste gehören auf die DS923 (
nas-bf, Portainer Endpoint 14) — nicht auf die User-Workstation. Aktuell laufen gitea-mcp + n8n-mcp noch auf dem macmini (historisch), gehören bei Gelegenheit auf die DS923 umgezogen. -
Tote MCP-Skripte → ins Projekt-Repo, nicht global. Wenn ein MCP nur für ein Projekt relevant ist (
cannaleofür terpene,terpene-pgfür terpene): gehört in~/dev/<projekt>/mcp.json+ Skript im Projekt-Repo. Globale~/.claude.json/~/.config/opencode/opencode.jsonc= nur projekt-übergreifende Tools. -
opencode-Skills: Default-Pfad kopieren, NICHT symlinken (TP35 Schicht 6, 2026-08-28). Claude-Skill in
~/.claude/skills/<name>/SKILL.mdist die Quelle; für opencode gehört er nach~/.config/opencode/skills/<name>/SKILL.md(cp, nichtln -s). Begründung: opencode scannt diesen Pfad als Default — keinskills.paths-Eintrag in~/.config/opencode/opencode.jsoncnötig, das wäre redundant. Symlink wurde in der Inventur-Session testweise angelegt, hat Christophs "opencode = eigene Pfade"-Direktive verletzt und wurde wieder entfernt. Schema-Referenz: https://opencode.ai/config.json →Config.skills.paths. Lehre aus dem Fall: opencode-CLI-Verhalten nicht raten, Doku lesen — und ein "load-bearing"-Detail (Default-Scan-Pfad) zuerst verifizieren, bevor man ihn umgeht. -
Skills sind IMMER global, niemals pro Projekt (Christoph-Direktive 2026-08-28).
projektstatusund alle übrigen Skills liegen ausschließlich unter~/.claude/skills/und~/.config/opencode/skills/. Kein Repo-lokaler Skill unter.opencode/skill/oder.claude/skill/. Begründung: keine Redundanz, keine Drift zwischen Skill-Versionen, eine Quelle pro Skill. Merkregel: wer einen Skill „überschreiben" will, editiert die globale Datei oder schreibt einen zweiten Skill mit anderem Namen — niemals ein Repo-Override. -
Kommentar in Code ≠ Verhalten.
mcp/shelly/.envtrug den Kommentar "gitignored, NICHT committen" — es gab aber keinmcp/shelly/.gitignore. Der Kommentar war Wunsch, nicht Wirklichkeit.routeros/cloudflared/credentials.json(Cloudflare-Tunnel-PrivateKey base64) lag ungegittoret im Working Tree. Merkregel: Behauptungen in Kommentaren/Konfig-Kommentaren durchgit check-ignore -v <datei>(odergit status) verifizieren, bevor man darauf vertraut. Lessons-Block erweitert 2026-08-28 nachcommit-Session. -
opencode-TUI: führendes Leerzeichen vor
/commandmanchmal nötig (Beobachtung 2026-08-28, Skill „projektstatus" getestet; identifiziert als bekannter Bug). Symptom: Tippen von/projektstatusführt nicht zum Skill,/projektstatus(führendes Leerzeichen) schon. Doku https://opencode.ai/docs/commands zeigt den Aufruf ohne Space. Verifiziert: Issue anomalyco/opencode#27528 „ACP: prompt starting with '/' is silently dropped if command name is not recognized" — Status Not planned (skipped), closed 2026-07-14. Maintainer haben es also gesehen und nicht gefixt. Konsequenz: Workaround (führendes Leerzeichen) ist der offizielle Pfad; Skill selbst ist nicht kaputt. Vor jedem neuen opencode-Build prüfen, ob das Verhalten weg ist. -
Manuell gepflegte Doku mit Live-Daten verliert gegen die Realität, wenn sie lang wird. Befund 2026-08-28: der „Nächster Schritt (2026-08-26): TP31 Schritt 5"-Satz stand zwei Tage nach seiner Formulierung noch im Ist-Stand-Block, obwohl ich in derselben Zeit drei Folge-Sessions eröffnet hatte. Merkregel pro Edit: drei Stellen berücksichtigen — Quick-Index (Tabelle), TP-Abschnitt, Ist-Stand-Block. Keine schlägt die andere; wer eine ändert, muss die anderen mitlesen. Strukturelle Antwort = TP36 (SSoT via Nautobot, Datei wird read-only Snapshot) — eigene Session, nicht nebenbei. Lessons-Block erweitert 2026-08-28 nach Skill-Verifikation in opencode.
-
sed 's/=.*/=<set>/'maskiert NICHT, wenn das.*greedy ist und der Wert Slashes/Backslashes enthält (Befund 2026-08-28, shelly-mcp-Deploy). Ich habesed 's/SHELLY_DEVICE_PW=.*/<set>/'auf die Stack-YML angewendet — der Wert selbst wurde in den Output geschrieben, weil mein Replacement-String nicht greedy genug war und ich die Pipe nicht mitcat -voderhead -15 | sed 's|=.*|=<set>|'abgesichert hatte. Folge:SHELLY_DEVICE_PWist jetzt als verbrannt zu betrachten — wandert in TP30 Posten #3 mit Hochstufung. Merkregel: bei jeder Ausgabe von Secret-haltigen Dateien entwederhead -c <n>(Abbruch nach Bytes), oder die Datei vor dem Read mittr -d '\n' | sed 's|=.*|=<set>|'über einen Stream-Filter schicken — niemals einfachcatund auf einen Replacement-Pattern vertrauen. Vergleichbar zum PSK-Leak 2026-08-24. -
Vor jeder Drift-Bewertung
python3 nautobot/gc-transaktion.py statuslesen (Befund 2026-08-31, zweimal am selben Tag gestolpert). Drift aus einem laufenden Versuch sieht identisch aus wie ein Versehen — die Compliance-Zahl unterscheidet nicht. Konkret:crs326-core/wifi-security(ft-over-ds=no) wurde dem 7.24.1-Upgrade zugeschrieben und als „braucht eine Entscheidung" weitergereicht; tatsächlich ist es TP25 Fund E Hebel 1, um 08:09 bewusst gesetzt, während der Core erst um 14:36 gehoben wurde, geführt als offene TransaktionTP25-fund-e-ft-over-ds. 🔴 Eine solche Drift grün zu machen macht den Versuch unsichtbar. Umgekehrt gilt: was dauerhaft gelten soll (z.B. dasveth/masqueradeaus TP16.3 §3.4), gehört nicht in eine Transaktion, sondern ins Soll — Transaktion = befristeter Versuch, Template = Dauerzustand. ⚠️ Das Skript löst den Token-Pfad gegen die Repo-Wurzel auf und findet ihn aus einem Worktree heraus nicht (210-nautobot.ymlliegt nur im Haupt-Checkout) — die Transaktionsliste kommt trotzdem, nur die Compliance-Spalte fehlt dann. -
Maskierungs-Muster auf den SCHLÜSSELNAMEN ankern, nie auf ein Trennzeichen (Befund 2026-08-31, zweiter Fall dieser Klasse nach
SHELLY_DEVICE_PW).grep -n "TOKEN" datei | sed 's/:.*TOKEN/…/'maskiert nicht:grep -nstellt<zeile>:voran, und das:.*TOKENmatcht diesen Präfix statt den Wert — ein nachgeschaltetescut -c1-60schneidet dann genau nichts weg. So sind zwei Nautobot-Tokens ins Transkript geraten. Sicher ist nursed -E 's/(TOKEN|PASSWORD|SECRET|KEY|PASS)=.*/\1=<redacted>/'. Und: Werte nie suchen, um sie zu sehen — den Schlüssel persed -n 's/^…KEY=//p'direkt in eine Variable holen, nie übergrepmit Ausgabe. Vergleichbar zum PSK-Leak 2026-08-24 und zumsed-Fall 2026-08-28. -
Vor jedem Flash-Budget auf MikroTik
docker export | wc -cmessen, nichtdocker image inspect .Size(Befund 2026-08-31, TP16.3)..Sizemeldete fürcloudflared27,5 MB, entpackt waren es 69,5 MiB — das offizielle Image passt damit auf keinen hAP/cAP. RouterOS braucht beim Import Tar UND entpacktes Abbild gleichzeitig. 🔴 Unddocker saveschreibt seit Docker 25 (containerd-Store) OCI statt docker-v1 → RouterOS meldetno config found in manifest, was nach kaputtem Image klingt und ein Formatproblem ist. Beides indocs/tp16.3-bf-public-ap-connector-runbook.md§2.2.
Checkliste vor jedem Edit-Vorschlag:
- Habe ich die SSoT für die Datei/das Repo benannt?
- Habe ich die Drift zwischen Doku und Live dokumentiert (nicht stillschweigend korrigiert)?
- Habe ich eine Fragenrunde mit allen offenen Annahmen gestellt?
- Habe ich für jeden Edit deine Bestätigung eingeholt (nicht nur für den Plan-Output)?
- MCP-Änderung? Vorher Live-Check (Port/Prozess), aktiv melden, Garbage-Collector-Pflicht.
- Output-Länge. Christoph hat sich heute (2026-08-28) wiederholt in langen Outputs verirrt. Merkregel: eine Antwort pro Edit-Vorschlag, eine Zeile pro Bestätigung, keine Wiederholung dessen, was im Diff steht. Keine Frage-Runden, wenn keine echte Annahme kollidiert. Erklärungstexte unter 6 Zeilen, sofern nicht ausdrücklich verlangt.