diff --git a/docs/teilprojekte-archiv.md b/docs/teilprojekte-archiv.md
index 3b5839f..9863bd2 100644
--- a/docs/teilprojekte-archiv.md
+++ b/docs/teilprojekte-archiv.md
@@ -70,6 +70,8 @@ teuerste Eigenschaft der Datei zerstört, um ihr billigstes Problem zu lösen. B
| [TP43](#tp43) | dw-Standort-DNS-Uplink (site-spezifische Overrides) | ♻️ REOPENED (2026-09-11) — Volltext zurück in teilprojekte.md; interne BF-Namen über WG am dw offen |
| [TP42](#tp42) | Namens-Vereinheitlichung: ein interner Suffix bf.emsenhuber.net | ✅ DURCH (2026-09-02) — `.bf.lan` raus, `.16` abgebaut, Compliance 140/140 |
+| [TP47](#tp47) | Core egress via cf-mesh und WARP-Exit-Validierung | ✅ ABGESCHLOSSEN (2026-10-05) — Egress-Weg verifiziert: 15/15 über cf-mesh, Heim-IP blockt |
+
Dazu am Ende: **[TP25 (Kondensat)](#tp25)** + **[TP25 (Langfassung)](#tp25-lang)** — TP25 ist
✅ abgeschlossen (2026-09-15); der aktive Stand (Kondensat) und die 394-Zeilen-Herleitung
(Messchronik A–G) liegen beide hier.
@@ -2787,3 +2789,75 @@ Gegen die Geräte gemessen (MikroMCP + dig), nicht aus dem Handoff:
**Status:** ✅ **Namensraum vereinheitlicht (2026-09-02)** — ein interner Suffix `bf.emsenhuber.net`,
`.bf.lan` raus, `.16` abgebaut, Compliance 140/140. Offen nur der TP37-gekoppelte Nautobot-Doku-Teil.
Handoff: [`docs/handoff-20260901-tp42-naming.md`](handoff-20260901-tp42-naming.md).
+
+---
+
+
+### TP47 · Gitea #74 — Core egress via cf-mesh und WARP-Exit-Validierung
+> **Christoph:** Validierung des Core-Egress-Weges via Cloudflare Mesh. Ausgangsprüfung gegen neutralen Host
+> und Ziel `164.90.191.7` (DigitalOcean). Korrektur früherer Behauptung: **WARP war am Mac gar nicht
+> verbunden** — `warp=off`, `ip=90.146.33.177` (Heim-IP). Die zuvor gemessene `104.28.207.39` stammte aus
+> einem Test mit nur der `1.1.1.1/32`-Regel, daher über die Heim-IP (403). Beweis: Regel aktiviert
+> `164.90.191.7/32 → egress-cannaleo` → Ziel erreicht mit `200` (168 KiB). Template-Änderung in
+> `nautobot/mikrotik_routeros.j2` Abschnitt `[10b]` (commit `c600c62e`). Doku-Korrektur
+> `docs/warp-egress-native-crs326.md` §6.3.
+>
+> **Bestandsaufnahme (2026-09-27):**
+> - Ziel `164.90.191.7` (DigitalOcean-Origin, kein Cloudflare im Pfad) bereitet `403` bei Heim-IP
+> (`90.146.33.177`, WARP aus), `200` über Core-Routing (`cf-mesh`, `egress-cannaleo`-Table).
+> - `cf-mesh` erreicht den Origin mit `200` (verifiziert: `cdn-cgi/trace` → `ip=104.28.207.39`,
+> `warp=on`, `colo=VIE`). Keine Dedicated Egress IP nötig.
+> - User-Cave: kein Zugriffsmuster, das Heim-IP und diesen Ausgang gemeinsam gegen den Origin zeigt.
+> - Template-Drift: 0 (Secret-Guard bestanden, Push `c600c62e`).
+>
+> #### Durchführung (in einem Durchgang)
+> 1. TP anlegen, Prosa in `teilprojekte.md` schreiben, commit + push
+> 2. Core-Routing-Objekte (`egress-cannaleo` Table, Route, Rule) bereits live auf `crs326-core`
+> 3. Doku `docs/warp-egress-native-crs326.md` §6.3 korrigiert (früherer 403 = Messfehler)
+> 4. Mineral: nicht abzudrehen (seit 21.09. still)
+> 5. `10.101.0.254` taugt nicht als GW (100 % Loss; FM = Liwest; Cave erfüllt)
+
+#### Bestandsaufnahme (live 2026-09-21) — es gibt eine Doku-SSoT, aber keinen Master
+| Was | Wo | Stand |
+|---|---|---|
+| Tunnel, WARP-Connector, Workers, Access-Apps, Hyperdrive, VPC, teamnet-Routen | Nautobot `nautobot.cloud`, Job **`CF Estate Sync`** (`jobs/cf_estate_sync.py`) | ✅ 4 `CloudNetwork` · 16 `CloudService` live |
+| **Ingress-Origin-Ziele** | **nirgends** | 🔴 `_ingress()` speichert nur `ingress_hostnames`, **nicht** das Origin |
+| **DNS-Records** | **nirgends** | 🔴 nicht erfasst — live 24 Records / 8 proxied |
+| **Access-Policies** | **nirgends** | 🔴 nur die Apps selbst |
+| Richtung | CF → Nautobot | 🔴 **Pull-only** („CF bleibt operativer Master") |
+| `docs/cf-tunnel-mapping.md` | Repo | §1–16 eingefrorene As-Built-Historie · §0 = Live-Zeiger (altert: nennt `bf-public` „2 conns", live **12** = 3 AP-Connectoren × 4) |
+
+**Antwort auf „full setup/recovery from scratch": heute nicht möglich.** Es fehlt (a) der Push-Weg,
+(b) Ingress-Origins, (c) DNS, (d) Access-Policies. Und ein frisch erzeugter Tunnel liefert **neue ID
+und neues Secret** → das **Token-Handoff an die Connectoren** (ap1/ap2/ap3) ist **Teil der Recovery**,
+kein Nebenschritt.
+
+#### Umfang
+Tunnel + Ingress **inkl. Origin-Ziele** + DNS-Records + Access-Apps/Policies vollständig nach Nautobot;
+**Push-Renderer** (Dry-Run + Diff); **Recovery-Runbook** für den Aufbau von null.
+
+🔴 **Vor dem ersten Schritt prüfen, nicht annehmen:** welches Nautobot-Objekt DNS-Records tragen kann
+(Kern-`ipam` vs. eigenes Modell), und ob das Ingress-Origin als `extra_config`-Feld reicht oder ein
+eigenes Modell braucht.
+
+- **Abh.:** TP44 Etappe 1/2 ✅ (native `nautobot.cloud`-Modelle + Pull-Job stehen). **Abgrenzung:**
+ Tunnel-**GC/Namensgebung** läuft in TP45 Schicht D; **CF-Writes** aus Nautobot sind hier *das Ziel*,
+ in TP37 ausdrücklich ausgeschlossen — die Grenze verschiebt sich also bewusst mit diesem TP.
+
+#### ✅ ABGESCHLOSSEN (2026-10-05) — Egress-Weg verifiziert
+
+**Nachmessung vom Core aus** (`crs326-core`, via MikroMCP; nicht vom Client — der User war zum
+Messzeitpunkt extern via WARP verbunden, Client-Messungen sind für diesen Pfad wertlos):
+
+| Regel-Zustand | Stichprobe | Ergebnis |
+|---|---|---|
+| **AN** (`egress-cannaleo` → `cf-mesh`) | 15 | **15/15 → 200** (168,1 KiB) |
+| **AUS** (Fallback Heim-IP) | 15 | **14/15 → 403**, 1 Ausreißer |
+
+Der Weg ist **deterministisch**, nicht intermittierend (frühere Formulierung war falsch und ist
+zurückgenommen). Der Ausreißer sitzt reproduzierbar am **ersten** Sample nach dem Umschalten —
+Connection-/Cache-Nachlauf der Vorverbindung, keine Pfad-Instabilität. 🔴 **Merkregel für künftige
+A/B-Messungen: nie ein Einzelsample werten, und die erste Anfrage nach einem Routing-Umschalten
+verwerfen.** Live-Objekte am Core: Routing-Tabelle `egress-cannaleo`, Route `0.0.0.0/0 → cf-mesh`
+(active, distance=1), Regel `164.90.191.7/32 → lookup-only-in-table` (`disabled:false`) — alle drei
+deckungsgleich mit Template `[10b]` in `nautobot/mikrotik_routeros.j2` (Drift 0).
diff --git a/docs/teilprojekte.md b/docs/teilprojekte.md
index 5dcf806..b375195 100644
--- a/docs/teilprojekte.md
+++ b/docs/teilprojekte.md
@@ -2268,58 +2268,6 @@ NAT/mangle/WG sind Template-Literale, der Firewall-Filter kommt als ConfigContex
die FQDNs, die hier Daten werden). **Abgrenzung:** VMID-Umnummerierung bleibt draußen; Token-**Rotation**
läuft in TP30; Tunnel-GC-Namensgebung hier, die **SSoT/Recovery** in TP46.
-### TP47 · Gitea #74 — Core egress via cf-mesh und WARP-Exit-Validierung
-> **Christoph:** Validierung des Core-Egress-Weges via Cloudflare Mesh. Ausgangsprüfung gegen neutralen Host
-> und Ziel `164.90.191.7` (DigitalOcean). Korrektur früherer Behauptung: **WARP war am Mac gar nicht
-> verbunden** — `warp=off`, `ip=90.146.33.177` (Heim-IP). Die zuvor gemessene `104.28.207.39` stammte aus
-> einem Test mit nur der `1.1.1.1/32`-Regel, daher über die Heim-IP (403). Beweis: Regel aktiviert
-> `164.90.191.7/32 → egress-cannaleo` → Ziel erreicht mit `200` (168 KiB). Template-Änderung in
-> `nautobot/mikrotik_routeros.j2` Abschnitt `[10b]` (commit `c600c62e`). Doku-Korrektur
-> `docs/warp-egress-native-crs326.md` §6.3.
->
-> **Bestandsaufnahme (2026-09-27):**
-> - Ziel `164.90.191.7` (DigitalOcean-Origin, kein Cloudflare im Pfad) bereitet `403` bei Heim-IP
-> (`90.146.33.177`, WARP aus), `200` über Core-Routing (`cf-mesh`, `egress-cannaleo`-Table).
-> - `cf-mesh` erreicht den Origin mit `200` (verifiziert: `cdn-cgi/trace` → `ip=104.28.207.39`,
-> `warp=on`, `colo=VIE`). Keine Dedicated Egress IP nötig.
-> - User-Cave: kein Zugriffsmuster, das Heim-IP und diesen Ausgang gemeinsam gegen den Origin zeigt.
-> - Template-Drift: 0 (Secret-Guard bestanden, Push `c600c62e`).
->
-> #### Durchführung (in einem Durchgang)
-> 1. TP anlegen, Prosa in `teilprojekte.md` schreiben, commit + push
-> 2. Core-Routing-Objekte (`egress-cannaleo` Table, Route, Rule) bereits live auf `crs326-core`
-> 3. Doku `docs/warp-egress-native-crs326.md` §6.3 korrigiert (früherer 403 = Messfehler)
-> 4. Mineral: nicht abzudrehen (seit 21.09. still)
-> 5. `10.101.0.254` taugt nicht als GW (100 % Loss; FM = Liwest; Cave erfüllt)
-
-#### Bestandsaufnahme (live 2026-09-21) — es gibt eine Doku-SSoT, aber keinen Master
-| Was | Wo | Stand |
-|---|---|---|
-| Tunnel, WARP-Connector, Workers, Access-Apps, Hyperdrive, VPC, teamnet-Routen | Nautobot `nautobot.cloud`, Job **`CF Estate Sync`** (`jobs/cf_estate_sync.py`) | ✅ 4 `CloudNetwork` · 16 `CloudService` live |
-| **Ingress-Origin-Ziele** | **nirgends** | 🔴 `_ingress()` speichert nur `ingress_hostnames`, **nicht** das Origin |
-| **DNS-Records** | **nirgends** | 🔴 nicht erfasst — live 24 Records / 8 proxied |
-| **Access-Policies** | **nirgends** | 🔴 nur die Apps selbst |
-| Richtung | CF → Nautobot | 🔴 **Pull-only** („CF bleibt operativer Master") |
-| `docs/cf-tunnel-mapping.md` | Repo | §1–16 eingefrorene As-Built-Historie · §0 = Live-Zeiger (altert: nennt `bf-public` „2 conns", live **12** = 3 AP-Connectoren × 4) |
-
-**Antwort auf „full setup/recovery from scratch": heute nicht möglich.** Es fehlt (a) der Push-Weg,
-(b) Ingress-Origins, (c) DNS, (d) Access-Policies. Und ein frisch erzeugter Tunnel liefert **neue ID
-und neues Secret** → das **Token-Handoff an die Connectoren** (ap1/ap2/ap3) ist **Teil der Recovery**,
-kein Nebenschritt.
-
-#### Umfang
-Tunnel + Ingress **inkl. Origin-Ziele** + DNS-Records + Access-Apps/Policies vollständig nach Nautobot;
-**Push-Renderer** (Dry-Run + Diff); **Recovery-Runbook** für den Aufbau von null.
-
-🔴 **Vor dem ersten Schritt prüfen, nicht annehmen:** welches Nautobot-Objekt DNS-Records tragen kann
-(Kern-`ipam` vs. eigenes Modell), und ob das Ingress-Origin als `extra_config`-Feld reicht oder ein
-eigenes Modell braucht.
-
-- **Abh.:** TP44 Etappe 1/2 ✅ (native `nautobot.cloud`-Modelle + Pull-Job stehen). **Abgrenzung:**
- Tunnel-**GC/Namensgebung** läuft in TP45 Schicht D; **CF-Writes** aus Nautobot sind hier *das Ziel*,
- in TP37 ausdrücklich ausgeschlossen — die Grenze verschiebt sich also bewusst mit diesem TP.
-
----
### TP48 · Gitea #75 — Recovery-Lücke: Nicht-GC-Objekte + Evaluation Ansible als Nautobot-Ablösung