Files
bf-infra/docs/teilprojekte.md
Christoph Emsenhuber bb2c87c68e Compliance 68/68 + TP24 (USV) ausgeloest + Doku auf den Stand nach dem Core-Cutover
Zwei Straenge, die sich dieselben Dateien teilen — deshalb ein Commit.

1) COMPLIANCE-DRIFTS BESEITIGT (User: "beseitige autonom alle") -> 68/68, 0 Drifts

Entfernt wurden die 4 verbliebenen `extra`-Drifts (der fuenfte, core 10.40.41.211,
war schon ueber TP19-vlan1-core-address gefallen):
- ap1 + ap2: Werks-defconf `/ip dns static router.lan -> 192.168.88.1`. Auf beiden
  APs der einzige statische Eintrag, die IP existiert im Netz nicht. Der Core hat
  sein eigenes, korrektes router.lan -> 10.40.99.1 (im Template, compliant).
- crs326-branch: Bring-up-Rest SVI vlan10 + 10.40.10.2, Adresse zuerst.

Richtung bewusst "am Geraet entfernen" statt "ins Template aufnehmen" — genau so
steht es seit 2026-08-13 im Switch-Zweig des Templates. Der frueher notierte
Vorbehalt "potenzielle Rettungspfade" ist per User-Entscheid aufgehoben; der
Mgmt-Fallback aus VLAN10 auf dem branch entfaellt damit (Rueckweg = MAC-Winbox am
selben Draht).

Weg: 3 manuelle GC-ConfigPlans, kein Direkt-Write. Vorher /system backup je Geraet.
Deploy gestaffelt — erst die APs, am Geraet gegengeprueft, dann branch.

Verifikation am GERAET, nicht am Job: DNS-Statics total=0 bei unveraenderten 28
bzw. 3 WLAN-Clients; branch nur noch vlan99 + 10.40.99.2/24; Ping vom Core 4/4.
Entscheidende Gegenprobe: /interface bridge vlan/export byte-identisch — `bridge`
steht dort statisch als tagged-Member von VLAN10, das Entfernen des SVI hat also
weder Traffic-Wirkung noch neuen Drift. Danach Backup -> Intended -> Compliance,
alle 4 Geraete mit frischem Success-Datum.

Neue API-Falle, die erklaert warum es bisher kein Skript gab:
POST /plugins/golden-config/config-plan/ ist 405 — das ViewSet ist read-only.
ConfigPlans entstehen nur ueber den Job "Generate Config Plans" (plan_type=manual
+ commands-TextVar). plan_type=missing erzeugt ausserdem nur add-Zeilen, also muss
alles Entfernende zwangslaeufig ein manueller Plan sein.

Neu: nautobot/gc-configplan.py — loest Device-UUIDs einzeln per Name auf und
assertet plan.device UND config_set gegen das Ziel (die alphabetische
Sortier-Falle aus TP21.1 kann damit nicht wiederkommen), verweigert config_sets
mit <<REDACTED>> (Regel 1) und print-Zeilen, kann gestaffelt deployen
(--deploy-ids) und kapselt Backup -> Intended -> Compliance (--refresh-compliance).
Dry-Run ist Default.

2) TP16.1: GATE OFFEN, Runbook ausfuehrungsreif, Governance-Ausnahme #4

Die Bedingung "gated bis TP21 den CRS326 abdeckt" ist seit der Adoption am
2026-08-13 erfuellt. §3 neu gemessen, §4.3 von Platzhalter auf echte Werte.
Die letzte inhaltliche Unbekannte hat sich erledigt: "Rueckweg-Routing TBD" —
der Core IST L3-Gateway aller VLANs, es braucht keine Zusatzroute. wg-hub liegt
auf demselben Geraet und ist damit direktes Vorbild statt fernes Muster.

Klarstellung, weil leicht zu verwechseln: das Mesh ersetzt die Tunnel NICHT. Es
loest nur den privaten Zugang (10.40.0.0/16 per IP, inkl. ICMP); die oeffentlichen
FQDNs bleiben auf cloudflared. Relevant dabei: die Route 10.40.0.0/16 zeigt auf
bf-pve — und der faehrt derzeit nur einen Connector.

Ausnahme #4 (User 2026-08-16): die WG-Zeilen des Mesh-Bring-ups auf crs326-core
duerfen direkt gesetzt werden. Grund ist strukturell, nicht bequem — TP22-Regel 1
schliesst jede <<REDACTED>>-Sektion vom Push aus, ein Privkey ist ueber Golden
Config gar nicht deploybar. Auflage: Backup vorher, intended config danach zurueck
nach Nautobot. Scope: nur dieser Bring-up.

Damit das ein einmaliger Bootstrap bleibt, ist das Secret-Handling-Design
dokumentiert: ein privater Schluessel ist Geraete-Identitaet, keine Konfiguration
(wie ein SSH-Hostkey). Der private Teil entsteht am Geraet und geht nie weg, nur
der oeffentliche kommt in die SoT und wird in die Peer-Configs gerendert.
Stufe 2 (nautobot-secrets-providers + Vault) lohnt bei 4 Geraeten und einem Key
nicht. Nebenbefund: die WG-Peers fm/dw stehen mit hartkodierten public-keys im
Jinja-Template — Public Keys sind Daten, keine Templatezeilen.

3) TP24 — USV aus TP15 herausgeloest (User-Entscheid)

Die DC-USV war der letzte offene Rest in TP15 und hat mit Switching/Routing
sachlich nichts zu tun (eigene Einkaufs-, Verkabelungs- und Elektrik-Logik).
TP15 ist damit inhaltlich zu. TP24 traegt jetzt 48V-Bus, MeanWell/LiFePO4,
Laufzeiten, DC-Eingangs-Tabelle und die 5 offenen Entscheidungen — darunter neu
die Standortfrage, die sich durch den Core-Umzug ins UG neu stellt.

4) DOKU-AUFRAEUM-SESSION A-E abgearbeitet

- Ist-Stand-Block neu geschrieben, live gegen die Geraete geprueft. Alte Fassung
  als ausklappbare Historie, ⚠️-Provisorium entfernt.
- Reihenfolge-Abschnitt auf den echten Rest-Pfad TP5 -> TP6 eingedampft (Punkt 3
  sagte noch "Jetzt: Omada-PVID-Fix").
- Stale ap1-als-Router-Aussagen in TP3/TP4/TP15/TP16.1/TP21/TP-R korrigiert —
  fortgeschrieben, nicht ueberschrieben.
- CLAUDE.md: Mac mini .41.167 -> .20.167 (war stale, live per ARP bestaetigt),
  branch-Uplink "sw1 GE8" -> "core ether8", ALLNET-/Omada-Gotchas als Historie.
- switch/ als Archiv gekennzeichnet (Geraet ausgebaut, Runner nicht ausfuehrbar).
- omada-mcp + CF-Ingress live gegengeprueft: beides sauber, Punkt abgehakt.

Nebenbefund, eingetragen aber nicht angefasst: das CF-Tunnel-HA ist degradiert
(bf-nas und bf-pve mit je nur einem Connector), und vier tote Alt-Tunnel tragen
VLAN1-Origins — darunter die Aufloesung des "unidentifizierten VLAN1-Geraets
.41.133" aus TP19 (es ist das coolify-Origin).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 19:28:50 +02:00

193 KiB
Raw Blame History

BF-Netzwerk — Teilprojekte (kanonische Steuerung, Stand 2026-06-10)

Zweck: Das große Netz-Projekt in einzeln durchziehbare Teilprojekte (TP) zerlegt — jedes mit klarem Scope, aktuellen Entscheidungen, Abhängigkeiten und Status. Jedes TP ist so geschnitten, dass es eine frische Session mit sauberem Kontext straight abarbeiten kann. Architektur/Historie: ~/.claude/plans/ich-will-das-delegieren-atomic-sunrise.md.

Kanonisches VLAN-Modell (gültig — überschreibt ältere Tabellen)

