Files
HiveGrid-Wireframes/hivegrid-cloud/docs/server-struktur.html
T

341 lines
24 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8">
<title>Server-Struktur &amp; Infrastruktur-Estate</title>
<style>
@page {
size: A4;
margin: 22mm 16mm 20mm 16mm;
@bottom-center { content: counter(page) " / " counter(pages); font-size: 9px; color: #9a958c; }
@bottom-left { content: "HiveGrid – Server-Struktur (Hetzner Estate)"; font-size: 9px; color: #9a958c; }
}
@page :first { margin: 0; @bottom-center { content: ""; } @bottom-left { content: ""; } }
* { box-sizing: border-box; }
html { font-size: 10.6px; }
body { font-family: -apple-system, "Segoe UI", "Helvetica Neue", Arial, sans-serif; color: #231F20; line-height: 1.48; margin: 0; }
.cover { height: 297mm; padding: 40mm 24mm 0 24mm; background: linear-gradient(158deg, #2b2521 0%, #1c1813 60%, #14110d 100%); color: #fff; page-break-after: always; }
.cover .logo { width: 250px; margin-bottom: 44mm; }
.cover .kicker { font-size: 13px; letter-spacing: 3px; text-transform: uppercase; color: #F1B729; font-weight: 600; }
.cover h1 { font-size: 38px; line-height: 1.1; margin: 14px 0 10px 0; font-weight: 700; color: #fff; }
.cover h1 .accent { color: #F1B729; }
.cover h2 { font-size: 16px; font-weight: 400; color: #d8d3c8; margin: 0 0 32px 0; }
.cover .meta { font-size: 12px; color: #ddd8cd; line-height: 1.9; border-top: 2px solid #F1B729; padding-top: 18px; max-width: 122mm; }
.cover .meta strong { color: #F1B729; }
.cover .badge { display:inline-block; background: rgba(241,183,41,.12); border:1px solid rgba(241,183,41,.55); color:#F1B729; border-radius: 20px; padding: 4px 13px; font-size: 11px; margin: 2px 4px 2px 0; }
.toc { page-break-after: always; }
.toc .logo-dark { width: 150px; display:block; margin: 4mm 0 8mm 0; }
.toc h2 { border:none; }
.toc ol { list-style: none; padding-left: 0; counter-reset: toc; column-count: 2; column-gap: 14mm; }
.toc > ol > li { counter-increment: toc; padding: 5px 0; border-bottom: 1px dotted #ddd6c5; font-weight: 600; color:#231F20; font-size: 11.5px; break-inside: avoid; }
.toc > ol > li::before { content: counter(toc) ". "; color:#c69412; font-weight:700; }
h2.section { font-size: 18px; color: #231F20; border-bottom: 3px solid #F1B729; padding-bottom: 5px; margin-top: 26px; break-after: avoid; }
h3 { font-size: 13.5px; color: #9a720b; margin-top: 16px; break-after: avoid; }
p { margin: 6px 0; }
ul, ol { margin: 6px 0; padding-left: 18px; }
li { margin: 3px 0; }
code { font-family: "SF Mono", "Menlo", Consolas, monospace; background: #fbf1d4; padding: 1px 5px; border-radius: 3px; font-size: 9.2px; color: #82610a; }
strong { color: #231F20; }
.lead { font-size: 12.5px; color: #5a544a; }
table { width: 100%; border-collapse: collapse; margin: 9px 0; font-size: 9.1px; break-inside: avoid; }
th { background: #231F20; color: #fff; text-align: left; padding: 5px 7px; font-weight: 600; border-bottom: 2px solid #F1B729; }
td { border-bottom: 1px solid #e7e1d2; padding: 4px 7px; vertical-align: top; }
tr:nth-child(even) td { background: #faf6ec; }
.callout { border-left: 4px solid #F1B729; background: #fdf6e2; padding: 9px 14px; margin: 12px 0; border-radius: 0 5px 5px 0; break-inside: avoid; }
.callout.warn { border-left-color: #c2410c; background: #fdf0e8; }
.callout.ok { border-left-color: #15803d; background: #edf8ef; }
.callout .t { font-weight: 700; display:block; margin-bottom: 2px; color:#231F20; }
.mono-box { font-family: "SF Mono","Menlo",Consolas,monospace; font-size: 8.6px; background:#fbf7ea; border:1px solid #e7e1d2; border-left: 4px solid #F1B729; border-radius:6px; padding:10px 12px; white-space:pre; line-height:1.35; color:#3a3326; break-inside: avoid; }
.pill { display:inline-block; font-size:8px; font-weight:700; padding:1px 6px; border-radius:10px; vertical-align:middle; }
.pill.live { background:#dcf3e1; color:#166534; }
.pill.plan { background:#fbe9b0; color:#82610a; }
.small { font-size: 9px; color:#6b665c; }
hr { border:none; border-top:1px solid #e7e1d2; margin: 16px 0; }
section.sw { break-inside: avoid; }
.group { break-inside: avoid; }
</style>
</head>
<body>
<div class="cover">
<img class="logo" src="assets/hivegrid-logo-white.svg" alt="HiveGrid">
<div class="kicker">Infrastruktur-Dokumentation</div>
<h1>Server-Struktur<br>&amp; <span class="accent">Hetzner-Estate</span></h1>
<h2>Komplette Bestandsaufnahme: Hosts, Netzwerk, Dienste, Deploy</h2>
<div>
<span class="badge">6 Hosts</span>
<span class="badge">Hetzner (EU)</span>
<span class="badge">Coolify Control-Plane</span>
<span class="badge">WireGuard-Mesh</span>
<span class="badge">Zentrale Dienste</span>
</div>
<div class="meta">
<strong>Zweck:</strong> Onboarding- &amp; Betriebsreferenz für die gesamte Server-Landschaft.<br>
<strong>Quelle der Wahrheit:</strong> Repo <code>infra/</code> (README, STANDARDS, ADRs, Runbooks).<br>
<strong>Stand:</strong> 14. Juni 2026<br>
<strong>Modell:</strong> per-Produkt getrennte Workloads, zentral gesteuert über Coolify,
zentrale Hilfsdienste über ein privates WireGuard-Mesh.
</div>
</div>
<div class="toc">
<img class="logo-dark" src="assets/hivegrid-logo.svg" alt="HiveGrid">
<h2 class="section">Inhalt</h2>
<ol>
<li>Überblick &amp; Architekturmodell</li>
<li>Server-Inventar (alle Hosts)</li>
<li>Hetzner-Projekt-Struktur</li>
<li>Netzwerk &amp; WireGuard-Mesh</li>
<li>Orchestrierung (Coolify, Traefik)</li>
<li>Zentrale Dienste (node1)</li>
<li>Observability &amp; Logging</li>
<li>Aufteilung pro Produkt</li>
<li>Deploy-Kette</li>
<li>DNS &amp; Domains</li>
<li>Secrets &amp; Zugänge</li>
<li>Backups &amp; Disaster Recovery</li>
<li>Standards &amp; Konventionen</li>
<li>Offene Punkte</li>
</ol>
</div>
<!-- 1 -->
<section class="sw">
<h2 class="section">1. Überblick &amp; Architekturmodell</h2>
<p class="lead">Schlankes, per-Produkt getrenntes SaaS-Infra-Modell mit zentraler Steuerung – alles auf Hetzner (EU).</p>
<ul>
<li><strong>Compute:</strong> Mehrere Produkt-Stagings teilen sich einen Host (<code>staging-1</code>); Produktion bekommt pro Produkt ein eigenes Hetzner-Projekt/VM (ADR-009).</li>
<li><strong>Control-Plane:</strong> Eine zentrale Coolify-Instanz (<code>control</code>) verwaltet alle Hosts und Deployments.</li>
<li><strong>Hilfsdienste:</strong> Einmal zentral auf dem dedizierten <code>node1</code> (Sentry, Loki, Meilisearch, ClamAV, mCaptcha, NSFW-API), erreichbar über ein privates WireGuard-Mesh.</li>
<li><strong>CI/CD:</strong> Build-once in der Gitea-Registry, Deploy über Coolify (Staging automatisch, Prod manuell/Promote).</li>
<li><strong>Governance:</strong> <code>infra/</code>-Repo mit STANDARDS, nummerierten ADRs und Runbooks ist die Quelle der Wahrheit.</li>
</ul>
<div class="mono-box"> ┌──────────────────────────────┐
│ control (Coolify-Plane) │
│ Prometheus + Grafana │
│ WireGuard-Server 10.42.0.1 │
└───────────────┬──────────────┘
SSH-Deploy / Mesh │
┌───────────────┬──────────────┬───────┴───────┬───────────────┐
▼ ▼ ▼ ▼ ▼
homeserver staging-1 marketing node1 (EX44) mitapp-prod
10.42.0.2 10.42.0.3 10.42.0.5 10.42.0.4 10.42.0.6
Gitea/CI/ alle SaaS- hivegrid.de / ZENTRALE mITapp
Authentik Stagings tool-ctrl.de DIENSTE (Prod)</div>
</section>
<!-- 2 -->
<section class="sw">
<h2 class="section">2. Server-Inventar</h2>
<table>
<tr><th>Host</th><th>Provider / Typ</th><th>Spec</th><th>Public-IP</th><th>Mesh-IP</th><th>Rolle</th></tr>
<tr><td><code>control</code></td><td>Hetzner Cloud · cx33</td><td>4C / 8 GB / 80 GB</td><td>195.201.133.26</td><td>10.42.0.1</td><td>Coolify-Control-Plane, Prometheus + Grafana, WireGuard-Server</td></tr>
<tr><td><code>staging-1</code></td><td>Hetzner Cloud · cpx42</td><td>8C / 15 GB / 75 GB</td><td>46.225.5.93</td><td>10.42.0.3</td><td>Gemeinsamer SaaS-Staging-Host (boilerplate, mITapp, künftig HiveGrid)</td></tr>
<tr><td><code>node1</code> (EX44)</td><td>Hetzner Dedicated (Robot)</td><td>20C / 62 GB / 436 GB</td><td>176.9.111.179</td><td>10.42.0.4</td><td>Zentrale Dienste: Sentry, Loki, Meilisearch, ClamAV, mCaptcha, NSFW-API</td></tr>
<tr><td><code>marketing</code></td><td>Hetzner Cloud · cx33</td><td>4C / 8 GB / 80 GB</td><td>128.140.109.153</td><td>10.42.0.5</td><td>Marketing-Sites (hivegrid.de, tool-ctrl.de)</td></tr>
<tr><td><code>mitapp-prod</code></td><td>Hetzner Cloud · cpx22</td><td>2C / 4 GB / 80 GB</td><td>178.104.213.13</td><td>10.42.0.6</td><td>mITapp Produktion</td></tr>
<tr><td><code>homeserver</code></td><td>Self-hosted (DDNS)</td><td>16C / 30 GB / 935 GB</td><td>DDNS</td><td>10.42.0.2</td><td>Gitea, CI-Runner, Authentik, Vaultwarden, interne Tools</td></tr>
</table>
<p class="small">Alle Cloud-Hosts in Hetzner-Regionen nbg1/fsn1 (Deutschland). <code>node1</code> im RZ FSN1-DC8. SSH per Schlüssel (<code>~/.ssh/node1</code> für alle Hetzner-Hosts).</p>
</section>
<!-- 3 -->
<section class="sw">
<h2 class="section">3. Hetzner-Projekt-Struktur</h2>
<p>Workloads sind in getrennte Hetzner-Cloud-Projekte aufgeteilt (Abrechnung/Isolation). Produktion erhält pro Produkt ein eigenes Projekt (ADR-009).</p>
<table>
<tr><th>Projekt</th><th>Zweck</th><th>VM(s)</th><th>Status</th></tr>
<tr><td>DevOps</td><td>Coolify-Control-Plane</td><td><code>control</code> (cx33)</td><td><span class="pill live">live</span></td></tr>
<tr><td>Staging</td><td>SaaS-Staging-Pool</td><td><code>staging-1</code> (cpx42)</td><td><span class="pill live">live</span></td></tr>
<tr><td>Marketing</td><td>Marketing-Workload</td><td><code>marketing</code> (cx33)</td><td><span class="pill live">live</span></td></tr>
<tr><td>probono</td><td>mITapp-Produktion</td><td><code>mitapp-prod</code> (cpx22)</td><td><span class="pill live">live</span></td></tr>
<tr><td>HiveGrid</td><td>HiveGrid-Cloud-Prod (geplant, ADR-009)</td><td>—</td><td><span class="pill plan">geplant</span></td></tr>
<tr><td>Boilerplate</td><td>Boilerplate-Prod (geplant, ADR-009)</td><td>—</td><td><span class="pill plan">geplant</span></td></tr>
</table>
<p class="small">Dazu: 1 Dedicated-Server (<code>node1</code>, Hetzner Robot) für zentrale Dienste und der self-hosted <code>homeserver</code> für CI/SSO.</p>
</section>
<!-- 4 -->
<section class="sw">
<h2 class="section">4. Netzwerk &amp; WireGuard-Mesh</h2>
<p>Alle Hosts hängen in einem privaten WireGuard-Mesh <code>10.42.0.0/24</code> mit Stern-Topologie: <code>control</code> ist Server (UDP 51820), alle anderen sind Peers. Inter-Host-Verkehr (Metrics-Scrapes, zentrale Dienste) läuft ausschließlich über Mesh-IPs – nicht über öffentliche Endpunkte.</p>
<table>
<tr><th>Host</th><th>Interface</th><th>Mesh-IP</th><th>Rolle im Mesh</th></tr>
<tr><td>control</td><td>wg0</td><td>10.42.0.1/24</td><td>Server</td></tr>
<tr><td>homeserver</td><td>wg1</td><td>10.42.0.2/24</td><td>Peer (eigenes wg0 bleibt Heim-VPN)</td></tr>
<tr><td>staging-1</td><td>wg0</td><td>10.42.0.3/24</td><td>Peer</td></tr>
<tr><td>node1</td><td>wg1</td><td>10.42.0.4/24</td><td>Peer (eigenes wg0 separates VPN)</td></tr>
<tr><td>marketing</td><td>wg0</td><td>10.42.0.5/24</td><td>Peer</td></tr>
<tr><td>mitapp-prod</td><td>wg0</td><td>10.42.0.6/24</td><td>Peer</td></tr>
</table>
</section>
<!-- 5 -->
<section class="sw">
<h2 class="section">5. Orchestrierung (Coolify, Traefik)</h2>
<p><strong>Coolify v4</strong> auf <code>control</code> (Dashboard <code>https://saas-ctrl.com</code>) ist die zentrale Steuerung. Jeder Ziel-Host ist als Server registriert; pro Produkt gibt es ein Projekt, pro Stufe ein Environment, pro Dienst eine Resource.</p>
<ul>
<li><strong>Resource-Modus <code>dockerimage</code>:</strong> Coolify baut nicht selbst, sondern zieht vorgefertigte Images aus der Gitea-Registry.</li>
<li><strong>Reverse-Proxy:</strong> Traefik (<code>coolify-proxy</code>) je Host, TLS via Let's Encrypt (ACME). Auf <code>node1</code> hat Traefik seit der ADR-013-Migration das frühere Host-Caddy ersetzt.</li>
<li><strong>Konvention:</strong> Projekt = Produkt · Environment = Stufe (staging/production) · Resource = Dienst.</li>
</ul>
</section>
<!-- 6 -->
<section class="sw">
<h2 class="section">6. Zentrale Dienste (node1)</h2>
<p>Einmal zentral betrieben, von allen Produkten über das Mesh genutzt. Index-/Key-Konventionen pro Produkt sorgen für Trennung.</p>
<table>
<tr><th>Dienst</th><th>Erreichbar</th><th>Zweck / Hinweise</th></tr>
<tr><td>Sentry</td><td><code>sentry.saas-ctrl.com</code></td><td>Error-Tracking. 1 Org, 1 Team pro Produkt, 1 Projekt pro Codebasis; Staging/Prod über Environment-Tags.</td></tr>
<tr><td>Loki</td><td><code>10.42.0.4:3100</code> (mesh)</td><td>Zentrale Logs; Promtail-Collector auf jedem Host; 7 Tage Retention.</td></tr>
<tr><td>Meilisearch</td><td><code>10.42.0.4:7700</code> (mesh)</td><td>Volltextsuche; pro Produkt ein Scoped-Key, Index-Präfix <code>&lt;produkt&gt;-*</code>.</td></tr>
<tr><td>ClamAV</td><td><code>10.42.0.4:3310</code> (mesh)</td><td>Virenscan per TCP-INSTREAM; Default fail-closed für Uploads.</td></tr>
<tr><td>NSFW-API</td><td><code>10.42.0.4:3000</code> (mesh)</td><td>Bild-Klassifikation; Default fail-open.</td></tr>
<tr><td>mCaptcha</td><td><code>captcha.saas-ctrl.com</code></td><td>Captcha-Service (Sitekey-verwaltet).</td></tr>
</table>
</section>
<!-- 7 -->
<section class="sw">
<h2 class="section">7. Observability &amp; Logging</h2>
<p>Zentral auf <code>control</code>: <strong>Prometheus</strong> scrapt jeden Host über das Mesh (alle 30 s), <strong>Grafana</strong> (<code>grafana.saas-ctrl.com</code>) visualisiert. Logs zentral in <strong>Loki</strong>, Errors in <strong>Sentry</strong>.</p>
<ul>
<li><strong>Scrape-Targets je Host:</strong> <code>node-exporter</code> (:9100) und <code>docker-stats-exporter</code> (:9487) auf der jeweiligen Mesh-IP.</li>
<li><strong>Labels:</strong> <code>project</code>, <code>environment</code>, <code>server</code>, <code>service</code> – damit lassen sich Dashboards pro Produkt/Host filtern.</li>
<li><strong>Dashboards:</strong> <code>estate-overview</code> (flottenweit) plus je Host/Produkt eines (<code>project-staging</code>, <code>project-hetzner-ex44</code>, <code>project-mitapp</code>, …).</li>
<li><strong>Offen:</strong> AlertManager-/Alert-Routing noch nicht aktiv; Dashboards aktuell nicht versioniert.</li>
</ul>
</section>
<!-- 8 -->
<section class="sw">
<h2 class="section">8. Aufteilung pro Produkt</h2>
<div class="group">
<h3>HiveGrid Cloud (SaaS) <span class="pill plan">Prod geplant</span></h3>
<p>SaaS-App in aktiver Entwicklung (Git-Downstream des Boilerplates). Noch nicht deployt. Folgt bei Reife dem Estate-Modell: Staging auf <code>staging-1</code> (oder eigenes Projekt), Prod als eigenes Hetzner-Projekt „HiveGrid" gemäß ADR-009; zentrale Dienste auf <code>node1</code>. Nicht verwechseln mit der Marketing-Site <code>hivegrid.de</code>.</p>
</div>
<div class="group">
<h3>HiveGrid Marketing-Site <span class="pill live">live</span></h3>
<p>Statische/Next.js-Site auf <code>marketing</code>, Domain <code>hivegrid.de</code> (Cloudflare-proxied), TLS über Traefik. Leads lokal in SQLite-Volume.</p>
</div>
<div class="group">
<h3>Boilerplate SaaS <span class="pill live">Staging live</span></h3>
<p><strong>Staging</strong> auf <code>staging-1</code> (Coolify-Projekt „boilerplate" / „validation"): web + api + 5 Worker, Postgres (Coolify-managed) + PgBouncer, Authentik unter <code>auth.boiler.saas-ctrl.com</code>; Suche/Virenscan/Errors zentral auf <code>node1</code>. <strong>Prod</strong> laut ADR-009 als eigenes Projekt geplant, noch nicht live.</p>
</div>
<div class="group">
<h3>mITapp <span class="pill live">live</span></h3>
<p><strong>Prod</strong> auf <code>mitapp-prod</code> (cpx22): App + Worker (Image-Tag <code>:prod</code>), Uploads-Sidecar (nginx), Postgres 16 + Redis 7 (Coolify-managed). Domains <code>mitapp.de</code> (kanonisch) + <code>api.mitapp.de</code> (mobil) sowie Legacy <code>mitapp.mitup.de</code> (muss <code>/api/*</code> + <code>/.well-known/*</code> für ausgelieferte Mobile-Apps bedienen). <strong>Staging</strong> auf <code>staging-1</code> (<code>staging.mitapp.de</code>, Basic-Auth). CI/CD nach Modell „Build-once / Promote".</p>
</div>
</section>
<!-- 9 -->
<section class="sw">
<h2 class="section">9. Deploy-Kette</h2>
<div class="mono-box">git push origin main
│
▼ Gitea Actions (Runner auf homeserver, Label "toolctrl-ci")
│ build image → push git.tool-ctrl.de/<org>/<repo>:latest + :sha-<short>
│
▼ Coolify (control) — Resource-Modus "dockerimage"
│ pull pre-built image → per SSH auf Ziel-Host → Container-Rolling-Restart (~25-30s)
│
▼ Traefik (Ziel-Host) → TLS-Termination (Let's Encrypt)
│
▼ Cloudflare DNS (+ Proxy bei user-facing Domains) → live</div>
<ul>
<li><strong>Staging:</strong> automatisch bei Push auf <code>main</code> (Tag <code>:latest</code> + <code>:sha-…</code>).</li>
<li><strong>Produktion (Modell „Promote"):</strong> manueller <code>promote.yml</code>-Dispatch retaggt ein bereits gebautes SHA-Image auf <code>:prod</code> (kein Rebuild) und deployt es. Rollback = altes SHA neu deployen.</li>
<li><strong>Registry-Auth:</strong> pro Host <code>docker login</code> gegen die Gitea-Registry.</li>
</ul>
</section>
<!-- 10 -->
<section class="sw">
<h2 class="section">10. DNS &amp; Domains (Cloudflare)</h2>
<p>DNS zentral in Cloudflare. User-facing-Domains laufen über den Cloudflare-Proxy; Origins, die selbst Let's-Encrypt-Zertifikate ausstellen, stehen auf „DNS-only". Transaktionsmails über Mailjet (DKIM/SPF je Zone), eingehende Mail über mailbox.org.</p>
<table>
<tr><th>Domain / Subdomain</th><th>Ziel-Host</th><th>Zweck</th></tr>
<tr><td><code>saas-ctrl.com</code></td><td>control</td><td>Coolify-Dashboard</td></tr>
<tr><td><code>grafana.saas-ctrl.com</code></td><td>control</td><td>Monitoring</td></tr>
<tr><td><code>sentry.saas-ctrl.com</code> · <code>captcha.saas-ctrl.com</code></td><td>node1</td><td>Zentrale Dienste</td></tr>
<tr><td><code>boiler.saas-ctrl.com</code> · <code>auth.boiler…</code></td><td>staging-1</td><td>Boilerplate-Staging + Admin-SSO</td></tr>
<tr><td><code>hivegrid.de</code></td><td>marketing</td><td>HiveGrid-Marketing-Site</td></tr>
<tr><td><code>tool-ctrl.de</code> (+ <code>git</code>,<code>auth</code>,…)</td><td>marketing / homeserver</td><td>Marketing + interne Tools (DDNS)</td></tr>
<tr><td><code>mitapp.de</code> · <code>api.mitapp.de</code> · <code>staging.mitapp.de</code></td><td>mitapp-prod / staging-1</td><td>mITapp Prod/API/Staging</td></tr>
</table>
<p class="small">Aktive Zonen u. a.: <code>saas-ctrl.com</code>, <code>hivegrid.de</code>, <code>mitapp.de</code>, <code>mitup.de</code>, <code>tool-ctrl.de</code>, <code>power-linked.de</code> (reserviert). TLS-Modus durchgehend „Strict".</p>
</section>
<!-- 11 -->
<section class="sw">
<h2 class="section">11. Secrets &amp; Zugänge</h2>
<ul>
<li><strong>App- &amp; Service-Secrets:</strong> zentral über <strong>Coolify</strong> verwaltet – als Environment-Variablen je Resource, nie im Repo.</li>
<li><strong>Admin-/CLI-Zugänge</strong> (Hetzner-Cloud-API, Coolify-API, Cloudflare) liegen lokal unter <code>~/.cli-tokens/</code> (chmod 600).</li>
<li><strong>Inter-Host:</strong> Coolify→Host per dediziertem SSH-Key; Apps→zentrale Dienste über das private Mesh (mesh-only).</li>
<li><strong>Admin-SSO &amp; Passwortverwaltung:</strong> Authentik + Vaultwarden auf dem <code>homeserver</code>.</li>
</ul>
<p class="small">Grundsatz aus STANDARDS: keine Secrets im Repo, keine Klartext-Weitergabe in Chats. (Konkrete Schlüsselwerte und Dateipfade sind in diesem Dokument bewusst ausgespart.)</p>
</section>
<!-- 12 -->
<section class="sw">
<h2 class="section">12. Backups &amp; Disaster Recovery</h2>
<div class="callout warn"><span class="t">Status: in Arbeit</span>Eine vollständige, automatisierte Backup-Strategie ist noch nicht abgeschlossen – wichtig, bevor eine DB zum alleinigen Speicher von Kundendaten wird.</div>
<table>
<tr><th>Komponente</th><th>Disposabel?</th><th>Backup / Recovery</th></tr>
<tr><td>Coolify-State (<code>/data/coolify</code> auf control)</td><td>nein (alle Deploy-Configs)</td><td>nur lokaler Teil-Snapshot; automatisiertes S3-Export geplant</td></tr>
<tr><td>Produkt-DBs (Postgres, Prod/Staging)</td><td>nein (Kundendaten)</td><td>Coolify-Auto-Backup zu aktivieren/dokumentieren</td></tr>
<tr><td>Sentry / Loki / ClamAV / mCaptcha (node1)</td><td>ja (nur Historie/Sig-DB)</td><td>kein Backup – werden bei Bedarf neu aufgesetzt</td></tr>
<tr><td>Meilisearch-Index</td><td>teilweise</td><td>aus Postgres reindizierbar; S3-Snapshots geplant</td></tr>
</table>
<p class="small">Geplant: Synology-NAS zieht per Hyper Backup von <code>control</code> und <code>homeserver</code>; Off-Site-/Objekt-Speicher über <strong>Hetzner Object Storage (S3, EU)</strong>.</p>
<p><strong>Recovery-Kurzform:</strong> <code>node1</code>-Ausfall → Dienste neu deployen (Daten verworfen). <code>control</code>-Ausfall → <code>/data/coolify</code> wiederherstellen, Coolify starten, Apps neu deployen. Produkt-DB → aus Postgres-Backup wiederherstellen (sobald aktiviert).</p>
</section>
<!-- 13 -->
<section class="sw">
<h2 class="section">13. Standards &amp; Konventionen</h2>
<p>Verbindlich aus <code>infra/STANDARDS.md</code> (Kurzfassung):</p>
<ol>
<li>Errors → zentrale Sentry (1 Team/Produkt, 1 Projekt/Codebasis, Environment-Tags).</li>
<li>Jeder Host liefert Metrics (node-exporter + docker-stats) und Logs (Promtail → Loki).</li>
<li>Jedes SaaS bekommt ein Grafana-Dashboard (gefiltert nach <code>project</code>/<code>server</code>).</li>
<li>Volltextsuche über zentrale Meilisearch (Index-Präfix, Scoped-Key).</li>
<li>Admin-SSO über zentrale Authentik.</li>
<li>Repos in Gitea, privat per Default; Dockerfile Pflicht.</li>
<li>Coolify ist das Deploy-Ziel: Projekt = Produkt, Environment = Stufe, Resource = Dienst.</li>
<li>Domains in Cloudflare (proxied für user-facing, DNS-only für LE-Origins).</li>
<li>Secrets außerhalb des Repos (<code>~/.cli-tokens/</code>, chmod 600, keine Chat-Pastes).</li>
<li>Backup dokumentieren, bevor etwas „live" geht.</li>
<li>ADR für nicht-triviale Entscheidungen (<code>decisions/</code>, nummeriert).</li>
<li>Uploads: Virenscan über zentrale ClamAV (fail-closed), Bildprüfung über NSFW-API (fail-open).</li>
</ol>
</section>
<!-- 14 -->
<section class="sw">
<h2 class="section">14. Offene Punkte</h2>
<p>Neutral zusammengefasst aus README-/LESSONS-To-dos (Stand Juni 2026):</p>
<ul>
<li><strong>Release:</strong> erster mITapp-<code>:prod</code>-Promote noch ausstehend; Legacy-Compose-Stack nach Bewährungsfenster abbauen.</li>
<li><strong>Prod-Topologie:</strong> eigene Hetzner-Projekte für Boilerplate- und HiveGrid-Cloud-Prod gemäß ADR-009 anlegen.</li>
<li><strong>Backups:</strong> Coolify-State-Export, Produkt-DB-Backups und NAS-/Off-Site-Ziel finalisieren.</li>
<li><strong>Sicherheit:</strong> Secret-Rotation, 2FA auf Cloudflare, Hetzner-Cloud-Firewall (SSH auf bekannte IPs), Cloudflare-Token auf „scoped" reduzieren.</li>
<li><strong>Observability:</strong> Alert-Routing aktivieren, Grafana-Dashboards versionieren.</li>
</ul>
<hr>
<p class="small">Stand 14. Juni 2026. Quelle der Wahrheit ist das <code>infra/</code>-Repository (<code>README.md</code>, <code>STANDARDS.md</code>, <code>LESSONS.md</code>, <code>decisions/</code>, <code>runbooks/</code>). Bei Abweichungen gilt das Repo. Konkrete Geheimnisse/Schlüssel sind hier bewusst ausgespart.</p>
</section>
</body>
</html>