--- type: reference description: "One Caddy fronts both hosts." tags: - architecture - network timestamp: 2026-10-18 --- # Network Architecture One Caddy fronts both hosts. It runs as a container on Dankclaw (192.168.0.143) and terminates TLS for every service on the LAN. Pi-hole on Dankclaw answers the service names for LAN clients; Porkbun answers them for the WAN. The G36 router firewall governs external access. SearXNG outbound traffic exits through the Gluetun VPN container. ## Topology As of 2026-08-28 the front door is a two-tier chain: Caddy terminates TLS, Anubis verifies the client (DENY known AI scrapers, CHALLENGE everyone else), and accepted traffic returns to Caddy's internal listener on `:8016`, which proxies every request whole to the Authentik server with the Host header preserved; Authentik's embedded outpost routes on that Host against each proxy provider's `external_host`, serves the login flow in place for sessionless clients, and forwards authenticated traffic back to Caddy's post-auth listener `:8017`, where per-vhost routing reaches the app. See [[Anubis]] and [[Authentik]]. ```mermaid graph TB EXT[Internet] -->|port forwards 80/443| CADDY[Caddy TLS :80 :443 — Dankclaw] LAN[LAN clients] -->|Pi-hole DNS :53| CADDY subgraph Dankclaw[Dankclaw 192.168.0.143] CADDY -->|unwrapped vhosts route direct| DIRECT{direct routes} CADDY -->|guarded vhosts| ANUBIS[Anubis verify :8923] ANUBIS -->|deny AI bots| EXT ANUBIS -->|challenge solved or transport-exempt| INT[Caddy internal listener :8016] INT -->|whole request, Host preserved| AUTH[Authentik embedded outpost :9000] AUTH -->|session valid, provider internal_host| POST[Caddy post-auth listener :8017] POST --> HOME[Homepage file_server] POST --> SEARCH[SearXNG via Gluetun netns :8081] POST --> PROM[Prometheus :9090] DIRECT -->|3000 + ssh 2222| GITEA[Gitea] DIRECT -->|3005| OWEBUI[OpenWebUI] POST -->|authenticated| GRAFANA[Grafana :3000] DIRECT -->|8083 web UI| PIHOLE[Pi-hole] AUTH -->|login UI + sessions| AUTHDB[(Authentik Postgres :5432)] SEARCH -.->|tunnel exit| GLU[Gluetun VPN] GLU -.-> EXT end subgraph Dankserver[Dankserver 192.168.0.235] ROMM[RomM :8085] COPY[Copyparty :3923] CAL[Calibre-Web :8083] PLEX[Plex :32400] JEL[Jellyfin :8096] end POST -->|romm| ROMM subgraph FRANKBOOK["FrankBook-Pro 192.168.0.112"] ARR[*arr stack + qBittorrent] end POST -->|files| COPY DIRECT -->|plex plain-HTTP passthrough| PLEX DIRECT --> CAL DIRECT --> JEL DIRECT -->|own auth, no Anubis| ARR ``` Guarded (Anubis `:8923` → Caddy `:8016` → Authentik outpost → Caddy `:8017`): homepage (`www.`), search, **grafana** (`dankclaw/Caddyfile:128-129`), prometheus (`:135-137`), loki (`:141-143`), seerr (`:148-150`), marreta (`:155-160`), romm (`:167-169`), files (`:172-174`). Exempt / own-auth: `auth.` (the IdP login itself), `ai.` (OpenWebUI, its own OIDC), gitea (git smart-HTTP clients cannot solve JS challenges — `/info/refs` and `git-*-pack` pass unchallenged), pihole (Caddy `basicauth`, `:167`), plex (plain HTTP), books/jellyfrank (own logins), `hermes.`/`hermesadmin.`, and the *arr vhosts that terminate on FrankBook-Pro (`:254-281`, "exempt by construction: own auth"). Grafana is **not** exempt even though it signs in through authentik as an OAuth2 relying party (`:123`) — the chain wraps it too. `sonarr.` has no vhost at all (LAN `192.168.0.112:8989` only, `:265-267`). Dankserver runs no proxy of its own. Its services expose published LAN ports, and the Dankclaw Caddy routes the names to those ports. ## Hostnames | Scope | Convention | Resolved by | |-------|------------|-------------| | LAN | `romm.franksdank.cloud` → 192.168.0.143 | Pi-hole (`addn-hosts`) | | WAN | `romm.franksdank.cloud` → 71.227.227.172 | Porkbun A record | Users type a name and nothing else. Caddy maps the name to the upstream. The split between the two resolvers removes the need for router NAT loopback. Verified 2026-10-18: `dig +short @192.168.0.143 romm.franksdank.cloud` → 192.168.0.143, and a public resolver returns 71.227.227.172 for the same name. See [[LAN-DNS]] for the flow and the router hand-off. ## TLS Caddy holds one Let's Encrypt certificate per vhost, issued and renewed through the DNS-01 challenge via the Porkbun module. No inbound port 80/443 is required. Details in [[Caddy]]. ## Gluetun Path (SearXNG + Marreta) `gluetun.container` and `searxng-valkey.container` set `Network=searxng.network` (runtime network `systemd-searxng`), while `searxng.container`, `marreta.container` and `marreta-browser.container` join the tunnel the modern way — `Network=gluetun.container` (`dankclaw/searxng/quadlet/searxng.container:18`, `dankclaw/marreta/quadlet/marreta.container:34`, `dankclaw/marreta/quadlet/marreta-browser.container:25`), the Quadlet equivalent of the retired compose `network_mode: service:gluetun`. **Five** containers share the VPN exit, not three, and Marreta's outbound fetches leave through the same WireGuard tunnel as SearXNG's. Pi-hole is a different kind of exception (`Network=slirp4netns`, `dankclaw/pihole/quadlet/pihole.container`); everything else on Dankclaw uses the normal LAN route. This matches KB Pattern 2 in [[Gluetun]]. See [[Container Network Namespaces]] for which addresses are reachable from that namespace (none of the unpublished host-netns listeners). ## Access Policy - The **G36 router firewall** governs what the WAN can reach. Port forwards are manual router steps; the repo tracks no router state. - Access is layered: Anubis challenges/denies bots at the front door, then Authentik's embedded outpost (whole-request proxy, routed by `Host` → provider `external_host`) gates the wrapped apps — one proxy provider per guarded host is mandatory, and Anubis exempts the `/outpost.goauthentik.io/*` transport paths so login can happen before a challenge cookie exists. Apps with their own login (Grafana, Gitea, Plex) run unwrapped. See [[Anubis]] and [[Authentik]]. - LAN subnet: `192.168.0.0/24`. ## See Also - [[LAN-DNS]] — name resolution, Pi-hole, router hand-off - [[Caddy]] — vhost map, TLS, basic auth - [[Dankserver Stack]], [[Dankclaw Stack]] - [[Gluetun]] - Knowledge base: [[Gluetun]], [[SearXNG]] - [[Container Network Namespaces]] — who can dial what on Dankclaw (host netns vs bridge nets)