Live-Prüfung 08.10.2026: nginx beantwortet die Anfragen direkt (kein CDN, kein vorgeschalteter Proxy), die App vertraute aber jedem Proxy (trustProxies at '*'). Jeder Client konnte per X-Forwarded-For seine IP vorgeben und damit IP-Limits bei Login und Passwort-Reset umgehen sowie IP-Protokolle fälschen. Jetzt steuert config/trustedproxy.php das Vertrauen: lokal (Traefik) weiterhin '*', sonst keinem Proxy; ein späteres CDN kommt über TRUSTED_PROXIES dazu. HSTS fehlte auf Live. SecurityHeaders setzt Strict-Transport-Security auf HTTPS-Antworten, standardmäßig nur in Produktion (max-age 1 Jahr, bewusst ohne includeSubDomains/preload). phpunit.xml leert MONITORING_NTFY_*, damit Testläufe keine echten Push-Nachrichten mehr auslösen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
355 lines
14 KiB
Text
355 lines
14 KiB
Text
APP_NAME=Pressekonto
|
||
APP_ENV=local
|
||
APP_KEY=
|
||
APP_DEBUG=true
|
||
APP_URL=https://pressekonto.test
|
||
|
||
APP_LOCALE=de
|
||
APP_FALLBACK_LOCALE=de
|
||
APP_FAKER_LOCALE=en_US
|
||
|
||
APP_MAINTENANCE_DRIVER=file
|
||
# APP_MAINTENANCE_STORE=database
|
||
|
||
# HTTP Basic Auth Gate (Staging-Schutz). Nur erzwungen, wenn aktiviert
|
||
# UND BASIC_AUTH_USER/BASIC_AUTH_PASSWORD gesetzt sind.
|
||
BASIC_AUTH_ENABLED=true
|
||
BASIC_AUTH_USER=
|
||
BASIC_AUTH_PASSWORD=
|
||
|
||
PHP_CLI_SERVER_WORKERS=4
|
||
|
||
BCRYPT_ROUNDS=12
|
||
|
||
LOG_CHANNEL=stack
|
||
# `daily`: eine Datei je Tag (laravel-YYYY-MM-DD.log), Rotation nach
|
||
# LOG_DAILY_DAYS. Fachliche Meldungen liegen daneben in eigenen Tagesdateien
|
||
# je Thema (billing-, ki-, monitoring-, market-, admin-slow-*.log) —
|
||
# siehe config/logging.php. `single` nur noch als Notnagel, wächst unbegrenzt.
|
||
LOG_STACK=daily
|
||
LOG_DAILY_DAYS=14
|
||
LOG_DEPRECATIONS_CHANNEL=null
|
||
LOG_LEVEL=debug
|
||
|
||
# Betriebs-Alerts (R5.3): fehlgeschlagene Scheduler-Tasks/Queue-Jobs werden als
|
||
# `critical` geloggt und – wenn aktiviert – zusätzlich an MONITORING_ALERT_EMAIL
|
||
# gemeldet (Fallback: LEGAL_CONTACT_EMAIL). In Prod echten Mailer voraussetzen.
|
||
MONITORING_ALERTS_ENABLED=true
|
||
MONITORING_ALERT_EMAIL=
|
||
# Zweiter Alarmweg per ntfy (Monitoring-Plan M3) — ohne URL/Topic aus.
|
||
MONITORING_NTFY_URL=
|
||
MONITORING_NTFY_TOPIC=
|
||
MONITORING_NTFY_TOKEN=
|
||
MONITORING_NTFY_PRIORITY=4
|
||
# Tiefer Health-Check /health für den externen Monitor (Bearer oder ?token=).
|
||
# Ohne Token gibt es den Endpunkt nicht.
|
||
MONITORING_HEALTH_TOKEN=
|
||
# Anmeldeversuche mit unbekannter Adresse: Tageszähler statt Verlauf.
|
||
# Löschfrist in Tagen und Warnschwelle je Tag im Systemstatus (Standardwerte).
|
||
MONITORING_FAILED_LOGIN_RETENTION_DAYS=90
|
||
MONITORING_FAILED_LOGIN_WARN_PER_DAY=1000
|
||
|
||
# Fehler an GlitchTip (selbst gehostet, Sentry-Protokoll; Monitoring-Plan
|
||
# 1.1/1.2). Ohne DSN passiert nichts. SENTRY_RELEASE bei jedem Deploy auf
|
||
# den Git-Hash setzen, dann ordnet GlitchTip Fehler der Version zu.
|
||
SENTRY_LARAVEL_DSN=
|
||
SENTRY_BROWSER_DSN=
|
||
SENTRY_ENVIRONMENT=
|
||
SENTRY_RELEASE=
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# Multi-Domain-Setup (config/domains.php)
|
||
# Hub: pressekonto (Portal-Backend + Hub-Landing) — Spokes: presseecho,
|
||
# businessportal24. Prod: .test durch echte Domains ersetzen
|
||
# (pressekonto.de, presseecho.de, businessportal24.com).
|
||
# ---------------------------------------------------------------------------
|
||
APP_PROTOCOL="https://"
|
||
|
||
APP_PORTAL_NAME="pressekonto.test"
|
||
APP_PORTAL_URL="https://pressekonto.test"
|
||
APP_PORTAL_PRIMARY="#526266"
|
||
APP_PORTAL_SECONDARY="#82a0a7"
|
||
# APP_PORTAL_ASSET_URL=
|
||
|
||
APP_PRESSEECHO_NAME=presseecho.test
|
||
APP_PRESSEECHO_URL=https://presseecho.test
|
||
APP_PRESSEECHO_PRIMARY="#345636"
|
||
APP_PRESSEECHO_SECONDARY="#6b8f71"
|
||
|
||
APP_BUSINESSPORTAL_NAME=businessportal24.test
|
||
APP_BUSINESSPORTAL_URL=https://businessportal24.test
|
||
APP_BUSINESSPORTAL_PRIMARY="#cf3628"
|
||
APP_BUSINESSPORTAL_SECONDARY="#f0834a"
|
||
|
||
# Sanctum: Hosts, die stateful (Session-Cookie) auf die API zugreifen dürfen.
|
||
# Prod: echte Domains kommasepariert eintragen.
|
||
SANCTUM_STATEFUL_DOMAINS=pressekonto.test,presseecho.test,businessportal24.test
|
||
|
||
DB_CONNECTION=sqlite
|
||
# DB_HOST=127.0.0.1
|
||
# DB_PORT=3306
|
||
# DB_DATABASE=laravel
|
||
# DB_USERNAME=root
|
||
# DB_PASSWORD=
|
||
|
||
# Wartezeit auf Tabellen-Sperren in Sekunden (siehe config/database.php).
|
||
# Migrationen auf großen Tabellen ggf. mit DB_LOCK_WAIT_TIMEOUT=300 starten.
|
||
DB_LOCK_WAIT_TIMEOUT=10
|
||
|
||
# Aufbewahrung des PM-Ereignis-Logs in Tagen (siehe config/press_releases.php).
|
||
# Bestimmt zugleich die Tiefe der Verlaufsanzeige. 395 = 13 Monate.
|
||
PRESS_RELEASE_EVENTS_RETENTION_DAYS=395
|
||
|
||
# Aufbewahrung der Konversions-Ereignisse in Tagen (siehe config/conversion.php).
|
||
# Bewusst derselbe Horizont wie oben. 395 = 13 Monate.
|
||
CONVERSION_EVENTS_RETENTION_DAYS=395
|
||
# Mail-Link-Klick (Stufe 1): derselbe Klick je IP und Meldung zählt höchstens
|
||
# einmal in diesem Fenster (Minuten). Audit 29.09.2026, R-08.
|
||
CONVERSION_MAIL_CLICK_THROTTLE_MINUTES=30
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# Legacy-Datenbanken (Alt-Systeme, read-only für Migration/Abgleich).
|
||
# Die businessportal-Verbindung ist ein Snapshot-Dump, nicht live.
|
||
# ---------------------------------------------------------------------------
|
||
DB_HOST_BUSINESSPORTAL=127.0.0.1
|
||
DB_DATABASE_BUSINESSPORTAL=
|
||
DB_USERNAME_BUSINESSPORTAL=
|
||
DB_PASSWORD_BUSINESSPORTAL=
|
||
|
||
DB_HOST_PRESSEECHO=127.0.0.1
|
||
DB_DATABASE_PRESSEECHO=
|
||
DB_USERNAME_PRESSEECHO=
|
||
DB_PASSWORD_PRESSEECHO=
|
||
|
||
SESSION_DRIVER=redis
|
||
SESSION_LIFETIME=120
|
||
SESSION_ENCRYPT=false
|
||
SESSION_PATH=/
|
||
SESSION_DOMAIN=null
|
||
# Prod: true setzen (Cookies nur über HTTPS).
|
||
# SESSION_SECURE_COOKIE=true
|
||
|
||
BROADCAST_CONNECTION=log
|
||
FILESYSTEM_DISK=local
|
||
QUEUE_CONNECTION=database
|
||
|
||
CACHE_STORE=redis
|
||
# CACHE_PREFIX=
|
||
|
||
MEMCACHED_HOST=127.0.0.1
|
||
|
||
REDIS_CLIENT=phpredis
|
||
REDIS_HOST=127.0.0.1
|
||
REDIS_PASSWORD=null
|
||
REDIS_PORT=6379
|
||
|
||
# Prod: echten Mailer konfigurieren — Default `log` versendet NICHTS.
|
||
MAIL_MAILER=log
|
||
MAIL_SCHEME=null
|
||
MAIL_HOST=127.0.0.1
|
||
MAIL_PORT=2525
|
||
MAIL_USERNAME=null
|
||
MAIL_PASSWORD=null
|
||
MAIL_FROM_ADDRESS="hello@example.com"
|
||
MAIL_FROM_NAME="${APP_NAME}"
|
||
# Bounce-Behandlung (Roadmap 1b.6): Envelope-Sender für Rückläufer — MUSS auf
|
||
# der From-Domain liegen (SPF-Ausrichtung), z. B. bounce@pressekonto.com.
|
||
# Leer = From-Adresse. Schwellen siehe config/bounces.php.
|
||
MAIL_RETURN_PATH=
|
||
# IMAP-Zugang zum Bounce-Postfach für `mail:fetch-bounces` (alle 10 Minuten).
|
||
# Leer = der Befehl tut nichts.
|
||
BOUNCE_IMAP_HOST=
|
||
BOUNCE_IMAP_PORT=993
|
||
BOUNCE_IMAP_ENCRYPTION=ssl
|
||
BOUNCE_IMAP_USERNAME=
|
||
BOUNCE_IMAP_PASSWORD=
|
||
# Signatur der Bounce-Referenz X-Pk-Ref (Audit 29.09.2026, R-04). Leer = aus
|
||
# APP_KEY abgeleitet. Ein Wechsel entwertet nur Signaturen bereits versendeter
|
||
# Mails (deren Rückläufer zählen dann weich).
|
||
BOUNCE_SIGNING_KEY=
|
||
# Bounce OHNE Signatur (hart wie weich): policy = nur protokollieren (sicher,
|
||
# Standard); soft = zählt auf den Schwellwert und ist damit fälschbar.
|
||
BOUNCE_UNSIGNED_KIND=policy
|
||
# Aufbewahrung der Rückläufer-Ereignisse (email_bounces) in Tagen; gelöscht von
|
||
# conversion:purge-events. Die Sperrliste bleibt.
|
||
BOUNCE_RETENTION_DAYS=395
|
||
|
||
AWS_ACCESS_KEY_ID=
|
||
AWS_SECRET_ACCESS_KEY=
|
||
AWS_DEFAULT_REGION=us-east-1
|
||
AWS_BUCKET=
|
||
AWS_USE_PATH_STYLE_ENDPOINT=false
|
||
|
||
VITE_APP_NAME="${APP_NAME}"
|
||
# Lokaler Vite-Dev-Server (dual-port Setup, siehe VITE-SETUP.md).
|
||
VITE_DEV_SERVER_URL=https://assets.pressekonto.test
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# Stripe / Billing (Laravel Cashier)
|
||
# Prod: Live-Keys + Live-Preis-IDs eintragen [EXTERN: R1.2].
|
||
# ---------------------------------------------------------------------------
|
||
STRIPE_KEY=
|
||
STRIPE_SECRET=
|
||
STRIPE_WEBHOOK_SECRET=
|
||
# Stripe-Preis-ID des Einzelkaufs (eine PM, 19 €).
|
||
STRIPE_PRICE_SINGLE_PM=
|
||
CASHIER_CURRENCY=eur
|
||
CASHIER_CURRENCY_LOCALE=de_DE
|
||
|
||
# Umsatzsteuer/Rechnungsangaben.
|
||
# BILLING_VAT_RATE=0.19
|
||
# BILLING_DEFAULT_CURRENCY=EUR
|
||
# BILLING_MANUAL_DUE_DAYS=14
|
||
BILLING_OWN_VAT_ID=
|
||
|
||
# Abo-/Bestandsschutz-Gate (WS-5). Default (auch ohne Env-Key) ist true:
|
||
# Einreichen zur Prüfung erfordert ein aktives Abo, einen bezahlten Einzelkauf
|
||
# oder Bestandsschutz (grandfathered) — siehe User::hasActiveBooking().
|
||
# Nur für lokale Entwicklung bewusst auf false setzen (Gate offen für alle).
|
||
BILLING_ENFORCE_BOOKING=true
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# KI: Klassifikation (Red-Flag-Gate) + Content-Score (config/scoring.php).
|
||
# Ohne OPENAI_API_KEY laufen die Jobs nicht — Queue-Worker erforderlich.
|
||
# ---------------------------------------------------------------------------
|
||
OPENAI_API_KEY=
|
||
# OPENAI_API_URL=
|
||
# OPENAI_MODEL=
|
||
# OPENAI_TIMEOUT=30
|
||
|
||
# CLASSIFICATION_PROVIDER=openai
|
||
# CLASSIFICATION_MODEL=
|
||
# CLASSIFICATION_TIMEOUT=15
|
||
# CLASSIFICATION_GREEN_DELAY=0
|
||
# CLASSIFICATION_YELLOW_MANUAL=true
|
||
|
||
# CONTENT_SCORE_PROVIDER=openai
|
||
# CONTENT_SCORE_MODEL=
|
||
# CONTENT_SCORE_TIMEOUT=30
|
||
# CONTENT_SCORE_HOCHWERTIG=80
|
||
# CONTENT_SCORE_GEPRUEFT=60
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# Newsletter-Sync (config/newsletter.php). Provider `placeholder` bindet den
|
||
# Null-Client; enabled=true mit anderem Provider wirft bewusst, bis eine
|
||
# echte Anbindung implementiert ist.
|
||
# ---------------------------------------------------------------------------
|
||
NEWSLETTER_SYNC_ENABLED=false
|
||
NEWSLETTER_SYNC_PROVIDER=placeholder
|
||
NEWSLETTER_SYNC_ENDPOINT=
|
||
NEWSLETTER_SYNC_API_KEY=
|
||
# NEWSLETTER_SYNC_TIMEOUT=10
|
||
|
||
# Drosselung interner Newsletter-Kampagnen: Wellen à batch_size Mails im
|
||
# Abstand von batch_interval_minutes (SMTP-Limit/Spam-Schutz).
|
||
# NEWSLETTER_CAMPAIGN_BATCH_SIZE=50
|
||
# NEWSLETTER_CAMPAIGN_BATCH_INTERVAL_MINUTES=15
|
||
|
||
# Kampagnen-Versand freischalten (E9-Schalter, Audit 29.09.2026). Standard
|
||
# aus, bis U05–U07 umgesetzt sind; Entwürfe, Test-Mails und Abmeldung gehen
|
||
# immer.
|
||
NEWSLETTER_SENDING_ENABLED=false
|
||
|
||
# Google-Login (Laravel Socialite). Die Redirect-URI muss in der Google Cloud
|
||
# Console EXAKT der Callback-URL der Portal-Domain entsprechen (Login läuft auf
|
||
# dem Portal-Host, nicht zwingend APP_URL). Prod: https://pressekonto.com/...
|
||
# Hinweis: Google akzeptiert keine .test-Domains – lokal nur localhost oder ein
|
||
# https-Tunnel; der echte Round-Trip wird auf Staging/Live getestet.
|
||
GOOGLE_CLIENT_ID=
|
||
GOOGLE_CLIENT_SECRET=
|
||
GOOGLE_REDIRECT_URI=https://pressekonto.com/auth/google/callback
|
||
|
||
# Empfänger des öffentlichen Kontaktformulars je Brand. Ohne Wert fällt der
|
||
# Versand auf MAIL_FROM_ADDRESS zurück. [EXTERN: echte Adressen eintragen]
|
||
CONTACT_EMAIL_PRESSEKONTO=
|
||
CONTACT_EMAIL_PRESSEECHO=
|
||
CONTACT_EMAIL_BUSINESSPORTAL=
|
||
|
||
# Depublikation (T12): Netto-Preis in Cent fuer die freiwillige Entfernung
|
||
# einer veroeffentlichten PM aus der oeffentlichen Ansicht (Tombstone bleibt).
|
||
# Default 3500 (35 EUR netto, Entscheidung O6 vom 06.07.2026); 0 deaktiviert.
|
||
BILLING_DEPUBLICATION_PRICE_CENTS=3500
|
||
|
||
# Selbst gehostete Umami-Webanalyse (sys.adametz.media; IDs je Brand in
|
||
# config/domains.php). Erst beim Go-Live der Webseiten auf true setzen —
|
||
# die Datenschutzerklärung (Abschnitt 5) beschreibt den Einsatz bereits.
|
||
UMAMI_ENABLED=false
|
||
# Kundenbereich und Zuordnung zur User-ID getrennt (Monitoring-Plan M6) —
|
||
# erst nach Ergänzung der Datenschutzerklärung einschalten.
|
||
UMAMI_PORTAL_ENABLED=false
|
||
UMAMI_IDENTIFY_USERS=false
|
||
|
||
# Haupt-E-Mail für alle rechtlichen Anliegen (Impressum, Datenschutz, AGB,
|
||
# Meldeverfahren, Richtlinien) — auf allen drei Domains identisch.
|
||
# Ohne Wert gilt der Default info@pressekonto.com (config/legal.php).
|
||
LEGAL_CONTACT_EMAIL=
|
||
|
||
# Optionales separates DSA-Melde-Postfach je Brand (Impressum + /melden).
|
||
# Ohne Wert fällt die Anzeige auf LEGAL_CONTACT_EMAIL zurück.
|
||
REPORT_EMAIL_PRESSEKONTO=
|
||
REPORT_EMAIL_PRESSEECHO=
|
||
REPORT_EMAIL_BUSINESSPORTAL=
|
||
|
||
# Crawler-Abruf-Protokoll (Roadmap 1c.0): Tag, ab dem Abrufe durch Such- und
|
||
# KI-Dienste je Meldung erfasst werden — auf den Deploy-Tag setzen.
|
||
CRAWLER_HITS_RECORDED_SINCE=2026-09-18
|
||
# Abruf-Protokoll der RSS-Feeds (Roadmap 3.1): beim Deploy auf den Deploy-Tag setzen.
|
||
FEED_FETCHES_RECORDED_SINCE=2026-09-21
|
||
|
||
# Google Search Console, URL Inspection API (Roadmap 1c.4). Läuft nur mit
|
||
# Service-Konto (JSON-Schlüssel außerhalb von public/), das in beiden
|
||
# Properties als Nutzer eingetragen ist. Kundenansicht bleibt zu (E8).
|
||
GSC_ENABLED=false
|
||
GSC_SERVICE_ACCOUNT_FILE=
|
||
GSC_PROPERTY_PRESSEECHO=sc-domain:presseecho.de
|
||
GSC_PROPERTY_BUSINESSPORTAL24=sc-domain:businessportal24.com
|
||
GSC_DAILY_BUDGET=300
|
||
GSC_CUSTOMER_VISIBLE=false
|
||
|
||
# Transparenzseite „kostenlos veröffentlichen": Prüfdatum der Portalliste
|
||
# (Einträge in config/transparency.php). Leer = Abschnitt erscheint nicht.
|
||
TRANSPARENCY_FREE_PORTALS_CHECKED_AT=
|
||
|
||
# Doppelveröffentlichung (Roadmap Anhang 2, 1c.8): zweite Fassung verweist per
|
||
# rel=canonical auf die Erstquelle, Zuordnung je Kategorie in config/canonical.php
|
||
# (E9, entschieden 18.09.2026 — Standard an). Aus = jede Fassung bleibt ihr
|
||
# eigenes Canonical. ACHTUNG Reihenfolge beim ersten Deploy: migrate →
|
||
# press-releases:hash-content → press-releases:assign-canonicals.
|
||
CANONICAL_ASSIGNMENT_ENABLED=true
|
||
CANONICAL_FALLBACK=first
|
||
# Anmeldelinks „Diesen Newsroom verwalten" je Newsroom und Tag (nur versendete
|
||
# zählen). Audit 29.09.2026, R-09: 3 → 5.
|
||
NEWSROOM_CLAIM_LINKS_PER_DAY=5
|
||
# API-Zuordnung neuer Firmen zu einem bestehenden Newsroom (Roadmap 1a.4,
|
||
# Audit 29.09.2026, R-01): nur für diese User-IDs (kommagetrennt, z. B. das
|
||
# connektar-Konto) — sonst nur, wenn der Nutzer der Firma schon angehört.
|
||
# Leer = keine Freischaltung.
|
||
NEWSROOM_API_ASSIGNMENT_USER_IDS=
|
||
|
||
# --- Bildkennzeichnung (Formular v2) und Beweisarchiv (Block F) ---
|
||
# IMAGE_RIGHTS_V2: Formular v2 im Bildmanager und in der API; false = Verhalten wie vor der Umstellung.
|
||
IMAGE_RIGHTS_V2=false
|
||
# Eigener Schlüssel für die IP-Ableitung im Bestätigungsprotokoll (Fallback APP_KEY).
|
||
CONFIRMATION_IP_KEY=
|
||
# Beweisarchiv: eigener Schlüssel (php artisan archive:key), getrennt sichern — ohne ihn ist das Archiv unlesbar.
|
||
ARCHIVE_KEY=
|
||
ARCHIVE_PREVIOUS_KEYS=
|
||
ARCHIVE_RETENTION_YEARS=3
|
||
ARCHIVE_QUARANTINE_DAYS=30
|
||
ARCHIVE_SWEEP_PAUSED=false
|
||
# Zustimmungsgate vor jeder Einreichung (Block G) — erst mit AGB v0.7 einschalten.
|
||
LEGAL_ACCEPTANCE_GATE=false
|
||
|
||
# IMAGE_PRECHECK: KI-Vorab-Check für Titelbilder (Sichtung auf offensichtliche Probleme, Block K).
|
||
# Erst nach AVV mit dem Anbieter und DSE-Absatz einschalten. STAGE 1 = nur Stock-Wasserzeichen, 2 = alle Kategorien.
|
||
IMAGE_PRECHECK=false
|
||
IMAGE_PRECHECK_STAGE=1
|
||
# IMAGE_PRECHECK_MODEL=
|
||
# IMAGE_PRECHECK_DOWNGRADE=
|
||
|
||
# Proxy-Vertrauen: leer = keinem Proxy (Live, nginx direkt). Lokal ohne Angabe `*` (Traefik).
|
||
# Steht ein Proxy/CDN davor, dessen Adressbereiche kommagetrennt eintragen.
|
||
# TRUSTED_PROXIES=
|
||
# HSTS: ohne Angabe nur bei APP_ENV=production an, max-age 1 Jahr.
|
||
# SECURITY_HSTS=
|
||
# SECURITY_HSTS_MAX_AGE=31536000
|