Spoke-FritzBoxen akzeptieren am WG-Peer nur Quell-IPs aus 10.40.0.0/16; WARP-Clients kommen ueber cf-mesh mit 100.96.0.0/12 und wurden verworfen. src-nat auf wg-hub-Egress legt die Quelle in den AllowedIPs-Raum.
Template (Soll) + idempotenter Job tp10-mesh-nat (manual Plan, nie full-intended). Compliance 145/145, end-to-end verifiziert (Mac/WARP -> 10.95.10.1).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
C1 - Der Core haelt jetzt 27 /ip dns static (vorher 10). Die 12 toten Namen
loesen wieder auf: 9 Caddy-Frontends, tv.bf/mfc.bf, das bisher nirgends
existierende ap3.bf, dazu pve.bf und pve-mcp.bf von der Host-IP auf .40 und
die 5 bare Namen. Neuer GC-Job tp45-c-dns; er liest die Zeilen aus dem
Template selbst und macht daraus einen idempotenten Upsert.
C2 - AGH entdoppelt: upstream_dns nur noch 10.40.20.1, alle 26 Rewrites raus.
Der Core ist damit die einzige Quelle. /admin ist lokal wieder erreichbar
(header-frei 200, mit CF-Connecting-IP weiter 404 - der Guard bleibt zu).
C3 - ds-bf-Ingress-Origin auf https://10.40.20.40:443 + SNI (CF-Write).
Oeffentlich 200 in 0,24 s statt 000 nach 20 s.
Abnahme: Geraete-Read (27 Statics), Compliance 145/145 / 0 Drifts, dig vom
Mac fuer alle Namen, /admin lokal 200, ds-bf oeffentlich 200, Filter
(doubleclick -> 0.0.0.0) intakt, vw/hass/n8n/gitea unveraendert.
Offener Befund: der Core reicht AAAA weiter und liefert damit fuer die bare
Namen die CF-IPv6. Heute wirkungslos (kein globales IPv6 im Netz), aber
latent - bewusst dokumentiert statt geaendert.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Zwei Christoph-Direktiven am 2026-09-21, in dieser Reihenfolge:
1. "mach die shellys sofort rueckgaengig" -- HA bindet sie per IP ein.
2. "wir sollten alle vlan30-aenderungen rueckgaengig machen" -- Sperr-Risiko
Nuki: ein Fehlgriff haette aussperren koennen.
Neu in tp45-leases.py: die Tabellen SHELLY/REVERT und das Kommando `revert`
(nur die 26 Shellys) sowie `restore` (alles zurueck auf vor B2, inkl.
NSPanelPro-Reservierung und wiederangelegter CCU-Zeile). In gc-configplan.py
die Jobs tp45-v30-revert und tp45-v30-restore; _lease_move nimmt jetzt ein
tag, damit die :log-Zeilen den Job unterscheidbar machen.
Abnahme: Geraete-Read (alle 36 Adressen zurueck, NSPanelPro ohne Reservierung,
CCU .232 wieder da) und --refresh-compliance 145/145, 0 Drifts.
Zwei Funde, die als Merkregel bleiben: `make-dynamic` gibt es in RouterOS 7.24
nicht (der Rueckweg ist `remove`), und der GC-Plan-Status beweist nichts --
beide Laeufe meldeten Failed, waehrend der Job-:log alle 36 Statements
protokollierte.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
36 DHCP-Reservierungen wandern aus dem Pool-Fenster (.5, .8, .202-.244)
in die Schema-Baender .11-.14 / .151-.176 / .177-.196. Dazu die CCU-Leiche
.232 (status=waiting, last-seen=never; die CCU lebt in VLAN20) entfernt und
SONOFF-NSPanelPro von einer dynamischen Lease auf .249 zu einer statischen
Reservierung auf .12 gepinnt.
Neuer Job `tp45-v30-leases` in gc-configplan.py. Er laedt MOVES/REMOVE/ADDS
per importlib aus tp45-leases.py, statt sie zu kopieren -- die Tabelle dort
ist die SSoT des Umzugs, eine zweite Kopie liefe still auseinander.
Warum der Job zwei Selektor-Formen traegt: die erste Fassung schrieb
ueberall `[/ip dhcp-server lease [find where ...]]`. In `[:len ...]` ist das
Syntaxfehler (`expected command name (line 1 column 55)`, live gemessen) --
Golden Config meldete trotzdem Job SUCCESS und Plan Completed, das Geraet
blieb unveraendert. Aufgefallen ist es an der Zahl der Log-Zeilen: von 38
`:if`-Statements hat kein einziger Zweig geloggt. Richtig ist
`[/ip dhcp-server lease find where ...]` ohne Klammerpaar um `find`;
die Argument-Form `set [find where ...]` bleibt.
Abnahme am Geraet, nicht am Plan-Status: 39 Leases im Soll-Schema, .232 und
.249 geraeumt, NSPanelPro dynamic=false; Compliance 145/145, keine Drifts.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Die 7 DHCP-Pools starteten alle bei .100 und ueberlappten damit die beiden
Oktett-Baender, die dieses TP gerade belegt: .100-.149 (Server-Hosts) und
.150-.199 (App-Services). In VLAN20 ist das eine echte Kollisionsflaeche --
nautobot .154-.158, gitea .157, pve-mcp .173 und shelly-mcp .180 tragen ihre
IP GERAETELOKAL und stehen in keinem Lease-Eintrag, der Core kann sie also
nicht schuetzen und einem DHCP-Client .154 anbieten.
Entscheid 2026-09-21: nur pool-v20 und pool-v30 werden verengt, v20 zuerst.
VLAN10/40/50/90/99 bleiben bei .100-.200 -- dort gibt es kein Schema-Band,
das etwas gewoenne, und VLAN40/VLAN10 haetten sonst 11 Sonos (.191-.201),
den iPhone (.196) und den Mac (.82) mitgezogen.
VLAN20 kostet die Verengung nichts: eine statische Reservierung (nas-bf
.20.10, in .10x) und 0 dynamische Leases.
pool-v30 bleibt bewusst unangetastet -- dort liegen 35 statische
Reservierungen IM Fenster .200-.249 (26 Shellys .202-.226/.233, SwitchBot
.227, Twinkly .228, Withings .230/.231, lichterketten .236/.237, Nuki
.241/.244, CCU .232). Erst Schicht B zieht die um, dann pool-v30.
Nebenbei faellt damit die .30.250-Kollision: der Sonoff Dongle-M sitzt dort
statisch, der Pool reicht heute bis .250.
Neuer Job `tp45-pool-v20` (gc-configplan.py) mit gezaehltem Selektor: ein
`find where`, das 0 trifft, sieht wie "nicht vorhanden" aus statt wie
"kaputt" -- ein blankes `set [find where name=...]` liefe ins Leere und der
Job meldete Erfolg.
🔴 Lehre aus dem ersten Fehlversuch: jedes config_set-Statement muss auf EINER
Zeile klammer-vollstaendig sein. Mit `:if (...) do={` am Zeilenende geht
RouterOS in den Fortsetzungs-Modus und zeigt einen anderen Prompt, der
netmiko-Treiber verliert sein Muster und der Deploy stirbt mit
`E1015: Pattern not detected: ...@crs326\-core\].*$|#.*$)` -- Plan auf
Failed, ohne etwas geschrieben zu haben.
Abnahme: Gerate-Read pool-v20 = 10.40.20.200-10.40.20.249 (total=50,
used=0); DHCPOFFER-Probe auf vmbr0.20 -> 10.40.20.200 mit
Domain Name Server 10.40.20.30, 10.40.20.31; Probe-Lease wieder entfernt;
Compliance 140/140 ohne Drift.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- nautobot/tp45-agh-firewall.py (neu): pflegt die vier `TP5-E2: LAN->AGH dns*`-
Regeln im `local_config_context_data.firewall_filter` (add/remove, Dry-Run
default, Unified Diff). Bewusst NICHT `ingest-core-firewall.py` -- das ist ein
RE-BASELINE-Werkzeug und wuerde die Aenderung mit sich selbst absegnen.
- gc-configplan.py: Jobs `tp45-agh-fw` (add + move vor die Log-Regel; `caddy`
dahinter, damit der AGH-Block zusammenbleibt) und `tp45-agh-dhcp`.
Fix in `create_plans`: unter derselben `change_control_id` liegen nach einem
zweiten Lauf zwei Plaene. Die Auswahl folgte der REST-Reihenfolge und traf den
ALTEN -> der config_set-Guard brach ab, obwohl der neue Plan korrekt erzeugt
war. Jetzt gewinnt der juengste; die alten bleiben Historie.
Fund: `find where dst-address=<bare-IP>` matcht in RouterOS nie (nur `~` als
Regex). Die Zeile stand als "Verifikation" in
docs/transaktionen/TP23-agh-firewall.commands.rsc:7 und hat dort seit dem
2026-08-25 immer `0` gemeldet, ohne je etwas zu pruefen. Gleiche Klasse wie
`nc -z` (09-01): eine Pruefung, die 0 meldet, sieht wie "fehlt" aus, nicht wie
"kaputt".
Abnahme: Reihenfolge am Geraet (vier neue Regeln vor dem Drop), Compliance
140/140 / 0 Drifts mit `RouterOS - ip-firewall-filter` (ordered, frisch
bewertet, `actual` traegt die neuen Zeilen), `dig` vom VLAN10-Client gegen
.30/.31/.36/.37 alle beantwortet.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Oktett-Klasse .150-.199 (App-Services). Nautobot gibt damit .30/.31 an
AGH frei. .157 ist gitea, deshalb nimmt redis .158.
- Datenmigration ueber neues nautobot/tp45-ip-move.py: Nautobot verbietet
das Aendern einer IP nach dem Anlegen -> anlegen/primary_ip4 umhaengen/
Altobjekt loeschen, in dieser Reihenfolge
- Stack-Redeploy (PUT /api/stacks/203), Caddy-Backend, Core-Literal *24
ueber GC deployt (Plan 51a90e25) mit Wirksamkeitspruefung am Geraet
- caddy/deploy.sh: push_verified() ersetzt den push||push-Fallback.
`pct push` meldet Exit 0 AUCH wenn es die Quelle nicht lesen kann, der
Fallback griff deshalb nie und `caddy validate` pruefte die ALTE Datei
-> gruene Abnahme bei unveraendertem Zustand (Symptom: 502 bei
gesundem Backend)
- 39 Referenzen in 28 Dateien mitgezogen; lebende Doku nachgezogen
(CLAUDE.md, ip-plan.md, cf-tunnel-mapping §0-Kopf, TP16.3/TP31-Prosa)
- GC-Compliance 140/140, 0 Drifts
Neue Funde (TP45-Prosa + CLAUDE.md):
- Die Core-Adressliste `nautobot` wird von GC nirgends gerendert -> ein
Recovery von null erzeugt einen Core, in den Nautobot nicht mehr per
SSH kommt. Gleiche Klasse wie die fehlenden DHCP-Reservierungen.
- Regel *24 (Literal) und *28 (Liste) sind Dubletten; der Literal steht
bei 0 Paketen. Zusammenlegung bleibt offen.
- gc-manual-plan.py: /api/extras/job-log-entries/ liefert 404 (Nautobot
3.x) -> bei gescheitertem Deploy fehlt die Fehlermeldung.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
RouterOS exportiert comment=TP38-OOB-MGMT ohne Quotes (kein Leerzeichen),
das Template hatte sie gesetzt -> bridge-port Drift. Fix: unquoten.
Der Deploy-`place-before=[find ...]` wurde von Netmiko ignoriert (Regel
landete hinter dem drop); im Job jetzt add + move.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Template mikrotik_routeros.j2: ether23 auf core+branch als OOB-Port
(pvid=99, admit-only-untagged-and-priority-tagged) + VLAN99 untagged=ether23.
Branch bekommt einen deny-by-default-Input-Filter (8 Regeln, src-Rahmen
spiegelt /ip service available-from), Core eine Regel "OOB->core mgmt".
Neue Skripte: ingest-tp38-oob.py (seedet firewall_filter in beiden
local_config_context_data) und der gc-configplan.py-Job tp38-oob (Deploy).
ConfigPlans angelegt+approved, Deploy steht noch aus.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
(A) DHCP-Search-Domain der 7 Netze emsenhuber.net -> bf.emsenhuber.net ueber
die GC-Pipeline (kein Direkt-Write): Template Z.133 gehoben, per
push-gc-template.py ins bf-golden-config-Repo (7a4068c4), GitRepo-Sync,
neuer manueller Job tp42-dhcp-domain in gc-configplan.py (set [find], weil
add das existierende Netz doppeln wuerde), Deploy an crs326-core.
Live 7/7 = bf.emsenhuber.net, Compliance 140/140 keine Drifts.
Nebenbefund: Remote-GC-Template hing dem lokalen hinterher (noch *.bf.lan +
router.lan) -> mit an den Live-Stand angeglichen.
Loest den Kernkonflikt: DHCP verteilte bare emsenhuber.net, Records leben
unter bf.emsenhuber.net -> bare macmini loest jetzt beim Lease-Renew.
(B) .16 (adguard_bf1) abgebaut: lag im mit FM geteilten Portainer-Stack 195
zusammen mit adguard_fm2 (.17, Standort FM). Statt Stack-Delete die compose
auf nur .17 getrimmt + redeployt (Prune). .16 weg, .17 laeuft (kurzer Blip).
Nautobot-Soll (Scope 4) an TP37 gekoppelt, separat.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>