VLAN Name Subnetz (Ziel) Zweck
10 MAIN 10.40.10.0/24 Trusted (Handy/Laptop/Apple Watch)
20 INFRA 10.40.20.0/24 NAS/VMs/Docker/HA
30 IOT 10.40.30.0/24 Smart-Home (HA-managed), Waage, Fitness-Tracker
40 MEDIA 10.40.40.0/24 Sonos, AppleTV, TV, VuPlus, Cast-Ziele (getauscht ggü. früher)
50 UNTRUSTED 10.40.50.0/24 Spielekonsole, eBook-Reader, Low-Trust-Internet
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, Bridge-Mode, Public-IP an ap1)
IP-Blöcke: .1–10 Router/Gateways · .11–20 Switches/APs. GW je VLAN = .1 (crs326-core).
Mgmt (VLAN99, Ist 2026-08-16): core .99.1 · branch .99.2 · ap1 .99.11 · ap2 .99.12.
(Historisch, überholt: „ap1=.1, ap2=.2 · ALLNET=.11, Omada=.12" — aus der Zeit vor dem Core-Cutover.)

Ist-Stand (Kurz)

Stand 2026-08-16 — live gegen die Geräte geprüft (MikroMCP), nicht aus der Doku abgeschrieben.

  • L3 + WAN-Edge = crs326-core (CRS326-24G-2S+, RouterOS 7.19.5, Technik UG): Routing, DHCP (7 Server, 54 Reservierungen), DNS, NAT und der WG-Hub (TP10, Peers fm/dw) laufen hier. Mgmt-SVI 10.40.99.1 (vlan99), Gateway .1 je VLAN (vlan10/20/30/40/50/90/99 alle running). WAN = vlan100 — DHCP-Client hält die öffentliche Liwest-IP 90.146.33.177/24 direkt vom Arris (Bridge-Mode) → kein Double-NAT. DNS-Upstream aktuell plain 1.1.1.1 (DoH-Gateway offen: TP21.1 TODO 1 = TP23 Stufe 1).
  • L2-Branch = crs326-branch (Technik OG, RouterOS 7.19.5): reiner L2-Switch, Mgmt 10.40.99.2, Uplink ether1 → core ether8. ✅ Bring-up-Reste entfernt (2026-08-16): SVI vlan10 + 10.40.10.2 („TEMP Bring-up VLAN10") sind weg — Mgmt läuft ausschließlich über vlan99.
  • WLAN = 2× hAP ax² als reine Standalone-APs (RouterOS 7.23, kein CAPsMAN): ap1 10.40.99.11 (an core ether15), ap2 10.40.99.12 (an branch ether24), Uplink je ether1 (PoE-Port). 6 SSIDs dirtyvader-*, jede mit eigenem datapath → kein ungetaggter VLAN1-Egress mehr. 802.11r/k/v aktiv (st-main, seit TP3.3 fester Bestandteil des GC-Templates).
  • Ausgebaut / gelöscht: ALLNET-sw1 und Omada-sw2 sind physisch weg (2026-08-08 bzw. 2026-07-23, in Nautobot Decommissioned); Omada-Controller + omada-mcp gelöscht (2026-07-16); FritzBox 6660 raus seit 2026-07-01 (Liwest-Tausch auf Arris). cudy1 hängt noch an core ether11, hat aber seit 2026-08-08 19:33 keinen Link (live bestätigt) und steht in Nautobot auf Offline; cudy2 tot. → Kein aktiver VLAN1-Leak mehr, der formale Teardown bleibt TP19.
  • Kontrolle / Governance: MikroMCP für Reads/Debug (alle 4 Geräte in der Registry: crs326-core, crs326-branch, ap1, ap2), Config-Änderungen ausschließlich über Nautobot Golden Config (TP21/TP22 — Deploy-Pipeline scharf, 68/68 Compliance-Checks grün, 0 Drifts seit 2026-08-16).
📜 Historische Fassung (Stand bis 2026-08-08, Topologie vor dem Core-Cutover — bewusst erhalten, nicht als Ist lesen)

ALLNET-Core .11 (FW2.2.1, VLANs 10/20/30/40/50/90/99/100; GE1/GE2/GE8 Trunk; TP1 ✅ 2026-06-15). Omada-Switch .12 (adopted, TP2 ✅ 2026-06-15; Trunk-ALL auf P1+P2, Access-Profile auf P3-P8; Profile noch PVID=1 bis Omada-PVID-Fix). Omada-Controller .175 (DS923/macvlan, mbentley 6.2, 🟢 live). omada-mcp .176 (pve-LXC, StreamableHTTP, 🟢 live, 84 read-Tools; Controller-API v2 intern via christoph@emsenhuber.com). 2× hAP ax² (7.23.1): ap1 .1 (ALLNET P1) — TP4 ✅ 2026-07-01: Router + WAN-Edge (VLAN-Subinterfaces, DHCP, DNS, NAT, Public-Edge). ap2 .2 (Omada P2 — Trunk). Edge-Cutover ✅ 2026-07-01: FB6660 physisch entfernt (Liwest-Kabelmodemtausch, kein Zurück), Arris-Kabelmodem im Bridge-Mode; WAN = sw1 gi16 Access VLAN100 → gi15 Trunk → ap1 ether2 → vlan100 auf bridge (NICHT ether1); ap1 hält Public-IP direkt (Liwest, kein CGNAT), Double-NAT weg. Telefonie war auf der FB und wird nicht weitergeführt. Vorarbeit: TP10-Kern durch (2026-06-27, WG-Hub live) → Bridge-Gate vorher gelöst. Runbook: routeros/wan-cutover-runbook.md. Kontrolle: MikroMCP (MikroTik) live · ALLNET = Klickanleitungen · Omada → Controller + omada-mcp 🟢.

Nächster Schritt (Stand 2026-08-14): TP5 — Deny-by-default am Ende von forward. Alles Davorliegende ist durch: CRS326-Bring-up + L3-Umzug ap1→crs326-core (TP15/TP21.1 ✅ 2026-08-08), WLAN-Neuprovisionierung ohne CAPsMAN (TP3 ✅ 2026-08-08), Nautobot-Plattform + Fleet-Adoption + Deploy-Pipeline (TP21/TP22 ✅ 2026-08-13). Danach TP6 (CAKE) = das Projektziel. Kleine Follow-ups nebenher: DoH-Cloudflare auf core, DC-USV (TP24, seit 2026-08-16 eigener TP). ⚪ Omada-PVID-Fix ist obsolet (User-Entscheid 2026-07-22 — Omada spielt seit der TP15-Korrektur keine Rolle mehr im Netz, nie mehr aufgreifen).

Vorherige Fassung (Stand 2026-07-22, erledigt): „TP15 CRS326-Bring-up als neuer L3-Core, danach WLAN-Neuprovisionierung ohne CAPsMAN; TP5 folgt nach TP21/CRS326-Adoption." 🎯 Ziel: sauberes System „from scratch", keine Leichen/Altlasten. Migrations-Session-Endstand = VLAN1 + altes cudy-WLAN komplett weg (VLAN1 nur noch Mgmt, defconf-Client-DHCP zurückgezogen, cudy-Uplinks aufs Ziel-Client-VLAN getaggt oder cudy abgebaut). Unterpunkte/Aufräum-Liste bei Migration: (a) die Interim-Härtung ap2 wifi1/wifi2 frame-types=admit-only-vlan-tagged (TP3.1, 2026-07-06) bleibt gültig — verschwindet automatisch mit VLAN1; (b) st-main rrm/wnm = reines Roaming-Provisorium → kein Provisorium mehr (2026-08-13, TP3.3): Steering ist fester Bestandteil des Golden-Config-Templates + ComplianceFeature wifi-steering — es war die Neuprovisionierung, die es stillschweigend verloren hatte; (c) cudy1 = verbleibender aktiver VLAN1-Leak → ERLEDIGT (User-bestätigt + verifiziert 2026-08-14): cudy1 ist offline. core ether11 trägt kein Link (kein R-Flag), Nautobot-Status Offline (TP22 Schritt 5). Damit existiert kein aktiver VLAN1-Leak mehr; der Dual-DHCP-Race aus project_vlan1_dhcp_incident ist Historie. Der formale Teardown (Gerät ausbauen, Doku/Inventar) bleibt TP19. ⚠️ HW-Constraint hAP ax²: 1 GB RAM, aber nur 128 MB Flash + KEIN USB → Container auf dem Router NICHT praktikabel. Folge: native RouterOS-Features bevorzugen (z.B. Built-in-mDNS statt Container); Container (mDNS-Reflector, BIND9-DNS, Omada-Controller) laufen auf NAS/pve, nicht am hAP. 🔒 Governance-Entscheid (User, 2026-07-16, ausgeweitet 2026-07-16): Config ausschließlich über Nautobot — keine Direkt-Changes mehr an einzelnen im Netz verstreuten Geräten. Ursprünglich als ap1/ap2-Config-Freeze bis TP21 formuliert, noch am selben Tag verallgemeinert: jede Netz-Gerätekonfiguration (Router, APs, künftig auch die CRS326-Switches) läuft ab jetzt exklusiv über Nautobot Golden Config (TP21), sobald das steht — keine ad-hoc MikroMCP-Writes/UI-Klicks mehr an Einzelgeräten. TP19.1 war die letzte Ausnahme (ap1-DNS-Fix + ap2/sw1/sw2-Mgmt-VLAN-Umzug, s.u.) — davon ist nur der risikolose ap1-Teil noch direkt gemacht worden, ap2 wurde bewusst auf „nach Nautobot" vertagt. Macht Nautobot + alle Prerequisites (TP18→TP20→TP20.1→TP21, jetzt auch Komodo) zum aktuell kritischen Pfad, vor TP5/TP6. Betrifft nicht Software-Migrationen auf NAS/pve (anderes Substrat) und die Frage, ob sw1/sw2 vor der CRS326-Ablösung noch angefasst werden (offen, s. TP15/TP19.1). ✅ Genehmigte Ausnahme (User, 2026-07-17): der zentrale CF-Tunnel-Zugang auf ap1 (TP16/TP16.1). Alle für den zentralen Cloudflare-Zugang auf ap1 notwendigen (Vor-)Arbeiten dürfen direkt via MikroMCP/CF-API gemacht werden, ohne auf Nautobot GC zu warten — konkret die nativen WireGuard-Mesh-Zeilen aus TP16.1 (und analoge cloudflared/WG-Anpassungen am ap1-Edge). Begründung: Remote-Erreichbarkeit ist die Voraussetzung, um überhaupt sicher am Netz zu arbeiten (Henne-Ei zu Nautobot). Scope-Grenze: Die Ausnahme gilt nur für ap1. Sobald der zentrale Zugang später auf die CRS326 wandert (TP15), gilt die Ausnahme dafür vorerst NICHT — CRS326-Config läuft dann regulär über Nautobot GC (oder braucht eine eigene, dann neu zu erteilende Ausnahme). Nachtrag der ap1-Ausnahmezeilen in die GC als Follow-up, sobald Nautobot steht. ✅ Genehmigte Ausnahme #4 (User, 2026-08-16): die WireGuard-Zeilen des Cloudflare-Mesh-Bring-ups auf crs326-core (TP16.1). Dürfen direkt via MikroMCP/REST gesetzt werden. Begründung ist strukturell, nicht bequem: TP22-Regel 1 schließt jede Sektion mit <<REDACTED>> generell vom GC-Push aus, und /interface wireguard ist genau so eine — ein privater Schlüssel ist über Golden Config nicht deploybar. Damit ist die frühere TP16.1-Zusage „CRS326-Zeilen laufen regulär über GC" gegenstandslos. Auflage: vorher /system backup, danach Ist-Stand als intended config zurück nach Nautobot. Scope-Grenze: nur der Mesh-Bring-up auf crs326-core — nicht spätere WG-Änderungen, nicht andere Geräte, und sie verlängert sich nicht automatisch. Langfristige Auflösung = das Secret-Handling-Design in TP16.1 (Privkey ist Geräte-Identität, nicht Config). ✅ Genehmigte Ausnahme #2 (User, 2026-07-22): der CRS326-Bring-up (TP15). Die Grundkonfiguration beider CRS326 — Management-IP, Bridge, VLAN-Filtering, Trunks/Access-Ports — darf direkt via MikroMCP/Winbox gemacht werden. Begründung = Henne-Ei: Golden Config kann nicht auf ein Gerät pushen, das im Werkszustand keine erreichbare IP hat, und die GC-Templates existieren noch nicht (TP21 „offen: Templates/intended config"). Auflage: der so entstandene Ist-Stand wird anschließend als intended config nach Nautobot zurückgeschrieben, danach übernimmt GC — die Ausnahme deckt das Bootstrapping, nicht den Dauerbetrieb. Scope-Grenze: gilt für die beiden CRS326 im Bring-up. Sie hebt die Regel für ap1/ap2 nicht auf (deren Neuprovisionierung läuft weiterhin über GC) und verlängert sich nicht automatisch auf spätere CRS326-Änderungen. Damit ist die frühere Formulierung in TP16.1 („CRS326-Config läuft regulär über Nautobot GC oder braucht eine eigene, dann neu zu erteilende Ausnahme") erfüllt — dies ist diese Ausnahme.


Teilprojekte

TP1 — ALLNET Core-Switch finalisieren ▸ ✅ FERTIG (2026-06-15) → ❌ Gerät AUSSER BETRIEB (2026-08-08, durch crs326-core ersetzt)

  • ✅ GE8 Trunk (native vlan1, tagged 10/20/30/40/50/90/99/100) — war bereits aktiv.
  • ✅ VLAN 40 umbenannt: UNTRUSTED → MEDIA (switch/cli/04-vlan50-media-untrusted.cli).
  • ✅ VLAN 50 angelegt + benannt: UNTRUSTED.
  • ✅ GE1/GE2 (AP-Trunks) um VLAN 50 ergänzt; GE8 auto-includiert (Trunk).
  • Ergebnis: Core trägt alle VLANs (10/20/30/40/50/90/99/100) an P1/P2/P8.

TP2 — Omada Access-Switch + Controller ▸ ✅ FERTIG (2026-06-15)

  • ✅ Omada Controller 6.2.10.17 auf DS923 (macvlan .175): omada/docker-compose.yml, Stack via Portainer endpointId=14. Wizard abgeschlossen (configured=true), Creds in mcp/omada/.env.
  • ✅ pve-mcp (samik081) in Claude Code eingetragen (~/.claude/settings.json, SSE .173:3000).
  • ✅ omada-mcp live auf pve-LXC (.126, endpointId=2), ipvlan-IP .176, Stack Id=200, URL in ~/.claude/settings.json.
    • Transport: StreamableHTTP http://10.40.41.176:3000/mcp (Health /health → 200). Settings-Bug (doppelter Eintrag) behoben 2026-06-15.
    • Auth: Omada Open API Client-Credentials. Creds in mcp/omada/.env. Token-Format: Authorization: AccessToken=<token> (nicht Bearer). OMADA_TOOL_CATEGORIES=all (alle 84 read-Tools aktiv; write für VLANs/Ports nicht im MCP implementiert → direkte Open API oder Controller-UI).
    • omadac_id=adbef8fa…, site_id=6a2fe68e….
  • ✅ VLANs im Controller: 1/10/20/30/40/50/90/99/100 — alle angelegt, Switch adopted (switchConsistent=true).
  • ✅ Port-Plan (ES208G, alle 8 Ports konfiguriert):
    Port Name Profil Gerät Status
    1 uplink Trunk-ALL → ALLNET GE8 ✅ korrekt
    2 ap2 Trunk-ALL hAP ax² #2 ✅ korrekt
    3 Port3 Access-MAIN — (frei) ⚠️ PVID=1
    4 dock/monitor Access-MAIN Docking-Station ⚠️ PVID=1 · Lifeline-Port, nicht anfassen
    5 drucker Access-MAIN Drucker ⚠️ PVID=1
    6 Port6 Access-MAIN — (frei) ⚠️ PVID=1
    7 macmini Access-INFRA Mac mini ⚠️ PVID=1 · fixe IP 10.40.41.x, nicht anfassen
    8 ds923+ Access-MAIN NAS (2. Port) ⚠️ PVID=1 · nicht anfassen
  • ⚠️ Profile-PVID-Constraint (bis TP4 offen): NAS, Mac mini und andere Geräte haben fixe IPs im Bereich 10.40.41.x (VLAN-1-Subnetz der FritzBox). Solange diese nicht auf VLAN-10/20-Subnetze migriert sind, muss nativeNetworkId=Default(VLAN 1) in allen Access-Profilen bleiben — sonst kein DHCP, kein Internet für diese Geräte. Fix nach TP4.
  • ⚠️ Bekannter Fehler untagNetworkIds (Analyse 2026-06-15 via Controller-API v2): Access-MAIN hat untagNetworkIds=[VLAN1, VLAN20, VLAN10] (3 untagged!) statt [VLAN10]. Access-INFRA hat untagNetworkIds=[VLAN20, VLAN1] statt [VLAN20]. Geräte erhalten Frames aus mehreren VLANs ungetaggt → Inter-VLAN-Tests verfälscht. Fix erst sinnvoll nach TP4 zusammen mit PVID-Korrektur.
  • Sichere Testports (kein kritisches Gerät): P3, P5, P6.
  • ✅ Portainer-Ablösung: Komodo empfohlen (einzige Option mit nativem MCP mcp-server-komodo). Eigener Task.
  • Abh. erfüllt: TP1 (GE8 Trunk ✅).
  • → TP4 wird vorgezogen: Alle VLANs im Controller definiert (status=false = kein Management-IP, normal). Traffic läuft noch auf VLAN 1 bis TP4 die L3-Interfaces + DHCP-Server je VLAN aktiviert.

TP2.1 — Omada-Switch (sw2/ES208G) → Standalone-Recovery (kein Controller) ▸ ✅ FERTIG (2026-07-16, User solo)

  • Root Cause: beim VLAN20-Umzug des Omada-Controllers (TP14.1, 2026-07-10) ist die Controller↔sw2-Verbindung zerbrochen (sw2 seither „Disconnected", ursprünglich als reiner Mgmt-Loss vertagt). Beim Rettungsversuch hat der User versehentlich einen Werks-Reset am Switch ausgelöst. Ein anschließender Wiederherstellungsversuch (durch Claude) hat Teile der Config unbemerkt halluziniert — der damalige „wiederhergestellte" Stand war nicht vertrauenswürdig.
  • Entscheidung (User 2026-07-15): Controller-Zustand als invalid betrachtet, sw2 komplett neu — diesmal OHNE Managementsoftware. Kein Omada-Controller-Re-Adopt, kein Omada-Ecosystem-Anschluss (s. TP15-Korrektur — Omada bekommt generell keine Rolle mehr, auch nicht WLAN). sw2 direkt über eigenes Web-UI konfiguriert, wie ein eigenständiger Switch.
  • ✅ 2026-07-16 (User-Aussage): „voll durch, Funktionsweise erfolgreich getestet." Nach dem gescheiterten Claude-Versuch hat der User sw2 komplett allein neu aufgesetzt (Runbook docs/tp2.1-omada-switch-standalone-runbook.md als Playbook-Referenz, nicht Schritt-für-Schritt-Log). Keine formale Einzelschritt-Verifikation dieser Session (kein Port-für-Port-Protokoll, keine Live-Prüfung durch Claude) — Status beruht auf User-Bestätigung „es geht einfach", nicht auf eigener Nachkontrolle.
  • sw2 bleibt Solo: „bekommt nie Geschwister" — keine weiteren Omada-Geräte geplant. Interimslösung bis zur Ablösung durch CRS326 (TP15).
  • Offen/nachzuziehen (nicht blockierend): finale Portbelegung + IP von sw2 in docs/ip-plan.md/CLAUDE.md-Hardware-Tabelle nachtragen (aktuell dort noch „Adoption pending" — stale, aber Wert unbekannt, nicht geraten); omada/.secrets auf neue Standalone-Creds prüfen; ob Omada-Controller-Container jetzt komplett überflüssig ist.
  • Abh.: TP1 ✅ (VLAN-Trunk am Uplink). Verwandt: TP15 (löst sw2 mittelfristig durch CRS326 ab), TP14.1 (Auslöser).

TP3 — WLAN (Standalone-APs) ▸ ✅ REBUILD DURCH (2026-08-08): beide hAP ax² sind reine Standalone-APs, CAPsMAN abgeschafft — Erstaufbau ✅ 2026-06-18 (routeros/tp3-capsman.rsc = historisch, aktuelle Configs routeros/.secrets/ap{1,2}-prov-base.rsc)

⚠️ Lesehinweis: die folgenden Punkte bis zum ✅-DURCH-Eintrag am Ende beschreiben die CAPsMAN-Ära (Juni/Juli 2026) und sind bewusst als Historie erhalten — der Ist-Stand steht im letzten Punkt dieses TPs.

  • ✅ 5 sichtbare SSIDs live, beide APs, beide Bänder, je SSID→VLAN (wifi datapath vlan-id): dirtyvader-main(10, WPA3-only, FT), -iot(30, nur 2,4 GHz, WPA2+3), -media(40, WPA2+3, FT=no wg. Sonos), -untrusted(50, WPA2+3), -guest(90, WPA2+3). Alt-SSIDs install*/BF-Main/wv* entfernt. Temp-PSK (User setzt echte).
  • ✅ CAPsMAN-Architektur: ap1=Manager+AP (lokale Radios), ap2=CAP (dynamisch via Provisioning, remote-cap=Ok). Kernlektion: Der Manager kann seine eigenen Radios NICHT über CAPsMAN provisionieren → ap1 lokal zuweisen (Master via set + statische Slave-VAPs), ap2 zieht Master+Slaves dynamisch. Ein CAP hält keine Eigen-Config.
  • ✅ Kanal-Plan (non-DFS, überlappungsfrei): ap1 ch36/5180 + ch1/2412 · ap2 ch149/5745 + ch11/2462. (Auto-Channel hatte beide APs auf 5500-DFS/2437 gelegt = Co-Channel → fix gepinnt. ap2-Kanaländerung braucht remote-cap provision-Push.) bound-Problem damit obsolet/sauber gelöst.
  • ✅ Sonos-Migration abgeschlossen (2026-07-02): alle 10 Sonos auf dirtyvader-media/VLAN 40 mit MAC-fest reservierten DHCP-Leases in dhcp-v40. Household geschlossen, iOS-App findet alle Geräte (mDNS via Repeater). Inventar/IPs = SSoT docs/sonos-inventar.md (IPs haben sich verschoben — Arc jetzt .199, Arbeitszimmer-Paar auf .191/.192).
  • Vertagt: hidden dirtyvader-infra(20)+-mgmt(99) (Mgmt verkabelt). TP8: eine 802.1X-SSID (dirtyvader-secure) mit FreeRADIUS-Dynamic-VLAN → dann PSK-main/infra einstampfen (Konsolidierung).
  • SSID-Strategie-Entscheid (Begründung): unterschiedl. WPA-Typ/Band erzwingen separate SSIDs; „alles in 1 SSID per Auth" nur via 802.1X (Geräte ohne EAP wie Sonos/TV/IoT brauchen ohnehin PSK) → Hybrid in TP8.
  • Mesh = NEIN (beide APs wired-trunk → Loop-Risiko). DoH-Upstream = Cloudflare Gateway (seit TP4 aktiv).
  • Abh. erfüllt: TP4 ✅ (L3/VLAN-Interfaces). Folge: TP8 (802.1X), Sonos-Migration.
  • 🔄 CAPsMAN wird abgeschafft (User-Entscheid 2026-07-22) — WLAN-Neuaufbau from scratch. Anlass: WLAN dauerhaft instabil, besonders wenn beide APs laufen. Beide APs bekommen künftig eigenständige, von Nautobot Golden Config identisch generierte RouterOS-Configs statt Manager/CAP.
    • Begründung (kein Widerspruch zu Nautobot, aber ein echter Reibungspunkt): CAPsMAN erzeugt auf dem CAP dynamische Objekte (wifi13–22), die nie in dessen eigener Config stehen → Golden-Config-Compliance sieht auf ap2 faktisch nichts. Genau diese Halbheit war die Ursache von TP3.1 (CAPsMAN schrieb datapath.vlan-id nicht auf die Leitung und legte die tagged-Member am CAP nicht an).
    • Was NICHT verloren geht: 802.11r/k/v braucht kein CAPsMAN, sondern nur gleiche SSID/PSK/Mobility-Domain → Fast-Roaming bleibt. Preis: Radio-/Kanaländerungen sind zwei Deployments statt eines Provision-Pushes — mit Nautobot ohnehin ein Job-Run.
    • Reihenfolge: WLAN-Neuprovisionierung erst nach dem CRS326-Bring-up (TP15), da Routing/DHCP/DNS vorher umziehen.
    • ✅ DURCH (2026-08-08): beide APs als reine Standalone-APs neu provisioniert, CAPsMAN weg. ap1 10.40.99.11 (an core ether15), ap2 10.40.99.12 (an branch ether24); Uplink geräteseitig auf ether1 (PoE-Port, poe-out=off) umgestellt, ether2 aus dem Trunk entfernt. 6 SSIDs (main WPA3+FT / infra/iot/media/untrusted/guest WPA2+3, iot nur 2,4 GHz, media FT=no); jede SSID mit eigenem datapath bridge+vlan-id → das TP3.1-„cfg-main-2g ohne datapath"-Leck ist strukturell weg. Kanäle non-DFS ap1 ch36/ch1, ap2 ch149/ch11. Client-Test grün (VLAN10/30-Leases korrekt). Configs routeros/.secrets/ap{1,2}-prov-base.rsc, PSKs routeros/.secrets/wlan-psks.env, As-built → Nautobot via nautobot/ingest-ap-rebuild.py. Einheitliches Admin-PW auf allen 4 MikroTik (User-Vorgabe). Henne-Ei beim Bring-up: Werks-192.168.88.1 über Core-Temp-Pfad (192.168.88.2 + masq, wieder entfernt) via scp+/import; Werks-Inline-Overrides auf wifi1/wifi2 mussten geleert werden (!configuration.ssid !security.*), sonst funkt die Main-VAP die Werks-SSID. Offen: DoH auf core (nur plain 1.1.1.1), breiterer CLAUDE.md-Gotcha-Cleanup (ap1-als-L3-Router-Referenzen sind überholt).

TP3.1 — ap2 VLAN-Trunk-Fix ▸ ✅ Config live (2026-07-01) — routeros/tp3.1-ap2-vlan.rsc

  • Root Cause: ap2 (CAP, local forwarding) hatte bridge vlan-filtering=no → datapath.vlan-id wurde nie auf die Leitung geschrieben → ALLE WLAN-Clients auf ap2 (main/media/iot/…) egressten ungetaggt in VLAN1 (defconf/10.40.41.x) statt ins Ziel-VLAN. Symptom: iPhone (VLAN10) im OG „verbunden, aber sporadisch kein WLAN-Symbol" — beim Roaming ap1↔ap2 sprang es VLAN10↔VLAN1 (Subnetz-Flip → Abbruch). Sonos-Terrasse landete auf ap2 fälschlich auf 10.40.41.183. TP3 „ein CAP hält keine Config" gilt nur für Manager-Forwarding — hier local forwarding, daher braucht ap2 doch VLAN-Config.
  • ✅ Fix (wie ap1): ap2-Bridge vlan-filtering=yes; VLAN1 untagged=bridge,ether2 (Mgmt), VLANs 10/30/40/50/90 tagged auf ether2. Da nur die Master-Radios wifi1(5G)/wifi2(2.4G) Bridge-Ports sind (Slave-VAPs forwarden über den Master), zusätzlich wifi1/wifi2 als tagged-Member je Client-VLAN (wifi1: 10/40/50/90, wifi2: 10/30/40/50/90). CAPsMAN legt diese auf dem CAP nicht automatisch an → manuell gesetzt. Danach remote-cap provision.
  • Lockout-Risiko gehandhabt: ap2-Mgmt (10.40.41.2) untagged VLAN1 blieb erhalten; kein Scheduler (device-mode=home) → Recovery = Power-Cycle OG. Ausgeführt im leeren-registration-table-Fenster.
  • ⏳ Offen — funktionaler OG-Test durch User: Handy auf dirtyvader-main im OG → Lease muss dhcp-v10/10.40.10.x sein (nicht defconf/10.40.41.x). Falls kein DHCP: Omada-Port-2-Trunk (tagged 10/30/40/50/90) prüfen.
  • Abh.: TP3 ✅, TP4 ✅ (Bridge-VLAN-Muster gespiegelt). Config ap2-lokal (nicht CAPsMAN-verwaltet).
  • 🔁 Interim-Härtung 2026-07-06 (Migrations-Provisorium, fliegt mit VLAN1 raus): Symptom „kein DHCP auf dirtyvader-main, Altnetz ebenso, nichts geändert". Diagnose via MikroMCP (ap1+ap2): Dual-DHCP-Race — dieselben Main-MACs (56:71:6C:22:E6:A9, F2:62:4C:E1:AF:09, C0:C7:DB:DE:71:BE) hielten gleichzeitig eine korrekte VLAN10-Lease (dhcp-v10/10.40.10.x) und eine VLAN1-Lease (defconf/10.40.41.18x); defconf und dhcp-v10 boten parallel → offering ... without success + Minutentakt-Flapping. bridge-ARP auf ap1 = 333 Einträge (flache VLAN1 lebt voll weiter; IoT/Shelly untagged auf 10.40.41.2xx). Griff: auf ap2 die Master-Radio-Bridge-Ports wifi1/wifi2 von frame-types=admit-all auf admit-only-vlan-tagged — untagged Main/Slave-Frames werden verworfen statt in VLAN1 gedumpt (im Zielbild ohnehin korrekt, daher kein Rückbau nötig, verschwindet mit VLAN1). ether2 bleibt admit-all (VLAN1-Mgmt/Trunk, kein Lockout). Rollback: frame-types=admit-all auf beiden Ports. ⚠️ Rest-Leak bleibt: cudy1 (altes WLAN) bridged Clients weiter untagged in VLAN1 — nur per Switch-Port-Tag bzw. Alt-WLAN-Abbau (Migration) zu schließen, NICHT auf der MikroTik-Seite. Zusätzlich st-main rrm=yes wnm=yes (802.11k/v, behandelt nur das Roaming-Symptom). SSoT-Detail: Memory project_vlan1_dhcp_incident.
  • 🎯 ROOT CAUSE des ap1-Lecks gefunden (2026-07-22, live aus /interface/wifi/configuration print) — es ist KEIN frame-types-Problem: cfg-main-2g (SSID dirtyvader-main auf 2,4 GHz, gebunden an Master-Radio wifi2) hat überhaupt keinen datapath-Block — weder bridge noch vlan-id. Das 5-GHz-Pendant cfg-main-5g/wifi1 taggt sauber nach VLAN 10, 2,4 GHz taggt gar nicht → egress ungetaggt in VLAN 1. Ein Client, der zwischen den Bändern derselben SSID wechselt, springt damit 10.40.10.x ↔ 10.40.41.x — Subnetzwechsel mitten in der Verbindung. Erklärt das Dual-DHCP-Race und das „verbunden, aber sporadisch kein WLAN" ohne jede Zusatzannahme. Fix nicht mehr direkt anwenden — geht mit der WLAN-Neuprovisionierung (CAPsMAN-Abschaffung, TP3) über Nautobot ein. Nebenbefund: cfg-infra existiert auf ap1, wird von keinem Interface benutzt (deckt sich mit TP3.3), und ap1 funkt real auf ch36/ch1 — die Interfaces überschreiben das Channel-Profil ch-5g (5745/ch149).
  • 🔴 Update 2026-07-15: gleiches Leck jetzt auf ap1 vermutet. dirtyvader-main-Clients bekommen zeitweise 10.40.41.x-IPs statt 10.40.10.x — identisches Dual-DHCP-Race-Muster wie oben, aber die 2026-07-06-Härtung wurde nur auf ap2 gemacht. Nächster Schritt: gleiche admit-only-vlan-tagged-Umstellung auf ap1s lokalen Main-Radio-Bridge-Ports (⚠️ NICHT ether2 anfassen — das ist ap1s Mgmt-/WAN-Trunk-Pfad). Diagnose+Fix-Schritte: docs/tp19-vlan1-cudy-teardown-runbook.md Phase A (~30-60 Min). Diese Härtung ist wie die ap2-Variante ein Provisorium, das mit TP19s VLAN1-Abbau automatisch obsolet wird.

TP3.2 — WLAN Coverage / P750W-Discovery ▸ ⏳ teil-erledigt (2026-07-02) — docs/wifi-p750w-coverage-and-map.md

  • ✅ Brother P750W (VLAN30) gelöst: DHCP-client-id-Fix (matcht über MAC → feste 10.40.30.5); an ap1 gepinnt (geordnete wifi-access-list accept w2-iot → blanket reject, überlebt ap2-Re-Provision) — dozende Legacy-Clients nur am co-located-GW ap1 per ARP erreichbar, CAP ap2 nicht; mDNS vlan30 in mdns-repeat-ifaces+5353 → in MAIN auffindbar. Druck+Selbsttest OK. Web-UI http://10.40.30.5/general/information.html.
  • ✅ Nebenbei: multicast-enhance=enabled (iot+media), Guest ft=yes, INFRA-SSID(20) angelegt (nur ap2), Steering-Profil st-main/neighbor-group=bf-main auf beide Main-Configs.
  • Nomenklatur: dynamische ap2-CAP-Slaves (wifi13–22) nicht praktikabel umbenennbar (name-format unzuverlässig) → Map dokumentiert statt umbenannt. Backups bf-ap{1,2}-pre-nomen-20260702.backup/.rsc auf den Geräten.
  • ⏳ Offene Coverage-Arbeit → ausgelagert nach TP3.3 (Fortsetzung in neuer Session).

TP3.3 — WLAN Coverage-Kur ▸ 🔜 TODO (Fortsetzung, neue Session) — docs/wifi-p750w-coverage-and-map.md

  • ✅ BEHOBEN 2026-08-13, noch am selben Abend (User: „aus Zeitgründen zuerst 11k/v, der nächste Call steht an"). st-main (rrm=yes wnm=yes neighbor-group=bf-main) auf beiden APs wiederhergestellt und an cfg-main-5g/cfg-main-2g gehängt — via GC-ConfigPlan, bewusst nicht per Direkt-Write: der Push läuft so NAS→Kabel→AP und nicht über die WLAN-Verbindung, die man gerade umkonfiguriert. /system backup je AP davor (ap{1,2}-pre-steering-20260813.backup). Gegengeprüft am Gerät (nicht am Plan-Status): st-main rrm=true wnm=true ng=bf-main, beide Main-Configs verweisen darauf.
    • ⚠️ Erwartete Nebenwirkung: alle Clients haben kurz reassoziiert (ap1 25→2, ap2 7→14 unmittelbar danach) — steering= auf einer wifi configuration zu setzen re-applied die VAPs, auch die nicht angefassten. Nach ~2 Min eingependelt (ap1 15 / ap2 13, VLAN30 = 28 Hosts auf der Core-Bridge). Nicht während eines Calls machen.
    • ✅ In GC verankert: Steering steht jetzt im AP-Zweig des Templates (st-main/bf-main als Konstante — es gibt genau eine Roaming-Domain pro Standort) + neues ComplianceFeature wifi-steering. Eine künftige Neuprovisionierung kann es damit nicht mehr stillschweigend verlieren — genau das war der Fehler vom 2026-08-08.
    • ⏳ Offen: der Praxistest. Dieselbe Route noch einmal gehen. Reißt es weiterhin ab, ist es echte Coverage → Dead-Spot-Kartierung.
  • 🔴 Der Befund (2026-08-13): Steering war auf BEIDEN APs weg. /interface/wifi/steering = 0 Einträge auf ap1 und ap2 (live geprüft). Vor dem Rebuild war st-main mit rrm=yes wnm=yes + neighbor-group=bf-main auf beiden Main-Configs gesetzt (s. TP3.2 + TP3.1-Interim). Die Neuprovisionierung am 2026-08-08 aus routeros/.secrets/ap{1,2}-prov-base.rsc hat es nicht wieder angelegt.
    • Wirkung: kein 802.11k (Neighbor Reports → der Client kennt die Nachbar-APs gar nicht) und kein 802.11v (BSS Transition → der AP kann den Client nicht wegschicken). Der Client bleibt am sterbenden AP kleben, bis die Verbindung zusammenbricht, und muss dann komplett neu scannen + assoziieren = harter Abriss statt Übergabe. Genau das Symptom „einmal durch die Wohnung, WLAN weg" (User-Erlebnis 2026-08-13 während eines Telegram-Videocalls, musste auf 5G ausweichen).
    • ⚠️ ft=yes ist KEIN Gegenbeweis (auf beiden APs gesetzt, ft-mobility-domain beidseitig leer = gleicher Default, FT selbst also intakt). 802.11r macht die Übergabe schnell, sobald der Client sie beschließt — es bringt ihn nicht dazu, sie zu beschließen. Ohne 11k/v fällt die Entscheidung viel zu spät. „FT ist an" ≠ „Roaming funktioniert".
    • Reihenfolge-Konsequenz: Diese Regression vor der Dead-Spot-Kartierung beheben und dieselbe Route nachlaufen — sonst kartiert man Löcher, die in Wahrheit Roaming-Verhalten sind, und zieht daraus womöglich Hardware-/Platzierungs-Entscheidungen (s. „ap1 ins Sideboard"-Idee unten).
    • Weg (Governance): Steering gehört ins GC-Template (/interface/wifi/steering + steering= an der Main-Configuration) → feature-scoped Deploy. Kein Direkt-Write. Betrifft wifi-configuration (bereits Compliance-Feature) + ein neues Feature wifi-steering.
  • 💡 Idee „ap1 ersetzt den Sideboard-Switch" (User 2026-08-13): ap1 hat 4 freie Ports (ether2–5; ether3/4/5 sind bereits Bridge-Ports mit pvid=1, ether2 ist seit der ether1-Umstellung kein Bridge-Port mehr und müsste wieder aufgenommen werden) → die Wohnzimmer-Multimediageräte könnten direkt an ap1, der Smart-Managed-Switch entfiele. Technisch machbar (untagged VLAN40 wie core ether2), aber platzierungsgebunden: ein Media-Sideboard ist RF-mäßig einer der schlechtesten AP-Standorte (geschlossen, Metallgehäuse der AV-Geräte, niedrig). User-Vorgabe: Platzierung hat Vorrang vor dem eingesparten Gerät. Entscheidung daher erst nach der Steering-Reparatur + Re-Walk. Problem: Kanten/Terrasse grenzwertig — Clients (iPhone/iPad) landen bei −76..−89 dBm und springen zwischen allen 4 Main-Radios (ap1-5G/2,4G ↔ ap2-5G/2,4G) → „0–1 Striche + häufiger Abriss"; Terrasse auch mittags schlecht. FT kaschierte das bisher als „Roaming" statt „schwache Abdeckung".

Bereits geklärt/erledigt (nicht nochmal aufrollen):

  • NICHT config-verursacht (ap1 kein Reboot, RF-Konfig unverändert). TX schon am AT-Max (18/15 dBm, EIRP-Deckel — kein Hebel).
  • Kanalplan intern überlappungsfrei (ap1 ch1/ch36, ap2 ch11/ch149). Steering st-main/neighbor-group=bf-main auf beiden Main-Configs gesetzt (hilft Client-Entscheidung, macht Signal aber nicht stärker).
  • ap1-only ist vom Tisch (echte Funklöcher) → beide APs nötig, aber besser platziert.

Offene Arbeit:

  1. Dead-Spot-Kartierung (Tageslicht): User stellt sich in jeden Problempunkt (Terrasse zuerst, entfernte Räume, dicke Wand) → je Punkt registration-table + monitor (Signal je Radio/Band/AP). Zusätzlich Nachbar-WLAN-Scan /interface/wifi/scan je AP (störend — wirft Clients kurz ab) gegen Abend-Congestion. Output → SSoT-Doku.
  2. Remediation nach Befund: Platzierung (ap2 Richtung Schwachseite umstellen / 3. AP / Outdoor-Unit / Kabelweg); Roaming-Feintuning (802.11v transition-Schwellen ab 7.21, Band-Overlap reduzieren); Kanalplan aus Scan nachziehen.
  3. INFRA(20) auf ap1 ergänzen (fehlt — sendet nur auf ap2): lokaler w2-/w5-infra-Slave analog w2-/w5-media.
  4. Danach Re-Walk-Test (wie 2026-07-02, aber mit Kartierung als Baseline).

Sicherheitsnetz: Backups bf-ap{1,2}-pre-nomen-20260702.backup/.rsc auf den Geräten. Kontext-Memory project_wifi_coverage

  • ref_wifi_powersave_multicast. Abh.: TP3 ✅, TP3.1 ✅, TP3.2 ✅.

TP4 — Router-Cutover (hAP#1 = Router + WAN-Edge) ▸ ✅ FERTIG (2026-07-01) — ⚠️ Rolle seit 2026-08-08 auf crs326-core (TP21.1); alles unten beschreibt den damaligen ap1-Aufbau und gilt als Historie/Referenz, nicht als Ist

  • ✅ Bridge VLAN-Filtering aktiviert (vlan-filtering=yes); VLAN-Tabelle: VLAN1 untagged bridge+ether2, VLANs 10/20/30/40/50/90/99/100 tagged bridge+ether2.
  • ✅ VLAN-Subinterfaces: vlan10/20/30/40/50/90/99 auf bridge; je GW .1 (10.40.10.1 … 10.40.99.1).
  • ✅ DHCP: dhcp-v10…dhcp-v99 je auf eigenem VLAN-Interface, pools .100-.200, GW+DNS = VLAN-GW.
  • ✅ DNS: RouterOS-nativ, allow-remote-requests=yes. Upstream seit 2026-06-16: DoH Cloudflare Gateway (use-doh-server=https://px35ynlol3.cloudflare-gateway.com/dns-query, verify-doh-cert, servers=1.1.1.1 als Bootstrap; FB-Upstream abgelöst). Statische Einträge für alle Infra-Hosts unter bf.lan (13 Einträge, Transition-IPs bis PVID-Fix).
    • ⚠️ Auto-Suffix .lan (RouterOS-Einschränkung, kann nicht auf leer gesetzt werden) — TP5-Cleanup.
  • ✅ NAT: srcnat masquerade out-interface=vlan100 (ap1 = Public-Edge; kein Double-NAT mehr). Fallback-Konfig für ether1 bleibt reversibel dokumentiert (routeros/wan-cutover-runbook.md).
  • ✅ Routing-Tabelle: 0.0.0.0/0 via Liwest-Gateway auf vlan100; alle 7 VLAN-Subnetze als Connected-Routes aktiv.
  • ✅ Edge-Cutover vollzogen (2026-07-01): FritzBox 6660 physisch weg (Liwest hat gegen ein Arris-Kabelmodem im Bridge-Mode getauscht — kein Zurück). WAN = Arris → sw1 gi16 Access VLAN100 → gi15 Trunk → ap1 ether2 → vlan100 auf bridge (NICHT ether1). ap1 hält Public-IP direkt (Liwest, kein CGNAT). WAN-Input-Seal live (TP10-seal-Regeln: est/inv/dhcp/icmp/lan/wandrop im input-Chain, list_firewall_rules 2026-07-02 verifiziert). Telefonie war auf der FB und wird nicht weitergeführt (User-Bestätigung 2026-07-02).
  • DNS-Entscheidung (Technitium und externer BIND verworfen): RouterOS-DNS als Resolver, keine DHCP→DNS-Auto-Registrierung (soweit möglich). Infra-Hosts als statische /ip dns static-Einträge unter bf.lan; dynamische Clients bewusst ohne Namen. Filtering/DNSSEC via DoH-Upstream (TP3). HA: 2. Resolver auf ap2, statische Einträge beidseitig pflegen.
  • Abh.: TP1 ✅. TP3-Abhängigkeit entfällt (WLAN läuft weiterhin über FB bis TP3).
  • Ermöglicht jetzt: PVID-Fix in Omada-Profilen (MAIN→PVID10, INFRA→PVID20), untagNetworkIds-Bereinigung, DHCP-Reservierungen für NAS/macmini/pve auf neue VLAN-Subnetze migrieren.

TP5 — Firewall & Inter-VLAN-Policy ▸ ⏳ TEIL-LIVE auf crs326-core (Holes + WAN-Seal re-verifiziert 2026-08-14; Deny-by-default steht aus) — jetzt das nächste Arbeitspaket, Session vorbereitet — 📋 docs/tp5-deny-by-default-runbook.md

  • 📋 Session-Runbook liegt (2026-08-14): docs/tp5-deny-by-default-runbook.md — Ist-Stand live erhoben, beobachtete Inter-VLAN-Flow-Matrix (conntrack + Counter), 6 offene Entscheidungen (D1–D6), 4 Etappen mit Absicherung, Verifikation + Rollback. Kernansatz: Etappe 1 ist ein reiner Log-Lauf ohne Blockierung (action=log ans Ende von forward) — die Whitelist wird aus dem Log abgeleitet, nicht am Schreibtisch geraten. Drei Funde, die vorab eine Entscheidung brauchen: ein iPhone sitzt in VLAN30, ein ICMP-Dauersweep von .10.82 auf alle Shellys, und vlan99 ist Mitglied der LAN-Liste (→ seal-lan erlaubt Mgmt-Input aus jedem VLAN). VLAN50/90 tragen null Inter-VLAN-Verkehr → Internet-only-Pinning ist dort risikofrei.
  • Deny-by-default + least-privilege (Ziel). MAIN→INFRA nur Whitelist-Dienste; MAIN↔MEDIA allow (+mDNS); UNTRUSTED/GUEST nur Internet; MGMT isoliert; Router-input gehärtet.
  • ⚠️ Gerätewechsel beachten (2026-08-14 nachgetragen): der ✅-Block unten war ursprünglich auf ap1 verifiziert (2026-07-02) — ap1 ist seit 2026-08-08 Werksreset und reiner AP. Die Regeln sind beim Core-Cutover (TP21.1) mitgewandert, waren am neuen Gerät aber nie gegengeprüft. Am 2026-08-14 auf crs326-core live nachverifiziert (list_firewall_rules, chains forward 13 Regeln / input 12 Regeln): alle unten gelisteten Regeln existieren, sind enabled, tragen ihre TP5:/TP10-seal--Kommentare und zählen Traffic. Merkregel: nach jedem Rollenwechsel eines Geräts gilt eine Verifikation vom Vorgänger nicht weiter.
  • ✅ Bereits live (ap1 2026-07-02 → re-verifiziert auf crs326-core 2026-08-14):
    • forward accept vlan10↔vlan40 — TP5: MAIN↔MEDIA (AirPlay/Sonos)
    • forward accept vlan20↔vlan40 — TP5: INFRA↔MEDIA (HA↔Sonos)
    • input accept udp/5353 vlan10/vlan20/vlan30/vlan40 — mDNS-Repeater (TP7-Anschluss)
    • forward accept vlan90↔vlan40 + input accept udp/5353 vlan90 — GUEST→MEDIA (Sonos/Cast), permanent (TP7.2, 2026-07-03)
    • WAN-Input-Seal komplett (TP10-seal-est/inv/dhcp/icmp/lan/wandrop + WG-Hub UDP/51821 accept)
  • ⏳ Offen: Deny-by-default am Ende von forward (live bestätigt 2026-08-14: die forward-Kette endet nach TP5: MEDIA->GUEST ohne Drop → alles nicht explizit Erlaubte läuft durch); UNTRUSTED/GUEST auf Internet-only pinnen; MGMT isolieren; IoT→INFRA-Whitelist bauen.
  • 🔍 Befund beim Re-Verify (2026-08-14) — die 5 TP5: mDNS in vlanXX-Input-Regeln sind derzeit tot: sie stehen hinter TP10-seal-lan (in-interface-list=LAN, action=accept), das jeden LAN-Input schon vorher annimmt → Counter aller 5 mDNS-Regeln stehen auf 0 Pakete, während seal-lan 3,5 Mio. zählt. Kein Fehler und nichts zu tun — sie sind die Vorbereitung auf genau den Schritt, der noch aussteht: sobald der Router-input gehärtet wird (seal-lan verengen/entfernen), werden sie scharf. Falle für die TP5-Session: erst seal-lan verengen, DANN testen — vorher beweist ein grüner mDNS-Test gar nichts, weil er über seal-lan läuft.
  • 📌 Ausgangslage für die Umsetzung (Live-Stand crs326-core, 2026-08-14): forward = defconf (est/rel/untracked accept · fasttrack · drop invalid · drop WAN !dstnat) → 2× TP10-WG → 6× TP5-Holes (MAIN↔MEDIA, INFRA↔MEDIA, GUEST↔MEDIA), kein abschließender Drop. input = TP10-seal komplett (est/inv/dhcp/icmp/lan/wandrop) + WG-Listen 51821 + 5× mDNS. Der Deny-by-default-Griff ist damit eine Regel am Ende von forward plus die Whitelists davor — nicht ein Umbau der Kette. ⚠️ Governance: läuft über Nautobot GC (ip-firewall-*-Features aus TP22), nicht per Direkt-Write; vorher /system backup (TP22-Regel 3). ⚠️ Rettungspfad: ein zu früh gesetzter Drop killt Mgmt/DHCP/DNS quer durchs Haus — Reihenfolge Whitelists zuerst, Drop zuletzt, und device-mode auf core erlaubt scheduler → Auto-Revert-Scheduler als Sicherung ist hier möglich (anders als auf den hAPs).
  • 🔊 Im Paket enthalten (User-Vorgabe 2026-08-14): der macOS-Sonos-Client-Fix. Ist-Stand: iOS-App findet alles (mDNS via Repeater ✅), HA steuert/gruppiert sauber (Sonos-Panel, via static hosts + advertise_addr, TP7.2 ✅) — nur die macOS-Desktop-App findet die Sonos-Umgebung nicht. Ursache ist bekannt und keine Firewall-Frage: die Desktop-App entdeckt per SSDP (UDP 1900), und RouterOS reflektiert nur mDNS, kein SSDP → die Discovery quert die VLAN-Grenze MAIN(10)→MEDIA(40) nicht. Die forward accept vlan10↔vlan40-Regel ist bereits offen und hilft hier nicht, weil das Problem vor dem Verbindungsaufbau liegt. Fix = SSDP-Reflector (TP7.1) auf NAS/pve (nicht am Router — kein Flash) mit Bein in vlan10 + vlan40, plus input accept udp/1900 auf den bedienten VLANs. TP7.1 wird damit aus „vorgemerkt" in dieses Arbeitspaket gezogen und ist mit der Deny-by-default-Arbeit gemeinsam zu testen (sonst prüft man zweimal dieselbe Strecke).
  • IoT→INFRA-Rückmeldung (deine Frage): IoT darf aktiv zu INFRA-Diensten initiieren — Firewall-Allow IoT→INFRA NUR auf MQTT (1883/8883), HA-API, InfluxDB (8086) o.ä.; Antworten via established/related; IoT→MAIN bleibt deny. So pusht ein Sensor Daten an HA/MQTT/Influx, ohne dass IoT „frei" ist.
  • Abh.: TP4 ✅ (L3-Basis, Rolle inzwischen auf crs326-core), TP14.5 ✅ (Freigabe-Gate), TP22 ✅ (GC-Deploy-Pipeline = der vorgeschriebene Weg für den Push). Keine offenen Blocker mehr — TP5 ist der kritische Pfad. Folge: TP6 (CAKE). Hängt dran: TP5.1 (DNS-Redirect/Torch für Low-Trust).

TP5.1 — DNS-Visibility & Flow-Observability für Low-Trust-Geräte (Airfryer-Fall) ▸ vorgemerkt

  • Kontext (2026-07-08/09): Airfryer „airschreier" (Philips/Versuni, ESP, 10.40.30.180) bekam per DHCP-Option 6 dns 1.1.1.1 als Override, weil das OTA-Update über den ap1-DoH-Gateway-Pfad nicht durchging (DoH-Latenz beim primitiven ESP-Stack — kein Content-Block, via Gateway-Logs bewiesen, s. memory/ref_esp_ota_doh_not_block). Nebenwirkung: public 1.1.1.1 = anonymer Consumer-Resolver ≠ accountgebundener Gateway-DoH (px35ynlol3.cloudflare-gateway.com) → DNS-Sicht auf das Gerät weg, Gerät umgeht zudem die Filterung.
  • (a) DNS-Redirect statt Geräte-Override (der dauerhafte Fix): dst-nat „UDP/53 (+TCP/53) von Low-Trust-Src → auf einen plain Resolver umbiegen" gibt Logging zurück UND funktionierendes OTA (kein langsamer DoH-Pfad fürs Gerät). Redirect-Ziel-Optionen: (i) ap1-Resolver mit nicht-DoH-Upstream für dieses Src-Set, (ii) direkt public 1.1.1.1 (dann nur Flow-Logging), (iii) eigener plain-Resolver. Sauberer als „Gerät verschwindet auf 1.1.1.1". Verortung: Teil des UNTRUSTED-Internet-only-Pinnings (TP5-„Offen"); Entscheidung v50-DNS-Policy (gefiltert vs. plain) hängt dran.
  • (b) Live-Torch als Ad-hoc-Observability: DNS ist bei direktem 1.1.1.1 blind, Flows nicht (NATen über ap1). torch auf vlan30/vlan50 gefiltert auf die Geräte-IP zeigt Ziel-IPs live (Versuni → AWS eu-west-1 IoT erkennbar). Voraussetzung: Gerät aktiv. Merkregel: Airfryer nur online wenn er kocht → Capture ans Einschalten koppeln.
  • Abh.: UNTRUSTED-Move des Airfryers (Kandidat, s. VLAN50) + TP5-Deny-by-default. Kein Blocker.

TP6 — QoS/CAKE + Call-Quality-A/B-Test ▸ DAS Ziel

  • CAKE am WAN-Upload (~85–95 % Linerate) → Bufferbloat weg. Verifikation waveform.com → A/A+.
  • ⚠️ Vor „Erfolg": A/B-Test, denn Kollegen-Befund sagt DE-Calls = internat. Pfad AT↔DE, nicht Heimnetz → ggf. WARP statt/zusätzlich CAKE. Nicht am einen Telefonat festmachen.
  • Abh.: TP4.

TP7 — Media-Discovery (mDNS) für VLAN 40 ▸ ✅ FERTIG (2026-07-02) — docs/tp7-mdns.md

  • ✅ Built-in mDNS-Repeater aktiv — Live-Stand crs326-core (verifiziert 2026-08-14): mdns-repeat-ifaces=vlan10,vlan20,vlan30,vlan40,vlan90 → MAIN/INFRA/IOT/GUEST ↔ MEDIA. Reversibel (Feld leeren). 📌 bridge (=VLAN1) ist nicht mehr dabei — mit dem VLAN1-Retirement erwartbar und korrekt, war aber nie nachgezogen. Historische Fassung (ap1, bis 2026-08-08): …,vlan90,bridge — bridge=VLAN-1 seit TP7.2 2026-07-03. Realbestand: MAIN=iOS-Client + MacBook, INFRA=Home Assistant, MEDIA=10× Sonos (siehe docs/sonos-inventar.md).
  • ✅ Funktional durchgetestet (2026-07-02): iOS-Sonos-App (vlan10) und Home Assistant (vlan20) finden die 9 online-Sonos per _sonos._tcp.local. (Bonjour) automatisch. Household nicht mehr gesplittet.
  • ✅ TP5-Anschluss live: forward accept vlan10↔vlan40 + forward accept vlan20↔vlan40 + input accept udp/5353 auf vlan10/20/30/40 sind gebaut (mit „TP5:"-Kommentar), damit Discovery + Steuerung + Streaming ohne Umwege gehen.
  • ⚠️ SSDP/Cast (UDP 1900) bewusst NICHT gebaut (bräuchte Reflector-Container auf NAS/pve; hAP hat kein USB/Flash). Konsequenz: macOS-Desktop-Sonos-App nutzt SSDP → quert die VLAN-Grenze NICHT und findet die Sonos nicht. iOS-App (mDNS) und HA-Integration decken den Nutzungsbedarf ab; Spotify Connect läuft über Cloud.
  • Abh.: TP4 ✅, TP5 (Sonos-Holes) ✅ live.

TP7.1 — SSDP-Reflector (macOS-Sonos-App über VLAN-Grenze) ▸ 🔜 IN TP5 GEZOGEN (User-Vorgabe 2026-08-14) — nicht mehr „vorgemerkt"

  • 📌 Status-Update 2026-08-14: Symptom vom User erneut bestätigt — iOS-App ✅, HA-Sonos-Panel ✅ (steuern/gruppieren), macOS-Desktop-App findet die Umgebung nicht. Damit ist das Bild vollständig: alles, was mDNS spricht, funktioniert; nur der SSDP-Weg fehlt. Der Fix wird Bestandteil des TP5-Pakets (gemeinsamer Test mit Deny-by-default statt zweimal dieselbe Strecke prüfen). ⚠️ Nicht als Firewall-Problem fehldiagnostizieren: forward accept vlan10↔vlan40 ist längst offen — es scheitert an der Discovery, nicht am Datenpfad.
  • Bedarf: macOS-Desktop-Sonos-App auf VLAN 10 findet die Sonos in VLAN 40 nicht (Discovery per SSDP UDP 1900, RouterOS reflektiert kein SSDP). iOS-App + HA decken den Alltag ab, aber User will die Desktop-App nutzen.
  • Lösung: kleiner SSDP-Reflector-Container mit Bein je Ziel-VLAN. Auf NAS oder pve (nicht am hAP, 128 MB Flash + kein USB). Kandidaten: bkupreck/ssdp-repeater, scyto/multicast-relay, oder eigenes Python-Skript.
  • Scope: vlan10 ↔ vlan40 (MacBook → Sonos). Optional vlan20 ↔ vlan40 falls HA je SSDP-Discovery brauchen sollte (aktuell nicht — HA nutzt zeroconf/mDNS).
  • Anschluss TP5: input accept udp/1900 auf den bedienten VLANs (falls Router-lokal reflektiert wird) bzw. Container-Port-Freigaben je Bein.
  • Alternative (verworfen): macOS-Client temporär in VLAN 40 hängen (unpraktikabel für Alltagsnutzung).
  • Abh.: TP7 ✅. Kein Blocker für TP5-Deny-by-default.

TP7.2 — HA↔Sonos static-host binding + VLAN1/GUEST-Discovery ▸ ✅ FERTIG (2026-07-03) — docs/sonos-inventar.md

  • Root Cause (widerlegt frühere Doku): HA-media_player.*-Sonos alle unavailable. HA sitzt nicht auf VLAN 20, sondern noch auf 10.40.41.105 (VLAN-1-Transfernetz, IP-Migration steht aus). Der mDNS-Repeater deckte VLAN 1 nicht ab, und die HA-Sonos-Integration ist SSDP-abhängig (manifest dependencies:[ssdp], soco 0.31.1) → quert die VLAN-Grenze nicht. Die alten „HA nutzt mDNS/zeroconf, fixed 2026-07-02"-Notizen waren falsch.
  • ✅ HA-Fix (discovery-unabhängig): sonos: media_player: hosts: mit allen 11 Sonos-IPs + advertise_addr: 10.40.41.105 in configuration.yaml (Backup .bak-sonos-20260703). advertise_addr fixt die UPnP-Event-Callbacks trotz HA-Multi-Homing (.105/172.16.0.18/docker). Core-Restart → alle 6 Online-Zonen idle/playing (Wohnzimmer-Push verifiziert = Callbacks OK). Terrasse .198 offline = kein Entity (kommt beim Einschalten). Kurzes Flapping direkt nach Restart = Settling, danach stabil.
  • ✅ Nebeneffekt: mit bridge im Repeater (s.u.) meldet HA jetzt „Automatic discovery is working, hosts not needed" — die statischen Hosts sind also Redundanz (Gürtel+Hosenträger), bleiben aber als Absicherung.
  • ✅ VLAN 1 (temp) + GUEST (perm) Discovery/Reach (User-Wunsch 2026-07-03):
    • mDNS-Repeater erweitert → mdns-repeat-ifaces=vlan10,vlan20,vlan40,vlan30,vlan90,bridge (VLAN-1-untagged=bridge).
    • Firewall: input accept udp/5353 vlan90 + forward accept vlan90↔vlan40 (ganzes MEDIA, beide Richtungen; User-Wahl). VLAN 1/bridge brauchte nichts (schon via TP10-seal-lan + permissiver Forward).
    • Bewusst in Kauf: flacher Repeater → Guests sehen auch MAIN/INFRA/IOT-mDNS-Announcements (Discovery-Leak; echte Verbindungen regelt TP5-Deny-by-default).
  • Offen/später: HA→VLAN 20 (IP-Migration nach Omada-PVID-Fix) → dann nur advertise_addr auf neue IP; macOS-Desktop-App weiter via SSDP-Reflector TP7.1. Abh.: TP7 ✅, TP5-Holes ✅.

TP8 — Identity & Access-Control ▸ research-gated

  • RADIUS = FreeRADIUS auf der NAS (✏️ User-Manager v5 kann KEIN LDAP → openldap nur via FreeRADIUS anbindbar).
  • EAP-TLS (Zert) je Gerät + dynamische VLAN-Zuweisung (RADIUS-Attribut Tunnel-Private-Group-ID). Einfachste Heim-Alternative: pro VLAN eine SSID mit WPA3-PSK (kein RADIUS) — Abwägung Komfort vs. Admin-Aufwand.
  • Abh.: TP3. (Research erledigt, s. TP-R.)

TP9 — Gateway-/Router-HA ▸ ❌ OBSOLET (User-Entscheid 2026-08-08)

  • ❌ Verworfen (User-Begründung 2026-08-08, „ganz klar nein"): am crs326-core führt ohnehin nie ein Weg vorbei — er ist der unumgängliche L3-Durchleitungspunkt. Ein zweites Gerät / VRRP beseitigt diesen SPOF nicht, sondern baut nur zusätzliche Sollbruchstellen ein (2× Config-Sync für DHCP/DNS/Firewall/NAT, Split-Brain-Risiko, VRRP-Eigenfehler) → netto ein Zuverlässigkeits-Verlust, kein -Gewinn. Die Frage ist nicht „Aufwand vs. Nutzen", sondern „mehr Fehlerquellen einbauen?" → nein. DR-Weg = Config-Backup-Restore (+ ggf. AP-Reprovisionierung). VRRP-Skizze unten = nur noch historische Recherche.
  • „Was, wenn ap1 ausfällt?" → VRRP löst Gateway-Failover je VLAN ~gratis (kreuzweise Master/Backup). ⚠️ Haken (nicht Last): DHCP/Firewall/NAT/DNS müssen auf BEIDEN APs identisch gepflegt sein, sonst „Gateway up, Internet tot". Empfehlung: VRRP nur für MAIN/INFRA, sonst ap2 als Kalt-Ersatz + Backup. DHCP/DNS schon redundant (Split + 2 Resolver).
  • Abh.: TP4. (Research erledigt, s. TP-R + Config-Skizze im Report.)

TP10 — Multi-Site VPN ▸ ✅ KERN DURCH (2026-06-27) — beide WG live auf ap1-Hub (docs/tp10-vpn-migration.md)

Details/Cutover/Gotchas/Eckdaten → docs/tp10-vpn-migration.md (Single Source of Truth). Research: docs/research/vpn-comparison.md.

  • ✅ KERN DURCH (2026-06-27) + EDGE-CUTOVER (2026-07-01): beide AVM-Tunnel → 2× WireGuard mit Hub auf ap1 migriert. Live: peer fm (10.101.0.0/24, bidirektional, myfritz-Endpoint) und peer dw (10.95.10.0/24, CGNAT-Dial-in) mit frischem Handshake + Traffic. Public-Edge direkt — ap1 hört UDP/51821 auf vlan100 (Edge-Cutover 2026-07-01), keine FB-Portfreigabe mehr, kein Double-NAT. FM-IPsec gekillt, Gelis alter FB↔FB-WG sauber geswappt.
  • ✅ DW/Geli on-site verifiziert (2026-06-27): Geli auf 10.95.10.0/24 renumbert, FB-WG single 10.95.10.1/24, Keepalive 25, Endpoint bf4…:51821. End-to-End von macmini/Linz → Gelis 24/7-APs 10.95.10.246/.247 sauber (echte LAN-IPs, kein 10.255.255.x-NAT-Leak).
  • 💡 Root Cause (FM-PoC bewiesen): FB masqueradet WG, wenn die Interface-Address eine Transfernetz-IP ist → muss die FB-LAN-IP sein (AVM Proxy-ARP). War der Schlüssel zum sauberen LAN-LAN.
  • Hub: wg-hub (UDP 51821, Key P/bzt…, MTU 1412, keine IF-IP). Endpoint = DDNS bf4.emsenhuber.net:51821 (Cloudflare, DNS-only → Public-IP). Apply gebündelt in routeros/tp10-cutover-apply.rsc.
  • ✅ Permanenter Edge-Cutover durch (2026-07-01) — Details in TP4 (WAN via sw1 vlan100, ap1 = Public-Edge, WAN-Input-Seal live). BF-FB-eigenen WG-Server 51820 entfällt (FB physisch weg).
  • Offen: DDNS-Auto-Updater für bf4.emsenhuber.net auf NAS/pve, damit der A-Record der Public-IP folgt (falls Liwest sie tauscht). Aktuell manuell in Cloudflare gepflegt.

TP11 — Monitoring & Mgmt-Control-Plane ▸ research-gated

  • MikroMCP + omada-mcp + SNMP/icinga-mikrotik, RoMON, NTP, E-Mail-Alerts, Netwatch. → Teil von docs/research/routeros-capabilities.md.
  • CF-Tunnel #1033 Track A ✅ live (2026-06-30): Round-Robin-1033er über 3 asymmetrische Connectors behoben durch Reduktion auf pve-only Connector (NAS + macmini scheiterten an pve-lokalen/stalen Origins). SSoT: docs/cf-tunnel-mapping.md. Track B/C läuft über TP14 (Ingress bereits v116 .41→.20 für NAS; pve-Connector→VLAN20 via TP14 Session B = #1033-Zielbild).

TP12 — IPv6 (intern + extern, 3 Standorte) ▸ research-gated

  • Konzept für FM/BF/DW. → TP-R.

TP13 — „Botschafts-WLANs" ▸ 🔜 VORBEREITET (2026-08-08), fun/low-prio

  • Idee: eine/mehrere Broadcast-only-SSIDs als „schwarzes Brett" an die Funk-Nachbarschaft — der SSID-Name ist die Botschaft (z.B. Kanal-11-bitte-freihalten, WLAN-Tuning-siehe-Portal). Optional ein offenes WLAN mit Captive-Portal auf eine statische Info-HTML (Kanalabsprache, Auto-Kanalwechsel-Bitte). Leitgedanke: wer die SSID sieht, kann auch stören → die Infoseite erreicht genau die Zielgruppe.
  • Technik (jetzt einfach, da WLAN standalone seit TP3-Rebuild): reine Namens-SSIDs = zusätzliche /interface/wifi Slave-VAPs auf wifi1/wifi2 mit eigener configuration (nur ssid=, kein Security/Datapath) bzw. in ein isoliertes Dummy-VLAN → reine Beacon-SSID (kein DHCP/Uplink). Captive-Portal-Variante: RouterOS-Hotspot auf eigenem isoliertem VLAN (Guest-Ableger) mit walled-garden auf die lokale Info-Seite (HTML als hotspot-Ordner am Gerät oder via NAS-nginx).
  • Umsetzungs-Skizze: (1) SSID-Namen + Info-Text final; (2) Beacon-SSIDs in routeros/.secrets/ap{1,2}-prov-base.rsc ergänzen (identisch auf beiden APs, via Nautobot-GC generierbar); (3) optional Hotspot-VLAN + walled-garden + Info-HTML; (4) beide APs deployen.
  • ⚠️ Recht vor Scharfschalten prüfen (User privat, AT): offenes WLAN + Portal berührt Impressumspflicht/Störerhaftung — reine Namens-SSIDs ohne offenen Zugang sind unkritisch (kein Fremd-Datenverkehr); im Zweifel nur die Beacon-Variante.
  • Abh.: TP3-Rebuild ✅ (Standalone-APs — SSID-Änderung = 1 Deploy/GC-Job); ideal nach TP21/TP22 (dann via Golden Config statt Direkt-Edit).

TP14 — Host/Container-IP-Migration VLAN1→VLAN20 (Docker-Cutover NAS+pve) ▸ ✅ DURCH (2026-07-13; live re-verifiziert 2026-08-08) — docs/nas-vlan20/RUNBOOK.md, Handoff docs/nas-vlan20/handoff/

  • ✅ ABGESCHLOSSEN — Migration komplett (Live-Check 2026-08-08 vom core: NAS .20.10, pve .20.100, Docker-LXC .20.126, platform-pg .20.20, Nautobot .20.30 alle 0% Loss). Alle Hosts + Container auf VLAN20. TP14.4 (CF-Access-Token-Rotation) = kleiner, nicht-blockierender Standalone-Follow-up (CF/TP11-Umfeld); TP14.5 war nie ein eigener Change (nur ein Reihenfolge-Gate) → erfüllt, TP5 damit entblockt.
  • ✅ NAS (DS923, ep14) migriert (2026-07-09/10): Docker von default_external (macvlan .41) auf ein vlan20-Macvlan (10.40.20.0/24, gw .1, parent ovs_eth0); DSM-Cutover .20.10; Portainer-Endpoint .20.10:9001. Root-Cause der Dauerfehler = Portainer Config-Only-Macvlan-Falle (Driver:null, kein Container-Attach) + Macvlan-Parent-Exklusivität (default_external muss weg für vlan20) + default_internal (172.18) weg. Container laufen auf .20.x.
  • ✅ CF-Ingress v116 (2026-07-10): 6 NAS-Origins .41→.20 (SSoT docs/cf-tunnel-mapping.md §8); vw 200 via Tunnel = pve→VLAN20-Routing steht (502 direkt nach Ingress-PUT = transient).
  • ⚠️ Abh./Reihenfolge: verzahnt mit Omada-PVID-Fix/IP-Migration (Reihenfolge #3); Sessions = Sub-TPs TP14.1–14.5, sequenziell A→B→C→(D jederzeit)→E, keine parallelen Branches (geteilte Live-Infra). Anschlussstatements: docs/nas-vlan20/handoff/README.md.
  • ✅ TP14.1 (Session A) durch 2026-07-10: alle ep14-Container auf vlan20 + pve-mcp neu; nur Omada-Inform-Adresse offen (Controller-UI-Task, s.u.).
  • ✅ TP14.2 (Session B) durch 2026-07-10: pve-LXC (400/docker) auf VLAN20 (.20.126, ipvlan parent=veth20); omada-mcp/adguard→.20.x; CF-Connector jetzt VLAN20 = #1033 gelöst. Nächste Session = TP14.3 (Session C, dormante Stacks + Referenz-Sweep ap1-DNS).
  • ✅ Netz-Abgleich 2026-07-11 (live via MikroMCP/pve-mcp): NAS .20.10, Mac mini .20.167, pve-LXC .20.126 alle reachable auf VLAN20 (+ Container .20.x populiert; iobroker .107/ccu .232/z2m .106/vw .127 jetzt auch VLAN20). Sonst in VLAN1 (bridge) nur noch: sw1 .11/sw2 .12/ap2-Mgmt .2 (Switch-/AP-Mgmt, interim bis MGMT-VLAN99) + Streu-Clients übers cudy-Alt-WLAN (MacBook .182, iPhone .186, SwitchBot .184, Sonoff) + die 16 VLAN1-Shellys (TP17). defconf/pool-v1-DHCP auf bridge läuft noch (VLAN1 aktiv).
  • ✅ pve-Host-Mgmt → VLAN20 (2026-07-13): letzter verbliebener Host-Umzug. vmbr0 bleibt VLAN1 .41.100 (ohne Default-Route, interim), neues vmbr0.20 (bereits vorbereitet, aber mit kaputter address 10.40.20.0/24 vorgefunden) korrigiert auf 10.40.20.100/24 → ifreload -a live (ifupdown2, kein Reboot, LXC 400 durchgehend unbeeinflusst) → Default-GW auf 10.40.20.1 geflippt. SSH-Key-Auth bestätigt auf .20.100. Nachgezogen: ap1-DNS (pve.bf.lan→.20.100; dabei Fund+Fix eines Alt-Bugs: pve-mcp.bf.lan zeigte noch auf .41.173 statt .20.173), CF-Tunnel bf-pve Ingress (pve+j44d6tua-SSH-Hostname .41.100→.20.100, PUT via CF_API_TOKEN, Smoke 200/200), CLAUDE.md, docs/ip-plan.md. ~/.claude.json war schon sauber.

TP14.1 — Session A: NAS fertigstellen ▸ ✅ DURCH (2026-07-10); Omada-Inform/sw2 VERTAGT

  • ✅ Alle ep14-Container auf vlan20 redeployed + verifiziert running/.20.x (via Portainer-Stack-Redeploy, Env preserved, Volumes intakt): oc-stack(112 → prometheus .151/node-exporter .154/grafana .109), default(72 → hms-mqtt .124), nginx(101 → dashboard-web .148), oc-central(160 → qdrant .159), oc-charlie(158 → .162+cf), oc-johnny2(150 → .161+cf), gitea(111 → .157, DB .20.23:3306 erreichbar ✓), drawio(107 → .139, intern; CF-CNAME war eh gelöscht).
  • ✅ pve-mcp NEU als Stack (id 202, vlan20-only .20.173), verbunden mit Proxmox .41.100 (v9.0.11) — ⚠️ StreamableHTTP am Root-Pfad /, NICHT /sse (mcp-pve v0.4.8, MCP_TRANSPORT=http). ~/.claude.json mcpServers.pve-mcp NEU angelegt {type:http, url:http://10.40.20.173:3000/} (war vorher gar nicht drin) → wirkt nach Claude-Code-Restart.
  • ✅ Tunnel-Smoke (CF-Ingress v116 zeigte schon auf .20): gitea 200, grafana 302, vw 200 — Origins existieren jetzt real.
  • ⚠️ alertmanager .153: korrekt an vlan20, aber crash-loop wegen PRE-EXISTING Config-Bug (${TELEGRAM_CHAT_ID}-Literal in /etc/alertmanager/alertmanager.yml → int64-Parse-Fehler; Alertmanager macht kein Env-Substitution). NICHT migrationsbedingt, braucht echte chat_id. Out-of-scope.
  • ⚠️ openclaw-DB 10.40.41.122:3306 down (shared MariaDB, pre-existing; Bots kamen trotzdem hoch). DB_HOST-Env unverändert gelassen. OLLAMA_BASE_URL=.41.167=Mac-mini bleibt korrekt .41 (migriert nicht → kein Blanket-Env-.41→.20!).
  • ⚪ OBSOLET (2026-07-16) — omada-controller Inform-Adresse + sw2 re-adopt: ursprünglich vertagt (2026-07-10, Controller-Login schlug fehl). Gegenstandslos seit TP2.1: der Controller-Login-Ausfall eskalierte zum Werks-Reset von sw2, User hat daraufhin entschieden, sw2 dauerhaft standalone ohne Controller zu betreiben — und den Omada-Controller-Container samt omada-mcp am 2026-07-16 komplett gelöscht (overkill für 1 Switch auf der Abschussliste, s. TP15/CRS326). Kein Re-Adopt mehr vorgesehen.
  • 🐞 Gotcha gefunden: Portainer-Stack 111 (gitea) hatte den vlan20-Networks-Block auskommentiert (Vorsession-Artefakt) → Container landete auf gitea_default/172.18. Fix = Redeploy mit Disk-File docs/nas-vlan20/stacks/111-gitea.yml als Override.

TP14.2 — Session B: pve-Docker-LXC (.126) → VLAN20 ▸ ✅ DURCH (2026-07-10) (ipvlan)

  • ✅ ep2-Container auf vlan20: omada-mcp .20.176 (OMADA_BASE_URL→.20.175), adguard_bf1 .20.16, adguard_fm2 .20.17 — alle running/reachable (health 200, adguard 302). Transf. Files docs/nas-vlan20/stacks/{195-adguard,200-omada-mcp}.yml.
  • ✅ LXC 400 (docker) net0 → tag=20, ip=10.40.20.126/24, gw=10.40.20.1 (⚠️ Retag brauchte Christoph — Token claude@pam!claude hat kein VM.Config.Network; UI-Edit, dann Reboot via API mit VM.PowerMgmt). Portainer wandert auf https://10.40.20.126:9443.
  • 🐞 Gotcha: in-Container-Iface heißt jetzt veth20, NICHT eth0 (UI setzte Name analog net1=veth16) → Docker vlan20=ipvlan parent=veth20 (nicht eth0 wie im Runbook). cloudflared(host)/portainer(bridge) unberührt.
  • ✅ CF-Connector db713b30 jetzt in VLAN20 (Tunnel bf.emsenhuber.net healthy/8 conns) = löst TP11-Track-B/C (#1033-Zielbild). Ingress portainer .41.126→.20.126. Smoke: portainer/gitea/vw 200, grafana 302.
  • ✅ Refs nachgezogen: mcp/portainer/.env (PORTAINER_URL), ~/.claude.json (omada-mcp .20.176), CLAUDE.md, docs/ip-plan.md, docs/cf-tunnel-mapping.md. Next = TP14.3 (Session C, dormante Stacks + Referenz-Sweep ap1-DNS).
  • ✅ Live-Smoke re-verifiziert 2026-07-10: alle 6 VLAN20-IPs ping OK + dienstseitig grün — Portainer .20.126:9443 HTTP 200 (LXC 400 running, net0 veth20 tag=20 gw/ns=.20.1), omada-mcp .20.176→Controller .20.175 v6.2.10.17 (HTTPS :8043 200), pve-mcp .20.173 liefert Proxmox-Config, adguard .20.16/.20.17 HTTP :80 302→login (nicht HTTPS/:3000).

TP14.2a — pve-mcp Single-Session-Fix + VLAN20-Host-DNS ▸ ✅ (2026-07-10)

  • pve-mcp „Failed to connect": Ursache = mcp-pve v0.4.8 kann im HTTP-Transport nur eine MCP-Session (ein globaler StreamableHTTPServerTransport, sessionIdGenerator gesetzt) → erster Client nach Boot belegt den Slot, jeder weitere Init (neue Claude-Code-Session, claude mcp list-Healthcheck, Reconnect) = 400 "Server already initialized". omada-mcp NICHT betroffen (frische Session-ID pro Init → Per-Session-Transport). Fix = Upgrade auf 0.6.1 (Per-Session-Map, serverFactory() je Session, gleiche Env-Vars = Drop-in); Stack 202 neu deployed, Container .20.173 = 0.6.1, Connected to Proxmox VE 9.0.11, tools/list liefert pve_*, Claude Code ✔ Connected. 3× init hintereinander = 3× 200 mit verschiedenen session-ids (verifiziert).
  • Kollateral-Fund — VLAN20-Host-DNS kaputt (beide Docker-Hosts): nach dem VLAN20-Umzug zeigten NAS und pve-LXC /etc/resolv.conf noch auf totes .41.254 → docker pull ghcr.io scheitert (EAI_AGAIN / 5s-Resolver-Timeout; Egress zu ghcr-IP 140.82.x:443 = OK 31 ms, also nur Resolver tot, nicht Egress). Fixes: LXC .20.126 → pct set 400 --nameserver 10.40.20.1 (Christoph). NAS → DSM-„Allgemein"-DNS war .20.1, ABER die Netzwerkschnittstelle zog DNS per DHCP und überschrieb resolv.conf; Fix = Schnittstelle „DNS manuell" 10.40.20.1 (Christoph) → NAS pullt wieder direkt. Merkregel Synology: GUI-DNS ≠ was dockerd liest (Quelle /etc/resolv.conf, per DHCP überschreibbar). Workaround solange NAS-DNS tot: docker save(LXC, hat DNS) → gzip (191-MB-tar reißt am Portainer-Agent-Socket-Proxy „context canceled"; ~70 MB gz geht) → Portainer POST /endpoints/14/docker/images/load → Stack-Redeploy pullImage:false. Gotcha: laufende Container frieren den 127.0.0.11-Upstream zum Erstellzeitpunkt ein (Recreate nötig; für pve-mcp egal — Proxmox per IP .41.100). Memory ref_pve_mcp_singlesession_and_vlan20_dns.

TP14.3 — Session C: Dormante Stacks vlan20-tauglich + Referenz-Sweep ▸ ✅ Referenz-Sweep durch (2026-07-16); dormante Stacks weiter bewusst nicht deployt

  • ✅ Dormante Stacks bereitgestellt (2026-07-11): transform.py auf core(43)/openclaw(92)/openclaw2(97)/oc20260307(119)/openspeedtest(74)/speedtest_tracker(80)/technikscheiss(42) → .new.yml + .orig.yml in docs/nas-vlan20/stacks/ (gitignored). Nicht deployt (User: „im Bedarfsfall später", Container-Zukunft ungewiss). 42/43 waren default_internal → jetzt vlan20-only (DHCP-.20.x). ⚠️ Deploy-Caveats: externe Refs bewusst NICHT umgeschrieben (DB_HOST=.41.122 [pre-existing tote openclaw-DB, in 80+119], OLLAMA_BASE_URL=.41.129, REDIS_URL=.41.111 in 119); Kollision 43-core will .20.23 = gitea-DB-IP (nicht gleichzeitig deploybar).
  • ✅ Referenz-Sweep .41→.20 (2026-07-16, im Rahmen der TP19-Backup-Vorbereitung live gefunden + gefixt): ap1 /ip dns static hatte noch nas.bf.lan→.41.10 und docker.bf.lan→.41.126 stale — korrigiert auf .20.10/.20.126 (MikroMCP manage_dns_entry, dry-run+apply). Zusätzlich per User-Entscheid Omada-Controller + omada-mcp komplett gelöscht (overkill für 1 Switch auf der Abschussliste) → DNS-Einträge omada.bf.lan/omada-mcp.bf.lan entfernt statt migriert. fritzbox.bf.lan (totes Gerät, physisch weg seit 2026-07-01) ebenfalls entfernt. docs/ip-plan.md + CLAUDE.md nachgezogen.
  • ✅ ERLEDIGT/GEGENSTANDSLOS (live geprüft 2026-08-16): ~/.claude.json (omada-mcp-Eintrag verwaist), CF-Ingress-Rest (omada/omada-mcp-Hostnames, ungeprüft)
    • ~/.claude.json: omada-mcp ist nicht mehr als MCP-Server registriert (mcpServers enthält ihn nicht). Er steht nur noch in disabledMcpServers einiger Projekt-Scopes — ein reiner Merkposten „bewusst aus", keine Verbindungsversuche, kein Handlungsbedarf.
    • CF-Ingress: alle 9 Tunnel durchgesehen (cfd_tunnel + /configurations) — kein einziger omada-Hostname mehr. Sauber.
  • ↪️ Shelly-Geräte-Übersiedelung ausgegliedert → TP17 (die ~26 IoT-Shellys sind eine eigenständige WLAN-Migration, nicht Teil des Docker-Ref-Sweeps).

TP14.4 — Session D: CF-Access-Profil für Claude ▸ 🔜 TODO (nicht cutoff-kritisch)

  • CF-Token „claude code mcp" hat jetzt Access: Service Tokens: Write+Read → claude-mcp rotieren (POST …/access/service_tokens/{id}/rotate) → CF_ACCESS_CLIENT_ID/_SECRET in mcp/cloudflare/.env (chmod600). Memory ref-cf-access-claude-profile.

TP14.5 — Session E: Freigabe-Gate → TP5-Firewall ▸ ✅ ERFÜLLT (2026-08-08) — kein eigener Change

  • Gate „TP14.1–14.3 durch (alle Origins/Connectoren in VLAN20)" ist erfüllt → TP5 (Deny-by-default) ist entblockt. Reines Reihenfolge-Gate, kein eigener Change.

TP15 — HW-Refresh: Switching + Router ▸ ✅ ABGESCHLOSSEN (2026-08-08) — Bring-up beider CRS326 + Branch-Tausch (2026-07-23) und Core-Tausch/L3-Umzug (2026-08-08, Durchführung s. TP21.1) sind durch — docs/hw-refresh-requirements.md, Memory project_mikrotik_commit_nautobot_gc

🔌 Scope-Änderung 2026-08-16 (User-Entscheid): die DC-USV ist aus diesem TP herausgelöst und heißt jetzt TP24. Sie war der letzte offene Rest hier, hat mit Switching/Routing sachlich nichts zu tun und trägt eigene Entscheidungen (Elektrik, Batterie, Standort). TP15 ist damit inhaltlich zu. Vorherige Header-Fassung (Stand 2026-08-05, überholt): „🟢 CRS326-Bring-up + Branch-Tausch DURCH (2026-07-23); Core-Tausch/L3-Umzug vorbereitet, Cutover-Config materialisiert (2026-08-05), Cutover = nächstes Wartungsfenster."

  • ✅ BRING-UP BEIDER CRS326 + BRANCH-TAUSCH gegen sw2 — DURCH (2026-07-23, Claude via REST unter Governance-Ausnahme #2). Ablauf/As-built:
    • Zugriffsweg (Henne-Ei gelöst): Erst-Reihenfolge war core zuerst (User-Korrektur — core hängt an sw1 und ist der L2-Pfad, über den branch überhaupt erreichbar wird; branch war isoliert). Zugriff auf die Werks-192.168.88.1 über temporäre ap1-Zweit-IP 192.168.88.2/24 + srcnat-Masquerade 192.168.88.0/24 (beide TP15-temp, nach Bring-up wieder entfernt — Rückweg nötig, weil die Werks-Box keine Route zu VLAN10 hat). Config dann komplett per REST (/system/script PUT+run), kein Winbox-Paste. Winbox-MAC-Connect scheiterte reproduzierbar („macconnection sync timeout" über WLAN/quer durch den Fabric — braucht selben Draht).
    • Identitäten am Gerät verifiziert (nicht nur Doku): core = sw-id 4W04-URHT, MAC 04:F4:1C:B4:17:76, Serial HKA0AREXJFM, PW X9…; branch = sw-id RGZJ-UK0T, MAC 04:F4:1C:B4:15:58, Serial HKA0AJJA52F, PW PHR… (ap2 hängt an branch ether24 — bridge-host bestätigt = definitiv branch). PWs in ~/.secrets/credentials.env.
    • branch = produktiv, volle SOLL-Portbelegung (User-manuell erhoben, maßgeblich): ether1 Uplink-Trunk→sw1 #8 (native VLAN1 + tagged 10/20/30/40/50/90/99, kein 100), ether2+ether8 = NAS 2×tagged-VLAN20 (zwei unabhängige Links), ether3 Drucker/VLAN10, ether4 Dock/VLAN10, ether5+ether6 Sonos/VLAN40, ether7 Mac mini/VLAN20, ether24 ap2-Trunk; ether9–23+sfp disabled. vlan-filtering=yes. Mgmt 10.40.99.13 (VLAN99) erreichbar über sw1 #8 (+ VLAN1-Temp .41.213 während Bring-up). Post-Swap-Abnahme grün: NAS .20.10, Mac mini .20.167 (VLAN20), Dock .10.194 (VLAN10), Sonos .40.191/.40.192 (VLAN40).
    • core = grundkonfiguriert, NICHT produktiv (bleibt an sw1 #12): identity/vlan-filtering/volle VLAN-Tabelle, ether1 Uplink, ether2 Temp-Downlink (war Config-Pfad für branch), Mgmt 10.40.99.11 (VLAN99, erreichbar erst wenn sw1 #12 99T trägt) + VLAN1-Temp .41.211. Port-Layout ab ether3 tentativ (final in Core-Swap-Session).
    • Beide in MikroMCP routers.yaml (crs326-core/-branch, ENV ROUTER_CRS326_* in ~/.claude.json — live erst nach MCP-Neustart, diese Session lief alles per REST-curl). Nautobot modelliert + GraphQL-verifiziert (nautobot/ingest-crs326-ports.py): branch Active + volle Ports + primary_ip4 .99.13; core Planned + tentatives Layout; sw1 verifizierte 16-Port-Belegung (löst „UNVERIFIZIERT"); sw2 → Decommissioning.
    • 🐞 Gotchas festgehalten: (1) Kabel ether3↔ether5 waren beim Umstecken vertauscht → ein AZ-Sonos landete als Access-VLAN10 in 10.40.10.190 und blutete via SonosNet nach VLAN40 (= Dual-Homing-Muster, project_dual_homing_slowness_incident) → Kabeltausch gefixt, beide AZ-Sonos jetzt sauber VLAN40 (.191/.192 bound). (2) RouterOS-Script-policy-Feld per REST wählerisch → weglassen (admin-Default = volle Policy). (3) Disable-Regex mit ^…$-Ankern griff nicht → [find name!="ether1" and …] nutzen. (4) REST-PUT-Antwort mit mehrzeiligem source ist kein sauberes JSON → ID separat via ?.proplist=.id holen. (5) Claude-Code-Auto-Mode-Classifier blockt Device-Config-Writes per Bash (Reads ok) → autoMode.allow-Regel in .claude/settings.local.json nötig (kann Claude sich nicht selbst setzen).
  • ✅ ERLEDIGT 2026-08-08 (Durchführung + Recovery s. TP21.1). 🔜 Als Nächstes (eigene Session, Übergabe): Core-Tausch = L3-Umzug (Routing/DHCP/DNS/CF-Mesh von ap1 → crs326-core) — Vollausfall-Fenster, s. docs/tp15-core-swap-session-handoff-prompt.md. Danach WLAN-Neuprovisionierung (TP3, CAPsMAN-Abbau). Finaler Mgmt-Renumber dann: branch .99.13→.99.12, core bleibt .99.11. ⚠️ Anders gekommen (Ist seit 2026-08-08, live bestätigt): core .99.1 · branch .99.2 · ap1 .99.11 · ap2 .99.12.
  • ✅ Core-Swap VORBEREITET (2026-07-23, reine Prep-Session, kein Netz-Eingriff): ap1s Live-L3-Config vollständig gelesen (MikroMCP) und als staged Config materialisiert → routeros/tp15-core-l3-staged.rsc (alles Betriebsumlenkende disabled=yes, im Cutover in einem Rutsch enable; ~50 Reservierungen + WG-Privkey bewusst erst im Fenster maschinell von ap1 geholt statt abgetippt). Runbook/Rollback/Verifikation im Plan. 3 Fenster-Start-Entscheide festgelegt: (1) WG-Hub (TP10 FM/DW-VPN) migriert MIT — war im Handoff-Prompt übersehen, hängt an ap1s Public-Edge und kann dort nicht bleiben; (2) Governance = frische Direkt-Ausnahme #3 (REST/MikroMCP), danach intended→Nautobot; (3) Rettungspfad = iPhone-Hotspot.
  • ✅ Nautobot-Modell + Golden-Config-Template für L3-Cutover vervollständigt (2026-08-05): crs326-core jetzt Rolle Router, alle VLAN-SVIs (vlan10/20/30/40/50/90/99/100) + Gateway-IPs (.1) modelliert; nautobot/mikrotik_routeros.j2 (Gitea bf-golden-config) erzeugt für crs326-core die vollständige staged L3-Konfig (Interface-Lists, GW-IPs, WAN-DHCP, DHCP-Server/Pools, DNS, NAT, Firewall, WG-Hub) und für alle anderen MikroTik-Geräte nur die Identität. Git-Sync + Generate Intended Config erfolgreich getestet; intended config für crs326-core liegt in Nautobot vor.
    • ✅ ap1-Cutover-Daten exportiert + in Cutover-Config eingebaut (2026-08-05): 54 statische DHCP-Leases aus ap1 (dhcp-v10/v20/v30/v40) nach routeros/ap1-leases-to-core.rsc überführt; WG-Hub-Privkey von ap1 geholt und in die lokale Cutover-Config routeros/tp15-core-cutover-intended.rsc eingesetzt (der WG-Privkey landet NICHT im Git-Template — Secret).
    • 🐞 Prep-Gotchas: (a) MikroMCP kann core noch nicht schreiben — ROUTER_CRS326_CORE_* im MCP-Prozess nicht geladen (jeder Zugriff „Missing environment variable") → vor dem Fenster MCP neu starten oder REST-curl gegen http://10.40.41.211/rest. (b) Mgmt-.99.11 tot (sw1 #12 trägt kein 99T), nur VLAN1-Temp .41.211 erreichbar. (c) WG-Hub: Privkey von ap1 mitnehmen (sonst FM/DW-Reconfig nötig). (d) core-vlan99 wird GW .1; Bring-up-.11 dadurch redundant (Fenster-Reconcile). (e) VLAN1 raus: Hybrid-VLAN1-Drops sw1 GE3/7/10 beim Umstecken einem echten VLAN zuordnen oder stilllegen.
    • ✅ WG-Endpoint-Frage geklärt (2026-07-23): Spokes adressieren den Hub über bf4.emsenhuber.net:51821 = CF-A-Record (proxied=false, TTL 120, aktuell 90.146.35.38). Kein Updater auf ap1 → nichts zu migrieren; bf4 folgt der IP manuell (offener TP10-Follow-up). Zieht core am WAN eine neue IP (Lease sticky, eher nicht), via CF_API_TOKEN nachziehen: bf4-Record + Access-Policy bf4-home-ip (90.146.35.38/32). Chance: core ist nicht device-mode=home → scheduler/fetch gehen → bf4-DDNS-Auto-Updater dort bauen (schließt TP10-Follow-up).
    • 📋 Session-Handover für die Cutover-Session: docs/tp15-core-swap-session-handover.md (Einstieg + Pre-Window-Checkliste + Kurz-Runbook; Original-Auftrag bleibt docs/tp15-core-swap-session-handoff-prompt.md).
  • 📦 2× CRS326-24G-2S+RM werden morgen (2026-07-17) geliefert, Produktivsetzung spätestens Montag 2026-07-20 (User-Entscheid 2026-07-16). Sequenzierung ggü. Nautobot geklärt: TP20+TP21 werden bis dahin fast-getrackt (TP18/Komodo als Blocker entfernt, s. TP20) — Ziel ist, dass Nautobot vor der CRS326-Produktivsetzung steht und die Switches direkt als Nautobot-verwaltete Geräte adoptiert werden, statt erst von Hand konfiguriert und später importiert. sw1/sw2-VLAN99-Umzug (TP19.1) läuft parallel dazu on-demand, unabhängig davon.
  • 🧭 Grundsatzentscheid (2026-07-14, korrigiert 2026-07-15): reines MikroTik-Ecosystem — Routing + Switching + WLAN. Omada bekommt KEINE Rolle (weder Switching noch WLAN — der ursprüngliche „Omada=WLAN"-Satz war ein Fehlschluss beim Dokumentieren, User-Korrektur 2026-07-15: „WLAN war NIE von Omada die Rede"). Begründung Switching: Projektziel #1 CAKE nur RouterOS/OpenWrt/OPNsense (Omada-Gateway kann kein CAKE) → Switching = MikroTik CRS. WLAN bleibt auf hAP ax² (/CAPsMAN — CAPsMAN am 2026-08-08 abgeschafft, Standalone aus der Golden Config; die Hardware-Aussage gilt), kein EAP-Kauf, Coverage-Fix bleibt Platzierungsfrage (TP3.3). Verworfene Vollvarianten (Omada-only, MikroTik-only) samt Begründung + Ansible/Nautobot-Recherche → Memory. Anlass der Korrektur: Omada-Controller↔sw2-Link beim VLAN20-Umzug (TP14.1) zerbrochen, versehentlicher Werks-Reset bei Rettungsversuch, fehlerhafter Wiederherstellungsversuch → sw2 wird standalone neu eingerichtet, s. TP2.1.
  • 🛒 Switches: 2× CRS326-24G-2S+RM bestellt (2026-07-14 — CSS326-Fehlbestellung storniert → CRS). 24×1G + 2×SFP+, lüfterlos, DC-input (USV-tauglich). Lösen ALLNET-Core (sw1) + Omada-ES208G (sw2, s. TP2.1 — Solo/Interim bis CRS-Tausch) + Cudy ab.
    • ✅ CRS = RouterOS-fähig → in-model: MikroMCP-Write + Nautobot Golden Config (TP21) managen auch die Switches, nicht nur den Router → config-as-code deckt den ganzen Kabel-Fabric. (SwOS-Boot bliebe als Fallback möglich, aber nicht genutzt.)
    • ⚠️ kein PoE (Sonoff/PoE-Verbraucher via Injektor, falls nötig).
  • 📶 WLAN bleibt hAP ax² — kein Hardware-/Ecosystem-Wechsel. ⚠️ Überholt am 2026-07-22/2026-08-08: /CAPsMAN — CAPsMAN ist abgeschafft, beide APs laufen seit dem Rebuild als Standalone-APs aus der Golden Config (s. TP3). Die Hardware-Aussage gilt unverändert. Coverage-Schwachstellen (P750W-Fall, Terrasse/Kanten) bleiben Platzierungs-/Roaming-Thema von TP3.3, nicht durch neue Hardware gelöst.
  • 🧭 Router-HW final (User-Entscheid 2026-07-22): kein RB5009. Kein Zukauf einer dedizierten Router-HW.
  • 🔄 KORREKTUR (User-Entscheid 2026-07-22, später am selben Tag): der CRS326-Core WIRD der neue L3-Router. Die frühere Fassung dieser Zeile („Router bleibt dauerhaft ap1/ap2, L3/CAKE/Firewall bleiben auf ap1") ist damit überholt. Routing, DNS und DHCP wandern vollständig von ap1 auf crs326-core; ap1/ap2 werden zu reinen Access Points. Auch der CF-Mesh-Endpoint terminiert dort (s. TP16.1, war schon vorher auf CRS326 retargetet — passt jetzt konsistent zusammen). Damit entfallen die device-mode=home-Einschränkungen des hAP (kein scheduler/fetch) für die Router-Rolle. Reihenfolge (User): erst Switches aufsetzen und in Betrieb nehmen, danach WLAN-Neuprovisionierung der APs. Betroffen/nachzuziehen: TP4 (Router-Rolle), TP5/TP6 (Firewall + CAKE landen auf dem CRS326), TP21 (Scope-Grenze).
  • 📍 CRS326 Live-Stand (2026-07-22, via MNDP von ap1): beide Geräte sind bereits am Netz und laufen — RouterOS 7.19.5, Werkszustand. crs326-core = MAC 04:F4:1C:B4:17:76 (software-id 4W04-URHT), crs326-branch = MAC 04:F4:1C:B4:15:58 (RGZJ-UK0T). ⚠️ Beide auf Werks-IP 192.168.88.1 im selben L2-Segment = IP-Konflikt → beim Bring-up nur eines nach dem anderen adressieren, Unterscheidung ausschließlich per MAC. Die frühere Notiz „Uplink-Port bislang ungenutzt/down" ist überholt. Standorte: crs326-core = „Technik UG" (virtueller Technikbereich im UG/Diele, KEIN Keller), crs326-branch = „Technik OG".
  • 🔌 PoE-Anschluss der APs (User-Vorgabe 2026-07-22): Verkabelung/Portbelegung bleibt sonst unverändert; ap1/ap2 sollen künftig aber am PoE-Port hängen. PoE selbst ist noch kein Thema, die Config soll es aber schon vorsehen.
    • ✅ Port = ether1. RouterOS nummeriert Interfaces ab 1 (ether1…ether5), nie ether0 — die Unix-Konvention eth0 gilt hier nicht. Auf dem hAP ax² ist ether1 der PoE-Port (live bestätigt: /interface/ethernet/poe listet ausschließlich ether1).
    • ⚠️ Aber: passives PoE 18–28 V, NICHT 802.3af/at. Ein normaler 802.3af/at-Switch oder -Injektor speist den hAP ax² nicht. Es braucht einen passiven PoE-Injektor (MikroTik-Standard). Dazu passend: der CRS326 hat gar kein PoE (bereits dokumentiert) → Strom kommt ohnehin aus einem Inline-Injektor zwischen Switchport und AP, nicht aus dem Switch.
    • ⚠️ Config-Konsequenz: ap1s Trunk liegt heute auf ether2 → wandert bei der Neuprovisionierung auf ether1. Und ether1 steht aktuell auf poe-out=auto-on (der hAP kann über denselben Port auch Strom abgeben) — beim Umbau auf „ether1 = versorgter Uplink" muss poe-out auf diesem Port off gesetzt werden, sonst speisen Injektor und AP gegeneinander.
  • 🔋 DC-USV → ausgelagert nach TP24 (2026-08-16). Inhalt unverändert gültig (MeanWell LRS-150-48 + DDR-120-48 + LiFePO4 20 Ah, ~€190, 48-V-Bus; die CRS326 haben passenden DC-Input) — steht ab jetzt dort, samt der offenen Entscheidungen (Mac mini/Inverter, Bus-Standort nach dem Core-Umzug, Batteriegröße, Shutdown-Signalisierung).
  • ✅ Standort-/Ablösungs-Mapping final (User 2026-07-22) — sauberes 1:1:
    Bereich heute wird abgelöst durch
    Technik UG (virtueller Bereich in der Diele, kein Keller) sw1 (ALLNET, 16 Ports) crs326-core (= neuer L3-Router)
    Technik OG sw2 (ES208G, 8 Ports) + ap2 + cudy2 crs326-branch
    Wohnzimmer Sideboard ap1 — (bleibt, wird reiner AP)
    Kapazität unkritisch: 16+8 belegte Ports heute gegen 2× 24 GbE + 2× SFP+ künftig. Vollständig in Nautobot gepflegt (alle 8 Devices haben einen Bereich).
  • 📻 cudy-Hardware (User 2026-07-22): beide sind Cudy WR3600E V1.0, Firmware 2.3.15-20250905-191956. cudy1 = Technik UG, am Core #11, aktiv als Übergangs-WLAN (elektrosmog.c, 10.40.10.246) — und Christophs aktueller WLAN-Pfad. cudy2 = Technik OG, außer Betrieb (Nautobot-Status Decommissioning), wird nicht reaktiviert.
  • ⚠️ Etappe 1 (Erstzugriff auf die CRS326) ist NICHT kritisch (User 2026-07-22): Werkszustand, Gerät steht griffbereit neben ihm → der IP-Konflikt auf 192.168.88.1 wird physisch/seriell aufgelöst, kein Lockout-Szenario.
  • ✅ Portplan-Widerspruch AUFGELÖST (User 2026-07-22): am Core ist #15 = ap1 und #11 = cudy1. Damit gilt die TP4-Variante (gi16 Arris/VLAN100 → gi15 Trunk → ap1 ether2); switch/cli/03-trunk-ap-ports.cli (gi1 = ap1 / gi2 = ap2) ist STALE — die Datei bezeichnet sich selbst als „provisorisch, 2026-06-09" und wurde vom Edge-Cutover am 2026-07-01 überholt. Nicht mehr als Quelle verwenden.
    • Zielport nach dem Tausch: ap1 wandert auf crs326-core Port 4 (User-Vorgabe). Gleichzeitig wandert ap1s Uplink geräteseitig von ether2 auf ether1 (PoE-Port, s.o.).
    • ⚠️ Nach wie vor NICHT vom Gerät gelesen: die restliche sw1-Belegung. switch/state-backups/sw1-running-config-20260716-*.txt sind 214-Byte-Fehlschläge (Incorrect User Name!!, toter claudecode-Login), switch/cli/running-PRE.txt ist Pre-TP1. gi16 (Arris) und gi8 (Trunk → sw2) stehen in Nautobot daher explizit als „UNVERIFIZIERT" markiert.
  • 🔴 Betriebliches Kernrisiko (Analyse 2026-07-22): der Core-Tausch ist KEIN Switch-Tausch, sondern ein Vollausfall. crs326-core ersetzt sw1 und wird gleichzeitig L3-Router — und an sw1 hängt auf #15 ap1, also Gateway, DHCP, DNS und der WAN-Trunk fürs gesamte Netz. Core-Tausch und L3-Umzug sind damit faktisch dasselbe Ereignis und brauchen ein geplantes Ausfallfenster. → Reihenfolge-Entscheid (User 2026-07-22): crs326-branch zuerst (gegen sw2, Technik OG), crs326-core in einer eigenen, späteren Session.
  • 🛟 Rettungspfade liegen günstig auf verschiedenen Switches: Christophs Notebook hängt per WLAN über cudy1 → Core #11; der kabelgebundene Plan B (Dock/Monitor Arbeitszimmer) hängt am sw2/Branch (P4). Beim Branch-Tausch stirbt also der Kabelweg, das WLAN überlebt. (Beim Core-Tausch stirbt beides — s. Kernrisiko oben.)
  • Hinweis: die A/B/C/D-Switch-Varianten in hw-refresh-requirements.md sind durch die CRS326-Beschaffung überholt (Doc = Recherche-Archiv + USV/Energie-Referenz).
  • Abh.: TP14 ✅ (IP-Migration). Verzahnt: TP20 (Platform-DB), TP21 (Nautobot GC), TP2.1 (sw2-Standalone-Recovery).

TP16 — CF-Tunnel: Round-Robin killen + reziprokes HA ▸ ✅ KERN UMGESETZT (2026-07-10) — ⚠️ HA aktuell degradiert (Live-Befund 2026-08-16) — docs/cf-tunnel-mapping.md §10, #1033

  • 🟠 BEFUND 2026-08-16 (beim Doku-Aufräumen live über die CF-API erhoben, kein Eingriff): das reziproke HA läuft nur noch auf einem von drei Tunneln.
    • bf-mm = 2 Connectoren (je 4 conns) ✅ · bf-nas = 1 Connector (3 conns) 🟠 · bf-pve = 1 Connector (4 conns) 🟠.
    • Bedeutung: die Dienste hinter bf-nas (gitea, grafana, pve-mcp, vw, ds-bf, nautobot) und bf-pve (pve, ccu, iobroker, z2m25, portainer, hass, ha-mcp) hängen jeweils an einem einzigen cloudflared-Prozess. Fällt dessen Host aus, sind sie weg — genau der Zustand, den TP16 beseitigt hat. Kein 530/Round-Robin-Problem (das bleibt strukturell gelöst), sondern Redundanzverlust.
    • Bekanntes Muster: identisch zum Incident vom 2026-07-16 (macmini-Connectoren flappten unter QUIC → Fix TUNNEL_TRANSPORT_PROTOCOL=http2). Diesmal sind die macmini-Connectoren oben (bf-mm hat 2), es fehlt je ein Gegenstück auf NAS/pve. Nächster Schritt (eigene Session): auf NAS und pve nachsehen, welcher der beiden cloudflared-Container nicht läuft, und ob es der http2-Konventionsfall ist.
  • 🧹 Nebenbefund beim selben Sweep — Alt-Tunnel mit VLAN1-Origins (alle down, 0 Connectoren, daher aktuell harmlos): openclaw (→ 10.40.41.147/.148/.139), coolify (→ 10.40.41.133), johnny2, TeilzeitCharlie. ⚠️ openclaw beansprucht n8n.emsenhuber.net, das produktiv auf bf-mm läuft — käme der Tunnel je wieder hoch, entstünde exakt das alte Round-Robin-Muster auf diesem Hostname. Empfehlung: die vier toten Tunnel löschen (user-gated, Löschen ist nicht umkehrbar) — gehört fachlich zu TP19 (VLAN1-Retirement: .41.133 war eines der „unidentifizierten VLAN1-Geräte" aus TP19 B1.2 — hiermit als coolify-Origin identifiziert).

✅ Umgesetzt (2026-07-10, eigene Session): reziprokes 3-Tunnel-HA live (bf-nas 70d6a4cf / bf-pve f7fdf3a6 / bf-mm 7a322261), 6 Connectoren (2/Host, je Fremdhost), 19 FQDNs Loop-Smoke 0×530, HA per Host-Ausfall-Sim bewiesen. Round-Robin-#1033 strukturell weg. As-Built = docs/cf-tunnel-mapping.md §10. Schritt 5 Access-Strang: vw bypass→geschützt via WARP — 🔴 GESTRICHEN (User-Constraint 2026-08-14, s. TP16.2). Verbleibend offen: hass/ha-mcp 400/403; gateway-dw (WG→404) klären.

  • Symptom (User): FQDNs über CF werden sporadisch / „sehr unsicher" aufgelöst = #1033-Muster zurück („jeden 3. Schuss eine Niete"). User hat alle 3 cloudflared-Images + Container aktualisiert/neu gestartet.

  • ✅ Root-Cause endgültig (User-Diagnose bestätigt): macvlan/ipvlan-Host-Isolation. Ein cloudflared-Connector ist blind für macvlan/ipvlan-Container auf seinem EIGENEN Host (Linux-Kernel-Regel), erreicht aber Fremdhost-L2 + co-lokierte Bridge-Container. Mehrere Connectoren auf EINEM Tunnel = geteilter Ingress + CF-Round-Robin → jede Origin, die auf dem Host eines Connectors liegt, fällt bei ~1/N aus. Strukturell, nicht durch „Origins fixen" heilbar. Bsp: omada .20.175/pve-mcp .20.173/gitea .20.157/grafana .20.109 = NAS-macvlan → vom NAS-Connector NICHT erreichbar.

  • Invariante: innerhalb EINES Tunnels ist kein Route→Connector-Pinning möglich (alle Connectoren teilen Ingress, CF round-robined). „Ausgewählte Routen für ausgewählte Connectoren" = getrennte Tunnel.

  • Ziel-Design (HA): reziproke Per-Host-Tunnel — jeder Host-Dienst wird von den Connectoren der ANDEREN Hosts bedient (nie vom eigenen):

    Tunnel (= Dienste eines Hosts) Connectoren auf HA
    pve-Dienste NAS + macmini 1 abdrehbar
    NAS-Dienste (omada/pve-mcp/gitea/grafana/…) pve + macmini 1 abdrehbar
    macmini-Dienste (.20.167) pve + NAS 1 abdrehbar

    3 Tunnel, jeder Host fährt 2 cloudflared → Host-Ausfall ⇒ jeder Tunnel ≥1 Connector = voll HA. Voraussetzung: jede Origin L2-erreichbar.

  • Ist-Inventory (live 2026-07-10, s. cf-tunnel-mapping.md §9): Ingress v119, 24 Hostnames, 2 live Connectoren. DOWN/stale: pbs .20.135, nginx-bf .41.123 (noch VLAN1!), hermes+hermes-geli .20.167. Zu klären (Host/Netz): vw .20.127/ccu .20.232/iobroker .20.107/z2m25 .20.106 — ggf. vw/ccu auf VLAN20-macvlan heben. gateway-dw (192.168.178.1) = WG-Sonderfall.

  • CF-Access-Strang (verzahnt, s. TP11 Track-B): hass-400 in der HA-iPhone-App = CF-Access davor (Companion-App kann Access-Login nicht) → Fix ohne Passthrough: iPhone in WARP enrollen (Device-Identity erfüllt Access; „Warp Login App" existiert bereits allow:christoph). ⚠️ vw = aktuell bypass (öffentlich!) → von bypass auf geschützt heben. 🔴 Überholt (2026-08-14): vw BLEIBT bypass — harter User-Constraint, Härtung stattdessen über TP16.2. „Passthrough-Hosts sind die kritischen" (User).

  • Umsetzungs-Reihenfolge (für die eigene Session): (1) Reachability-Matrix je Host (pve/NAS/macmini → jede Origin, read-only) BEVOR gruppiert wird; (2) Interim-Sofortfix: bf-Tunnel auf pve-only (pve erreicht alles außer gateway-dw) → 0×1033, reversibel; (3) reziprokes 3-Tunnel-HA bauen (CNAMEs re-pointen, 6 Connectoren, Token je Tunnel; NAS/macmini-Alt-Token entfernen); (4) tote Origins bereinigen; (5) Access-Strang (WARP + vw); (6) Loop-Gegentest.

  • Abh.: TP14.1/14.2 ✅. Verwandt: TP11 (CF-Tunnel-Track-B/C + Access), #1033. SSoT: docs/cf-tunnel-mapping.md. Memory project_cf_tunnel_1033.

  • ✅ Incident 2026-07-16 (User unterwegs, WARP „spinnt", keine BF-IPs mehr) ▸ Public-FQDN-Teil gefixt: macmini-Connectoren (cloudflared-bfnas/-bfpve) flappten live unter QUIC/UDP (Docker-Desktop-Bridge-NAT) → bf-nas/bf-pve von 2 auf 1 Connector degradiert (HA faktisch weg, nicht down). Fix: beide auf TUNNEL_TRANSPORT_PROTOCOL=http2 umgestellt (rollend, 0 Downtime), wieder voll HA. Konvention: macmini-Connectoren künftig immer http2. ICMP-Befund (getrennt, strukturell): TCP über die WARP-Route ging überall, ICMP nicht (fm 36 %/bf 100 %/dw 100 % Loss) — cloudflared ist QUIC-Proxy, kein WireGuard. Kurz als „bf-pve-Route wegen USV" notiert (Commit 1c1a676) → REVIDIERT (Begründung invertiert: bf-pve-Connectoren laufen auf NAS+macmini, nicht pve). Details: docs/cf-tunnel-mapping.md §13. → Richtungswechsel für Private Access = TP16.1.

TP16.1 — Private Access via native Cloudflare Mesh (WireGuard) auf crs326-core ▸ 🟢 GATE OFFEN, ausführungsreif (2026-08-16) — Scharfschalten = eigene Session — docs/tp16.1-warp-mesh-native-crs326.md

  • ✅ Gate erfüllt (2026-08-16): die Bedingung „gated bis TP21 den CRS326 abdeckt" ist eingelöst — crs326-core ist seit 2026-08-08 der produktive L3-Router und seit 2026-08-13 in Nautobot adoptiert (68/68 compliant). Runbook §3 neu gemessen, §4.3 von Platzhalter auf echte Werte gezogen.
  • 🧭 Erwartung geraderücken (wichtig, weil leicht zu verwechseln): das Mesh ersetzt die CF-Tunnel NICHT. TP16.1 löst ausschließlich den privaten Zugang (unterwegs → 10.40.0.0/16 per IP, inklusive ICMP) und tauscht dort den cloudflared-QUIC-Proxy gegen natives WireGuard auf dem Core. Die öffentlichen FQDNs bleiben auf dem 3-Tunnel-HA aus TP16. Beide Stränge laufen nebeneinander — die HA-Degradierung aus TP16 (2026-08-16) wird davon nicht geheilt und bleibt ein eigener offener Punkt.
  • 📌 Live-Befunde 2026-08-16 (read-only erhoben, kein Eingriff):
    • warp_connector = [], aber der GET antwortet (kein 403) → die Free-Plan-Frage entscheidet sich erst am POST (§4.0). CF_API_TOKEN ist active; ob er die separate Permission „Cloudflare One Connector: WARP" trägt, zeigt genau dieser POST — dort schlug es am 2026-07-17 mit 10000 fehl.
    • use_zt_virtual_ip: false → Unique-Device-IP muss weiterhin eingeschaltet werden. Gateway-Proxy TCP+UDP sind an.
    • Nur EIN Device-Profil (Default). Der „auf beiden Profilen"-Hinweis im Runbook meint also: Default jetzt, Mesh-Profil sobald es existiert — es gibt kein zweites, auf dem man etwas übersehen könnte.
    • Split-Tunnel-Exclude enthält unverändert 100.64.0.0/10 (verschluckt die Mesh-Overlay-Range) → der §4.1.4-Schritt bleibt nötig.
    • Die vnet-Route 10.40.0.0/16 (5546dcb6…) zeigt weiter auf bf-pve — der Fallback steht. ⚠️ Aber bf-pve fährt derzeit nur einen Connector, der private Pfad hängt also an einem einzelnen cloudflared-Prozess.
    • ✅ Die letzte inhaltliche Unbekannte ist weg: „Rückweg-Routing TBD" hat sich erledigt, weil der Core L3-Gateway aller VLANs ist — es braucht keine Zusatzroute. Und wg-hub (TP10) liegt auf demselben Gerät, ist also direktes Vorbild statt fernes Muster.
  • ✅ GOVERNANCE-AUSNAHME #4 (User, 2026-08-16): die WG-Zeilen für den Mesh-Bring-up auf crs326-core dürfen DIREKT gesetzt werden (MikroMCP plan_changes dryRun → apply_plan). Begründung — es ist kein Bequemlichkeits-, sondern ein struktureller Grund: TP22-Regel 1 schließt jede Sektion, die <<REDACTED>> enthält, generell vom GC-Push aus, und /interface wireguard ist genau so eine Sektion. Ein privater Schlüssel ist über Golden Config also gar nicht deploybar — die frühere Zeile „CRS326-Zeilen laufen regulär über Nautobot GC" trägt nicht mehr. Auflage (wie Ausnahme #2): Ist-Stand danach als intended config zurück nach Nautobot; vorher /system backup (Regel 3). Scope-Grenze: nur der Mesh-Bring-up auf crs326-core — nicht spätere WG-Änderungen, nicht andere Geräte.
  • 🔑 Damit die Ausnahme ein einmaliger Bootstrap bleibt — Secret-Handling-Design (Runbook §4.3): ein privater Schlüssel ist Geräte-Identität, keine Konfiguration (wie ein SSH-Hostkey). Regel: der private Teil entsteht am Gerät und verlässt es nie; nur der öffentliche geht in die SoT und wird in die Peer-Configs gerendert. RouterOS kann das nativ (/interface wireguard add erzeugt den Privkey selbst). Compliance bleibt grün, weil die ConfigReplace-Maske aus TP22 Schritt 0 byte-identisch zu der im Template ist. ⚠️ Vorbehalt: bei CF Mesh erzeugt CF das Schlüsselpaar — dann fällt die On-Device-Erzeugung weg, das Prinzip („nie in Git, nie in der intended config") hält trotzdem. Stufe 2 (nautobot-secrets-providers + Vault, Wert erst bei der Plan-Erzeugung einsetzen) lohnt bei 4 Geräten und einem Key nicht — eigener TP nach TP18/TP20.1.
  • 🧹 Sofort fälliger Modellierungs-Nachzug, unabhängig vom Mesh: die WG-Peers fm/dw stehen mit hartkodierten public-key="…" im Jinja-Template. Public Keys sind Daten, keine Templatezeilen — gehören nach Nautobot und von dort gerendert.
  • ⚠️ Für TP5 vormerken: sobald deny-by-default greift, braucht der Mesh-Verkehr explizit forward accept 100.96.0.0/12 ↔ 10.40.0.0/16 — heute passiert er nur, weil die forward-Kette ohne Drop endet.
  • ⏭️ Was die Scharfschalt-Session macht: POST warp_connector (Plan-Gate) → account-weite Prereqs (Unique Device IP, Split-Tunnel auf beiden Profilen) → wgcf-connector-Token → 4 WG-Zeilen auf dem Core → Route 5546dcb6… umhängen (vnet-Gotcha, Weg A/B) → Ping-Abnahme. Trifft alle Live-WARP-Geräte mit einem kurzen Reconnect → Zeitpunkt user-gated.
  • ✅ Update (User-Entscheid 2026-07-22): Ziel-Device für den WG-Mesh-Node ist jetzt der CRS326 Core-Switch statt ap1 — konsistent mit dem TP15-Endstand (kein RB5009) — ⚠️ die damalige Klammer „Router bleibt ap1/ap2 rein für L3/WLAN" ist seit dem Core-Cutover 2026-08-08 überholt: L3 liegt auf crs326-core, ap1/ap2 sind reine APs. Der Retarget auf den Core war damit im Ergebnis richtig, nur anders begründet als hier notiert und TP21s "ganzer Kabel-Fabric via RouterOS"-Modell (CRS326 ist ohnehin RouterOS-fähig und wird Nautobot-verwaltet). Reine Ziel-Doku-Änderung — das Scharfschalten bleibt weiterhin eine eigene, spätere Session und bleibt gated, bis TP21 den CRS326 als Device abdeckt. Keine neue Governance-Ausnahme: die bestehende ap1-Ausnahme (s. Ist-Stand-Zeile 31) gilt explizit nicht automatisch für den CRS326 — CRS326-Konfiguration soll regulär über Nautobot GC laufen, sobald TP21 so weit ist. Die CRS326 selbst sind aktuell noch Werkszustand (physisch bereits mit dem aktuellen Core verkabelt, aber die betreffende Uplink-Port bislang ungenutzt/down) — konkrete Management-IP/Interface-Zuordnung für den CRS326 als L3/WG-Endpoint ist noch nicht getroffen (TBD nach TP15-Bring-up).
  • Trennung: Public FQDNs bleiben auf cloudflared-3-Tunnel-HA (TP16); Private Access (unterwegs → 10.40.0.0/16 per IP inkl. ICMP) wandert vom cloudflared-QUIC-Proxy auf einen nativen WireGuard-Mesh-Node auf dem CRS326 Core-Switch.
  • Warum CRS326-nativ: Cloudflare Mesh spricht WireGuard, RouterOS kann natives WireGuard (TP10-Hub auf ap1 ist einer, Muster überträgt sich) → Mesh-Node = WG-Peer zu CF-PoP, kein Container/TUN/LXC/neue HW. Verlagert die Rolle vom Router (ap1, der weiterhin Public-Edge + WLAN-Anker bleibt) auf den Core-Switch, der als zentraler Kabel-Fabric-Knoten ohnehin alle VLANs sieht. RouterOS-Container-Weg bleibt tot (kein /dev/net/tun-Passthrough auf JEDER Router/Switch-HW) — daher weiterhin natives WireGuard, kein Container-Ansatz.
  • Verworfen: WG-Roadwarrior (User will keine Extra-App/Profil je Gerät), alle Helper-Host-ZT-Anbieter (SPOF-Verletzung).
  • Warum heute nur Konzept: TCP-Pfad funktioniert (nur ICMP-Komfort fehlt), und die CF-Prerequisites sind account-weit (Unique-Device-IP, Split-Tunnel-Exclude, Device-Profile) → treffen Live-WARP-Geräte (1× kurzer Reconnect) → nicht unter Zeitdruck. Zusätzlich jetzt gated durch den Retarget: CRS326-Bring-up (Management-IP, VLAN-Filtering) muss erst stehen.
  • Nächste Session (nach CRS326-Bring-up): Plan-Probe POST warp_connector (Free-Plan-Gate; sonst LXC-Fallback) → account-weite Prereqs → wgcf-connector-Token→WG-Config → 4 WG-Zeilen auf dem CRS326 → Subnetz-Route + vnet-Route 5546dcb6… von bf-pve umhängen (Rollback trivial) → Verifikation (Ping verlustfrei). Team-Domain emsenhuber.cloudflareaccess.com. Governance „Config nur via Nautobot" → CRS326-Zeilen regulär via TP21/GC, sobald das Device dort adoptiert ist.
  • ✅ Recherche-Härtung 2026-07-17 (Primärquellen animmouse + offizielle CF-Docs + OpenAPI-Spec, alles verifiziert, gilt unabhängig vom Zielgerät): (1) Produkt heißt seit 2026-04-14 offiziell Cloudflare Mesh (API-Pfade bleiben warp_connector, im Spec bestätigt inkl. Token- und sogar HA-Endpoints); (2) ICMP-Proxy ist bereits AN (User-Korrektur: fm-Ping 10.101.0.0/24 beweist es — kein To-Do; mein „fehlt" war ein Fehlschluss aus dem API-Spec, das Flag ist Dashboard-only); (3) Split-Tunnel-Exclude auf beiden Device-Profilen (Default + Mesh); (4) vnet-Gotcha beim Route-Umzug („virtual networks only for cloudflared") → Weg A PATCH, Weg B DELETE+POST dokumentiert; (5) v4-only-Entscheid (Mesh-IPv6-site-to-site laut Guide broken, NAT66-Gefrickel nicht nötig); (6) keepalive 1m statt 25s ok. Alles in docs/tp16.1-warp-mesh-native-crs326.md §2/§4 eingearbeitet (Config-Block dort als Platzhalter markiert, IP/Interface TBD).
  • Abh.: TP16 ✅ (Public-Teil), TP10 ✅ (WG-Grundmuster, jetzt als Vorbild statt Direktziel), TP15 (CRS326-Bring-up), TP21 (Nautobot-Adoption des CRS326, Voraussetzung fürs Scharfschalten). SSoT: docs/tp16.1-warp-mesh-native-crs326.md, docs/cf-tunnel-mapping.md §14.

TP16.2 — Härtung der öffentlich erreichbaren Dienste (bypass-Strang) ▸ 📋 KONZEPT (2026-08-14), Umsetzung offen

  • 🔴 HARTER CONSTRAINT (User 2026-08-14): vw wird NICHT WARP-/Access-geschützt. Gilt dauerhaft, nicht „vorerst". Begründung (technisch belastbar, nicht Bequemlichkeit): die Bitwarden-Clients (Browser-Extension, Mobile, Desktop) können den interaktiven Access-Login nicht durchführen — dasselbe Muster, das die HA-Companion-App auf 400 laufen lässt. Ein Passwortmanager hinter einem Client, den man im Notfall (fremdes Gerät, Neuinstallation, Handyverlust) nicht installieren kann, ist ein Aussperr-Risiko gegen den eigenen Schlüsselbund — und damit gefährlicher als das, wovor die Maßnahme schützen soll. Die frühere TP16-Schritt-5-Absicht „vw von bypass auf geschützt heben" ist damit gestrichen.
  • Ziel stattdessen: Schutz erhöhen, ohne Clients zu brechen — gilt für alle bypass-Dienste (vw, hass, gitea).
  • ✅ Schicht 0 — bereits vorhanden, nicht unterschätzen: keine offenen Ports, alles ausschließlich über CF-Tunnel (3-Tunnel-HA, TP16), TLS terminiert an der Edge, DDoS-Schutz. Der Origin ist nicht direkt erreichbar. Das ist bereits mehr als bei den meisten Selfhostern.
  • ✅ Schicht 1 — chirurgisch: Access nur auf die Pfade, die kein Client anfasst. CF Access wertet die spezifischere Application zuerst → man kann eine zweite Access-App auf vw.emsenhuber.net/admin* legen (echte Policy) und den Host im Übrigen auf bypass lassen. Das Admin-Panel wird ausschließlich per Browser bedient — dort funktioniert der Access-Login einwandfrei, während /api, /identity und /notifications (die Client-Pfade) unberührt bleiben. Kein Client bricht, das wertvollste Ziel ist weg.
    • Alternativ noch härter: Admin-Panel ganz aus (ADMIN_TOKEN leer lassen) und nur bei Bedarf temporär aktivieren.
    • Analog für gitea: /admin* + Registrierung path-scoped schützen (Git-über-HTTPS mit Token bleibt offen).
  • ✅ Schicht 2 — App-eigene Bremsen (kostenlos, sofort): Vaultwarden hat Rate-Limits per Default an (LOGIN_RATELIMIT_SECONDS=60, LOGIN_RATELIMIT_MAX_BURST=10, dazu ADMIN_RATELIMIT_*) → Werte prüfen/verschärfen; SIGNUPS_ALLOWED=false, INVITATIONS_ALLOWED=false, SHOW_PASSWORD_HINT=false, 2FA erzwingen. HA hat ip_ban_enabled + login_attempts_threshold. Gitea: Registrierung aus.
  • ✅ Schicht 3 — Edge-Regeln (CF), wirkt für alle Dienste gemeinsam: WAF-Custom-Rules + Rate Limiting gezielt auf die Login-Endpunkte (/identity/connect/token bei vw, /auth/login bei HA, /user/login bei Gitea); optional Geo-/ASN-Filter. ⚠️ Free-Plan-Kontingente vorher im Dashboard prüfen (nicht belegt).
  • ✅ Schicht 4 — ein Mechanismus für alle: CrowdSec (oder fail2ban) mit Cloudflare-Bouncer. Bans landen dann an der Edge statt erst am Origin. Vaultwarden hat ein offizielles Fail2Ban-Setup-Wiki (Log-Muster für Login und Admin-Panel).
  • 🔴 Voraussetzung für Schicht 2–4, und der eigentliche Knackpunkt: die echte Client-IP muss am Origin ankommen. Hinter dem CF-Tunnel sieht die Anwendung sonst nur die IP des cloudflared-Containers → jede IP-Sperre bannt den Tunnel statt den Angreifer = Selbstaussperrung aller Nutzer, und Rate-Limits greifen ins Leere. Genau dieser Defekt ist bereits offen dokumentiert: HAs trusted_proxies fehlen .20.10/.20.167 (TP16, hass-400-Strang). ➡️ X-Forwarded-For/CF-Connecting-IP sauber durchreichen ist Schritt 1 dieses TPs, alles andere baut darauf auf.
  • Reihenfolge: (1) Client-IP-Weitergabe fixen (inkl. HA trusted_proxies) → (2) /admin-Pfade path-scoped schützen → (3) App-Limits + 2FA → (4) Edge-Rules → (5) CrowdSec/fail2ban mit CF-Bouncer.
  • Abh.: TP16 ✅ (Tunnel-HA). Verwandt: TP20.1 (vw wandert auf Platform-Postgres — DB-HA ≠ Access, getrennt halten), TP23 (dessen WARP-Managed-Network-Idee ist durch diesen Constraint entlastet: vw verlangt keine Device-Identity, also kollidiert „WARP zuhause aus" nicht mehr mit dem Access-Strang).

TP17 — Shelly-Übersiedelung VLAN1 → VLAN30 (IOT) + shelly-mcp ▸ ✅ KOMPLETT (User-Bestätigt 2026-08-13): alle Shellys auf VLAN30 — Geräte-PW (TP17.2) weiterhin bewusst zurückgestellt — mcp/shelly/, Runbook docs/tp17-shelly-go-runbook.md

  • ✅ Abschluss 2026-08-13 (User-Bestätigt): auch die zuletzt offenen saisonalen Shellys (s1/s2/s3/s12) sind inzwischen umgeklemmt — alle Shellys auf dirtyvader-iot/VLAN30, kein VLAN1-Shelly-Rest mehr. Migration-Teil von TP17 damit vollständig abgeschlossen.
  • ✅ GO durchgezogen 2026-07-13 (16 Shellys VLAN1→VLAN30, 0 Fehler): Pilot s26 + Welle A (s4–s9) + Welle B (s10/11/13/15/17/19/20/21/25) → alle auf dirtyvader-iot/10.40.30.<octet> verifiziert (SSID+IP+rssi). Mit den 6 Vorab-Migrierten (s14/16/18/22/23/24) = 22/26 auf VLAN30; VLAN1-Sweep .180-250 = 0 Shellys. Restliche 4 saisonal ausgesteckte (s1/s2/s3/s12) inzwischen ebenfalls migriert (s.o.).
  • 🔧 Anker-Verfahren (User-Idee, statt sta1-Backup): je Gerät vor dem sta-Write die geräteeigene AP-Funktion aktiviert (WiFi.SetConfig ap.enable — Firmware verlangt WPA2, kein offenes AP → PSK=IOT-PSK, live/kein Reboot) = Notanker, falls VLAN30-Join scheitert (Gerät bleibt über Shelly*-<mac>-AP erreichbar, kein physischer Reset). Nach Verify alle 22 AP-Anker wieder deanchort. Runner: mcp/shelly/migrate.py (migrate/deanchor/auth/table, Dry-Run default, stoppt bei erstem Fehler, reused discover.py).
  • ✅ HA-Cross-Check nach Batch: einzige unavailable Shelly-Entities = s12 (saisonal aus) — alle 22 migrierten reconnecten via mDNS/zeroconf (TP7-Repeater vlan30↔vlan20-HA) auf neuer .30.x-IP, Entity-IDs unverändert (keyt per MAC). Inventar inventory/bf.csv .41→.30 nachgezogen (22 Zeilen, Section→IOT).
  • ⚠️ Coverage-Flag: s4 kueche_mikrowelle verbindet mit rssi −93 (Metallgehäuse), hat aber verifiziert — grenzwertig, im Auge behalten. s7 Tiefkühl −85. Rest gut (−26…−81).
  • ✅ Eco-Mode-Policy (2026-07-13, PROVISORISCH): eco senkt CPU-Takt + WiFi-TX-Leistung → eco AN für RSSI ≥ −80 (Energie-Win, TX irrelevant), eco AUS für < −80 (Uplink-Headroom): s4/−91, s7/−88, s17/−81, s18/−79…−82, s19/−86 → 17× ON / 5× OFF, live via Sys.SetConfig device.eco_mode (kein Reboot). s4 (Küchen-Sockel/Metall, −91, fix) bewusst eco-OFF = schwächster Link. ⏳ 2. Eval nötig nach TP3.3 (WLAN-Ausrichtung/Aufbau ändert RSSI → Grenzfälle neu bewerten; User 2026-07-13).
  • ✅ HA-Referenz http vs https geklärt (2026-07-13): HA speichert configuration_url für alle 22 Shellys bereits http://10.40.30.x:80 (Integration korrekt + neue IPs übernommen). Das „https" ist client-seitig (Mixed-Content-Upgrade, wenn HA selbst über https/Tunnel geöffnet) — kein HA-Fix möglich/nötig, Shelly-SSL bewusst NICHT (CPU/Energie, IoT-Netz). ⚠️ Rand-Fund: 1 HA-Shelly-Device zeigt auf 10.95.10.206 (Fremd-Subnetz, nicht BF) → separat prüfen.
  • ⏳ Rest (nicht mehr Migration, s. TP17.2/TP19): (1) einheitliches Geräte-PW → zurückgestellt als #TP17.2 (Kosmetik). (2) Nicht-Shelly-Mover + cudy-Teardown + VLAN1-Client-Retirement → läuft in TP19. (3) DNS/ip-plan Restfeinschliff (mit TP19).

TP17.2 — einheitliches Geräte-Passwort (Shelly SetAuth) ▸ ⏸️ ZURÜCKGESTELLT (Kosmetik, User 2026-07-13)

  • Was: je Gerät Shelly.SetAuth (User admin, realm-individueller ha1 = sha256("admin::"), einheitliches Klartext-PW) via mcp/shelly/migrate.py auth --go. SHELLY_DEVICE_PW liegt in mcp/shelly/.env (gesetzt, len 13). Runner + Digest-Auth-Fallback in _rpc fertig.
  • Warum zurückgestellt: rein kosmetisch/Härtung; bricht die HA-Shelly-Integration, bis pro Config-Entry (22×) das Credential neu eingetragen ist → disruptiv, kein Mehrwert im abgeschotteten IOT-VLAN. Erst nach dem Netz-Teardown (TP19) neu bewerten.
  • Ziel: ~26 Shellys (aktuell untagged VLAN1 10.40.41.2xx, weil auf alt-WLAN cudy, s. TP3.1/project_vlan1_dhcp_incident) auf SSID dirtyvader-iot (VLAN30, w2-iot, WPA2/WPA3-mixed, PSK 74c9L283$669) umklemmen → DHCP dhcp-v30/pool-v30 (live). Kein netzseitiger Move möglich (anderes WLAN) → per-Gerät WiFi-Reconfig.
  • User-Entscheide (2026-07-11): (1) volles shelly-mcp bauen (reusable, nicht nur Skript); (2) Pilot 2–3, dann Rest; (3) DHCP-Reservierungen je MAC in VLAN30 (stabile IPs für HA/ioBroker/DNS).
  • ✅ Recon durch (mcp/shelly/discover.py, read-only /shelly-Sweep): 16 live, alle Gen3/Gen4 (uniforme RPC-API WiFi.SetConfig, auth=off — kein Gen1-Sonderfall). 10 „offline" = weihnachten_* (s1/s2/s3, saisonal ausgesteckt Juli) + s12/s14/s16/s18/s22/s23 (aus/DHCP-drift). ⚠️ Inventar-Fix: s6 (kueche_kuehlschrank, .213) MAC falsch erfasst (0c:72:74:36:90:9e) → real d0:cf:13:d8:18:fc. s24 hat gar keine MAC.
  • Mechanismus je Gerät: Gen2+ RPC WiFi.SetConfig {sta:{ssid,pass,enable}} → Reboot → landet .30.x. ⚠️ Risiken: falsches PSK → AP-Mode (physischer Reset); Gerät kurz offline; HA/ioBroker-Refs brechen bis re-pointed; WPA3-mixed ggf. zickig auf alter FW; ap1 2,4-GHz-Master wifi2 running:false → Coverage v.a. via ap2 prüfen.
  • ✅ Live-Abgleich 2026-07-11 (go-ready): (a) shelly-mcp (server.py) fertig — discover/get/set_sta/verify/reservation_plan, PSK in .env gesetzt (noch nicht als MCP registriert, optional). (b) Alle VLAN30-DHCP-Reservierungen liegen (.30.2xx, Oktett erhalten, außerhalb Pool .100–.200); s6-Reservierung .30.213 MAC gefixt (falsch 0C:72…9E → real D0:CF:13:D8:18:FC). (c) IOT-SSID live verifiziert: ap1 w2-iot running:true, 8 Clients −26…−68 dBm (ap1-wifi2-„running:false" ist kosmetisch). (d) Migration bereits ~5/26 durch: s14/16/18/22/23 seit ~2,5 d stabil auf reservierten .30.221/.223/.225/.202/.203 (+ .30.204 unbekannt, evtl. s24) → Pilot faktisch bestanden.
  • ✅ HA-Cross-Check 2026-07-11 (alle IPv4-Geräte in HA): Shellys deckungsgleich; .30.204 = Shelly „Bad/Spiegelschrank" (Plus RGBW PM, 14:33:5c:d8:2d:7c) = vmtl. s24 → 6 statt 5 schon migriert. Netzweite Aufstellung + VLAN1-Leerstand-Analyse im Runbook: nach TP17 + pve-Host→VLAN20 + Christophs Endgeräte→VLAN10 ist VLAN1 endgeräte-frei; es bleiben nur Switch-/AP-Mgmt (sw1/sw2/ap2 → VLAN99-Cutover, eigener Schritt) + 1 unidentifiziertes Gerät .41.246 (morgen klären). Nicht-Shelly-VLAN30-Mover (User-bestätigt): ✅ SwitchBot floor-lamp .41.184 + ✅ Twinkly-Girlande .30.228 (beide 2026-07-16 übersiedelt, s. TP19) + Creality Ender (fehlt noch, off). Withings schon übersiedelt; Tasmota existiert nicht mehr (kein aktives Gerät → stale HA-Eintrag). EOS-Smartwatches/eBook-Reader später + von Christoph selbst (eBook eher VLAN50).
  • Scope-Erweiterung (User 2026-07-11): je Gerät 3 Änderungen — WLAN→dirtyvader-iot + IP (.41.2xx→.30.2xx) + einheitliches Geräte-Passwort (Shelly.SetAuth, User admin, realm-individueller ha1 → Tool set_auth in server.py nachrüsten). PW entschieden: eigenes Shelly-Admin-PW (getrennt vom WLAN-PSK) → SHELLY_DEVICE_PW in .env (Christoph liefert Wert). HA-Entities unverändert (keyt per MAC). HA-Entities bleiben unverändert (HA keyt per MAC). Projektion TP17.1 (experimentell, nachrangig): Gen4 (~11 St.) auf Thread/Matter flashen (Firmware-Verfügbarkeit + Thread-Border-Router offen) — Konflikt mit „Entities unverändert" (Matter = neue Entities) → erst WiFi-Migration stabilisieren.
  • ✅ Reservierungs-/Pool-Konvention (User 2026-07-11): alle Shellys bekommen statische IP (10.40.30.<octet>, gw/dns .30.1) + in-pool DHCP-Reservierung als Backup. pool-v30 auf .100-.250 erweitert (Reservierungen jetzt in-pool); set_sta auf static=true umgestellt; s24-Inventar + .30.204-Reservierung angelegt. Vollständiger HA-MAC-Scan: 2 verpasste Media-Geräte Apple TV 4K + Philips TV (aus, → VLAN40) — vor cudy-Abschuss VLAN verifizieren; Apple TV = vmtl. Thread-Border-Router (TP17.1). .41.246/.247 = cudy1/cudy2 (Teardown-Ziele). Tasmota bestätigt tot.
  • Offene Schritte (morgen, „nur noch go", Runbook docs/tp17-shelly-go-runbook.md): die 16 verbliebenen VLAN1-Shellys (s4–s11, s13, s15, s17, s19–s21, s25, s26 auf 10.40.41.2xx) per WiFi.SetConfig→dirtyvader-iot umklemmen (Batch, je Gerät .30.x verifizieren) → Geräte-PW setzen → HA/ioBroker-Re-Point → SwitchBot/Tasmota/3. Nicht-Shelly → saisonale s1/s2/s3/s12 beim nächsten Einstecken → cudy-Alt-WLAN abbauen → DNS/Inventar/ip-plan nachziehen.
  • Abh.: SSID/VLAN30 (TP3/TP4 ✅), DHCP-v30 ✅. Verwandt: project_vlan1_dhcp_incident (cudy-Rest-Leak), TP5 (IOT-Firewall danach). Memory project_shelly_migration_tp17.

TP18 — Souveräner Objektspeicher (Garage, Multi-Site, S3-Cold-Tier) ▸ 🔜 KONZEPT (2026-07-12)

  • Auslöser: 9-TB-Syno-Backup-Volume zu 2/3 voll; reine Daten ~3 TB, Tendenz steigend, „schlagartiger" Bedarf nicht ausgeschlossen. „Wenn skalieren, dann jetzt Syno-Nachfolger mitdenken."
  • Kriterien (User-priorisiert): (1) keine Fremd-Abhängigkeit / kein Cloud-Lock-in (Cloud-Opex abschreckend); (2) Multi-Site BF/FM/DW; (3) HA (Standort-Verlust überlebbar); (4) billig/teuer trennen — Variante „kalt = Objektspeicher, heiß = lokal" genügt (kein Hot/Cold innerhalb des Stores nötig); (5) horizontal + vertikal skalierbar; (6) S3-API als Branchenstandard, den jedes Backup-Tool spricht.
  • Entscheid Engine = Garage (Deuxfleurs, Rust): einziger Kandidat, der geo-verteilt über offenes Internet/WAN latenztolerant ist (CRDT-Metadaten, eventually consistent) → passt aufs 3-Keller-Modell BF/FM/DW. Ceph verworfen (will Sub-ms-LAN, Single-Cluster-über-WAN = Anti-Pattern; zu schwer, ~1 GB RAM/TB). MinIO (AGPL + Console/Feature-Abbau 2025) und SeaweedFS (kann Hot/Cold + Erasure-Coding, aber komplexer, Multi-Site async) = Fallback, falls später feingranulares Tiering / EC-Effizienz bei zweistelligen TB dominant wird.
  • Entscheid Disk-Layout = JBOD, KEIN RAID. Redundanz gehört in eine Schicht: Garage RF=3 über 3 Sites ist die Redundanz. RAID 1 darunter = Doppelt-Bezahlen (3 Sites × 2 Mirror = 6× roh für 1× nutzbar, absurd). RAID 0 verworfen (1 Platte tot = ganzer Node-Rebuild über WAN). JBOD = jede Platte eigenes Garage-Data-Dir (v1 Multi-HDD): Failure-Domain = 1 Platte, Rebuild nur deren Anteil, kein Kapazitätsverlust. Merksatz: distributed store = „just a bunch of disks", Redundanz ausschließlich über die Sites.
  • Was man dafür bezahlt: Plattentod triggert Re-Replikation über WAN (durch Upload-BW gedeckelt). Bei ~3 TB/Site tolerabel; wenn Bedarf „schlagartig" auf zweistellige TB springt → mehr/kleinere Platten (Blast-Radius) oder 4. Node, ggf. Wechsel auf SeaweedFS-EC.
  • Dimensionierung (Planungsanker 10 TB nutzbar; heute 3 TB unique / 6 TB Backup-used): RF=3 → 30 TB roh gesamt = 1 volle Kopie ~10 TB/Site (provisionieren ~16 TB roh/Site: 2 Platten = halber Blast-Radius + Headroom). Linear skalierend.
  • Kostenmodell (Annahmen: HDD neu CMR ~€18/TB · recert ~€10/TB · Strom €0,25/kWh · Lean-Node Mini-PC/pve 12 W + 2 HDD ~20–28 W):
    • Capex kumulativ (3 Sites): Platten €540 (recert) – €900 (neu) + Nodes €400 (BF=pve €0; FM/DW je Mini-PC €200) = **€600–1.300**. Vergleich: ein resilienter Syno-Nachfolger für 10 TB = €1.000–1.600 capex ohne Geo-HA → Garage-3-Node gleich teuer/billiger mit HA.
    • Opex kumulativ: ~€135–185/J Strom — aber getrennt gerechnet: FM/DW laufen über fremde Zähler, auf Christophs Zähler landet nur der BF-Node ≈ €50–60/J (Gegenleistung: er hostet deren Kopie, reziprok wie TP16).
    • JBOD-vs-RAID1-Ersparnis: Strom bloß €9–22/J (Christophs Zähler) → kein Entscheidungsgrund; der Hebel ist Capex ~€900 (nicht gekaufte Spiegelplatten) + architektonische Korrektheit. (Reicht nicht für ein BW-Upgrade €120–240/J, taugt aber als Seed für HW-Rückstellungen.)
  • Konzeptioneller Bruch (Syno-Nachfolger): Kein großes resilientes NAS mehr nötig — die kollektive 3-Site-Redundanz ist die Garantie. Syno-Rolle splittet: Hot/SMB/VM-Disks bleiben lokal (Syno oder ZFS auf pve, klein/schnell); Backup/Cold wandert in Garage über BF/FM/DW → das 9-TB-Volume wird aufgelöst statt ersetzt (aus 1 SPOF werden 3 geo-redundante Kopien). Backup-Tool (restic/Kopia) sprechen S3 nativ.
  • Impacts auf Bestand (Kurz): (a) Storage-Redundanz-Philosophie kippt — weg von SHR/RAID pro NAS, hin zu 3× JBOD; (b) Backup-Kette Hyper-Backup/SMB → S3-Bucket umstellen; (c) Container-Host-System (TP14) kriegt Garage als LXC/Container je Site, aber auf rohen Platten, nicht auf bestehenden SHR-Volumes; (d) Upload-BW wird Durchsatz-/Rebuild-Limit; (e) HW-Planung verzahnt mit TP15 (Node-HW, pve-Standort) und TP24 (Stromversorgung je Site), FM/DW brauchen je 1 Node.
  • Startpfad (inkrementell/reversibel): ① BF zuerst — Garage-Container auf pve, 2 HDDs JBOD, Backups lokal auf Bucket (sofort Nutzen, noch keine Redundanz). ② FM/DW nachziehen (Mini-PC + 2 Platten), RF→3, ab jetzt geo-redundant+HA. ③ Syno entlasten (Backup-Last abgeben).
  • Offen (Modell scharfstellen): realer Strompreis; ob FM/DW eigene Nodes stellen oder Christoph HW hinstellt; Backup-Tool-Wahl (restic vs Kopia); ob heißes Zeug künftig auf ZFS/pve statt Syno.
  • Abh.: TP10 (WG-Multi-Site = Transport, Upload-BW-Constraint), TP14 (Container-Hosts), TP15 (Node-HW) / TP24 (Stromversorgung), Muster von TP16 (reziprokes Per-Host-HA). Kein Blocker für laufende TPs. SSoT dieser Session.

TP19 — cudy-Teardown + VLAN1-Client-Retirement ▸ ⏳ WEIT FORTGESCHRITTEN — VLAN1 ist client-frei, es geht nur noch um Aufräumen (Live-Sweep 2026-08-16) — docs/tp19-vlan1-cudy-teardown-runbook.md

  • ✅ Live-Nachprüfung 2026-08-16 (MikroMCP + CF-API, reine Reads):
    • cudy1 ist tot, nicht nur „offline gemeldet": core ether11 steht auf running=false, letzter Link-Down 2026-08-08 19:33, seither kein Link. Die Formulierung „aktiver VLAN1-Leak" ist damit endgültig hinfällig — der Dual-DHCP-Race hat keine Quelle mehr. Offen bleibt nur der physische Ausbau + Inventar/Doku.
    • crs326-core hat keine VLAN1-IP mehr: /ip/address listet nur noch die 7 SVIs + die WAN-DHCP-Adresse — die Bring-up-Altlast 10.40.41.211 ist weg. Damit fällt einer der 5 Compliance-Drifts aus TP22 raus, sobald wieder Backup → Intended → Compliance gelaufen ist (die Compliance vergleicht gegen das letzte Backup, nicht gegen das Gerät — s. TP22-Falle).
    • ✅ crs326-branch-Interim ERLEDIGT (2026-08-16): 10.40.10.2 + SVI vlan10 über einen GC-ConfigPlan entfernt (s. TP22). Mgmt läuft über .99.2; /interface bridge vlan blieb byte-identisch, es gibt also weder einen Traffic- noch einen Drift-Effekt.
    • .41.133 identifiziert (war TP19 B1.2 „unidentifiziertes VLAN1-Gerät"): es ist das coolify-Origin aus dem gleichnamigen, seit Monaten toten CF-Tunnel. Ebenso .41.147/.148/.139 aus dem toten openclaw-Tunnel. Keine aktiven Geräte — Karteileichen in der Cloudflare-Config, nicht im Netz. Aufräumen = die 4 toten Tunnel löschen (user-gated, s. TP16-Nebenbefund).
  • ⚠️ Eskaliert 2026-07-15 (historisch — durch den WLAN-Rebuild 2026-08-08 gegenstandslos): der unter TP3.1 dokumentierte VLAN1-Leak tritt jetzt auch auf ap1/dirtyvader-main auf — die ap-seitige Interim-Härtung (TP3.1) mildert das Symptom, die endgültige Lösung ist hier, TP19: VLAN1 physisch weg = kein Leck mehr möglich. Das erhöht die Dringlichkeit dieses TPs, verschiebt aber keinen Scope hierher.
  • 🟢 cudy-Stand (User 2026-07-16): cudy1 (.41.246) auf 10.40.10.246 umgehängt — bewusst kein Abbau-Ziel mehr, dient als Standalone-WLAN-Sender während der Umstellungsphase (nicht Teil des Teardowns, extern vom User selbst umkonfiguriert). cudy2 (.41.247) wird nicht mehr reaktiviert — faktisch tot, ARP-Sweep bestätigt keine Präsenz mehr auf VLAN1. B2.2 („cudy1/cudy2 stromlos/Reset/ausbauen") ist damit erledigt/gegenstandslos (cudy2 aus, cudy1 schon migriert statt abgebaut).
  • 🟢 Live-Sweep 2026-07-16 (ap1, vor geplantem Teardown): VLAN1 ist bereits viel weiter zurückgebaut als dokumentiert:
    • defconf/pool-v1-DHCP-Server ist bereits disabled=yes (Export bestätigt) — B2.4 („defconf zurückziehen") ist de facto schon erledigt, Ursprung/Zeitpunkt dieser Änderung nicht aus dieser Session bekannt (nicht in dieser Doku protokolliert).
    • ARP auf bridge (VLAN1): nur noch 2 „reachable"-Hosts — sw1 (.41.11, by design, bleibt bis MGMT-VLAN99) und pve-Interim-Fallback (.41.100) (B1.5-Aufräumziel, noch offen — Host-seitige Änderung auf pve, nicht Router). Alle anderen ~20 ARP-Einträge sind stale/„failed" (Cache-Reste längst migrierter Hosts wie macmini/.20.167, NAS/.20.10, grafana/.20.109 etc.), keine aktive Kommunikation mehr.
    • B1.1 MacBook (Claude-Sandbox-Client) bereits sicher migriert: aktive DHCP-Leases nur noch auf dhcp-v20 (10.40.20.101) und dhcp-v10 (10.40.10.187), keine defconf-Lease mehr. ⚠️ Dual-Homed über 2 VLANs gleichzeitig (project_dual_homing_slowness_incident-Muster) — funktional ok, aber im Auge behalten.
    • B1.2 unidentifizierte Geräte .41.133/.41.198: kein ARP-Eintrag mehr vorhanden — entweder migriert oder abgeklungen, gelten für den Teardown als nicht mehr blockierend.
    • ⚠️ B1.3 (Media→VLAN40) und B1.4 (saisonale Shellys) nicht in diesem Sweep verifiziert — weiterhin offen, kurz vor dem finalen Cutover gegenchecken.
  • Backup-Stand vor Cutover (2026-07-16): nur ap1 gesichert (Binary bf-ap1-pre-vlan1-teardown-20260716.backup auf dem Router + lokaler Export routeros/state-backups/ap1-export-20260716-pre-vlan1-teardown.rsc). ap2 nicht gesichert, aber unkritisch: bewusst ausgesteckt (User-bestätigt 2026-07-16, kein Ausfall) — kein Backup nötig, solange es aus bleibt. sw1 nicht per Telnet gesichert (claudecode-Login 2× deterministisch abgelehnt), aber kein Blocker: User macht sw1-Änderungen ohnehin direkt im UI/CLI selbst (effizienter als über den Claude-Runner, gemeinsame Entscheidung) — Telnet-Automation für sw1 ist hier kein Muss.
  • Ziel (Kapstein der Segmentierung, s. Zeile 27): altes cudy-WLAN weg + VLAN1 als Client-Netz stilllegen (defconf/pool-v1-Client-DHCP zurückziehen, ✅ bereits disabled). Nicht VLAN1 komplett: Switch-/AP-Mgmt (sw1 .11/sw2 .12/ap2 .2) bleiben by design in VLAN1 (statisch, kein DHCP nötig) bis zum separaten MGMT-VLAN99-Cutover (eigener TP). cudy1/cudy2 = .41.246/.247 (Teardown-Ziele) — cudy1 migriert auf .10.246 (Reuse statt Abbau), cudy2 tot/nicht reaktiviert (s.o.).
  • 🎯 User-Gate (Update 2026-07-14): Shelly ist wieder da (kein Blocker mehr) — SwitchBot ist KEIN Prereq mehr, funktioniert laut User erst NACH VLAN1-Retirement (Reihenfolge ggü. 2026-07-13 umgedreht) → wandert in die Teardown-Nachlese. TP19 damit direkt startbereit. Twinkly nebenher (saisonal).
    • Verlorene Shelly — IDENTIFIZIERT via HA 2026-07-13 (User-Hinweis „du siehst ihn im HA"): HA-Device shellyplugsg3-e4b063e41ab8, Modell = Shelly Plug S Gen3, MAC e4:b0:63:e4:1a:b8, HA-device_id 7a21e8f3349e6660f8b35974fc989a7b. NICHT im 26er-Inventar (Spare/Küchen-Steckdose, die Christoph beim Einrichten verbockt hat; er nannte sie „küche s20", aber s20=98:a3:16:7c:5f:ec läuft sauber auf .30.206 — Verwechslung). KEIN AP-Mode, nicht tot: assoziiert am Altnetz elektrosmog.c (cudy → untagged VLAN1-L2), aber mit bogus-IP 10.95.10.206 (Fremd-Subnetz, User-Fehler) → unrouting-bar, daher weder in VLAN1/30-Sweep noch normal per IP erreichbar, aber per MAC in HA/WiFi-Table sichtbar. Der .219/s12-Verdacht war Sackgasse (s12 nur saisonal aus).
    • Update 2026-07-14: Shelly wieder da (User-Meldung) — Live-Status (noch bogus-IP 10.95.10.206 oder inzwischen normal erreichbar?) zu Sessionbeginn per HA/ARP verifizieren; Recovery-Plan unten ggf. obsolet, falls sich das Gerät selbst erholt hat.
    • Recovery-Plan (falls beim Verify noch nötig): ap1 bekommt temporäre Zweit-IP 10.95.10.1/24 auf der Bridge → 10.95.10.206 per L2 erreichbar → ein WiFi.SetConfig {sta: ssid=dirtyvader-iot, pass=IOT-PSK, static, ip=10.40.30.<frei>} + Shelly.Reboot → Gerät kommt auf VLAN30 hoch → temp-IP entfernen. Danach .30.x-Reservierung + Inventar (MAC e4:b0:63:e4:1a:b8) + eco-Policy. Umgeht die „blutige Nase" (IP am Gerät ändern während man drauf hängt = Verbindungsverlust) — vom Router aus in einem Rutsch. Christophs manueller Direkt-Versuch (AP-IP auf 10.40.30 „verdrehen") war genau deshalb gescheitert.
    • ✅ SwitchBot floor-lamp .41.184 (b8:f8:62:09:f2:04) → erfolgreich auf VLAN30 übersiedelt (2026-07-16, Christoph via App/BLE), Res. .30.227.
  • Prereqs-Status (live nachgezogen 2026-07-16, s. Live-Sweep oben):
    1. ✅ Christophs Endgeräte → VLAN10/20 erledigt: MacBook-Air (c0:c7:db:de:71:be, Claude-Sandbox-Client) hat aktive Leases nur noch auf dhcp-v10/dhcp-v20, keine defconf-Lease mehr. iPhone .41.186 nicht im aktuellen Sweep erneut geprüft (zuletzt off/asleep) — kurz vor Cutover gegenchecken.
    2. ✅ 2 unidentifizierte VLAN1-Geräte (.41.133/.41.198) aus dem ARP verschwunden — kein aktueller Eintrag mehr, gelten als nicht mehr blockierend (entweder migriert oder abgeklungen).
    3. ⏳ Media → VLAN40 weiterhin offen: Apple TV 4K + Philips TV nicht in diesem Sweep verifiziert.
    4. ⏳ Saisonale Shellys s1/s2/s3/s12 weiterhin offen — nicht blockierend, kann übersprungen werden (Re-Onboarding-Aufwand akzeptiert).
    5. ⏳ pve-Interim-IP .41.100 weiterhin aktiv/reachable — Host-seitiger Cleanup auf pve selbst noch offen (nicht Router-Config).
  • Teardown-Sequenz (angepasst an den Live-Stand): VLAN1-Client-Sweep ist schon fast leer (nur sw1-Mgmt + pve-Interim reachable) → defconf/pool-v1 ist bereits disabled → cudy-Abbau entfällt (cudy1 umgenutzt statt abgebaut, cudy2 tot) → ✅ SwitchBot + Twinkly erledigt (2026-07-16) → verbleibt: Media-Check (B1.3), pve-.41.100 abräumen (B1.5), Interim-Krücken bewerten (ap2 frame-types=admit-only-vlan-tagged, st-main rrm/wnm), Inventar/ip-plan/DNS .41→Ziel final.
  • Abh.: TP14 ✅ (Hosts raus aus VLAN1), TP17 ✅ (Shellys raus). Verwandt: project_vlan1_dhcp_incident (cudy-Dual-DHCP-Leak), TP5 (Deny-by-default danach sauberer). SSoT hier + docs/tp19-vlan1-cudy-teardown-runbook.md. Folge: TP19.1.

TP19.1 — VLAN99-MGMT-Cutover (sw1/sw2/ap2-Mgmt VLAN1 → VLAN99) ▸ ✅ ERLEDIGT (2026-08-14 nachgezogen) — alle lebenden Geräte auf VLAN99, die beiden Rest-Punkte gegenstandslos

  • ✅ Abschluss-Nachtrag 2026-08-14 (Status-Korrektur, kein neuer Change): der Header stand noch auf „ap2 vertagt, sw1 offen" (Stand 2026-07-16) — beides ist durch spätere TPs eingelöst bzw. gegenstandslos geworden, ohne dass es hier nachgetragen wurde. Ist-Stand: core .99.1 · branch .99.2 · ap1 .99.11 · ap2 .99.12 — jedes lebende Netzgerät hat sein Mgmt auf VLAN99. sw1/sw2 existieren nicht mehr (physisch ausgebaut, in Nautobot per TP22 Schritt 5 auf Decommissioned). Damit ist TP19.1 vollständig; offen bleibt nur der VLAN1-Client-Teil, und der gehört zu TP19 (Hauptteil), nicht hierher.
  • Scope: die einzigen Dinge, die TP19s Hauptteil bewusst in VLAN1 belässt — sw1 (.41.11), sw2, ap2-Mgmt (.41.2) — auf VLAN 99 (MGMT, 10.40.99.0/24) umziehen. Erst danach ist VLAN1 wirklich komplett weg, nicht nur als Client-Netz.
  • User-Entscheid (2026-07-16): kein eigenständiges TP-Nummer — bewusst als Unterpunkt von TP19 geführt („VLAN1 killen"), nicht als TP22 o.ä.
  • ✅ ap1-Teil erledigt (2026-07-16): ap1 brauchte keinen funktionalen Umzug (vlan99-Interface/GW .99.1 läuft schon seit TP4) — nur der kosmetische DNS-Static-Nachtrag ap1.bf.lan 10.40.41.1→10.40.99.1 war offen, per MikroMCP erledigt (dry-run→apply, manage_dns_entry).
  • ✅ sw2-Teil erledigt (2026-07-16, Christoph selbst über die Klickanleitung): sw2 jetzt auf 10.40.99.12 (statisch, planmäßiger Zielwert aus ip-plan.md) — per Ping verifiziert. Zwischenzeitlich war das Gerät kurz auf einer dynamischen 10.40.99.200-Lease aufgetaucht (unklare Ursache, s. Memory project_tp20_tp21_fasttrack_20260716), jetzt aber sauber auf der geplanten statischen Adresse. CLAUDE.md/ip-plan.md nachgezogen.
  • ✅ ap2-Teil erledigt (2026-08-08, im TP3-Rebuild) — die Vertagung wurde wie geplant eingelöst. ap2-Mgmt liegt auf 10.40.99.12 (VLAN99), gesetzt bei der Standalone-Neuprovisionierung, nicht isoliert von Hand; primary_ip4 in Nautobot per TP22 Schritt 5 nachgezogen. Der ap2.bf.lan-DNS-Static wurde im TP22-Pilot-Deploy (2026-08-13, GC-ConfigPlan) von 10.40.41.2 auf .99.12 gezogen. Ursprüngliche Vertagungs-Begründung zur Historie:
  • 🔜 ap2-Teil bewusst vertagt (User-Entscheid 2026-07-16): erst NACH Nautobot (TP21). Nautobot übernimmt dann in einem Rutsch ap1 + ap2 + die neuen CRS326-Switches (TP15) als config-as-code-Fleet — der ap2-Mgmt-VLAN-Umzug wird also nicht mehr isoliert von Hand gemacht, sondern Teil der Nautobot-Erstadoption. ap2.bf.lan-DNS-Static bleibt bewusst auf 10.40.41.2 bis dahin.
  • 🔒 TP19.1 war die letzte Ausnahme vom ap1/ap2-Config-Freeze — der jetzt (2026-07-16) zur generellen Regel ausgeweitet wurde (s. Governance-Entscheid oben): jede Gerätekonfiguration künftig ausschließlich über Nautobot, keine direkten Änderungen mehr an einzelnen im Netz verstreuten Geräten. Der ap1-DNS-Fix oben war die letzte direkte MikroMCP-Write-Aktion unter der alten (engeren) Regel.
  • ⚪ sw1-Teil GEGENSTANDSLOS (2026-08-14 festgestellt): Christoph hat sich für „auf CRS326 warten" entschieden — sw1 wurde am 2026-08-08 durch crs326-core ersetzt und physisch ausgebaut (Nautobot: Decommissioned, TP22 Schritt 5). Ein Mgmt-VLAN-Umzug für ein nicht mehr existierendes Gerät entfällt ersatzlos. switch/ui-guides/02-vlan99-mgmt-cutover.md ist damit historisch (nie ausgeführt, nicht mehr ausführbar — der Telnet-Runner switch/ insgesamt nur noch Archiv). Ursprünglicher Stand zur Historie: Klickanleitung fertig, Ausführung on-demand (User-Entscheid 2026-07-16), nicht blockierend.
  • Bereits vorbereitet: docs/ip-plan.md Phase 2 hat schon einen Entwurf (ap2→10.40.99.2, sw1→10.40.99.11, „Lockout-Risiko, sorgsam", sw1 zuletzt).
  • Abh.: TP19-Hauptteil (VLAN1-Client-Retirement) — logisch danach, da VLAN99 als eigenes Mgmt-Netz erst dann wirklich nötig wird, wenn VLAN1 sonst leer ist. sw1-Teil braucht sw1-Telnet-Zugriff (User macht sw1-Changes direkt, s. Feedback-Memory) — Lockout-Risiko am größten bei sw1 (einziger Netzpfad zu sw2 hängt am Uplink).

TP20 — Platform-Datenbank (Postgres Precious-Tier + MariaDB Tier-2) ▸ ✅ Plattform live (2026-07-16) — Nautobot-DB angelegt, vw-Migration (TP20.1) offen — Memory project_mikrotik_commit_nautobot_gc

  • ✅ Platform-Postgres live (2026-07-16): LXC 105 auf pve, platform-pg.bf.lan / 10.40.20.20, Ubuntu 24.04 (native PG16-Paket, kein PGDG-Repo nötig — bewusste Abweichung von der sonstigen Debian/community-scripts-Konvention der übrigen pve-LXCs, aus PG16-Verfügbarkeitsgründen). Cluster mit --locale en_US.UTF-8 -e UTF8 neu initialisiert (Erstinstallation lief in C/SQL_ASCII, für Nautobot ungeeignet, korrigiert). wal_level=replica gesetzt; archive_mode bewusst noch NICHT an — ohne pgBackRest/Garage-S3 (TP18) zum Prunen würde archivierte WAL unbegrenzt wachsen und die kleine 8GB-Disk füllen; wird nachgezogen sobald TP18 steht. Rolle+DB nautobot angelegt, pg_hba.conf erlaubt scram-sha-256 aus 10.40.20.0/24. End-to-End-Verbindungstest (Netzwerk, nicht nur localhost) erfolgreich. Creds in postgres/.secrets (gitignored, chmod 600).
  • Auslöser: Mehrere Dienste brauchen zentrale DB (Nautobot-SoT [TP21], Vaultwarden, HA-Recorder). Grundsatzentscheid statt Nautobot-Insellösung — „wir skalieren schneller als wir schauen können" (User).
  • ⚠️ Abh.-Korrektur (User 2026-07-16): TP18 (Garage-S3) ist KEINE Vorbedingung mehr — „hat mit dem Netz nichts zu tun", transitiv aus der TP21-Kette entfernt. pgBackRest→Garage-S3 (s.u.) wird nachgezogen sobald Garage existiert, blockiert aber nicht mehr den Start. wal_level=replica/archive_mode können mit einem Platzhalter/lokalen Archive-Ziel hochgezogen werden, produktives Off-Site-PITR kommt später.
  • ⚠️ Komodo-Abh. (s. TP2/Memory project_komodo_tp_pending) vorerst zurückgestellt: Komodo selbst existiert noch nicht als laufendes System — kann also nicht „darüber" deployt werden. Entscheidung (Claude, 2026-07-16, transparent statt stillschweigend): Postgres-LXC + Nautobot-Stack werden jetzt direkt gebaut (LXC/Docker-Compose), Komodo adoptiert sie später — gleiches Muster wie bei den CRS326 (Hardware/Stack zuerst lauffähig, Management-Layer zieht nach). Sag Bescheid, falls das nicht gewünscht ist.
  • 🔍 Ressourcen-Check 2026-07-16 (live geprüft, SSH):
    • pve (10.40.20.100): 4 vCPU, 15 GB RAM total, ~12 GB belegt, nur ~2-3 GB frei, 3 GB bereits im Swap — eng! Größter Verbraucher: HAOS-VM (haos, vmid 100) mit ~8,2 GB aktiv von 10 GB Limit. Disk unkritisch (94 GB Root, 85 GB frei).
    • Mac mini (10.40.20.167): 10 CPU, 24 GB RAM, nur ~1,9 GB "wired" (nicht reklamierbar) — deutlich mehr echter Headroom als pve trotz "23G used"-Anzeige (macOS-Cache-Artefakt). Docker läuft bereits dort (n8n, n8n-redis, cloudflared×2, gitea-mcp, portainer_agent).
    • NAS nicht geprüft (kein funktionierender SSH-Zugriff von hier aus mit den vorhandenen Keys/User) — für diese beiden Stacks laut TP20/TP21-Plan ohnehin nicht Zielhost.
    • ✅ Entschieden (2026-07-16): Postgres-LXC auf pve, Nautobot-App-Stack auf NAS (nicht Mac mini) — NAS hat mehr Headroom (~12 GB verfügbar) und hostet bereits Gitea (Nautobots Git-Backend für Golden-Config-Templates), kürzere Wege als Mac mini. Deploy-Weg: Portainer-Stack auf ep14, analog dem bestehenden Muster (docs/nas-vlan20/stacks/*.yml), Basis = offizielles nautobot-docker-compose (github.com/nautobot/nautobot-docker-compose), nicht von Null gebaut. Wird als separate Session angestoßen (parallel zum Postgres-LXC-Aufbau auf pve).
    • Nachtrag (2026-07-16, nach Aufräumen): pve hat sich entspannt — nach Abschalten mehrerer mittelfristig eh stillzulegender Services (User) jetzt 6,7 GB frei, 0 Swap (vorher 2-3 GB frei, 3 GB Swap). Postgres-LXC passt entspannt.
    • RAM-Upgrade-Idee (32GB-Stick) zurückgestellt (User 2026-07-16): Intel N100 (pve-CPU) ist offiziell auf max. 16 GB spezifiziert + Single-Channel; ein dokumentierter Fall mit demselben Corsair-Modul auf vergleichbarer N100-Hardware endete in Memtest86-Fehlern, stabil erst nach Rückbau auf 8GB. Empfehlung: 16GB als Decke für diese Kiste behandeln statt auf einen Settings-Fix zu hoffen. User rechnet ohnehin mittelfristig mit einem HW-Tausch für pve — gehört ggf. in TP15s HW-Refresh-Scope, noch nicht formal aufgenommen.
  • ✅ 2-Tier (User 2026-07-14):
    • Tier-1 „Platform-Postgres" (precious): EIN Postgres-Cluster, getrennte DBs für Nautobot (hier provisioniert) + Vaultwarden (Onboarding = TP20.1). PG 16 (Nautobot-Matrix-safe; Nautobot unterstützt kein MariaDB, nur PG/MySQL-8 → Postgres ist gesetzt, nicht optional).
    • Tier-2 (Bestand): HA-Recorder bleibt MariaDB (Write-Churn, andere Zuverlässigkeitsklasse, single-site). (Vaultwarden liegt heute ebenfalls auf MariaDB → wandert per TP20.1 nach Tier-1.) Engine-Split fällt sauber entlang der Tier-Grenze.
  • ✅ Single-Site-first (User 2026-07-14): jetzt nur BF-Primary („gehostet wird vorerst auf der bf-site", 2026-07-16 reconfirmed). Nimmt das 2-Site-Quorum/Split-Brain-Problem (bräuchte 3. Witness) vom Tisch. ABER replication-ready ab Tag 1: wal_level=replica, archive_mode=on (Ziel vorerst lokal/Platzhalter, s.o.) — späterer Standby = pg_basebackup, kein Primary-Umbau/Restart. pgBackRest→Garage-S3 folgt nach TP18, nicht mehr Blocker.
  • Substrat: Postgres in LXC auf pve (DB-Klasse, nicht Docker).
  • Scope TP20 = Plattform: Cluster hoch, Tiering, Backup/PITR, Nautobot-DB anlegen. vw-Migration + Cross-Site-HA = TP20.1 (ausgelagert).
  • Offene Sub-Steps: PG-Version final (16 vs 17); Rollen/DB-Anlage Nautobot; pgBackRest-Bucket in Garage folgt nach TP18 (nicht mehr Blocker, s.o.).
  • ⚠️ Komodo-Abh. (User 2026-07-16) vorerst zurückgestellt, s.o.: ursprünglich als Blocker gedacht (TP20 + neue Softwarepakete über Komodo statt direkt/Portainer), aber Komodo existiert noch nicht lauffähig und hat keine eigene TP-Nummer (nur unter TP2/TP11 erwähnt) → entkoppelt, um die Montag-Deadline zu halten. Komodo-TP-Nummer weiterhin zu klären, aber nicht mehr auf dem kritischen Pfad für TP20-Start.
  • Abh.: keine harten Blocker mehr (TP18 entfernt, Komodo entkoppelt). Blockt weiterhin TP21 + TP20.1.

TP20.1 — Vaultwarden → Platform-Postgres + Cross-Site-HA ▸ 🔜 KONZEPT (2026-07-14, ausgelagert aus TP20)

  • Ziel: vw (.20.127, heute MariaDB) auf den Precious-Tier holen und das eigentliche User-Ziel erfüllen: DB-Replikation über 2 Sites (Standort-Verlust überlebbar).
  • Phase 1 — Migration (single-site): vw MariaDB→Postgres (Export → Import in Tier-1-DB vaultwarden → DATABASE_URL umstellen → verifizieren). Danach ist vw precious + im pgBackRest/PITR-Schirm von TP20. (Alternative verworfen: vw auf MariaDB lassen + MariaDB-Repl — zugunsten EINER Replikations-/Backup-Mechanik im Precious-Tier.)
  • Phase 2 — Cross-Site-Standby: db-platform-standby-LXC am 2. Standort → pg_basebackup/Streaming-Replication über WG (TP10) → manuelles Promote-Runbook. Repliziert den ganzen Tier-1-Cluster (vw = HA-Ziel, Nautobot bekommt Gratis-DR-Kopie). Patroni + 3. Witness nur, falls später Auto-Failover statt manuellem Promote gewünscht.
  • Verwandt/Abgrenzung: vw ist auch in TP16 offen (CF-Access: bypass→WARP-geschützt) — DB-HA ≠ Access, getrennt behandeln. Reziprokes 2-Site-Muster analog TP16/TP18.
  • Abh.: TP20 (Tier-1 Postgres muss stehen), TP10 (WG-Transport für Phase-2), TP18 (Backup-Schirm). Kein Blocker für TP21.

TP21 — Nautobot Golden Config (config-as-code Control-Plane) ▸ 🟢 Plattform + Datenmodell + Templates live (App 2026-07-22, Basisdaten + Git-Backend + L3-Core-Template 2026-08-05, ap1/ap2 As-built 2026-08-08); Device-Adoption/Compliance-Push ausgelagert → TP22 — Memory project_mikrotik_commit_nautobot_gc

  • Ziel: modellierter Control-Plane über den MikroTik-RouterOS-Kern: GUI-SoT + Config-Compliance/Drift + intended-config + Push. Ergänzt MikroMCP (interaktiv/Debug), ersetzt es nicht (deklarativ/DR/Compliance). Aus User-Recherche 2026-07-14: MikroTik in Golden Config erstklassig (mikrotik_routeros + _api, Nornir/NAPALM, backup+deploy).
  • Scope-Grenze (Fassung 2026-08-16, nachgezogen): RouterOS in-model = crs326-core (L3-Router) + crs326-branch (L2) + ap1/ap2 (reine APs) — alle 4 Geräte sind seit TP22 adoptiert, Golden Config deckt damit den ganzen Fabric inkl. WLAN. Es bleibt nichts außerhalb: der frühere Vorbehalt „außerhalb bleibt Omada-WLAN" ist gegenstandslos, seit der Omada-Stack gelöscht (2026-07-16) und CAPsMAN abgeschafft (2026-08-08) ist.

    Vorherige Fassung (überholt): „RouterOS in-model = Router (ap1/ap2, dauerhaft — kein RB5009) + beide CRS326-Switches (RouterOS-Boot) → Golden Config deckt den ganzen Kabel-Fabric. Außerhalb bleibt nur Omada-WLAN (eigener Controller, spielt aber laut TP15-Korrektur ohnehin keine Rolle mehr)."

  • 🔒 Wird zum alleinigen Config-Weg für Netzgeräte (Governance-Entscheid 2026-07-16, s. oben): ab hier keine ad-hoc MikroMCP-Writes/Switch-UI-Klicks mehr auf Dauer — Nautobot managed den Fleet.
  • ✅ Nautobot-Stack live (2026-07-16), auf NAS statt Mac mini (Ressourcen-Check hatte Mac mini vorgeschlagen, dann auf NAS umentschieden — mehr Headroom + Gitea-Co-Location, s. TP20-Log): Portainer-Stack nautobot (Id 203, ep14), 4 Container — nautobot (App/API, 10.40.20.30:8080), nautobot-worker (Celery), nautobot-scheduler (Celery Beat), nautobot-redis. Bild ghcr.io/nautobot/nautobot:3.1.7 (offizielles Image direkt, nicht das nautobot-docker-compose-Repo — das ist ein invoke/Poetry-getriebenes Dev-Tooling-Repo ohne eigenständige docker-compose.yml, für einen Portainer-String-Stack ungeeignet; bewusste Abweichung, dokumentiert statt stillschweigend). DB = Platform-Postgres (10.40.20.20, TP20). Compose-Datei docs/nas-vlan20/stacks/210-nautobot.yml (gitignored, wie alle NAS-Stack-Files).
    • 🐛 Gotcha beim ersten Deploy: alle 3 Nautobot-Container liefen initial gleichzeitig migrate (Standard-Entrypoint-Verhalten) → Race-Condition, IntegrityError auf pg_type. Fix: nautobot-Service kriegt einen healthcheck (curl .../health/), nautobot-worker/nautobot-scheduler starten erst bei depends_on: nautobot: condition: service_healthy — serialisiert die Migration sauber auf einen Container. DB dafür einmal DROP+CREATE (sauberer Neustart, kein Datenverlust — war eh leer).
    • ✅ End-to-End verifiziert: /api/status/ mit Token 200, Nautobot-Version 3.1.7, alle 4 Container laufen. Superuser admin angelegt, Creds im Stack-File (gitignored) + postgres/.secrets (DB-Seite).
    • ⚠️ Bekannt offen, nicht blockierend: kein HTTPS/Reverse-Proxy vor Nautobot (Django-Security-Warnings beim Start, erwartet für internen HTTP-only-Betrieb auf VLAN20 — analog anderen internen Diensten); Celery-Worker-Registrierung beim allerersten Start nicht sofort sichtbar (celery-workers-running:0 initial), unkritisch, braucht kurz zum Einpendeln.
  • PoC-Pfad (angepasst an die Montag-Deadline): Nautobot-Stack hoch ✅ → ap1 + ap2 in einem Rutsch adoptieren (ap2 bekommt dabei gleich seinen VLAN99-Mgmt-Umzug aus TP19.1, s.o. — nicht isoliert vorher) → sobald CRS326 verkabelt sind, direkt als Nautobot-verwaltete Geräte adoptieren statt erst manuell konfigurieren → Compliance/Drift + Templates/intended für VLAN-/Firewall-Standards danach.
  • ✅ Basisdaten-Ingest durchgeführt (2026-07-22) — Ist-Zustand, nicht Zielzustand (User-Entscheid: „wir gehen vom status quo aus"). Quelle = ap1 live via MikroMCP. Skript-Muster idempotent (get-or-create + Delta-PATCH).
    • Locations: neuer LocationType Bereich unter Site; BF → Technik UG (crs326-core, crs326-branch, ap2) und BF → Wohnzimmer Sideboard (ap1).
    • Devices: ap1 = Rolle Router (Ist am 2026-07-22 — ⚠️ seit der Adoption 2026-08-13 ist ap1 in Nautobot Wireless AP und crs326-core der Router, s. TP22), ap2 = neue Rolle Wireless AP + Status Offline, beide CRS326 Platform RouterOS nachgetragen (fehlte → hätte jeden GC-Job auf ihnen verhindert) + Werks-MAC/software-id in comments. Neu als Übergangs-Devices ohne Platform (nie GC-fähig, bewusst): sw1 (ALLNET), sw2 (ES208G), cudy1.
    • IPAM: VLAN-Group BF + 9 VLANs (1 Deprecated, 10/20/30/40/50/90/99/100 aktiv) + 8 Prefixes, jeweils an ihr VLAN gebunden. VLAN100/WAN bewusst ohne Prefix (öffentliches Liwest-/24, gehört uns nicht).
    • ap1-Interfaces (26): ether1-5, bridge, lo, wg-hub, vlan10-100, wifi1/wifi2, 7 VAPs — inkl. MACs, ether2 als Trunk (untagged VLAN1 + tagged 10-100), VAPs als Access-Ports auf ihrem VLAN. 9 Gateway-IPs an ihren Interfaces. Platzhalter-Interface mgmt auf ap1 gelöscht (existiert real nicht).
    • WLAN: logischer Controller bf-wlan (kein CAPsMAN — controller_device bewusst leer, die Control-Plane ist Nautobot GC) + Gruppe BF-WLAN-APs (ap1+ap2) + 6 WirelessNetworks mit VLAN-Bindung + 2 RadioProfiles (2,4 GHz ch1 / 5 GHz ch36, Domain AT).
    • 🐞 API-Gotcha: tagged_vlans wird vom REST-Serializer in Detail-/List-Views als null zurückgegeben, obwohl korrekt gespeichert — Verifikation nur über GraphQL verlässlich. Kostet sonst eine Stunde Fehlersuche an einem Nicht-Problem. Ebenso: Relationen kommen per Default ohne display (?depth=1 nötig), und VLANGroup/Prefix/VLAN brauchen passende content_types am LocationType, sonst 400.
    • ✅ CRS326-Core L3-Device-Modell + Ports angelegt (2026-08-05): crs326-core jetzt Rolle Router, alle VLAN-SVIs (vlan10/20/30/40/50/90/99/100) + Gateway-IPs .1 (außer vlan100) in Nautobot; Portbelegung via nautobot/ingest-crs326-ports.py aktualisiert. crs326-branch bleibt Switch mit Mgmt-IP .99.13 (Zielrenumber .12 im Cutover-Fenster).
    • ⚠️ Offen/nachzutragen: exaktes Modell von sw1 + cudy1 (aktuell … (Modell TBD)); Standort von sw1/sw2/cudy1 (hängen an BF statt an einem Bereich); ap2s primary_ip4 zeigt noch auf das tote 10.40.41.2 → ✅ erledigt (TP22 Schritt 5): 10.40.99.12.
  • ✅ Git-Backend an Gitea angebunden (2026-08-05): Repository bf-golden-config (jinja_templates/mikrotik_routeros.j2) sync-t in Nautobot; Generate Intended Config für crs326-core erfolgreich, intended config in GC-Tabelle vorhanden.
    • ✅ Lokale Cutover-Config materialisiert: routeros/tp15-core-cutover-intended.rsc enthält die aus Nautobot gerenderte Config plus die 54 ap1-Leases und den WG-Hub-Privkey (lokales Fenster-Artefakt, nicht im Golden-Config-Repo).
  • ↪️ Adoption + Compliance-Push → ausgelagert nach TP22 (eigener TP, User-Entscheid 2026-08-08): ap1/ap2/crs326-Adoption, RouterOS-Nornir-Dispatcher, enable_deploy, WG-Privkey/PSK-Secret-Handling. TP21 selbst = Plattform + Datenmodell + Templates (steht). Datenmodell-Nachträge (branch/core-Ports final, sw1 außer Betrieb, cudy1) laufen in TP22 mit.
  • Abh.: TP20 (Postgres muss stehen), Gitea ✅, RouterOS-Fleet (core/branch/ap1/ap2).

TP21.1 — Core-Cutover-Recovery (ap1 → crs326-core als L3-Router) ▸ ✅ NETZ WIEDER LIVE (2026-08-08); Aufräum-Todos offen

Kern-Erkenntnis (wichtig gegen Doku-Verwirrung): crs326-core IST jetzt der produktive L3-Router. Der Core-Cutover (TP15) wurde faktisch vollzogen — ap1 wurde plattgemacht/getrennt, core hat Routing/DHCP/DNS/NAT/WG-Hub übernommen. core-Mgmt = 10.40.99.1 (vlan99-SVI) bzw. same-subnet-Fallback 10.40.10.1 (RouterOS-Version 7.19.5 identifiziert das Gerät zweifelsfrei als CRS326, nicht als hAP/ap1). Die CLAUDE.md-Zeile „core grundkonfiguriert, NICHT produktiv" ist damit überholt.

  • Verlauf: Ausgangssymptom war ein L3-Ausfall-Eindruck (kein DHCP, „kein Routing nach .20.x"). Ursache = unvollständiger Cutover: core hatte L3-SVIs + Firewall, aber es fehlten DHCP-Server, WAN-Egress, WG-Hub und der DNS-Resolver-Upstream. ap1 (bisher alleiniges L3-Gerät) war bereits Werksreset + getrennt.
  • ✅ DHCP wiederhergestellt aus letztem ap1-Backup (routeros/ap1-pre-core-cutover-20260806-073616.rsc): 7 Server (dhcp-v10…v99) + 8 Networks + 54 statische Reservierungen + Option dns 1.1.1.1. Reproduzierbar in routeros/tp21-core-dhcp-from-ap1.rsc. Legacy VLAN1 (pool-v1/defconf) + tote omada-inform-Option bewusst weggelassen.
  • ✅ WAN/Internet: dhcp-client auf vlan100 → Liwest-Public-IP 90.146.33.177 gebunden (Arris hängt physisch an core.vlan100), NAT-Masquerade out-interface-list=WAN, Default-Route korrekt; die self-pointing Blackhole-Route 0.0.0.0/0 via 10.40.99.1 entfernt. disabled-Falle beachtet: die ap1-Masquerade-Regeln out-interface=bridge/=ether1 sind disabled=yes und wurden NICHT übernommen.
  • ✅ WG-Hub (TP10) neu aufgebaut: wg-hub (listen 51821, Privkey aus routeros/ap1-wg-hub.privkey, Hub-Pubkey unverändert) + Peers fm/dw + Routen. Beide Tunnel handshaken (dw + fm live). Reproduzierbar (Privkey redacted) in routeros/tp21-core-wan-wg-restore.rsc.
  • ✅ DNS gefixt: core hatte allow-remote-requests=no + leeren Upstream → Client-DNS tot UND Hub konnte myfritz (FM-Endpoint) nicht auflösen. Fix = allow-remote-requests=yes + servers=1.1.1.1 (plain, weil core certs=0 → verify-doh-cert unmöglich). Das entsperrte auch FM.
  • ✅ Identity auf crs326-core gesetzt (war fälschlich „ap1" — genau diese Altlast stiftete Verwirrung). mcp/mikromcp/config/routers.yaml auf Zielschema: core .99.1, branch .99.2, ap1 .99.11, ap2 .99.12 (wirkt nach MCP-Neustart; danach core via routerId crs326-core).
  • ✅ disabled/invalid-Falle bereinigt (User-Hinweis „keine disabled-Falle?"): (a) die 2 TP10-WG-Forward-Regeln (allow from/to WG spokes) waren I=invalid, weil aus dem ap1-Backup mit der stale Interface-ID *1D übernommen → auf in/out-interface=wg-hub (per Name) korrigiert; (b) 2 inaktive WG-Route-Leichen (gateway="", aus einem früheren Cutover-Versuch vor Existenz von wg-hub) entfernt, nur die aktiven gateway=wg-hub bleiben. Finaler Sweep sauber: filter-invalid=0, nat-invalid=0, routes-inactive=0, if-disabled=2 (nur ungenutzte sfp-sfpplus1/2, gewollt).
  • ✅ Backups: core-pre-dhcp-restore-20260808.backup (vor Eingriff) + core-post-tp21-recovery-20260808.backup (nach allen Fixes neu gezogen) auf dem Gerät.
  • ℹ️ branch ist gesund (L2, forwardet), Mgmt 10.40.99.2 (+ temp 10.40.10.2 — die Temp-IP ist am 2026-08-16 entfernt worden) — via core-MNDP bestätigt. Das in Winbox-MAC gesehene „0.0.0.0" ist der bekannte Discovery-Artefakt (Gerät hat auf dem Discovery-VLAN keine IP), kein echtes Problem.
  • 🔜 TODO (Aufräum-Liste, spätere Session — danach WLAN-Neuprovisionierung):
    1. DoH-Cloudflare-Gateway auf core wiederherstellen (aktuell nur plain 1.1.1.1, keine Filterung/Account-Bindung): CA-Bundle importieren → use-doh-server=https://px35ynlol3.cloudflare-gateway.com/dns-query verify-doh-cert=yes (Referenz-Config liegt auf ap1).
    2. Intended config nach Nautobot zurückschreiben (die Direkt-Writes liefen unter der CRS326-Cutover-Governance-Ausnahme).
    3. branch 192.168.88.1-Rest verifizieren (Bootstrap Rev 4 entfernt es in Zeile 70; via MCP noch nicht gegengeprüft, weil Registry-Umstellung MCP-Neustart braucht).
    4. ✅ core ether4 GEKLÄRT (2026-08-13) — es ist das Wohnzimmer-Sideboard (Smart-Managed-Switch, nur VLAN40-Multimedia), und der Port ist der falsche. Symptom war „TV meldet, LAN funktioniert nicht".
      • Ursache = VLAN-Mismatch, kein Kabeldefekt: ether4 hat pvid=40 (untagged rein → VLAN40 ✓), ist in VLAN40 aber tagged-Member (untagged sind ether2/ether3/ether5) → alles raus ist 802.1Q-getaggt und der untagged Switch/TV verwirft es. Einseitige Kommunikation.
      • Belege: TX 3,4 GB / 16 M Pakete gegen RX 452 KB / 1.338 Pakete, 0 stabil gelernte MACs; TV-Lease 10.40.40.11 (70:AF:24:B8:C9:86) bound, last-seen 1h54m, gleichzeitig ARP-Status failed — ein Gerät, das nachweislich sendet, aber nicht antwortet. Der bound-Lease ist kein Gegenbeweis: beim Einschalten macht der TV einen INIT-REBOOT-REQUEST auf seine alte IP, RouterOS quittiert mit ACK und markiert bound — das ACK muss den Client nie erreichen.
      • Herkunft: ether4 war als ap1-Uplink-Trunk vorbereitet („ap1 wandert #15→#4"); ap1 blieb auf ether15, in den übrigen Trunk-Port wanderte das Sideboard-Kabel.
      • ✅ BEHOBEN 2026-08-13 — am Port, nicht am Kabel (User-Vorgabe): „Patchfeld 1–10 ist durchnummeriert und komplett gepatcht, die Nummerierung wird nicht wegen einer Config geändert." Richtig so — die Portnummer ist physische Infrastruktur. Also ether4 sauber auf untagged VLAN40 gezogen statt das Kabel zu verschieben.
      • Ausgeführt via Nautobot-GC-ConfigPlan (plan_type=manual, 6 Zeilen, change_control_id TP21.1-4 …), nicht per Direkt-Write — Governance gewahrt. Vorher /system backup = core-pre-ether4-vlan40-20260813.backup (Regel 3). ether4 aus den Tagged-Listen von VLAN 10/20/30/40/50/90 entfernt + als untagged-Member zu VLAN40 ergänzt. pvid=40 stand bereits — das war nie das Fehlende. Verifiziert: vlan40 untagged=ether2,ether3,ether4,ether5, tagged=ether1,ether8,ether15; vlan99 (Mgmt) und vlan100 (WAN) unangetastet. Damit ist auch der Flood-Leak aus IoT/INFRA/MAIN zum Wohnzimmer-Switch weg.
      • ✅ End-to-End-Beweis ERBRACHT (2026-08-14, live via MikroMCP auf crs326-core) — der TV war am 08-13 tatsächlich nur im Standby. Alle vier Kriterien grün: ping 10.40.40.11 = 5/5, 0 % Loss (avg 563 µs); ARP complete=true/status=delay auf vlan40 (vorher failed); /interface/bridge/host lernt 70:AF:24:B8:C9:86 auf ether4, VID 40; ether4 rx-unicast=6.909 / 3,0 MB RX (am 08-13 noch 1.338 Pakete / 452 KB → der Downstream lebt jetzt). DHCP-Lease tv weiterhin bound auf dhcp-v40. Damit ist TP21.1 Punkt 4 vollständig abgeschlossen und die Diagnose „tagged Egress auf einem untagged Port" ist end-to-end bestätigt (SSoT-Merkregel: ref_tagged_egress_oneway_port).
      • 🔴 INCIDENT + Lehre (2026-08-13): Der erste Deploy lief gegen das FALSCHE GERÄT (ap1 statt core). Ich hatte 4 Device-UUIDs mit einer Sammelabfrage geholt und die Reihenfolge angenommen; die Nautobot-REST-API sortiert aber alphabetisch → ap1, ap2, crs326-branch, crs326-core. Der ConfigPlan trug damit ap1.
        • Schaden: keiner. RouterOS wies jedes set ab (invalid value for argument interface), weil ether8/13/15 auf einem 5-Port-hAP nicht existieren; die Ablehnung ist pro Kommando atomar → kein Teilzustand. ap1 nachgeprüft: VLAN-Tabelle vollständig, 25 WLAN-Clients, Mgmt .99.11. Das war Glück, kein Können — bei einem auf beiden Geräten gültigen Kommando (z.B. tagged=ether1) hätte es ap1s VLAN-Tabelle zerlegt.
        • Regel daraus: Device-UUIDs IMMER einzeln per Name auflösen, nie aus einer Sammelabfrage positional zuordnen — und vor dem Deploy plan.device.display gegen den Zielnamen assert'en (im Skript jetzt so gemacht).
        • Bestätigt zum zweiten Mal: der Deploy-Job meldete SUCCESS und der Plan Completed, obwohl auf dem Gerät nichts ankam. Weder Job- noch Plan-Status sind ein Wirksamkeitsnachweis — nur die Gegenprüfung am Gerät zählt. Der Fehl-Plan wurde gelöscht (irreführender Datensatz).
      • ✅ Lücke geschlossen (2026-08-13, User: „compliance rules! die Änderung machen wir"): /interface bridge port + /interface bridge vlan sind jetzt für alle drei Geräteklassen im Template und als ComplianceFeatures scharf — s. TP22.
    5. ap1/ap2 Neuprovisionierung (WLAN-Rebuild, eigene Session) — ap1 ist Werksreset; danach greifen die .99.11/.99.12-Registry-Einträge.

TP22 — Device-Adoption + Golden-Config Compliance-Push (RouterOS-Fleet) ▸ ✅ DURCH (2026-08-13), Compliance-Backlog abgearbeitet 2026-08-16 → 68/68, 0 Drifts — alle 5 Schritte erledigt (Schritt 4 per Entscheid „keine Secret-Injection"); aus TP21 ausgelagert 2026-08-08

  • Ziel: die 4 Live-Geräte (crs326-core, crs326-branch, ap1, ap2) real unter Nautobot Golden Config nehmen — Backup → Compliance/Drift → intended → Push (enable_deploy). Damit greift der Governance-Entscheid „Config exklusiv über Nautobot" operativ. Macht die aktuellen routeros/.secrets/*.rsc-Handoffs mittelfristig überflüssig.
  • Stand aus TP21: Plattform + Datenmodell + Git-Template + intended-config für core stehen; ap1/ap2 als reine APs zurückgeschrieben (nautobot/ingest-ap-rebuild.py, 2026-08-08). enable_plan/enable_deploy in nautobot_config.py noch bewusst aus (pull/read-only).
  • ✅ Schritt 0 DURCH (2026-08-13) — Backup-Scrubbing + Repo-Neuanlage. Vorgezogen aus Schritt 4, weil ohne Scrubbing jedes weitere Backup Secrets geleakt hätte.
    • 🔴 Root-Cause-Fund: Es gab keine ConfigReplace/ConfigRemove-Regeln → der Backup-Job vom 2026-07-22 hatte den ap1-/export ungefiltert nach Gitea geschrieben. backups/ap1.cfg enthielt im Klartext den damals wie heute live genutzten WG-Hub-private-key (bei der Core-Recovery unverändert auf crs326-core übernommen, damit FM/DW-Spokes matchen) und 6 WLAN-PSKs.
    • ✅ 5 ConfigReplace-Regeln (Platform RouterOS) via nautobot/ingest-gc-scrubbing.py (Dry-Run-Default, Regex-Selbsttest bei jedem Lauf): 10-wifi-passphrase, 20-wireguard-private-key, 30-preshared-key, 40-password, 50-secret → Maske "<<REDACTED>>". Gegen den echten Export kalibriert: RouterOS quotet nur bei Bedarf (4 von 6 PSKs standen unquotiert), deshalb Alternation ("(?:[^"\\]|\\.)*"|\S+) mit der quotierten Variante zuerst (\S+ ist greedy und würde bei einem PSK mit Leerzeichen den Rest exponieren). Replace normalisiert immer auf die quotierte Form → Maske ist formunabhängig byte-identisch (Voraussetzung für das Template in Schritt 1). Negativtest: public-key= wird nicht maskiert (sonst bräche der WG-Peer-Block).
    • 🐞 Blocker nebenbei gefunden + gefixt: Nautobot hatte das ALTE Router-Passwort. Der erste Backup-Lauf meldete Job-SUCCESS, schrieb aber nichts — backup_last_attempt_date sprang, backup_last_success_date nicht (GC meldet Job-Erfolg auch bei Geräte-Fehlern; nur das Success-Datum je Gerät zählt). Ursache: NAUTOBOT_ROUTEROS_PASSWORD im Stack war 12 Zeichen aus der Zeit vor der PW-Vereinheitlichung (2026-08-08, 32 Zeichen, identisch auf allen 4 MikroTik). Fix = Stack-Env nachgezogen + Stack 203 redeployed; Datei zusätzlich auf chmod 600 (Backup .bak-20260813).
    • ✅ Gitea-Repo admin/bf-golden-config gelöscht + neu angelegt (User-Entscheid: Repo neu, nicht rotieren — History-Rewrite verworfen, weil Gitea unerreichbare Blobs bis zum server-seitigen gc behält). Struktur + Template + README wiederhergestellt. Vollscan aller Blobs der neuen History: 0 unmaskierte Secrets (die 6 Regex-Treffer sind 5× README-Doku der Feldnamen + 1× der Template-Platzhalter). Nautobot-GitRepository re-synct, End-to-End-Backup ins frische Repo verifiziert (112 Z., 6/6 PSKs maskiert).
    • 📌 Risiko-Akzeptanz (User 2026-08-13): keine Rotation. Faktenlage: Repo private=true, anonymer Zugriff 404. Aber Gitea hängt auf CF-bypass am Internet und ha-xstate ist ein Service-Account mit Gitea-Admin (kann jedes private Repo lesen) → Lesbarkeit für 2 Accounts, nicht fürs Internet. Trigger für nachträgliche Rotation: ändert sich die Gitea-Exposition oder der ha-xstate-Account → WG-Key rotieren (FM/DW-Klickanleitung routeros/fritzbox-wg-lanlan-klickanleitung.md).
    • ⚠️ DR-Konsequenz: ein maskiertes Backup ist kein Wiederherstellungs-Artefakt. Restore hängt weiter an ~/.secrets/, routeros/.secrets/* und routeros/ap1-wg-hub.privkey (alle gitignored).
  • ✅ Schritt 1 DURCH (2026-08-13) — AP-Zweig im Golden-Config-Template.
    • Datenmodell = ConfigContext (nautobot/ingest-ap-configcontext.py). Verworfen: Wireless-RadioProfile (haengt über ControllerManagedDeviceGroup an einem Controller — den gibt es seit der CAPsMAN-Abschaffung bewusst nicht mehr; ein Fake-Controller wäre Modellschuld), CustomFields (skalieren nicht), Ableitung aus VAP-Descriptions (SSID nur als Freitext → stille Brüche). Angelegt: ConfigContext ap-common (Scope Role Wireless AP + Platform RouterOS, weight 1000) mit zones/radios/Site-Konstanten + local_config_context_data je AP mit nur den 2 Kanalnummern. Frequenz wird im Template gerechnet (5000+5*ch / 2407+5*ch) statt gespeichert.
    • Cross-Check im Skript: die zones-Tabelle wird gegen die 11 real modellierten VAP-Interfaces (Name-Präfix + untagged_vlan) validiert, Divergenz = Abbruch → ConfigContext und DCIM können nicht auseinanderlaufen.
    • Template: neuer Zweig {% elif obj.role.name == "Wireless AP" %} (rollen-, nicht namensbasiert). Scope bewusst nur die compliance-verwalteten Sektionen (channel/datapath/security/configuration/bridge-vlan/vlan99/ip-address/route) — nicht die ganze Config: defconf-Boilerplate (ipv4/ipv6-Firewall, /disk, /tool mac-server) und die VAP-Zeilen bleiben draussen, weil deren MACs gerätevergeben und in Nautobot nicht modelliert sind. Die früheren „Ausnahmen" fallen aus den Daten heraus: IOT hat bands:["2g"] → landet automatisch nur auf wifi2; VLAN1/VLAN99 stehen als literale Zeilen ausserhalb der Schleife (VLAN99 auf bridge, NICHT auf die Radios — sonst Mgmt over-the-air).
    • 🔑 Formatregel gelernt: gegen den echten /export schreiben, nicht gegen die .rsc. RouterOS sortiert Attribute alphabetisch und schreibt ft-over-ds nur bei ft=yes; die handgesetzte .rsc hat abweichendes Padding/Reihenfolge. Referenz-Export: routeros/state-backups/ap1-gc-backup-scrubbed-20260813.cfg. RouterOS exportiert CRLF, Jinja rendert LF — GC normalisiert das korrekt (die schon vorher grüne system-identity-Compliance war der Beleg).
    • ✅ Abnahme: ap1 Intended == Geräte-Ist, 0 Drift (33 Zeilen, zeilenweise identisch); ap2 unterscheidet sich in exakt den 4 erwarteten Werten (identity, 5180↔5745, 2412↔2462, .99.11↔.99.12).
    • ✅ Mitgenommen: WG-Privkey-Platzhalter im core-Zweig auf dieselbe Maske "<<REDACTED>>" vereinheitlicht (sonst Dauer-Diff, sobald eine Feature auf /interface wireguard liegt); DNS-Statics korrigiert (s. Nummernplan unten). routeros/push-gc-template.py gehärtet: Pre-Push-Secret-Guard (verweigert den PUT bei unmaskierten Werten), --dry-run mit Diff, -m statt hartkodierter Commit-Message, sha optional (sonst 404 nach Repo-Neuanlage).
  • ✅ Schritt 2 DURCH (2026-08-13) — Adoption + Backup/Compliance grün, 20/20.
    • SecretsGroup „RouterOS Nornir" an crs326-core + crs326-branch gehängt (ap1/ap2 hatten sie schon) — einheitliches Admin-PW seit 2026-08-08 macht eine Gruppe für alle 4 ausreichend.
    • Datenmodell-Korrekturen vorgezogen (technisch Voraussetzung): crs326-core Status Planned → Active (ist der produktive L3-Router), crs326-branch primary_ip4 .99.13 → .99.2 (live verifiziert).
    • ✅ Backup über alle 4 erfolgreich, Sichtprüfung: 0 unmaskierte Secrets (ap1/ap2 je 6 PSKs maskiert, core WG-Privkey maskiert, branch secret-frei).
    • ✅ 4 neue ComplianceFeatures (wifi-channel, wifi-datapath, wifi-security, wifi-configuration, alle config_ordered=false) + die bestehende system-identity → 20/20 compliant über alle 4 Geräte.
    • ⚠️ Bewusst noch KEINE Features für bridge-vlan/ip-address/ip-route: ComplianceRules gelten pro Platform, nicht pro Rolle — und der crs326-core-Zweig ist in Slash-Form geschrieben (/ip/address add …), was den Export (Space-Form, /ip address add …) nicht matcht → das gäbe permanenten Falsch-Drift auf dem Core. Follow-up: core-Zweig auf Export-Form umstellen, dann diese Features nachziehen. Die wifi-Features sind unkritisch, weil core/branch keine WLAN-Hardware haben (beide Seiten leer = compliant).
  • ✅ Follow-up DURCH (2026-08-13) — crs326-core-Zweig auf /export-Form. Der Zweig war als staged Cutover-Skript geschrieben (Slash-Form /ip/address, plus disabled=yes-Flags aus der Vorbereitungszeit) und matchte den Export nicht → er hätte platform-weite ComplianceRules dauerhaft rot gefärbt. Jetzt Space-Form + alphabetische Attribute, disabled=yes entfernt (core ist seit 2026-08-08 produktiv), VLAN-Tabelle als Jinja-Konstante, GW-IPs weiter datengetrieben. Ergebnis: 68 von 69 Zeilen byte-identisch zum Gerät. Zusätzlich Switch-Zweig für crs326-branch (Mgmt-Layer) ergänzt — vorher fiel branch auf „nur /system identity" durch und meldete legitime Config als Drift. 9 weitere ComplianceFeatures (interface-list/-vlan/-wireguard, ip-address/-pool/-dhcp-server/-dns/-firewall-nat/-route); bei ip-dhcp-server bewusst drei spezifische Präfixe statt /ip dhcp-server, sonst fingen die 54 dynamischen lease-Zeilen mit.
  • ✅ Schritt 3 DURCH (2026-08-13) — Deploy-Pipeline scharf, Pilot erfolgreich.
    • Image 3.1.7-gc9 auf der NAS gebaut (/volume1/docker/nautobot-image), Stack 203 retaggt. ⚠️ Falle: ein bereits vorhandenes gc7 trug dieselben Flags; ein Build ohne geänderte Quelldatei zieht den Docker-Cache und liefert eine identische Image-ID — immer gegen die Flags im fertigen Image verifizieren. scp auf die NAS schlug fehl („Connection closed"), Übertragung per ssh 'cat > datei'.
    • Jobs sind in Nautobot zusätzlich einzeln freizuschalten (enabled=true): die Plugin-Flags machen sie nur verfügbar.
    • 🔴 Kernbefund: napalm-ros ist ein READ-ONLY-Treiber. Der Default-Dispatcher wählt NAPALM (weil Platform.network_driver_mappings napalm: ros enthält) → Backup (get_config) geht, merge_config scheitert grundsätzlich, mit leerer Fehlermeldung (E1015 … ``). **Fix:** in den Golden-Config-Settings (nicht in den Nornir-Settings!) custom_dispatcher: {"mikrotik_routeros": "nornir_nautobot.plugins.tasks.dispatcher.mikrotik_routeros.NetmikoMikrotikRouteros"}— dieser Dispatcher pusht vianetmiko_send_config`. Danach lief der Deploy sofort.
    • ⚠️ Gescheiterter Deploy = sauberer Fehlschlag: das Gerät blieb beim NAPALM-Versuch vollständig unverändert (kein Teilzustand). Zusätzlich meldet der Deploy-Job SUCCESS, obwohl der Plan auf Failed steht — wie beim Backup gilt: nur der Objekt-Status zählt, nicht der Job-Status.
    • ✅ Pilot-Deploy: die DNS-Statics auf dem Core. Vorher /system backup (core-pre-tp22-first-deploy-20260813.backup). ⚠️ config_set eines generierten Plans ist schreibgeschützt, und plan_type=missing erzeugt nur add — das hätte Duplikate ergeben (ap1.bf.lan gleichzeitig auf .99.3 und .99.11). Deshalb manueller Plan mit Purge-vor-Add. Ergebnis verifiziert: ap1→.99.11, ap2→.99.12, branch→.99.2, weiterhin 11 Einträge, keine Duplikate. Compliance 51/56.
  • ✅ Schritt 5 DURCH (2026-08-13) — Datenmodell-Nachträge, nautobot/ingest-tp22-datamodell.py. Quelle war durchgehend das Gerät, nicht die Doku: bridge/port (pvid), bridge/vlan (tagged/untagged), bridge/host (gelernte MACs je Port+VID) plus ARP/DHCP-Leases zur Identifikation der Gegenstellen.
    • crs326-core: von 5 auf 26 Ports. Vorher waren 3 davon als „GEPLANT (tentativ)" modelliert und ether1 zeigte auf das tote sw1 #12. Belegt (MAC-hart): ether8→crs326-branch, ether13→pve .20.100, ether14→Sonoff .30.250, ether15→ap1, ether16→Arris/WAN, ether5/ether6→Sonos Schlafzimmer .195/.193. Die Trunk-Sets sind exakt wie am Gerät übernommen, inkl. Schiefstände (z.B. trägt ether1 kein VLAN20/99) — bewusst nicht geradegezogen.
    • crs326-branch: ether1-Ziel sw1 #8 → crs326-core ether8; ether5/ether6 = Sonos Arbeitszimmer (aktuell kein Link, die Schlafzimmer-Paare hängen am Core); ether9–23+sfp waren als „disabled" modelliert, sind live aber enabled (Bootstrap Rev4) → korrigiert.
    • Standort-Fehler gefunden: crs326-branch und ap2 standen in Nautobot auf Technik UG, real Technik OG (die Doku war richtig, das Modell falsch) → korrigiert.
    • Status-Endzustände: sw1/sw2/cudy2 → Decommissioned (physisch ausgebaut; „Decommissioning" heißt Prozess läuft). Nautobot liefert diesen Status mit, aber nur für circuits.circuit → content_types um dcim.device erweitert statt einen zweiten gleichnamigen anzulegen. cudy1 → Offline (core ether11 hat kein Link; der endgültige Teardown bleibt TP19). sw1s 4 Duplikat-Ports (gi8/gi11/gi15/gi16 neben GE*) entfernt.
    • ✅ Compliance-neutral verifiziert: Generate-Intended + Compliance über alle 4 Geräte neu gelaufen → weiterhin 51/56 mit exakt denselben 5 bekannten Drifts. Grund: der core-Zweig des Templates iteriert nur Interfaces mit Präfix vlan und hält die Bridge-VLAN-Tabelle als Jinja-Konstante. Umkehrschluss als Dauerregel: die vlanXX-Descriptions auf dem Core sind NICHT kosmetisch — sie landen als comment= in der intended config.
    • 🔍 Nebenbefund, halbkonfigurierte Ports (NICHT angefasst, gehört zu TP19/TP5): core ether11 (cudy1) hat pvid=10 ohne VLAN10-Membership → funktionslos; core ether3 hat pvid=1 bei untagged-VLAN40-Membership; core ether2 ist gleichzeitig statisches untagged-Member von VLAN1 und VLAN40; branch ether9–23 tragen pvid=20 ohne jede Membership. Alles Bring-up-Reste, betriebsneutral (kein Link), aber sie gehören in die VLAN1-Aufräumrunde.
  • ✅ Nachtrag 2026-08-13 — Compliance-Abdeckung erweitert auf 68 Checks (63/68 grün; seit 2026-08-16 68/68). Drei neue ComplianceFeatures, alle auf Anhieb grün:
    • wifi-steering (/interface wifi steering) — sichert die TP3.3-Reparatur ab.
    • bridge-port + bridge-vlan (/interface bridge port / /interface bridge vlan) — der L2-Layer war bis dahin komplett unüberwacht: der core-Zweig emittierte gar keine Bridge-Sektion, der AP-Zweig nur die VLAN-Tabelle ohne Ports. Jetzt für alle drei Geräteklassen im Template.
    • 🔑 Bewusst als Literal-Block, nicht aus DCIM abgeleitet. Der As-built trägt akzeptierte Schiefstände (core ether3 ist untagged-Member von VLAN40, hat aber pvid=1; core ether2 hängt zusätzlich in VLAN1; branch ether9–23 haben pvid=20 ohne Membership). Würde man pvid aus untagged_vlan generieren, meldete die Compliance genau diese bekannten Altlasten als Dauerdrift. Der Literal-Block liefert das, was hier gebraucht wird: Drift-Erkennung gegen den Ist-Zustand.
    • ⚠️ /interface bridge selbst bleibt außen vor — trägt die gerätespezifische admin-mac, wäre pro Gerät verschieden und ist kein Fleet-Standard.
    • 🐞 Falle dabei: Compliance vergleicht gegen das letzte BACKUP, nicht gegen das Live-Gerät. Nach dem Steering-Deploy meldete sie erst Drift auf wifi-configuration/wifi-steering, weil das Backup vom Nachmittag stammte. Reihenfolge nach jeder Geräteänderung: Backup → Intended → Compliance.
  • ✅ Compliance-Backlog ABGEARBEITET (2026-08-16, User-Auftrag „beseitige autonom alle Compliance-Drifts") — Ergebnis: 68/68, 0 Drifts.
    • Was entfernt wurde (alle vom Typ extra, also am Gerät vorhanden und in der intended config bewusst nicht vorgesehen): ap1+ap2 /ip dns static 192.168.88.1 router.lan (Werks-defconf, zeigte auf eine IP, die es im Netz nicht gibt — der Core hat sein eigenes, korrektes router.lan → 10.40.99.1); crs326-branch vlan10 "TEMP Mgmt VLAN10" + 10.40.10.2 (Bring-up-Interim). Der fünfte Drift, crs326-core 10.40.41.211, war schon vorher über den ConfigPlan TP19-vlan1-core-address gefallen.
    • Richtung der Beseitigung bewusst „am Gerät entfernen", nicht „ins Template aufnehmen" — das entspricht der Absicht, die im Switch-Zweig von mikrotik_routeros.j2 seit 2026-08-13 schriftlich steht („Bewusst NICHT emittiert … gehoeren entfernt"). Die frühere Zurückhaltung („potenzielle Rettungspfade") ist mit dem User-Entscheid vom 2026-08-16 aufgehoben; der Mgmt-Fallback aus VLAN10 entfällt damit, Rückweg im Notfall = MAC-Winbox am selben Draht.
    • Weg: 3 manuelle GC-ConfigPlans (TP22-drift-aps-defconf-dns, TP22-drift-branch-temp-vlan10), Governance gewahrt — kein Direkt-Write. Vorher /system backup je Gerät ({ap1,ap2,branch}-pre-drift-cleanup-20260816.backup). Deploy gestaffelt: erst die beiden APs, am Gerät gegengeprüft, dann branch.
    • 🔑 Neue Falle gelernt: POST /plugins/golden-config/config-plan/ ist 405 Method Not Allowed — das ViewSet ist read-only. ConfigPlans entstehen ausschließlich über den Job Generate Config Plans (bei plan_type=manual landet der commands-TextVar als config_set auf jedem ausgewählten Gerät). Das erklärt, warum es bis dahin kein Skript gab und alle 17 Vorgänger-Pläne per Hand entstanden sind.
    • ✅ Verifikation am Gerät, nicht am Job: ap1/ap2 list_dns_entries → total: 0 bei unveränderten 28 bzw. 3 WLAN-Clients; branch /interface vlan → nur vlan99, /ip address → nur 10.40.99.2/24, ping vom Core 4/4 mit 0 % Loss. /interface bridge vlan/export byte-identisch zu vorher — die Gegenprobe darauf, dass kein neuer Drift entsteht (bridge steht dort statisch als tagged-Member von VLAN10, unabhängig vom SVI). Danach Backup → Intended → Compliance, alle 4 Geräte mit frischem *_last_success_date.
    • 🛠️ Tooling neu: nautobot/gc-configplan.py — legt manuelle Pläne über den Job an, löst Device-UUIDs einzeln per Name auf und assertet plan.device.display und config_set gegen das Ziel (die alphabetische Sortier-Falle aus TP21.1 kann damit nicht wiederkommen), verweigert jedes config_set mit <<REDACTED>> (Regel 1) und jede print-Zeile, kann gestaffelt deployen (--deploy-ids) und kapselt die Reihenfolge Backup → Intended → Compliance als --refresh-compliance. Dry-Run ist Default.
  • 🔒 GOVERNANCE — verbindliche Deploy-Regeln (festgeschrieben 2026-08-13, VOR dem Scharfschalten von enable_deploy):
    1. Config Plans werden ausschliesslich feature-/manual-scoped erzeugt, NIEMALS als „full intended". Ein Voll-Push schriebe wörtlich passphrase="<<REDACTED>>" auf die APs (= alle WLAN-Clients raus, inkl. ~26 Shellys) bzw. private-key="<<REDACTED>>" auf den Core (= WG-Hub tot, FM/DW-Spokes weg). Jede Sektion, die <<REDACTED>> enthält (/interface wifi security, /interface wireguard), ist vom Push generell ausgeschlossen.
    2. Das AP-Template wird NIE zeilenweise auf ein laufendes Gerät gepusht. Die Provisionierung setzt zwischenzeitlich vlan-filtering=no → das reisst den vlan99-Mgmt-Pfad ab, die restlichen Zeilen kämen über dieselbe SSH-Session nie an (halb konfigurierter AP). Voll-Reprovisionierung ausschliesslich per Datei-Upload + /import (routeros/.secrets/ap{1,2}-prov-base.rsc). Verschärfend: device-mode=home blockt auf den hAPs scheduler/fetch → die übliche Auto-Revert-Absicherung steht dort nicht zur Verfügung; der VLAN1-untagged-Rettungspfad darf nie wegoptimiert werden.
    3. Vor jedem Push ein /system backup auf dem Zielgerät. Das GC-Backup ist wegen des Scrubbings kein vollwertiges Restore-Artefakt.
  • ✅ Schritt 4 ENTSCHIEDEN (User 2026-08-13): Variante (a) — keine Secret-Injection, bleibt wie es ist. Ursprünglich geplant war „WLAN-PSKs + WG-Privkey via Nornir-Secrets injizieren"; die Governance-Regel 1 (unten) hat das überholt: Sektionen mit <<REDACTED>> sind vom Push generell ausgeschlossen, die Secrets werden also nie gebraucht. Konsequenz, bewusst akzeptiert: Secrets bleiben dauerhaft außerhalb von Modell und Push, DR läuft weiter über ~/.secrets/ + routeros/.secrets/* + routeros/ap1-wg-hub.privkey; „Gerät vollständig aus dem Modell wiederherstellbar" gilt nur ohne Secrets. Die verworfene Alternative (b) hätte Regel 1 aufweichen müssen — ein Render-Fehler hätte dann WLAN bzw. WG-Hub abgeschossen. Damit ist TP22 inhaltlich abgeschlossen.
  • Schritte: (1) AP-GC-Template: mikrotik_routeros.j2 um role == "Wireless AP"-Zweig erweitern (Bridge/vlan-filtering/ether1-Trunk/vlan99-Mgmt/6 SSIDs ohne PSK/L3) → APs komplett aus GC generierbar; (2) Nornir/NAPALM-Adoption aller 4 (SecretsGroup „RouterOS Nornir"; einheitliches Admin-PW seit 2026-08-08 vereinfacht das), Backup/Compliance-Job grün; (3) enable_plan+enable_deploy scharf — erst Dry-Run/Plan, dann kontrollierter Push; (4) Secret-Handling WG-Privkey (core) + WLAN-PSKs (bleiben aus dem Git-Template, via Nornir-Secrets injizieren); (5) Datenmodell-Nachträge: branch/core-Ports final, sw1 (außer Betrieb) + cudy1-Status.
  • Nutzen: Drift-Erkennung + reproduzierbarer Rebuild jedes Geräts aus dem Modell.
  • Abh.: TP21 ✅ (Plattform/Modell/Templates), TP20 ✅, einheitliches Admin-PW ✅. Danach ist der kritische Pfad frei für TP5 (Deny-by-default) → TP6 (CAKE).

TP23 — DNS-Policy & Self-Service-Ausnahmen ▸ 📋 KONZEPT ENTSCHIEDEN (2026-08-14), Umsetzung offen

  • Auslöser: sobald gefiltertes DNS scharf ist, gehen einzelne Seiten/Dienste kaputt. Es braucht Ausnahmen ohne Admin-Eingriff — per User/Client/Gruppe/global, temporär oder permanent, mit Oberfläche. Bedarf ist belegt, nicht vermutet: der bisherige Workaround war „Client von Hand auf 1.1.1.1 stellen", regelmäßig (User 2026-08-14).
  • 🔴 Zentrale Reihenfolge-Regel: genau dieser Workaround IST der DNS-Bypass, den TP5 schließen will → erst der Ersatz, dann die Sperre. Ohne funktionierende Ausnahme-Oberfläche verliert u.a. der Entwicklungsrechner samt Agenten-Toolchain seinen Ausweg — und DNS-Blocks äußern sich dort nicht als klare Meldung, sondern als diffus kaputte Build-Umgebung.
  • Zwei Befunde, die die Architektur bestimmen:
    1. DoH ist nicht das Problem. Clients sprechen plain mit dem lokalen Resolver, der macht DoH nach oben — die empfohlene Auflösung des DoH/Split-Horizon-Konflikts, bf.lan bleibt heil. Was Seiten blockiert, ist die Policy. ➡️ Nie einen „DoH aus"-Schalter bauen, sondern Policy-Ausnahmen.
    2. Identität existiert nur dort, wo die Client-IP sichtbar ist. Router-DoH aggregiert alle Clients hinter einer Quelle — CF sieht nur den Core. Per-User-Identität bei CF verlangt einen Agent (WARP; bei NextDNS/ControlD deren CLI) → auf Shelly/Sonos/TV unmöglich. RouterOS kann keine per-Client-Ausnahme (keine DNS-Views; /ip dns static wirkt domain-weit — verifiziert). ➡️ Ein lokaler Resolver mit Client-Policy ist unvermeidbar.
  • ✅ Architektur-Entscheid: Filter zentral bei CF, Identität + Ausnahmen lokal. Die Filterliste bleibt vollständig bei Cloudflare (Core via DoH, unterwegs via WARP); lokal wird nie eine Filterliste vorgehalten, nur die Diffs. Vorteile: kein RAM-Thema auf dem Core (eine Adlist kostet ~1,9 KB/Domain, 50k ≈ 95 MB — Konkurrenz mit Conntrack/DHCP/WG), eine Policy-Quelle statt zweier, gleiche Regelbasis zuhause wie unterwegs.
  • ✅ Resolver-Wahl: AdGuard Home (läuft bereits, 10.40.20.16). Vergleich gegen die realen Anforderungen:
    • Pi-hole disqualifiziert: spricht kein DoH → bräuchte cloudflared/unbound als Sidecar (vierte QUIC-Komponente neben den drei TP16-Connectoren). Dazu belegtes Bruchrisiko v5→v6 (FTL/API/Web neu, Docker-Env-Variablen tot).
    • AGHs „keine Gruppen"-Schwäche verschwindet hier, weil Clients als CIDR eintragbar sind → 10.40.30.0/24 = die IoT-Gruppe. Die VLAN-Segmentierung ist bereits die Policy-Grenze. Dazu natives DoH, per-Device-ID auch bei wechselnder IP, „Unblock for this client", API + HA-Integration.
    • Technitium = ernsthafte Alternative (autoritativer DNS + DHCP + Apps, networkGroupMap); seine IP/Subnet-Bindung trifft uns kaum, weil die IPs per Reservierung faktisch statisch sind. Neubewertung legitim (die TP4-Ablehnung galt einer anderen Frage) — sinnvoller Anlass wäre, DNS und DHCP zusammenzuführen. Eigener TP, nicht dieser.
    • Verworfen: eigene Self-Service-UI bauen · per-Device-DoH via NextDNS/ControlD (Agent-Zwang, bricht bf.lan) · Umbrella ($30–45/User/Jahr) · Cloudflare verlassen (wirft TP16/TP16.1 weg) · Adlist auf dem Core.
  • Zuweisung ist nativ möglich: /ip/dhcp-server/lease hat die Property dhcp-option (verifiziert) → Option 6 pro Lease, bei 54 Reservierungen ein Konfigurationsschritt. (Nebenfund: Leases haben address-lists → automatische Aufnahme in eine Firewall-Liste, direkt nützlich für die TP5-JIT-Gruppe.)
  • ✅ WARP im LAN abschalten — Mechanismus: Cloudflare „Managed Networks". Der Client sucht bei jedem Netzwechsel einen selbst gehosteten TLS-Endpunkt, prüft dessen SHA-256-Fingerprint und zieht ein Device-Profil, das WARP deaktiviert. Beacon auf NAS/pve, nicht auf den Core (certs=0). Beseitigt die ICMP-Falle vom 2026-08-13 (ref_warp_local_icmp_trap) an der Wurzel.
    • ✅ Kollision aufgelöst (2026-08-14): die frühere Sorge war, dass TP16-Schritt 5 vw auf WARP-geschützt hebt und damit „WARP zuhause aus" bricht. Der User-Constraint aus TP16.2 streicht genau das — vw bleibt bypass und verlangt nie Device-Identity. Damit steht der Managed-Networks-Weg frei. Rest-Risiko nur für Dienste, die künftig doch Device-Identity verlangen sollten → dann Access-Policy zusätzlich auf die Heim-IP (bf4-home-ip existiert bereits).
    • ✅ Kein Verlust beim Fremdnetz-Zugriff: FM/DW laufen über den WG-Hub (TP10), nicht über WARP. Managed Networks schaltet nur im Heimnetz ab.
    • ⚠️ Free-Plan-Verfügbarkeit des Features nicht belegt → im Dashboard gegenprüfen. Holzhammer-Alternative (WARP-Endpunkte per Firewall sperren) nicht empfohlen: bricht still, Client probiert Transport-Fallbacks, trifft auch Gäste.
  • Umsetzung in Stufen — Sperre zuletzt:
    1. DoH auf dem Core scharf → schließt TP21.1-TODO 1 (heute plain 1.1.1.1); CA-Bundle nötig.
    2. WARP-Managed-Network (Beacon auf NAS/pve) → WARP zuhause aus.
    3. AGH in den Pfad, zweite Instanz + Config-Sync, beide per DHCP als DNS 1 + 2.
    4. Client-Gruppen per CIDR (ein VLAN = eine Gruppe); per-Lease Option 6 nur für Abweichler.
    5. HA-Buttons für temporäre Freigaben + JIT (AGH-API, RouterOS-REST, CF-API), RouterOS-scheduler als Backstop, damit Ablauf auch ohne HA greift.
    6. 🔴 Erst jetzt (in TP5): DNS-Bypass sperren — Vorbedingung: Freigabe über HA funktioniert nachweislich, und die permanente Ausnahmegruppe dns-bypass-exempt (Entwicklungsrechner + Build-/Agent-Tooling) steht. Dann: nur Resolver dürfen 53/853 nach außen (schließt Geräte, die eigene Resolver hardcoden — Smart-TV mit 8.8.8.8 ist der Klassiker).
  • Grenzen, offen benannt: ein Resolver im Datenpfad ist unvermeidbar (zwei Instanzen federn ab, ⚠️ AGH hat kein natives HA → Config-Sync ist Betriebsaufwand, sonst gilt nach Failover eine andere Policy) · keine Blockseite bei HTTPS (gilt für jede DNS-Filterlösung, auch Umbrella) · CF-Log-Retention 24 h im Free-Plan — laut User ausreichend, weil Ausnahmen JIT entstehen und ältere Fälle reproduziert werden.
  • ⚠️ 802.1X löst die IoT-Identität NICHT: Consumer-IoT (Shelly, Sonos) hat keinen 802.1X-Supplicant; der Standardweg ist MAB = wieder MAC-basiert → kein Identitätsgewinn gegenüber der DHCP-Reservierung. TP8 bleibt richtig, aber als Sicherheitsthema (dynamische VLAN-Zuweisung, kein MAC-Spoofing bei Managed-Geräten), nicht als DNS-Identitätsquelle.
  • Abh.: TP21.1 TODO 1 (DoH auf core), TP5 (Firewall/Bypass-Sperre — Reihenfolge s.o.), TP16 Schritt 5 (Access-Kollision), TP16.1 (falls WARP zuhause bleiben soll), TP8 (nur Sicherheit). Kein Blocker für TP5 → TP6, aber TP5 darf die Bypass-Sperre nicht vorziehen.

TP24 — Unterbrechungsfreie Stromversorgung (DC-USV, 48V-Bus) ▸ 🔜 KONZEPT — aus TP15 herausgelöst (User-Entscheid 2026-08-16) — docs/hw-refresh-requirements.md §9

  • Warum ein eigener TP: Die USV hing bisher als Unterpunkt in TP15 (HW-Refresh Switching/Router) mit. TP15s Kernauftrag — CRS326-Beschaffung, Bring-up, Core-Tausch — ist seit 2026-08-08 durch; die Stromversorgung ist als einziger Rest übrig geblieben und hat mit Switching sachlich nichts zu tun. Sie ist ein eigenständiges Infrastruktur-Thema mit eigener Einkaufs-, Verkabelungs- und Entscheidungslogik (Elektrik, Batterie, Standort) und blockiert nichts auf dem kritischen Pfad. Darum eigener TP.

  • Was es ist (Begriffsklärung): eine Pufferbatterie für die Netz-/Infrastruktur-Hardware — keine klassische AC-USV mit 230-V-Steckdosen. Ein 48-V-Gleichspannungsbus versorgt die Geräte direkt an ihrem DC-Eingang; die Batterie hängt am selben Bus und übernimmt bei Netzausfall ohne Umschaltlücke. Der Umweg AC→DC→AC→DC (und damit dessen Dauerverlust) entfällt.

  • Zielbild (Aufbau, unverändert gültig aus der TP15-Recherche):

    Teil Typ Preis Wirkungsgrad
    Netzteil MeanWell LRS-150-48 (150 W, 48 V) ~€30 ~88 %
    DC-USV-Modul MeanWell DDR-120-48 (120 W) ~€80 ~95 % (pass-through)
    Batterie LiFePO4 12,8 V / 20 Ah (256 Wh) ~€50 —
    DC-DC 12 V MeanWell SD-100A-12 (100 W, für NAS/pve) ~€30 ~85 %
    Summe ~€190

    230 V AC → LRS-150-48 → 48-V-Bus → Switches direkt · → DC-DC 12 V → DS923/pve · → DDR-120-48 → LiFePO4.

  • Warum 48 V: PoE-Technik arbeitet nativ mit 44–57 V (kein Wandler nötig), höhere Spannung = kleinere Ströme = weniger Leitungsverlust, und 48 V ist der Telecom-/USV-Standard.

  • Laufzeit (256 Wh): ~2,5 h bei 100 W reiner DC-Last · ~5,5 h wenn nur Switch + PoE (45 W) · ~2 h wenn ein Inverter für den Mac mini mithängt.

  • Effizienz-Argument (der eigentliche Grund für DC statt AC): eine AC-USV verheizt bei 100 W Dauerlast ~25–40 W (≈ €66–105/Jahr), die DC-Variante 5–12 W (≈ €13–32/Jahr) — bei praktisch gleicher Anschaffung (€190 vs. €150–250). Der Mehrpreis amortisiert sich über den Dauerbetrieb.

  • ✅ DC-Eingang vorhanden (geprüft): crs326-core + crs326-branch (12–57 V, DC-Jack) · ap1/ap2 hAP ax² (12–57 V, alternativ PoE-in). ⚠️ Adapter/Sonderfall: DS923+ (externes 12 V/7 A, 4-Pin Mini-Fit JR) · pve/N100 (12 V Hohlstecker) · Mac mini hat gar keinen DC-Eingang (eingebautes Netzteil → nur über Inverter).

  • Offene Entscheidungen (aus TP15 mitgenommen, unverändert offen):

    1. Mac mini auf USV? Braucht einen Inverter (~15–20 % Verlust + Komplexität). Vorfrage: läuft er überhaupt 24/7? Wenn nein, reicht ein Graceful-Shutdown-Script auf USV-Warnung.
    2. Standort/Verkabelung: Der Bus soll laut Skizze im Arbeitszimmer/Technik OG sitzen — der L3-Core steht aber in Technik UG. Entweder zweiter Zweig nach unten oder Bus dorthin, wo der Core steht. Diese Frage ist durch den Core-Umzug neu zu stellen (die Skizze stammt aus der Zeit, als oben der Verteiler geplant war).
    3. Arris-Kabelmodem: vorentschieden auf Grid (muss ohnehin am Netz bleiben, DC-Zweig nach unten wäre Aufwand für ~1–2 W).
    4. Batteriegröße: 20 Ah als Startwert; 50 Ah wenn die gepufferte Last über Switch+AP hinaus wächst.
    5. Monitoring/Shutdown: wie meldet die USV einen Netzausfall an NAS/pve (Signalkontakt vs. nichts)? Ohne das gibt es kein sauberes Herunterfahren, nur Durchhalten bis die Batterie leer ist.
  • Nutzen konkret: bei Stromausfall bleiben Core-Switch, Branch-Switch und beide APs online → Netz, WLAN und der WG-Hub überleben (der WAN-Uplink nur, solange auch der Arris Strom hat, s. Entscheidung 3). Für NAS/pve zählt vor allem das kontrollierte Herunterfahren statt des harten Abschaltens.

  • Abh.: keine harten. TP15 ✅ (die zu versorgende Hardware steht und hat DC-Eingänge). Verwandt: TP18 (Garage-Nodes brauchen je Site eine Stromversorgung), TP11 (USV-Zustand ins Monitoring). Kein Blocker für TP5/TP6.

  • Historie: war bis 2026-08-16 Unterpunkt von TP15 („🔋 DC-USV: MeanWell LRS-150-48 + DDR-120-48 + LiFePO4 20Ah (~€190), 48V-Bus; CRS326 DC-input passt"). Vollständige Recherche inkl. Schaltbild, Effizienzvergleich und Gerätetabelle: docs/hw-refresh-requirements.md §9 (+ §10 D/E/F für die offenen Fragen).

TP-R — Router-Capabilities-Sweep ▸ FERTIG (docs/research/routeros-capabilities.md)

  • Leitsatz: nativ vor Container, NAS vor Router (128MB-Flash-Limit).
  • Sofort nativ (~0 RAM): RoMON · Netwatch · Scheduler + Auto-Backup/Mail · SNMP→externes Monitoring · mDNS-Repeater · spectral-scan · 802.11r/k/v (Fast-Roaming statt Mesh) (die Sweep-Fassung nannte hier CAPsMAN — das ist seit 2026-08-08 abgeschafft; 11r/k/v läuft standalone weiter, s. TP3/TP3.3) · Back-to-Home/WireGuard.
  • Auf NAS: FreeRADIUS(+LDAP) · NPM · icinga/Grafana · CF-Tunnel.
  • Weglassen: LoRa (keine HW) · OpenFlow · CALEA · ZeroTier (CPU-Fresser bis ~60 % → WireGuard) · lokales dude.
  • ✏️ Korrektur Reverse-Proxy: RouterOS 7.22+ HAT einen nativen (/ip/reverse-proxy, ACME/SNI) — aber ersetzt NPM NICHT (kein WebSocket/gRPC/Sticky, nur IPv4-Backends, TLS frisst Router-CPU) → NPM bleibt auf NAS.
  • Speist TP8/9/11/12.

Reihenfolge / Abhängigkeiten

🎯 Kritischer Pfad (Stand 2026-08-16)

TP5 (Deny-by-default)  ──►  TP6 (CAKE)  ◄ Projektziel
  1. TP5 — Firewall & Inter-VLAN-Policy. Runbook liegt (docs/tp5-deny-by-default-runbook.md), Etappe 1 ist ein reiner Log-Lauf. Mit im Paket: TP7.1 (SSDP-Reflector für die macOS-Sonos-App) — gemeinsam testen, sonst prüft man zweimal dieselbe Strecke. Keine offenen Blocker.
  2. TP6 — QoS/CAKE + Call-Quality-A/B-Test. Das eigentliche Projektziel; hängt nur an TP5.

Eine harte Reihenfolge-Regel dazwischen: TP5 darf die DNS-Bypass-Sperre erst schließen, wenn TP23s Ausnahme-Oberfläche steht — der bisherige Workaround „Client von Hand auf 1.1.1.1" ist genau dieser Bypass. Alles andere aus TP5 ist davon unabhängig und kann vorher laufen.

Follow-ups nebenher (blockieren nichts)

  • DoH-Cloudflare auf crs326-core (heute plain 1.1.1.1) — ist zugleich TP21.1 TODO 1 und TP23 Stufe 1.
  • TP24 — DC-USV (seit 2026-08-16 eigener TP, aus TP15 herausgelöst).
  • TP19 — VLAN1-Client-Retirement / cudy-Teardown: Rest sind Media-Check, pve-.41.100, Inventar/DNS und die 5 bekannten Compliance-Drifts (Bring-up-Altlasten).
  • TP3.3 — WLAN-Coverage-Kur: 802.11k/v ist repariert (2026-08-13); offen sind Re-Walk + Dead-Spot-Kartierung.
  • TP10 — DDNS-Auto-Updater für bf4.emsenhuber.net (auf dem Core möglich, device-mode erlaubt scheduler).
  • TP14.4 — CF-Access-Service-Token rotieren.
  • Doku-Aufräumen (docs/doku-cleanup-session.md) — Punkte A–D am 2026-08-16 abgearbeitet, E teilweise.

Nachgelagert / research-getrieben

TP8 (Identity/802.1X) · TP11 (Monitoring) · TP12 (IPv6) · TP13 (Botschafts-WLANs) · TP16.1 (natives CF-Mesh auf dem Core) · TP16.2 (Härtung bypass-Dienste) · TP18 (Garage-Objektspeicher) · TP20.1 (Vaultwarden → Platform-PG) · TP23 (DNS-Policy, s. Regel oben).

✅ Durch (Fundament — nicht mehr planen, nur nachschlagen)

TP1 · TP2 · TP2.1 · TP3 (Rebuild 2026-08-08) · TP3.1 · TP4 (Rolle seit 08-08 auf dem Core) · TP7 · TP7.2 · TP10 (Kern) · TP14 (+14.1/14.2/14.2a/14.3/14.5) · TP15 · TP16 (Kern) · TP17 · TP19.1 · TP20 · TP21 · TP21.1 · TP22 · TP-R. Verworfen: TP9 (Gateway-HA, User-Entscheid 2026-08-08). Obsolet: Omada-PVID-Fix (User-Entscheid 2026-07-22 — „nie wieder aufgreifen").

📜 Historische Fassung dieses Abschnitts (bis 2026-08-14 fortgeschrieben, hier bewusst erhalten)

🎯 AKTUELLER KRITISCHER PFAD (Stand 2026-07-16, überschreibt die Reihenfolge unten): TP20 (Platform-Postgres) → TP21 (Nautobot) — Ziel: fertig vor CRS326-Produktivsetzung, spätestens Montag 2026-07-20. TP18 (Garage-S3) und die Komodo-Abhängigkeit wurden am selben Tag als Blocker entfernt/entkoppelt (s. TP20), um diese Deadline zu halten — beide werden später nachgezogen, nicht mehr vorher abgewartet. ➡️ Stand-Update 2026-08-08: TP20/TP21-Plattform steht, Core-Cutover (TP21.1) + WLAN-Rebuild (TP3) + VLAN20-Migration (TP14) sind durch. Neuer Rest-Pfad: TP22 (Device-Adoption + Compliance-Push) → TP5 (Deny-by-default) → TP6 (CAKE, das Ziel). Kleine Follow-ups davor: DoH-Cloudflare auf core, USV (TP24). ➡️ Stand-Update 2026-08-13 (Abschluss): TP22 ist DURCH (alle 5 Schritte; Fleet adoptiert, Deploy-Pipeline scharf, Datenmodell as-built, Secret-Handling entschieden) → kritischer Pfad jetzt: TP5 (Deny-by-default) → TP6 (CAKE). Kleine Follow-ups davor: DoH-Cloudflare auf core, DC-USV (TP24), Doku-Aufräumen (docs/doku-cleanup-session.md, vorbereitet 2026-08-14 — dieses Dokument beschreibt an mehreren Stellen noch die Topologie vor dem Core-Cutover). ➡️ Neu 2026-08-14: TP23 (DNS-Policy & Self-Service) hängt an TP5 — und zwar in beide Richtungen: TP23 braucht TP5s Firewall, TP5 darf aber die DNS-Bypass-Sperre erst schließen, wenn TP23s Ausnahme-Oberfläche steht (der bisherige Workaround „Client auf 1.1.1.1" ist dieser Bypass). Ebenfalls in TP5 gezogen: TP7.1 (SSDP-Reflector für die macOS-Sonos-App). Kabel core ether4→ether2 (Sideboard/TV) entfällt (2026-08-14): gelöst wurde am Port, nicht am Kabel (User-Vorgabe „Patchfeld-Nummerierung wird nicht wegen einer Config geändert") — ether4 steht seit 08-13 auf untagged VLAN40, End-to-End-Beweis am 08-14 erbracht, s. TP21.1 Punkt 4. Historisch: TP22 war das nächste Arbeitspaket — TP17 (Shelly-Migration) ist parallel dazu vollständig abgeschlossen (alle Shellys auf VLAN30, User-bestätigt). Grund für den Nautobot-First-Ansatz: Governance-Entscheid „Config exklusiv über Nautobot" (s. oben) macht Nautobot zur Voraussetzung für praktisch jede weitere Router-/Switch-Änderung. Die Punkte 3-4 unten (Omada-PVID-Fix etc.) sind obsolet (User-Entscheid 2026-07-22 — Omada spielt seit TP15-Korrektur keine Rolle mehr, nie wieder aufgreifen) — ganze Liste braucht mal eine Aufräum-Session, hier nur der aktuelle Fokus vorangestellt. ✅ TP16 (CF-Tunnel Round-Robin/macvlan-Host-Isolation → reziprokes 3-Tunnel-HA) KERN UMGESETZT 2026-07-10 (#1033 strukturell weg). Offen nur Access-Strang (WARP/vw) = user-gated. As-Built docs/cf-tunnel-mapping.md §10.

  1. TP1 (GE8) → TP2 (Omada Controller). ✅ erledigt 2026-06-15.
  2. TP4 (Router-Cutover L3-Infra + WAN-Edge). ✅ erledigt 2026-06-15 (L3) + 2026-07-01 (Edge-Cutover, FB physisch weg).
  3. Jetzt: Omada-PVID-Fix — Omada Access-Profile korrigieren (PVID 10/20, untagNetworkIds bereinigen, sicher P3/P5/P6 zuerst), dann IP-Migration NAS/macmini/pve auf VLAN-Subnetze + DNS-Einträge updaten. IP-Migration NAS/pve = TP14 (NAS ✅ 2026-07-10, pve 🔜 Session B).
  4. Dann: TP5 (Deny-by-default fertigziehen — Sonos-/HA-/mDNS-Holes + WAN-Input-Seal stehen bereits; TP5.1 = DNS-Redirect + Live-Torch für Low-Trust-Geräte/Airfryer hängt dran) → TP6 (QoS/CAKE). TP7 (mDNS) ✅ 2026-07-02 (inkl. HA/vlan20), TP3 (WLAN/CAPsMAN) ✅ 2026-06-18, TP3.1 (ap2-VLAN-Trunk) ✅ 2026-07-01, TP3.2 (P750W/Discovery) ✅ 2026-07-02, Sonos-Migration ✅ 2026-07-02 (alle 10 auf VLAN 40). ⏳ TP3.3 (WLAN-Coverage-Kur) offen — Fortsetzung neue Session (docs/wifi-p750w-coverage-and-map.md).
  5. TP10 (VPN) ✅ Kern durch (2026-06-27) + Edge-Cutover (2026-07-01) → DURCH. Offen nur: DDNS-Auto-Updater.
  6. Nachgelagert/Research-getrieben: TP8 (Access), TP9 (HA), TP11 (Monitoring + CF-Tunnel-Track-B/C), TP12 (IPv6), TP13 (Spaß).
  7. Foundation (2026-07-14): TP15 (MikroTik-Switching / Omada-WLAN, 2× CRS326 bestellt) ∥ TP20 (Platform-Postgres, single-site replication-ready) → TP21 (Nautobot Golden Config) ∥ TP20.1 (Vaultwarden→PG + 2-Site-HA). TP20 hängt an TP18 ① (Garage-Bucket für pgBackRest).