Zero-Backend Service-Portal: Homer Statisches PWA-Dashboard, Live API-Widgets & Subresource-Integrität
Erstellt: 06.11.2026 Aktualisiert: 06.11.2026
Management Summary: Viele moderne Dashboards erfordern ressourcenintensive Node.js- oder Python-Hintergrunddienste mit privilegiertem Docker-Socket-Zugriff und speichern sensible API-Tokens serverseitig in Datenbanken. Dieser Leitfaden beschreibt das Deployment von Homer – einer völlig zustandslosen, statischen Vue.js Single Page Application (SPA) und Progressive Web App (PWA). Homer wird über eine einzige deklarative
config.ymlgesteuert, bietet dynamische Client-seitige Telemetrie-Karten (Pi-hole, AdGuard Home, Uptime Kuma, Prometheus) und wird durch eine strikte Content-Security-Policy (CSP) abgesichert.
-
Minimale Angriffsfläche ohne Backend
Liefert reine statische HTML/JS/CSS-Dateien bei
< 15 MBRAM-Bedarf aus – ganz ohne serverseitige Codeausführung oder Docker-Socket-Mounts. -
Dynamische Smart-Service-Cards
Zeigt geblockte DNS-Anfragen, Prometheus-Messwerte, Uptime Kuma SLAs und Proxmox-Zustände in Echtzeit direkt auf den Service-Kacheln an.
-
Tastaturfokussierte Navigation
Mit
/blitzschnell nach Diensten filtern, mitEnterden ersten Treffer öffnen und mittzwischen hellem und dunklem Design umschalten. -
Offline-Fähiges PWA-Manifest
Cacht
config.ymlund SVG-Iconsets über einen Browser Service Worker, sodass das Infrastrukturportal selbst bei partiellen Netzwerkausfällen lädt.
1. Architektur: Statische Bereitstellung vs. Client-seitige Smart-Cards
Im Gegensatz zu servergerenderten Dashboards, die API-Anfragen serverseitig weiterleiten, wird Homer von einem minimalen Webserver als statisches Dateipaket ausgeliefert. Beim Aufruf von https://portal.lan.nejat-hakan.de lädt die Vue.js-Applikation /assets/config.yml und führt fetch()-Anfragen direkt aus dem Browser des Administrators (über LAN oder WireGuard VPN) an die jeweiligen Dienst-APIs aus:
%%{init: {'flowchart': {'wrappingWidth': 600, 'subGraphTitleMargin': {'top': 15, 'bottom': 15}}}}%%
flowchart TB
subgraph ClientBrowser["Administrator-Browser (LAN / WireGuard)"]
SPA["Homer Vue.js PWA<br/>Tastatursuche ('/')"]
SW["Service Worker Cache<br/>Offline Icon- & Konfig-Speicher"]
end
subgraph StaticHost["Homer Statischer Container (<15MB RAM)"]
Assets["Statisches Bundle +<br/>/assets/config.yml"]
end
subgraph InternalAPIs["Interne Service-APIs (CORS erlaubt)"]
PiholeAPI["Pi-hole v6 / AdGuard<br/>Live Blockrate in %"]
PromAPI["Prometheus /api/v1/query<br/>CPU- & Storage-Nutzung"]
KumaAPI["Uptime Kuma Status<br/>Public Slug Badge"]
end
SPA -->|"1. GET /assets/config.yml"| Assets
Assets --> SW
SPA -->|"2. Client-Side Fetch"| PiholeAPI
SPA -->|"3. Client-Side Fetch"| PromAPI
SPA -->|"4. Client-Side Fetch"| KumaAPI
2. Schreibgeschütztes Container-Deployment
Da Homer nach dem Bereitstellen der config.yml keinerlei Schreibzugriffe benötigt, wird der Container mit read_only: true und no-new-privileges:true gehärtet:
services:
homer:
image: b4bz/homer:latest
container_name: homer-portal
restart: unless-stopped
user: "1000:1000"
read_only: true
security_opt:
- no-new-privileges:true
ports:
- "127.0.0.1:8090:8080/tcp"
environment:
INIT_ASSETS: 0
volumes:
- ./assets:/www/assets:ro
Legen Sie das Verzeichnis ./assets/icons an und befüllen Sie es mit lokalen SVG-Icons (z. B. von selfh.st/icons), um externe CDN-Aufrufe vollständig zu vermeiden:
3. Deklarative Produktivkonfiguration (assets/config.yml)
---
title: "Souveränes Infrastruktur-Portal"
subtitle: "nejat-hakan.de • Systembetrieb & Telemetrie"
documentTitle: "Ops Portal | nejat-hakan.de"
header: true
footer: '<p>Souveräner Self-Hosted Stack • <code>/</code> zum Suchen • <code>t</code> für Theme</p>'
columns: "3"
connectivityCheck: true
defaults:
layout: list
colorTheme: dark
theme: default
colors:
dark:
highlight-primary: "#1e293b"
highlight-secondary: "#0f172a"
highlight-hover: "#334155"
background: "#090d16"
card-background: "#111827"
text: "#f8fafc"
text-header: "#f8fafc"
text-title: "#e2e8f0"
text-subtitle: "#94a3b8"
card-shadow: rgba(0, 0, 0, 0.5)
link: "#38bdf8"
link-hover: "#7dd3fc"
links:
- name: "Dokumentation"
icon: "fas fa-book"
url: "https://nejat-hakan.de"
target: "_blank"
- name: "Git Repositories"
icon: "fas fa-code-branch"
url: "https://git.nejat-hakan.de"
- name: "Status-Seite"
icon: "fas fa-heart-pulse"
url: "https://status.nejat-hakan.de/status/infra"
services:
- name: "Netzwerk & Edge-Sicherheit"
icon: "fas fa-shield-halved"
items:
- name: "AdGuard Home / Pi-hole"
logo: "assets/icons/pihole.svg"
subtitle: "DNS Sinkhole & Unbound Resolver"
tag: "dns"
tagstyle: "is-success"
url: "https://dns.lan.nejat-hakan.de"
type: "PiHole"
apiType: "pihole6"
- name: "Traefik Edge Router"
logo: "assets/icons/traefik.svg"
subtitle: "Ingress TLS 1.3 + HTTP/3"
tag: "edge"
tagstyle: "is-info"
url: "https://traefik.lan.nejat-hakan.de"
type: "Traefik"
- name: "WireGuard VPN"
logo: "assets/icons/wireguard.svg"
subtitle: "10.88.0.1/24 Mesh Gateway"
tag: "vpn"
tagstyle: "is-warning"
url: "https://vpn.lan.nejat-hakan.de"
- name: "Beobachtbarkeit & Speicher"
icon: "fas fa-server"
items:
- name: "Uptime Kuma"
logo: "assets/icons/uptime-kuma.svg"
subtitle: "Synthetische Verfügbarkeit & Push-Probes"
tag: "sla"
tagstyle: "is-success"
url: "https://status.nejat-hakan.de"
type: "UptimeKuma"
slug: "infra"
- name: "Zabbix 7 LTS"
logo: "assets/icons/zabbix.svg"
subtitle: "TimescaleDB Hardware- & SMART-Telemetrie"
tag: "metrics"
tagstyle: "is-danger"
url: "https://zabbix.lan.nejat-hakan.de"
- name: "Proxmox VE Cluster"
logo: "assets/icons/proxmox.svg"
subtitle: "KVM- & ZFS-Virtualisierungshost"
tag: "hv-01"
tagstyle: "is-primary"
url: "https://proxmox.lan.nejat-hakan.de"
- name: "Souveräne Produktivität"
icon: "fas fa-cubes"
items:
- name: "Vaultwarden"
logo: "assets/icons/vaultwarden.svg"
subtitle: "Zero-Knowledge Passwort-Tresor"
tag: "sec"
tagstyle: "is-success"
url: "https://vault.nejat-hakan.de"
- name: "Nextcloud Hub"
logo: "assets/icons/nextcloud.svg"
subtitle: "Cloud-Sync, CalDAV & Collabora"
tag: "cloud"
tagstyle: "is-info"
url: "https://cloud.nejat-hakan.de"
- name: "Forgejo Git"
logo: "assets/icons/forgejo.svg"
subtitle: "Self-Hosted Git & Actions Runner"
tag: "git"
tagstyle: "is-warning"
url: "https://git.nejat-hakan.de"
4. CORS & Reverse-Proxy CSP für Smart-Cards konfigurieren
Da Smart-Cards Browser-fetch()-Aufrufe von Homers Origin an Ziel-APIs initiieren, müssen die Zielsysteme CORS (Access-Control-Allow-Origin) gestatten und Homers CSP-Header connect-src zulassen:
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self' https://*.nejat-hakan.de https://*.lan.nejat-hakan.de;" always;
Niemals sensible Admin-API-Keys in öffentlich lesbare config.yml schreiben
Da /assets/config.yml von jedem Browser heruntergeladen werden kann, sind eingetragene API-Schlüssel in den Entwicklertools sichtbar. Nutzen Sie ausschließlich Leseschlüssel oder Status-Slugs (slug: "infra") und sichern Sie das Dashboard über VPN, mTLS oder ForwardAuth ab.
5. Verifikation & YAML-Validierung
# 1. Strenge YAML-Syntax prüfen
python3 -c 'import yaml, sys; yaml.safe_load(open("/opt/homer/assets/config.yml"))' && echo "YAML Valid"
# 2. Prüfen, ob alle referenzierten Icons lokal vorhanden sind
grep -oP 'assets/icons/[^"]+' /opt/homer/assets/config.yml | while read -r icon; do
test -f "/opt/homer/${icon}" || echo "FEHLENDES ICON: ${icon}"
done