- DnsProvisioner (dependency-frei, cURL) legt pro Reseller *.<slug>.<portal>
A-Record via Hetzner-Cloud-DNS-API an (deckt firma.reseller.portal ab,
was der globale *.<portal>-Eintrag nicht kann)
- ResellerDnsListener (Doctrine postPersist/preRemove), fail-soft,
überspringt Plattform-Reseller
- Env HCLOUD_DNS_TOKEN/HCLOUD_DNS_ZONE_NAME (leer = aus); Terraform reicht
Cloud-Token + Zone an die App-Nodes durch (nur bei manage_dns)
- Ziel-IP = APP_EDGE_IP oder DNS-Auflösung der Portal-Domain
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Caddy ersetzt den Hetzner-LB: terminiert TLS (Portal-Domain automatisch) und
load-balanced per reverse_proxy über die App-Nodes. Für Custom-Domains (§11)
On-Demand-TLS, autorisiert über GET /internal/tls-allowed.
- TlsCheckController + DomainRepository::findVerifiedByHostname: erlaubt Zertifikate
nur für Portal-Domain oder verifizierte Domains (Schutz vor Cert-Flooding).
- Terraform: hcloud_load_balancer entfernt, Caddy-Server + Firewall (80/443) +
cloud-init-caddy (Caddyfile templated mit Upstreams/Domain/ACME).
- Optional Hetzner DNS via API (manage_dns): A-Record Portal + Wildcard → Caddy.
- nginx.prod: /internal zu Symfony geroutet; APP_PORTAL_DOMAIN-Env.
Validiert: Caddyfile (caddy validate), Terraform (validate), /internal/tls-allowed (200/403/400).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Macht die App-Nodes zustandslos (horizontal skalierbar): Hintergrund-PDFs
und Schriften liegen nicht mehr lokal, sondern im S3-kompatiblen Object
Storage (Flysystem + async-aws). In der DB stehen Storage-Keys.
- flysystem-bundle + async-aws (Storage "card_assets"), env-getrieben
(S3_ENDPOINT/REGION/BUCKET/KEY/SECRET/PATH_STYLE) → lokal MinIO, prod Hetzner OS
- CardAssetUploadController: Upload/Read/Delete über Storage; GET streamt PDF
- CardPdfRenderer: liest Hintergrund (FPDI StreamReader) & Schriften (Temp-Datei) aus S3
- docker-compose: minio + minio-init (Bucket) + zweiter App-Node php2 (Profil scale-test)
- app:render-card Command für den Cross-Node-Nachweis
Verifiziert: Upload über Node 1 → identisches PDF-Render (51897 B, mit
Hintergrund) auf Node 2, der nur DB + Object Storage liest.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>