Files
bf-infra/docs/teilprojekte.md
Christoph Emsenhuber e76289c8e9 feat 🔌 shelly-mcp + TP17 Shelly-Migration VLAN1→VLAN30; TP14.3 dormante Stacks bereitgestellt
TP17 (neu): shelly-mcp (eigenbau, FastMCP StreamableHTTP :3030 + Status/Health-Seite,
in .claude/launch.json) mit Tools discover/get/set_sta[guarded]/verify/reservation_plan.
Recon: 16 Shellys live, alle Gen3/4 (RPC WiFi.SetConfig, auth=off). Inventar-Fix:
s6/kueche_kuehlschrank MAC 0c:72… → real d0:cf:13:d8:18:fc.

TP14.3: 7 dormante Stacks als .new.yml bereitgestellt (nur lokal, gitignored) — SSoT
in docs/teilprojekte.md nachgezogen.

mcp/INVENTORY.md: shelly-mcp-Zeile + Richtungsentscheid „MCPs zentral, nicht Client;
MikroMCP+shelly-mcp sind pragmatische Interims, später zusammenziehen".

.gitignore: __pycache__/*.pyc.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 01:15:32 +02:00

383 lines
48 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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/APs (ap1=.1, ap2=.2) · `.11–20` Switches (ALLNET=.11, Omada=.12). GW je VLAN = `.1`.
## Ist-Stand (Kurz)
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: Omada-PVID-Fix** (MAIN→PVID10, INFRA→PVID20, untagNetworkIds bereinigen) + IP-Migration Geräte + TP5 Deny-by-default fertigziehen (Sonos-/HA-/mDNS-Holes und WAN-Input-Seal stehen bereits).
> 🎯 **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, prüfen ob nach Migration noch nötig; (c) **cudy1 = verbleibender aktiver VLAN1-Leak** (Dual-DHCP-Race, Doku Memory `project_vlan1_dhcp_incident`). Bis dahin sind diese Griffe Krücken, kein Zielzustand.
> ⚠️ **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.
---
## Teilprojekte
### TP1 — ALLNET Core-Switch finalisieren ▸ ✅ FERTIG (2026-06-15)
- ✅ 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.
### TP3 — WLAN / CAPsMAN ▸ ✅ KERN FERTIG (2026-06-18) — `routeros/tp3-capsman.rsc`
- ✅ **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.
### 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`.
### 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`
**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)
- ✅ **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 (Sonos/HA-Holes + WAN-Seal gebaut, Deny-by-default steht aus)
- **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.
- ✅ **Bereits live** (verifiziert `list_firewall_rules` 2026-07-02):
- `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` (aktuell noch permissiv nach fasttrack); UNTRUSTED/GUEST auf Internet-only pinnen; MGMT isolieren; IoT→INFRA-Whitelist bauen.
- **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 ✅.
### 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: `/ip/dns set mdns-repeat-ifaces=vlan10,vlan20,vlan40,vlan30,vlan90,bridge`
(RouterOS 7.23.1; `vlan90`+`bridge`=VLAN-1 seit TP7.2 2026-07-03). MAIN/INFRA/IOT/GUEST/VLAN1 ↔ MEDIA. Reversibel (Feld leeren).
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) ▸ vorgemerkt
- **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 ▸ research-gated
- **„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" ▸ fun, low-prio
- Nutzlose SSIDs mit Botschaft („Kanal 500 bitte nicht nutzen"), offenes WLAN mit Captive-Portal das auf **statische Info-HTML** zeigt (Nachbarschafts-WLAN-Tuning, Auto-Kanalwechsel). Idee: wer's sieht, kann auch stören → Infoseite = beste Adresse. ⚠️ **Recht prüfen** (Impressum/TMG, Störerhaftung bei offenem WLAN — User privat). RouterOS-Hotspot/Captive-Portal kann das.
### TP14 — Host/Container-IP-Migration VLAN1→VLAN20 (Docker-Cutover NAS+pve) ▸ ⏳ WIP — `docs/nas-vlan20/RUNBOOK.md`, Handoff `docs/nas-vlan20/handoff/`
- ✅ **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)**.
### 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!**).
- ⏸️ **VERTAGT — omada-controller Inform-Adresse → `10.40.20.175` + sw2 re-adopt** (2026-07-10): **Login an der Controller-UI `https://10.40.20.175:8043` schlägt fehl** (Christoph kommt nicht rein) → Recovery blockiert, verschoben. **Unkritisch:** sw2 „Disconnected" ist reiner Management-Loss — sw2 (ES208G Easy-Managed) switcht mit letzter Config weiter, Netz läuft (Live-Traffic bestätigt). Kein Auto-Fix: Open API hat kein Inform/Hostname-Setting, kein Controller-Admin-Login lokal, **sw2 = statische IP `.41.12` → keine DHCP-Option-138-Recovery**. **Wenn Login wieder geht:** UI → Settings → Controller → „Controller Hostname/IP" = `10.40.20.175`, dann sw2 forget+re-adopt (L3-Discovery). Vorher **Controller-Login klären** (Passwort-Reset / Recovery — Creds liegen nicht lokal).
- 🐞 **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 ▸ ⏳ TEIL (Stacks ✅ 2026-07-11)
- ✅ **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`** (offen): ap1 bf.lan-DNS (MikroMCP, ap1 via `10.40.10.1`), CLAUDE.md, `~/.claude.json`, `docs/ip-plan.md`, CF-Ingress-Rest.
- ↪️ **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 ▸ 🔜 (ZULETZT)
- Erst wenn TP14.1–14.3 durch (alle Origins/Connectoren in VLAN20) → dann TP5 (Deny-by-default) starten. Reines Reihenfolge-Gate, kein eigener Change.
### TP15 — HW-Refresh: Core-Switch + OG-Verteiler ▸ 🔜 PLANUNG — `docs/hw-refresh-requirements.md`
- **Ziel:** ALLNET-Core (sw1, 5,8W) + 2 Branch-Switches (je 1,5W) ablĂśsen durch energieeffizientere, zentral managebare LĂśsung mit PoE + DC-USV.
- **Architektur:** Unten (Diele) = lĂźfterloser Core-Switch mit PoE, PoE-powered von oben (gepuffert). Oben (Arbeitszimmer) = Switch mit PoE + DC-USV (48V-Bus, LiFePO4).
- **Anbieter-Recherche:** MikroTik (RouterOS) ⭐, TP-Link Omada, Ubiquiti UniFi, Netgear, Aruba Instant On, Zyxel, Cisco. **Favorit: MikroTik** (RouterOS, Config-as-Code, MikroMCP).
- **Kandidaten unten:** CRS318-16P-2S+OUT (16 PoE, lüfterlos, ~16W, ~€286) ⭐ oder CRS326-24G-2S+RM (24 Ports, lüfterlos, ~10W, ~€200) + PoE-Injektor.
- **Kandidaten oben:** CRS328-24P-4S+RM (24 PoE, 4× SFP+, ~25W, ~€400) ⭐ oder CRS418-8P-8G-2S+5axQ2axQ-RM (16 Port, 8 PoE, WiFi 6, ~19W, ~€403).
- **WiFi:** hAP be³ Media (WiFi 7, 5× 2,5G, ~€179) als separater AP ⭐ vs. CRS418 (WiFi 6 integriert).
- **DC-USV:** MeanWell LRS-150-48 + DDR-120-48 + LiFePO4 20Ah (~€190) — 48V-Bus, ~2,5h Laufzeit bei 100W.
- **Energiebilanz:** Neue Lösung ~35–60W (je nach Variante) vs. IST ~15W (nur Switching). Jahreskosten +€50–105.
- **Offene Entscheidungen:** Variante (A/B/C/D), Mac mini auf USV?, PVE-Standort, WiFi-Strategie.
- **Nächster Schritt:** Entscheidungen treffen, dann Konfiguration + Bestellung.
- Abh.: TP14 (IP-Migration VLAN1→VLAN20 sollte abgeschlossen sein, damit Ziel-IPs feststehen).
### TP16 — CF-Tunnel: Round-Robin killen + reziprokes HA ▸ ✅ KERN UMGESETZT (2026-07-10) — `docs/cf-tunnel-mapping.md §10`, #1033
> **✅ 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`. **Offen: Schritt 5 Access-Strang (`vw` bypass→geschützt via WARP, `hass`/`ha-mcp` 400/403 → iPhone-WARP-Enrollment) = user-gated; `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. „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`.
### TP17 — Shelly-Übersiedelung VLAN1 → VLAN30 (IOT) + `shelly-mcp` ▸ ⏳ WIP (Recon ✅ 2026-07-11) — `mcp/shelly/`
- **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.
- **Offene Schritte:** `shelly-mcp` Server (Tools discover/get/set-sta/verify) fertig + registrieren → VLAN30-DHCP-Reservierungen je MAC anlegen (MikroMCP) → **Pilot** (unkritischer Always-on, z.B. `bad_luefter`/`technik_pve`) → HA/ioBroker-Wiederanbindung verifizieren → Rest in Batches → 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`.
### 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 · **CAPsMAN + 802.11r/k/v (Fast-Roaming statt Mesh)** · 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
> ✅ **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ß).