diff --git a/docs/handoff-20260824-session.md b/docs/handoff-20260824-session.md
index 8760d1d..3749df8 100644
--- a/docs/handoff-20260824-session.md
+++ b/docs/handoff-20260824-session.md
@@ -20,6 +20,12 @@ Fünf Commits: `d245d10`, `e8f7f80`, `0a7f2e2`, `2f18319`, `e9a5b36`.
## 🔴 Womit die nächste Session anfangen sollte
+> ⚠️ **ÜBERHOLT am 2026-08-24 (User-Entscheid):** die Rotation ist **nicht** der Sessionstart.
+> Sie ist zu **[TP30 — Credential-Rotation](teilprojekte.md)** geworden, einem Sammel-TP, der
+> **gegen Projektende gebündelt** abgearbeitet wird — Begründung: es kommen weitere Passwörter
+> hinzu, und der teure Teil sind die Konsumenten (HA-Re-Auth, Stack-Redeploy), den man nicht
+> mehrfach zahlen will. **Der Inhalt unten bleibt sachlich gültig** und steht jetzt in TP30.
+
**Passwort-Rotation, Etappe 2.** Die Vorarbeit steht komplett, der Rest ist die einzige Sache mit
echter Dringlichkeit — das `admin`-Passwort ist zweimal in ein Transkript geraten (22.08. und, durch
meinen eigenen Enumerationsfehler, nochmal am 23.08.; **derselbe Wert**, da alle vier Geräte ihn teilen).
diff --git a/docs/teilprojekte.md b/docs/teilprojekte.md
index 6bc40f3..be3da5f 100644
--- a/docs/teilprojekte.md
+++ b/docs/teilprojekte.md
@@ -91,10 +91,13 @@ ab jetzt bei jeder Änderung mitgezogen, nicht nur die TP-Abschnitte.)*
> bleibt WLAN-Client; `s9` zurückgebaut) · ✅ **TP17.3 ERLEDIGT** (Ursache **`eco_mode`** bewiesen;
> `s23` + `s26` repariert; **`eco_mode` flottenweit aus**, 22/22 außer `s24`) ·
> 🔜 **TP17.2 aktiviert** (Shelly-Gerätepasswort, an die PSK-Rotation gekoppelt) ·
-> 🔑 **Rotation des MikroTik-Admin-Passworts** — seit 08-23 bewusst **hinter** TP17.1 eingereiht.
+> 🔑 **Alle Passwort-/Token-Rotationen sind seit 2026-08-24 TP30** (Sammel-TP) — **bewusst
+> gebündelt gegen Projektende**, User-Entscheid. Damit ist die ältere Einreihung „hinter TP17.1"
+> gegenstandslos (TP17.1 ist verworfen). **Etappe 1 — die Service-Accounts — ist durch;**
+> das `admin`-Passwort selbst ist **nicht rotiert** und bleibt es bis dahin bewusst.
> 📌 **Einstieg für die nächste Session: [`handoff-20260824-session.md`](handoff-20260824-session.md)**
-> — Start bei der **Passwort-Rotation Etappe 2** (Nautobot + HA auf die neuen Service-Accounts
-> umhängen, dann `admin` rotieren). Ältere Sitzungsvorschläge:
+> — ⚠️ dessen Empfehlung „Start bei der Passwort-Rotation Etappe 2" ist durch den TP30-Entscheid
+> **überholt**; der Rest der Übergabe gilt. Ältere Sitzungsvorschläge:
> [`handoff-20260822-naechste-schritte.md`](handoff-20260822-naechste-schritte.md).
> ✅ **`crs326-core` ist transaktionsfrei** (seit 2026-08-22, geprüft): ein gemeldeter Drift ist ab
> jetzt **echt**, nicht beabsichtigt — `python3 nautobot/gc-transaktion.py status`.
@@ -925,6 +928,167 @@ die Redundanzfrage oben.
- **Abh.:** keine harten. Profitiert von TP25 (Abdeckung/AP-Zuordnung) und TP11 (Monitoring), wenn es
dahin kommt. **Kein Blocker für irgendetwas.**
+
+### TP30 — Credential-Rotation (Sammel-TP) ▸ 🔜 **ANGELEGT 2026-08-24, bewusst GEBÜNDELT gegen Projektende**
+> **User-Entscheid 2026-08-24:** „ich würde gerne daraus einen eigenen punkt machen. ich bin mir
+> sicher, dass es noch öfters passwörter zu rotieren geben wird. das machen wir dann alles gegen
+> projektende."
+>
+> **Warum ein eigener TP und nicht vier verstreute Follow-up-Zeilen:** die Rotationen teilen sich Werkzeug,
+> Fallen und vor allem den **teuren Teil — den Konsumenten** (HA, Nautobot, MikroMCP, `~/.secrets`).
+> Wer sie einzeln macht, zahlt den Re-Auth-Aufwand mehrfach. Gebündelt ist es **ein** Durchgang je
+> Gerät. Deshalb steht hier die Liste, nicht der Termin.
+
+- **Auslösekriterium (statt Datum):** wenn die offenen TPs abgearbeitet sind bzw. Christoph das
+ Projekt schließt. 🔴 **Harte Ausnahme, die den Termin vorzieht:** ein *neuer* Leak, ein
+ fremdzugängliches Gerät oder ein Personenwechsel — dann sofort und einzeln, nicht gebündelt.
+
+| # | Credential | Umfang | Anlass | Vorarbeit | Preis der Rotation |
+|---|---|---|---|---|---|
+| 1 | **MikroTik `admin`** | core · branch · ap1 · ap2 (**ein** geteilter Wert) | 🔴 **zweimal in ein Transkript geraten** (2026-08-22 via `sshpass -p`; 2026-08-23 durch einen Enumerationsfehler) | ✅ **Etappe 1 durch** (s.u.) | nach Etappe 2 nur noch `~/.secrets/credentials.env` + `~/.claude.json` + MikroMCP-Neustart |
+| 2 | **IoT-WLAN-PSK** (`sec-iot`) | `dirtyvader-iot`, 26 Clients | steht **im Klartext** im TP17-Text ⇒ auch in der **Git-Historie**; Redigieren holt ihn dort nicht raus | Hebel `multi-passphrase` am Core verifiziert vorhanden, Tabelle leer | 23 Shellys per Skript + **3 von Hand** (`.249` NSPanel Pro, `.244` Nuki Bridge, `.227` SwitchBot) |
+| 3 | **Shelly-Gerätepasswort** | 23 Shellys | Härtung (bisher **keine** Auth auf den Geräten) | ✅ Werkzeug fertig (`migrate.py cmd_auth` + Digest-Fallback) | **je Gerät ein HA-Re-Auth-Flow** — das ist der Aufwand, nicht `SetAuth` |
+| 4 | **CF-Access-Service-Token** (`CF_API_TOKEN`) | Cloudflare-Zugang, s. TP14.4 | ⏳ **läuft am 2027-01-09 ohnehin ab** — harter Termin, unabhängig vom Projektende | Runbook liegt (`tp14.4-cf-access-token-runbook.md`) | Token neu ausstellen + `~/.secrets` nachziehen; **account-owned**, anderer verify-Endpoint |
+
+> ℹ️ **#3 ist TP17.2 und bleibt dort inhaltlich zu Hause** — hier steht es, weil es denselben
+> Geräte-Durchgang wie #2 benutzt (ein Reboot statt zwei), nicht als Duplikat.
+
+#### Detail — Radius, Etappe 1 und Etappe 2 (am 2026-08-24 aus den Follow-ups hierher gezogen)
+
+**Vorgeschichte des Entscheids** (Stand bis 2026-08-23, User: „machen wir, aber erst später"):
+⏭️ ~~**Nachgelagert seit 2026-08-23: erst TP17.1, dann rotieren.**~~
+**Revidiert noch am selben Tag (User): beides läuft PARALLEL.** Der Passphrase-Override je MAC
+(s.u.) macht den Flag-Day überflüssig, damit war die Reihenfolge nie technisch nötig — nur
+ökonomisch. Regel für die Parallelität: **ein Gerät, das ohnehin auf Thread/Zigbee wandert,
+bekommt den neuen PSK gar nicht**, sondern nur einen Übergangs-Eintrag in der `access-list`.
+⚠️ Der Entscheid betrifft **beide** Passwörter: der **IoT-WLAN-PSK** steht im Klartext im
+TP17-Text ([Archiv](teilprojekte-archiv.md#tp17)) und damit auch in der Git-Historie — **bleibt
+auf User-Entscheid 2026-08-23 vorerst unverändert**, weil Redigieren ihn nicht aus der Historie
+holt und Rotieren heute ~22 Shellys kostet. ~~Nach TP17.1 sind es deutlich weniger.~~
+🔴 **Hinfällig seit 2026-08-23: TP17.1 ist verworfen** — die Geräte bleiben WLAN-Clients, es
+werden also *nicht* weniger. Die Rechnung „~23 Shellys + 3 Fremdgeräte" gilt unverändert. Anlass: das Core-Passwort ist bei einer Debug-Ausgabe im Klartext in
+ein Sitzungstranskript geraten. Ursache war `sshpass -p`, das es in die **Kommandozeile**
+schreibt — sichtbar in `ps` und in jedem argv-Trace.
+#### 🔑 Radius-Befund 2026-08-23: das Admin-Konto teilen sich FÜNF Konsumenten
+Vor dem Rotieren erhoben — das ist deutlich mehr als „vier Geräte + eine Datei":
+
+| Halter | Nachziehen |
+|---|---|
+| `~/.secrets/credentials.env` | Datei |
+| `~/.claude.json` (MikroMCP-Env, `ROUTER_*`/`CRS326_*`) | JSON + MCP-Neustart |
+| **Nautobot-Stack** `NAUTOBOT_ROUTEROS_USERNAME/PASSWORD` | Stack-Datei + **Portainer-Redeploy auf der DS923** |
+| **HA: VIER `mikrotik`-Integrationen** (`.99.1/.2/.11/.12`, 40 `device_tracker`) | 🔴 **`supports_reconfigure: false` — NICHT skriptbar**, 4× Re-Auth von Hand |
+| Repo-Skripte (`routeros/*.py`, `nautobot/*.py`, `docs/tp*.sh`) | ziehen aus `~/.secrets` → automatisch |
+
+⇒ **User-Entscheid 2026-08-23: Konten trennen statt ein Passwort teilen.** Nicht das Rotieren ist
+das Problem, sondern dass ein einziges `admin` von fünf Stellen benutzt wird — jede Rotation kostet
+sonst wieder vier Re-Auths und einen Stack-Redeploy.
+
+#### ✅ Etappe 1 DURCH (2026-08-23): dedizierte Service-Accounts auf allen vier Geräten
+Rein **additiv**, kein laufender Dienst berührt. Vorher existierte **nur** `admin` (Gruppe `full`).
+
+| Gruppe | Policy | User | Zweck |
+|---|---|---|---|
+| `grp-ha` | `read,api,rest-api,test` | `ha` | HA-Mikrotik-Integration, **lesend** über API 8728 |
+| `grp-nautobot` | `ssh,read,write,test,sensitive` | `nautobot` | Golden Config über SSH/Netmiko (Backup **und** Deploy) |
+
+Passwörter: `MIKROTIK_HA_PASS` / `MIKROTIK_NAUTOBOT_PASS` in `~/.secrets/credentials.env`
+(32 Zeichen, je Dienst eigen, geräteübergreifend gleich).
+🔒 **Angelegt über ein Skript, das die Werte aus der Datei liest** — bewusst *nicht* über
+`manage_user`, dessen `password`-Parameter im Sitzungstranskript landen würde.
+
+**Abnahme, auf allen vier Geräten geprüft:**
+- `ha` liest (`identity`, `dhcp-lease` — am Core 59 Einträge, genau was das Device-Tracking braucht),
+ **Schreibversuch wird abgewiesen**.
+- `nautobot` meldet sich per SSH an und exportiert; **kein REST** (bewusst kein `rest-api` in der Policy).
+- 🎯 **Der Export von `nautobot` ist byte-identisch zu dem von `admin`** (391/56/87/87 Zeilen je Gerät)
+ ⇒ **die GC-Compliance driftet beim Umstellen nicht.** Das war das Hauptrisiko dieser Etappe.
+- `/user` wird von **keiner** ComplianceRule abgedeckt ⇒ die neuen Konten erzeugen selbst keinen Drift.
+
+#### ⏭️ Etappe 2 offen — die Konsumenten umhängen, DANN `admin` rotieren
+1. **Nautobot** auf `nautobot` umstellen (Stack-Env + Portainer-Redeploy), GC-Backup-Lauf als Abnahme.
+2. **HA**: die vier `mikrotik`-Einträge auf den `ha`-User bringen. ⚠️ Reconfigure ist nicht skriptbar;
+ Entfernen-und-neu-Anlegen **wäre** skriptbar, riskiert aber die 40 `device_tracker`-Entity-IDs
+ (sie leiten sich aus der MAC ab, kämen also gleich zurück — **aber erst nachdem** die alten
+ Registry-Einträge weg sind, sonst hängt HA ein `_2` an; dieselbe Falle wie beim Shelly-Rename).
+3. **Erst danach** `admin` rotieren — dann berührt es nur noch `~/.secrets` + `~/.claude.json`.
+
+🔴 **Eigener Fehler, protokolliert:** beim Erheben des Radius hat ein `json.dumps`-Aufruf über
+`~/.claude.json` das **ap1-Passwort im Klartext ins Transkript geschrieben**. Da alle vier Geräte
+dasselbe `admin`-Passwort teilen, ist es **derselbe Wert, der am 2026-08-22 bereits verbrannt ist** —
+keine neue Expositionsklasse, aber ein zweiter Beleg dafür, dass die Rotation fällig ist.
+**Merkregel: Secrets-Enumeration gibt nur Schlüsselnamen aus, nie Werte** — auch nicht abgeschnitten.
+
+✅ **Vorbedingung erfüllt, geprüft 2026-08-23:** alle vier
+Skripte laufen auf `sshpass -e` (`tp6-marker/server.py`, `tp6-monitor-core.sh`,
+`tp25-walk-messung.sh`, `tp25-pin-s22.sh`) — die frühere Notiz, zwei davon hätten die Schwäche
+noch, war veraltet. Übrig blieb nur **`CLAUDE.md`, das `sshpass -p` weiterhin aktiv als Reboot-Weg
+empfahl** — am 2026-08-23 auf `sshpass -e` korrigiert. **Damit ist die Rotation ohne Rückfallgefahr
+fahrbar.**
+Weg: `manage_user set-password` (⚠️ `/user set*` ist per Deny-Policy blockiert), danach
+`~/.secrets/credentials.env` (`ROUTER_*_PASS`/`CRS326_*_PASS`) und `~/.claude.json` nachziehen.
+Es ist **ein** Passwort für alle vier Geräte — die „Überlebensversicherung" aus CLAUDE.md.
+- 🔎 **Nachtrag 2026-08-23 (Recherche-Session, read-only — kein Eingriff): der PSK in der Doku ist
+ noch der LIVE konfigurierte.** Gegengeprüft am Manager `crs326-core` (`/interface wifi security`,
+ Profil `sec-iot` → `cfg-iot` → SSID `dirtyvader-iot`, `datapath.vlan-id=30`,
+ `wpa2-psk,wpa3-psk`) — und zwar **per Fingerprint statt Klartext** (Länge + drei Zeichenpaare),
+ damit der Wert nicht ein zweites Mal in ein Transkript wandert. **Diese Methode ist die Merkregel
+ daraus:** einen Verdacht auf Übereinstimmung prüft man mit Stichproben, nicht durch Ausgeben.
+- ✅ **User-Entscheid 2026-08-23 zur Git-Historie: belassen, im Nachtrag dokumentieren** — **kein**
+ `git filter-repo`. Begründung trägt: genau **eine** getrackte Fundstelle
+ ([Archiv](teilprojekte-archiv.md#tp17)), drei Commits (`e76289c` Ersteintrag 2026-07-11,
+ `f0d1ff9` 08-08, `7ee64b5` Doku-Split 08-23), Remote **ausschließlich** `gitea.emsenhuber.net`,
+ **ein** lokaler Klon. Eine History-Rewrite hätte hier also nichts gerettet, was nicht ohnehin nur
+ auf der eigenen Infrastruktur liegt — und den Preis (alle Hashes neu) für nichts bezahlt.
+- 📊 **Warum die Rotation heute teuer ist — live gemessen 2026-08-23:** **26 der 27 WLAN-Clients des
+ gesamten Netzes hängen auf `dirtyvader-iot`** (`dirtyvader-main` 1, MEDIA/UNTRUSTED/GUEST je 0).
+ VLAN30 führt **41 Leases/Reservierungen** (5 aktuell `bound`, der Rest sind statische
+ Reservierungen ausgeschalteter Geräte). Neben den ~26 Shellys hängen dort **P750W** (Drucker),
+ **Airfryer** (`espressif`, `.180`), **SwitchBot floor-lamp**, **Twinkly** `.228`, **DTUBI** `.8`,
+ **SONOFF-NSPanelPro** und ein **iPhone** `.177`. ⇒ Ein Passwortwechsel ist heute eine
+ Batch-Aktion über ~26 Geräte, und **das ist genau die Rechnung, die TP17.1 verkleinert.**
+- 🔧 **Zwei Umsetzungshinweise fürs spätere Rotations-Paket:**
+ **(a)** `mcp/shelly/migrate.py` hat **kein** Rotate-Kommando (nur `migrate`/`deanchor`/`auth`/
+ `table`) — es bräuchte eine kleine Variante über denselben `set_sta`-Pfad.
+ **(b) Ein Flag-Day ist vermeidbar:** RouterOS 7 kann in `/interface wifi access-list`
+ **Passphrase-Overrides je MAC**. Also neuer PSK aufs Profil, Altgeräte übergangsweise per MAC mit
+ dem alten weiter, danach die Liste leeren. 💡 Nebeneffekt: der TP25-Befund „keine `access-list`"
+ bekäme damit endlich einen Zweck — und seit dem 08-23 gibt es die ComplianceRule
+ `wifi-access-list`, die Einträge dort auch sichtbar macht.
+- **Mitzuziehen bei der Rotation** (legitime, gitignorete Ablagen desselben Werts):
+ `~/.secrets/credentials.env` (+ `.bak-20260808`), `routeros/.secrets/wlan-psks.env`,
+ `mcp/shelly/.env`.
+
+- **CF-Access-Service-Token** — **Rotationsposten #4**, am 2026-08-24 aus den Follow-ups hierher
+ gezogen (stand dort als „TP14.4 — CF-Access-Service-Token rotieren"). Sachlich gehört er zu
+ **TP14.4** (CF-Access-Profil für Claude, Runbook `docs/tp14.4-cf-access-token-runbook.md`); hier
+ steht er, damit die Rotationsliste vollständig ist. ⚠️ Der `CF_API_TOKEN` läuft ohnehin
+ **2027-01-09** ab — spätestens dann ist dieser Posten fällig, unabhängig vom Projektende.
+
+#### Fallen, die für jede künftige Rotation gelten (nicht neu erheben)
+- 🔴 **Niemals `sshpass -p`** — schreibt das Passwort in die Kommandozeile, sichtbar in `ps` und in
+ jedem argv-Trace. **Genau so entstand Leak #1.** `-e` hält es in der Umgebung des Kindprozesses.
+ Der Repo-Stand ist bereits durchgängig `-e`.
+- 🔴 **Passwörter nie per MCP-`password`-Parameter setzen**, sondern per Skript aus `~/.secrets`
+ (`manage_user set-password`; `/user set*` ist per Deny-Policy blockiert).
+- 🔴 **Keine Secrets-Enumeration im Transkript** — das war Leak #2, ohne dass irgendein Werkzeug
+ fehlerhaft war.
+- 🔒 **`wifi-security` ist als ComplianceRule grün, aber NICHT deploybar** (Passphrasen stehen als
+ `<>` im Template). Ist-Stand für #2 immer per REST vom Gerät lesen
+ (`GET http://10.40.99.1/rest/interface/wifi/security`), **nie** aus `wlan-psks.env`.
+- ⚠️ **CAPsMAN, für #2:** eine Profiländerung wirkt erst nach `remote-cap provision` für **beide**
+ APs — und die Abnahme ist eine **frische DHCP-Lease je SSID**, nicht die Registration-Table.
+- ⚠️ **Für #2 zuerst `multi-passphrase` gegen WPA3/SAE testen — auf einer Wegwerf-SSID, nie an
+ `dirtyvader-iot`.** `sec-iot` fährt `wpa2-psk,wpa3-psk`; trägt multi-passphrase SAE nicht, sperrt
+ man **26 IoT-Geräte auf einmal** aus, inklusive Nuki und NSPanel.
+- ⚠️ **`s1`/`s2`/`s3` sind ausgesteckt** und kommen im Dezember mit dem **alten** PSK zurück — der
+ Übergangs-Eintrag muss sie überleben oder sie werden bei Rückkehr von Hand nachgezogen.
+- 🔴 **HA- und Repo-Nummerierung der Shellys weichen ab** (`s10`, `s17`) — **immer über MAC/IP
+ zuordnen, nie über die s-Nummer.**
+- **Abh.:** keine harten; #2 und #3 laufen sinnvollerweise in **einem** Geräte-Durchgang.
+ **Blockiert nichts.** Die frühere Kopplung „erst TP17.1, dann rotieren" ist **gegenstandslos** —
+ TP17.1 ist am 2026-08-23 verworfen, die Geräte bleiben WLAN-Clients.
+
---
## Abgeschlossene Teilprojekte ▸ [`teilprojekte-archiv.md`](teilprojekte-archiv.md)
@@ -1046,109 +1210,10 @@ den Weg, über den man den Fehler zurücknehmen wollte.
Re-Walk + Dead-Spot-Kartierung laufen als Schritt 5 von TP25 — und zwar erst **nach** den Funkparameter-Fixes,
weil vorher gemessene Werte wertlos sind.
- **TP10 —** DDNS-Auto-Updater für `bf4.emsenhuber.net` (auf dem Core möglich, `device-mode` erlaubt scheduler).
-- 🔑 **Admin-Passwort der vier MikroTik rotieren (offen seit 2026-08-22, User: „machen wir,
- aber erst später").** ⏭️ ~~**Nachgelagert seit 2026-08-23: erst TP17.1, dann rotieren.**~~
- **Revidiert noch am selben Tag (User): beides läuft PARALLEL.** Der Passphrase-Override je MAC
- (s.u.) macht den Flag-Day überflüssig, damit war die Reihenfolge nie technisch nötig — nur
- ökonomisch. Regel für die Parallelität: **ein Gerät, das ohnehin auf Thread/Zigbee wandert,
- bekommt den neuen PSK gar nicht**, sondern nur einen Übergangs-Eintrag in der `access-list`.
- ⚠️ Der Entscheid betrifft **beide** Passwörter: der **IoT-WLAN-PSK** steht im Klartext im
- TP17-Text ([Archiv](teilprojekte-archiv.md#tp17)) und damit auch in der Git-Historie — **bleibt
- auf User-Entscheid 2026-08-23 vorerst unverändert**, weil Redigieren ihn nicht aus der Historie
- holt und Rotieren heute ~22 Shellys kostet. Nach TP17.1 sind es deutlich weniger. Anlass: das Core-Passwort ist bei einer Debug-Ausgabe im Klartext in
- ein Sitzungstranskript geraten. Ursache war `sshpass -p`, das es in die **Kommandozeile**
- schreibt — sichtbar in `ps` und in jedem argv-Trace.
- ---
- #### 🔑 Radius-Befund 2026-08-23: das Admin-Konto teilen sich FÜNF Konsumenten
- Vor dem Rotieren erhoben — das ist deutlich mehr als „vier Geräte + eine Datei":
-
- | Halter | Nachziehen |
- |---|---|
- | `~/.secrets/credentials.env` | Datei |
- | `~/.claude.json` (MikroMCP-Env, `ROUTER_*`/`CRS326_*`) | JSON + MCP-Neustart |
- | **Nautobot-Stack** `NAUTOBOT_ROUTEROS_USERNAME/PASSWORD` | Stack-Datei + **Portainer-Redeploy auf der DS923** |
- | **HA: VIER `mikrotik`-Integrationen** (`.99.1/.2/.11/.12`, 40 `device_tracker`) | 🔴 **`supports_reconfigure: false` — NICHT skriptbar**, 4× Re-Auth von Hand |
- | Repo-Skripte (`routeros/*.py`, `nautobot/*.py`, `docs/tp*.sh`) | ziehen aus `~/.secrets` → automatisch |
-
- ⇒ **User-Entscheid 2026-08-23: Konten trennen statt ein Passwort teilen.** Nicht das Rotieren ist
- das Problem, sondern dass ein einziges `admin` von fünf Stellen benutzt wird — jede Rotation kostet
- sonst wieder vier Re-Auths und einen Stack-Redeploy.
-
- #### ✅ Etappe 1 DURCH (2026-08-23): dedizierte Service-Accounts auf allen vier Geräten
- Rein **additiv**, kein laufender Dienst berührt. Vorher existierte **nur** `admin` (Gruppe `full`).
-
- | Gruppe | Policy | User | Zweck |
- |---|---|---|---|
- | `grp-ha` | `read,api,rest-api,test` | `ha` | HA-Mikrotik-Integration, **lesend** über API 8728 |
- | `grp-nautobot` | `ssh,read,write,test,sensitive` | `nautobot` | Golden Config über SSH/Netmiko (Backup **und** Deploy) |
-
- Passwörter: `MIKROTIK_HA_PASS` / `MIKROTIK_NAUTOBOT_PASS` in `~/.secrets/credentials.env`
- (32 Zeichen, je Dienst eigen, geräteübergreifend gleich).
- 🔒 **Angelegt über ein Skript, das die Werte aus der Datei liest** — bewusst *nicht* über
- `manage_user`, dessen `password`-Parameter im Sitzungstranskript landen würde.
-
- **Abnahme, auf allen vier Geräten geprüft:**
- - `ha` liest (`identity`, `dhcp-lease` — am Core 59 Einträge, genau was das Device-Tracking braucht),
- **Schreibversuch wird abgewiesen**.
- - `nautobot` meldet sich per SSH an und exportiert; **kein REST** (bewusst kein `rest-api` in der Policy).
- - 🎯 **Der Export von `nautobot` ist byte-identisch zu dem von `admin`** (391/56/87/87 Zeilen je Gerät)
- ⇒ **die GC-Compliance driftet beim Umstellen nicht.** Das war das Hauptrisiko dieser Etappe.
- - `/user` wird von **keiner** ComplianceRule abgedeckt ⇒ die neuen Konten erzeugen selbst keinen Drift.
-
- #### ⏭️ Etappe 2 offen — die Konsumenten umhängen, DANN `admin` rotieren
- 1. **Nautobot** auf `nautobot` umstellen (Stack-Env + Portainer-Redeploy), GC-Backup-Lauf als Abnahme.
- 2. **HA**: die vier `mikrotik`-Einträge auf den `ha`-User bringen. ⚠️ Reconfigure ist nicht skriptbar;
- Entfernen-und-neu-Anlegen **wäre** skriptbar, riskiert aber die 40 `device_tracker`-Entity-IDs
- (sie leiten sich aus der MAC ab, kämen also gleich zurück — **aber erst nachdem** die alten
- Registry-Einträge weg sind, sonst hängt HA ein `_2` an; dieselbe Falle wie beim Shelly-Rename).
- 3. **Erst danach** `admin` rotieren — dann berührt es nur noch `~/.secrets` + `~/.claude.json`.
-
- 🔴 **Eigener Fehler, protokolliert:** beim Erheben des Radius hat ein `json.dumps`-Aufruf über
- `~/.claude.json` das **ap1-Passwort im Klartext ins Transkript geschrieben**. Da alle vier Geräte
- dasselbe `admin`-Passwort teilen, ist es **derselbe Wert, der am 2026-08-22 bereits verbrannt ist** —
- keine neue Expositionsklasse, aber ein zweiter Beleg dafür, dass die Rotation fällig ist.
- **Merkregel: Secrets-Enumeration gibt nur Schlüsselnamen aus, nie Werte** — auch nicht abgeschnitten.
-
- ✅ **Vorbedingung erfüllt, geprüft 2026-08-23:** alle vier
- Skripte laufen auf `sshpass -e` (`tp6-marker/server.py`, `tp6-monitor-core.sh`,
- `tp25-walk-messung.sh`, `tp25-pin-s22.sh`) — die frühere Notiz, zwei davon hätten die Schwäche
- noch, war veraltet. Übrig blieb nur **`CLAUDE.md`, das `sshpass -p` weiterhin aktiv als Reboot-Weg
- empfahl** — am 2026-08-23 auf `sshpass -e` korrigiert. **Damit ist die Rotation ohne Rückfallgefahr
- fahrbar.**
- Weg: `manage_user set-password` (⚠️ `/user set*` ist per Deny-Policy blockiert), danach
- `~/.secrets/credentials.env` (`ROUTER_*_PASS`/`CRS326_*_PASS`) und `~/.claude.json` nachziehen.
- Es ist **ein** Passwort für alle vier Geräte — die „Überlebensversicherung" aus CLAUDE.md.
- - 🔎 **Nachtrag 2026-08-23 (Recherche-Session, read-only — kein Eingriff): der PSK in der Doku ist
- noch der LIVE konfigurierte.** Gegengeprüft am Manager `crs326-core` (`/interface wifi security`,
- Profil `sec-iot` → `cfg-iot` → SSID `dirtyvader-iot`, `datapath.vlan-id=30`,
- `wpa2-psk,wpa3-psk`) — und zwar **per Fingerprint statt Klartext** (Länge + drei Zeichenpaare),
- damit der Wert nicht ein zweites Mal in ein Transkript wandert. **Diese Methode ist die Merkregel
- daraus:** einen Verdacht auf Übereinstimmung prüft man mit Stichproben, nicht durch Ausgeben.
- - ✅ **User-Entscheid 2026-08-23 zur Git-Historie: belassen, im Nachtrag dokumentieren** — **kein**
- `git filter-repo`. Begründung trägt: genau **eine** getrackte Fundstelle
- ([Archiv](teilprojekte-archiv.md#tp17)), drei Commits (`e76289c` Ersteintrag 2026-07-11,
- `f0d1ff9` 08-08, `7ee64b5` Doku-Split 08-23), Remote **ausschließlich** `gitea.emsenhuber.net`,
- **ein** lokaler Klon. Eine History-Rewrite hätte hier also nichts gerettet, was nicht ohnehin nur
- auf der eigenen Infrastruktur liegt — und den Preis (alle Hashes neu) für nichts bezahlt.
- - 📊 **Warum die Rotation heute teuer ist — live gemessen 2026-08-23:** **26 der 27 WLAN-Clients des
- gesamten Netzes hängen auf `dirtyvader-iot`** (`dirtyvader-main` 1, MEDIA/UNTRUSTED/GUEST je 0).
- VLAN30 führt **41 Leases/Reservierungen** (5 aktuell `bound`, der Rest sind statische
- Reservierungen ausgeschalteter Geräte). Neben den ~26 Shellys hängen dort **P750W** (Drucker),
- **Airfryer** (`espressif`, `.180`), **SwitchBot floor-lamp**, **Twinkly** `.228`, **DTUBI** `.8`,
- **SONOFF-NSPanelPro** und ein **iPhone** `.177`. ⇒ Ein Passwortwechsel ist heute eine
- Batch-Aktion über ~26 Geräte, und **das ist genau die Rechnung, die TP17.1 verkleinert.**
- - 🔧 **Zwei Umsetzungshinweise fürs spätere Rotations-Paket:**
- **(a)** `mcp/shelly/migrate.py` hat **kein** Rotate-Kommando (nur `migrate`/`deanchor`/`auth`/
- `table`) — es bräuchte eine kleine Variante über denselben `set_sta`-Pfad.
- **(b) Ein Flag-Day ist vermeidbar:** RouterOS 7 kann in `/interface wifi access-list`
- **Passphrase-Overrides je MAC**. Also neuer PSK aufs Profil, Altgeräte übergangsweise per MAC mit
- dem alten weiter, danach die Liste leeren. 💡 Nebeneffekt: der TP25-Befund „keine `access-list`"
- bekäme damit endlich einen Zweck — und seit dem 08-23 gibt es die ComplianceRule
- `wifi-access-list`, die Einträge dort auch sichtbar macht.
- - **Mitzuziehen bei der Rotation** (legitime, gitignorete Ablagen desselben Werts):
- `~/.secrets/credentials.env` (+ `.bak-20260808`), `routeros/.secrets/wlan-psks.env`,
- `mcp/shelly/.env`.
-- **TP14.4 —** CF-Access-Service-Token rotieren.
+- 🔑 **Alle Passwort-/Token-Rotationen laufen seit 2026-08-24 unter [TP30](#tp30) — Sammel-TP,
+ gebündelt gegen Projektende (User-Entscheid).** Dort steht die Rotationsliste (MikroTik `admin`,
+ IoT-PSK, Shelly-Gerätepasswort, CF-Access-Token), der Radius der fünf Konsumenten, der
+ Etappe-1-Abschluss und die Fallenliste. **Etappe 1 ist durch**, Etappe 2 wartet bewusst.
- ~~**Doku-Aufräumen** (`docs/doku-cleanup-session.md`)~~ ✅ **abgeschlossen 2026-08-23.** Punkte A–D
(08-16), E (08-18/19) und **F — der Split** (08-23) sind durch: abgeschlossene TPs liegen jetzt in
[`teilprojekte-archiv.md`](teilprojekte-archiv.md), TP25 ist von 394 auf ~100 Zeilen kondensiert,
@@ -1161,7 +1226,8 @@ den Weg, über den man den Fehler zurücknehmen wollte.
**TP8** (Identity/802.1X) · **TP11** (Monitoring) · **TP12** (IPv6) · **TP13** (Botschafts-WLANs) ·
**TP16.2** (Härtung bypass-Dienste) · **TP18** (Garage-Objektspeicher) · **TP20.1** (Vaultwarden → Platform-PG) ·
-**TP23** (DNS-Policy, s. Regel oben).
+**TP23** (DNS-Policy, s. Regel oben) · **TP30** (Credential-Rotation — nicht research-, sondern
+**termin**-gated: gebündelt gegen Projektende, User-Entscheid 2026-08-24).
*(**TP17.1** — Shelly Gen4 → Thread, aktiviert 2026-08-23 — läuft **nicht hier**, sondern im
HA-Projekt `~/dev/ha-xstate`; netzseitig ist nichts zu bauen. Es blockt bewusst die PSK-Rotation.)*
*(**TP16.1** stand hier bis 2026-08-17 fälschlich als offen — es ist seit demselben Tag ✅ abgeschlossen und