Plex Deployment had no Plex env vars. Add via the ServiceDefinition env list (rendered verbatim by catalog/render.py): - PLEX_UID/PLEX_GID=1000 (image chowns /config to this and drops; matches the Longhorn ownership convention seerr/semaphore use) - TZ=Etc/UTC (no other service sets TZ repo-wide) - ALLOWED_NETWORKS=10.0.0.0/8,192.168.0.0/16 so in-cluster clients (Seerr -> plex:32400) are trusted as LAN (pod CIDR 10.0.0.0/16 + svc 10.43.0.0/16 + LAN) PLEX_CLAIM intentionally not set (4-min TTL is unusable via git; one-time live claim persists on /config). ADVERTISE_IP deferred: the only candidate host resolves via split-horizon DNS to the internal Gateway VIP and its remote reachability is unverified — a wrong value breaks remote access, so omitted per prefer-safety, documented as a follow-up in catalog/plex.yaml. Homepage still labeled seerr as "Overseerr"; renamed the tile to "Seerr" (icon mdi-play-box-multiple keeps — not overseerr-specific). Co-Authored-By: Claude Opus 4.8 <[email protected]>
platform/homepage
Homepage (gethomepage) — the apex landing dashboard at https://home.keithjackson.dev, aggregating links to the cluster's services.
- Serves: a single pane of glass for the homelab (docs/09 apex UI).
- Tier: non-gating post-platform Flux Kustomization
(
clusters/base/homepage.yaml,dependsOn: platform-config,wait: false) — mirrorsheadlamp/oauth2-proxy. A leaf UI must never gate theplatformtier (DECISIONS-LOG "Headlamp gating stall"). - Namespace:
homepage— a PLATFORM namespace (noapp-namespace=true), so the Kyverno app-scoped policies (default-deny gen, auth-mode, egress-class) do not apply; we ship an explicit default-deny + Cilium ingress policy as defense-in-depth.
What this component owns
Plain manifests (not a HelmRelease — the app is one stateless Deployment + a config ConfigMap):
| File | Purpose |
|---|---|
namespace.yaml |
homepage ns, gateway-access=shared |
configmap.yaml |
Homepage config (settings/services/bookmarks/widgets + empty docker/kubernetes + custom.css/js) |
deployment.yaml |
ghcr.io/gethomepage/homepage (non-root, subPath config mounts, HOMEPAGE_ALLOWED_HOSTS) |
service.yaml |
ClusterIP 80 -> 3000 |
networkpolicy.yaml |
default-deny (DNS-only egress) + Cilium Gateway-only ingress on 3000 |
httproute.yaml |
apex ingress on the Gateway https-apex listener |
Apex nuances (why this isn't just another *.home route)
- TLS: the apex
home.keithjackson.devis already a SAN on the wildcard Certificate (platform/gateway/wildcard-certificate.yaml) — no new cert. - Gateway listener: the wildcard listeners' hostname
*.home.keithjackson.devdoes not match the bare apex. This component's route binds a dedicatedhttps-apexlistener added to the shared Gateway (platform/gateway/gateway.yaml);http-apex-redirectcovers :80. HOMEPAGE_ALLOWED_HOSTS: Homepage >= 1.0 returns 403 for any Host not in this list — set tohome.keithjackson.dev+ the pod IP (for kubelet probes).
Forward-auth — FOLLOW-UP (not yet enforced)
The HTTPRoute is annotated auth-mode: forward-auth (intended posture: oauth2-proxy -> Keycloak).
The Gateway ext-authz attachment that actually enforces forward-auth does not exist yet — it is a
Phase-6 WS-NET TODO (platform/oauth2-proxy is the authenticator; there is no CiliumEnvoyConfig /
ext-authz wiring anywhere in the repo). Until that mechanism lands, Homepage — like every other
forward-auth app (jackett, qbittorrent, pihole, radarr, sonarr) — is reachable unauthenticated.
When the Gateway ext-authz pattern is built, attach it to this route (and the others) with
allowed-groups: platform-admins. PM decision flagged in the deploy report.
Deliberately deferred (avoid scope creep)
- Service-status widgets (Grafana/Plex/Sonarr/… API tiles) need per-service API-key secrets —
not wired; tiles are simple links. Add as SOPS secrets +
widgets.yamllater. - Kubernetes widget would need a read-only ServiceAccount + RBAC — skipped (
kubernetes.yamlempty,automountServiceAccountToken: false). - Brand icons (selfh.st/dashboard-icons) need world:443 egress — we use bundled
mdi-*icons so egress stays DNS-only.