Zum Hauptinhalt springen

Konfiguration

Postbox nutzt sechs Custom Config-Dateien und eine angepasste Laravel-Queue-Config neben den Laravel-Standard-Konfigurationen. Alle projektspezifischen Werte sind ueber .env-Variablen steuerbar.

Location: config/

Config-Dateien Uebersicht​

DateiZweck
postbox.phpZentrale App-Konfiguration (Admin, Rotation, Health, Sanitizer, Retry, Tags, Mail, Notifications)
queue.phpLaravel Queue mit angepasstem retry_after (900s) fuer langlebige Jobs
collector.phpCollector-Job-Settings (Lease TTL)
feedback.phpFeedback-Feature (Empfaenger, Rate-Limit)
google_api_usage.phpGoogle API Quota Monitoring
server-monitoring.phpServer-Alert-Schwellwerte und Metriken
vantage.phpVantage Queue Monitoring Dashboard

Doppelt gesetzte Schlüssel in der .env​

Eine Variable, die zweimal in derselben .env steht, ist syntaktisch gültig und erzeugt keine Warnung. Es gilt der zuletzt gesetzte Wert; die früheren Zeilen sind wirkungslos.

Das ist am echten Code-Pfad gemessen, nicht aus der Bibliothek abgeleitet: phpdotenv schützt nur Variablen, die schon vor dem Laden in der echten Umgebung standen — innerhalb derselben Datei überschreibt eine spätere Zeile eine frühere.

Warum das eine eigene Prüfung braucht​

Der Anlassfall stand monatelang unbemerkt in der Produktions-.env:

DB_QUEUE_RETRY_AFTER=3600
DB_QUEUE_RETRY_AFTER=5400

Es ging gut aus — die gewollte Zeile stand zufällig unten. Hätten sie andersherum gestanden, wäre eine Einstellung dagewesen, hätte nicht gegolten, und nichts hätte darauf hingewiesen.

Dubletten entstehen durch Anhängen statt Ändern — die naheliegendste Art, eine .env zu pflegen, besonders wenn eine Anleitung sagt „setze X auf Y".

Die Prüfung​

# Die .env dieser Installation
php artisan env:lint

# Eine bestimmte Datei
php artisan env:lint /pfad/zur/.env

# Für den Einsatz im Deploy: schweigt, solange nichts zu melden ist
php artisan env:lint --quiet-when-clean
Exit-CodeBedeutung
0keine Dublette
1mindestens ein Schlüssel mehrfach gesetzt
2Datei nicht lesbar — ausdrücklich NICHT als „sauber" zu lesen

Der dritte Code ist der wichtigste. „Nichts gefunden" und „nicht nachgesehen" erzeugen dieselbe Ausgabe, wenn man sie nicht trennt — und wer den Befehl in einen Deploy hängt, verlässt sich darauf, dass ein grüner Ausgang etwas bedeutet.

Wo die Prüfung von selbst läuft​

OrtWas
TestsuiteEin Test hält .env.example frei von Dubletten — sie ist die Vorlage, aus der jede neue Umgebung entsteht, und eine Dublette darin wird in jede kopiert.
Zeitplanenv:lint --quiet-when-clean täglich um 06:45 UTC, mit dem Heartbeat env_lint. Ein Fund endet mit Exit 1, der Heartbeat wird als fehlgeschlagen gebucht, und der Fund selbst geht ins Log, wo der Admin-Bereich ihn anzeigt.

⚠️ Was ins Log geht, ist redigiert — und das ist Absicht. Die Konsolen-Ausgabe nennt die Werte, weil jemand, der auf dem Server vor dem Befund steht, wissen muss, welcher Wert gilt. Ins Log gehen nur Schlüsselname, Zeilennummern und das Urteil „gleiche" oder „verschiedene Werte". Eine .env besteht aus Zugangsdaten, und ein Log hat ein Nachleben — es wird durchsucht, exportiert und an einen Fehler-Tracker weitergereicht.

Bis 2026-09-04 ging der Fund gar nicht ins Log. Der geplante Lauf hinterliess damit nur Scheduled command [...] failed with exit code [1] im Fehler-Tracker — 38-mal seit Juni, ohne ein einziges Mal zu sagen, welcher Schlüssel doppelt stand. | Deploy (optional, serverseitig) | Der Befehl mit --quiet-when-clean in das Deploy-Skript, damit ein Deploy an einer Dublette scheitert statt sie mitzunehmen. |

Was der Detektor NICHT für eine Dublette hält​

Ein Falsch-Positiv wäre hier schlimmer als eine Lücke — wer eine Warnung über eine korrekte Datei bekommt, schaltet die Prüfung ab. Deshalb sind diese Fälle ausdrücklich behandelt und einzeln getestet:

  • Auskommentierte Zeilen (# KEY=alt) — der häufigste Grund, warum ein Schlüssel mehrfach dasteht und nur einmal gilt.
  • Das export-Präfix gehört nicht zum Schlüsselnamen.
  • Werte mit Gleichheitszeichen (DSN=host=db;user=app) — getrennt wird am ersten =.
  • Mehrzeilige gequotete Werte — ein eingebettetes Zertifikat enthält Zeilen, die wie Zuweisungen aussehen.
  • Prosa mit Gleichheitszeichen in Kommentaren.

Ein leerer Wert (KEY=) zählt dagegen mit: Er setzt den Schlüssel auf leer und überschreibt damit eine vorige Zeile.


postbox.php​

Zentrale Konfiguration mit allen projektspezifischen Einstellungen.

Location: config/postbox.php

Admin​

KeyEnv-VariableDefaultBeschreibung
admin_emailsPOSTBOX_ADMIN_EMAILS''Kommaseparierte Admin-E-Mail-Adressen. Wird normalisiert (Lowercase, Trimming).

Pflicht-Checkboxen fuer Datenschutzerklaerung und Nutzungsbedingungen auf der Registrierungsseite. Die Checkboxen verlinken auf CMS-Seiten, deren Inhalt in Alpine.js-Modals angezeigt wird.

KeyEnv-VariableDefaultBeschreibung
legal.privacy_page_idLEGAL_PRIVACY_PAGE_ID0CMS Page-ID fuer Datenschutzerklaerung (0 = kein Modal-Link)
legal.terms_page_idLEGAL_TERMS_PAGE_ID0CMS Page-ID fuer Nutzungsbedingungen (0 = kein Modal-Link)

YouTube​

KeyEnv-VariableDefaultBeschreibung
youtube_custom_url_search_fallbackPOSTBOX_YOUTUBE_CUSTOM_URL_SEARCH_FALLBACKfalseFallback ueber search.list fuer Custom-URL-Aufloesung (Quota-intensiv)
youtube_rotation_daysPOSTBOX_YOUTUBE_ROTATION_DAYS3Rotation-Kadenz fuer taegliche YouTube-Scrapes (Hard-Cap: max 4 Tage). 1 = taeglich.
leaderboard_candidate_limitPOSTBOX_LEADERBOARD_CANDIDATE_LIMIT200Kandidaten-Limit fuer Priority-Scraping (Top/Flop + Rising Stars)

Low-Priority-Rotation​

Profile unter dem Follower-Threshold werden im langsameren Zyklus gescrapt. Gilt plattformuebergreifend fuer Instagram und YouTube. Favorites und PRO-Profile sind ausgenommen.

KeyEnv-VariableDefaultBeschreibung
low_priority_follower_countPOSTBOX_LOW_PRIORITY_FOLLOWER_COUNT100Follower-Schwelle fuer Low-Priority (Minimum: 100). Profile darunter werden seltener gescrapt. Watched/favorited/PRO Profile sind ausgenommen.
low_priority_rotation_daysPOSTBOX_LOW_PRIORITY_ROTATION_DAYS7Rotation-Kadenz fuer Low-Priority-Profile (Hard-Cap: max 7 Tage)

Instagram​

KeyEnv-VariableDefaultBeschreibung
instagram_rotation_daysPOSTBOX_INSTAGRAM_ROTATION_DAYS3Rotation-Kadenz fuer taegliche Instagram-Scrapes (Hard-Cap: max 4 Tage)
instagram_priority_defaultPOSTBOX_INSTAGRAM_PRIORITY_DEFAULT0Basis-Prioritaet fuer Rotation-Scrapes
instagram_priority_low_priority_new_profilePOSTBOX_INSTAGRAM_PRIORITY_LOW_PRIORITY_NEW_PROFILE5Prioritaet fuer nie gescrapte Profile mit < 100 Followers (low-prio Imports)
instagram_priority_catch_upPOSTBOX_INSTAGRAM_PRIORITY_CATCH_UP10Prioritaet fuer verpasste Scrapes
instagram_priority_favoritePOSTBOX_INSTAGRAM_PRIORITY_FAVORITE20Prioritaet fuer Favoriten-Profile
instagram_priority_candidatePOSTBOX_INSTAGRAM_PRIORITY_CANDIDATE40Prioritaet fuer Leaderboard-Kandidaten
instagram_priority_top_leaderPOSTBOX_INSTAGRAM_PRIORITY_TOP_LEADER50Prioritaet fuer Top-Leader
instagram_priority_new_profilePOSTBOX_INSTAGRAM_PRIORITY_NEW_PROFILE60Hoechste Prioritaet fuer nie gescrapte Profile mit >= 100 Followers (regulaere Imports)

Health Monitoring​

KeyEnv-VariableDefaultBeschreibung
health.tokenHEALTH_TOKENnullToken fuer /up_system-Endpoint (X-Health-Token Header)
health.failed_jobs_thresholdHEALTH_FAILED_JOBS_THRESHOLD100Warnschwelle fuer Failed Jobs
health.collector_heartbeat_minutesHEALTH_COLLECTOR_HEARTBEAT_MINUTES30Heartbeat-Timeout fuer Collector-Clients
health.queue_quiet_startHEALTH_QUEUE_QUIET_START4Queue Quiet Hours Start (UTC-Stunde, 0-23)
health.queue_quiet_endHEALTH_QUEUE_QUIET_END6Queue Quiet Hours Ende (UTC-Stunde, 0-23)

Cron-Heartbeat-Monitoring (health.cron_heartbeats):

HeartbeatMax MinutesLabel
instagram_scrape180Instagram Scrape (2h)
youtube_scrape180YouTube Scrape (2h)
explore_metrics1560Explore Metrics (daily)
scores_calculate1560Score Calculation (daily)
google_api_sync150Google API Sync (1h)
queue_metrics45Queue Metrics (15min)
server_alerts15Server Alerts (5min)
cross_platform_auto_fill15Cross-Platform Auto-Fill (5min)

Cross-Platform​

KeyEnv-VariableDefaultBeschreibung
cross_platform.stale_daysCROSS_PLATFORM_STALE_DAYS90Tage bis Re-Berechnung faellig wird

Profile Retry​

Steuerung der Retry-Strategie fuer deaktivierte Profile (Fail-Streak).

KeyEnv-VariableDefaultBeschreibung
profile_retry.fail_streak_thresholdPOSTBOX_FAIL_STREAK_THRESHOLD14Tage konsekutiver Fehler bis zur Auto-Deaktivierung
profile_retry.fail_streak_cooldown_afterPOSTBOX_FAIL_STREAK_COOLDOWN_AFTER3Ab welchem Fail-Streak der Cooldown greift
profile_retry.fail_streak_cooldown_daysPOSTBOX_FAIL_STREAK_COOLDOWN_DAYS3Tage zwischen Retry-Versuchen im Cooldown
profile_retry.initial_retry_interval_daysPOSTBOX_INITIAL_RETRY_INTERVAL_DAYS14Tage zwischen Retries (Phase 1: intensiv)
profile_retry.initial_retry_countPOSTBOX_INITIAL_RETRY_COUNT3Anzahl Retries in der intensiven Phase
profile_retry.monthly_retry_interval_daysPOSTBOX_MONTHLY_RETRY_INTERVAL_DAYS30Tage zwischen Retries nach Phase 1 (monatlich)
profile_retry.archive_after_monthsPOSTBOX_ARCHIVE_AFTER_MONTHS6Monate bis zur permanenten Archivierung
profile_retry.queuePOSTBOX_RETRY_QUEUEprofile-retryQueue-Name fuer Retry-Jobs

Profile Sanitizer​

Automatische Deaktivierung von Low-Value-Profilen.

KeyEnv-VariableDefaultBeschreibung
sanitizer.min_age_daysPOSTBOX_SANITIZER_MIN_AGE_DAYS7Mindestalter (Tage) vor Sanitization
sanitizer.min_data_pointsPOSTBOX_SANITIZER_MIN_DATA_POINTS3Mindest-Metriken vor Sanitization
sanitizer.youtube_min_subscribersPOSTBOX_SANITIZER_YT_MIN_SUBSCRIBERS100YouTube: Mindest-Subscriber
sanitizer.youtube_min_videosPOSTBOX_SANITIZER_YT_MIN_VIDEOS1YouTube: Mindest-Videos
sanitizer.instagram_min_followersPOSTBOX_SANITIZER_IG_MIN_FOLLOWERS100Instagram: Mindest-Follower
sanitizer.instagram_min_postsPOSTBOX_SANITIZER_IG_MIN_POSTS1Instagram: Mindest-Posts
sanitizer.recheck_monthsPOSTBOX_SANITIZER_RECHECK_MONTHS3Monate bis Re-Check bereits sanitizter Profile

Tag Consolidation​

KeyEnv-VariableDefaultBeschreibung
tag_consolidation.chunk_sizePOSTBOX_TAG_CHUNK_SIZE50Tags pro Gemini-Chunk
tag_consolidation.max_chunksPOSTBOX_TAG_MAX_CHUNKS100Max. Chunks pro Lauf (Safety Limit)
tag_consolidation.rate_per_minutePOSTBOX_TAG_RATE_PER_MINUTE10API-Call-Limit pro Minute
tag_consolidation.established_thresholdPOSTBOX_TAG_ESTABLISHED_THRESHOLD5Min. Profile fuer "established" Tag
tag_consolidation.auto_execute_high_confidencePOSTBOX_TAG_AUTO_EXECUTEtrueHigh-Confidence Merges auto-ausfuehren
tag_consolidation.min_tag_lengthPOSTBOX_TAG_MIN_LENGTH3Minimale Tag-Laenge
tag_consolidation.consolidation_cooldown_daysPOSTBOX_TAG_COOLDOWN_DAYS90Cooldown pro Tag (Tage)

Mail Flood Guard​

KeyEnv-VariableDefaultBeschreibung
mail_flood.cache_storeMAIL_FLOOD_CACHE_STORECache DefaultCache-Store fuer Flood Guard Counter
mail_flood.threshold_per_hourMAIL_FLOOD_THRESHOLD_HOUR200Max. E-Mails pro Stunde
mail_flood.threshold_per_minuteMAIL_FLOOD_THRESHOLD_MINUTE50Max. E-Mails pro Minute
mail_flood.auto_pauseMAIL_FLOOD_AUTO_PAUSEtrueAutomatisch pausieren bei Schwelle
mail_flood.admin_notificationMAIL_FLOOD_ADMIN_NOTIFICATIONtrueAdmin-Alert bei Pause

Notification Types​

Die Konfiguration unter notification_types definiert alle Benachrichtigungstypen mit Labels, Defaults und Kategorien. Jeder Typ hat:

  • label / description: Anzeige in der Settings-UI
  • default_app / default_email: Default-Kanal (all, important, none)
  • supports_workspace: Workspace-bezogene Filterung
  • category: Gruppierung (profiles, data, discovery, system)
  • force_app (optional): Kann nicht deaktiviert werden

queue.php (Laravel Queue — angepasst)​

Location: config/queue.php

Standard-Laravel-Konfiguration mit einem wichtigen angepassten Wert:

KeyEnv-VariableDefaultBeschreibung
connections.database.retry_afterDB_QUEUE_RETRY_AFTER3600Reservierungs-Timeout in Sekunden. Muss hoeher sein als der laengste Job-$timeout (aktuell 2400s fuer Leaderboard-Pipeline-Jobs).

Warum 3600s statt Laravel-Default 90s?

Der Database-Queue-Driver markiert Jobs als "abandoned" nach retry_after Sekunden. Wenn ein Job laenger laeuft als retry_after, gibt der Driver den Job frei und ein zweiter Worker nimmt ihn auf — das fuehrt zu MaxAttemptsExceededException oder doppelter Ausfuehrung. Die Leaderboard-Pipeline-Jobs haben $timeout = 2400 (wachsende Datenmengen erfordern Spielraum), daher muss retry_after deutlich hoeher liegen. 3600s bietet einen komfortablen Puffer.

Tradeoff: Wenn ein Worker abstuerzt waehrend er einen Job verarbeitet, wird der Job erst nach 60 Minuten erneut verfuegbar (statt 90 Sekunden). Da Postbox mehrere Worker-Prozesse parallel laeuft, ist dieser Tradeoff akzeptabel.


collector.php​

Location: config/collector.php

KeyEnv-VariableDefaultBeschreibung
lease_ttl_secondsCOLLECTOR_LEASE_TTL_SECONDS300Lease-Dauer fuer Collector-Jobs (Sekunden). Nach Ablauf wird der Job frei fuer andere Clients.

feedback.php​

Location: config/feedback.php

KeyEnv-VariableDefaultBeschreibung
recipientFEEDBACK_RECIPIENTMAIL_FROM_ADDRESSE-Mail-Empfaenger fuer Feedback-Reports
from_addressFEEDBACK_FROM_ADDRESShello@postbox.soAbsender-Adresse (sollte vom Empfaenger abweichen)
from_nameFEEDBACK_FROM_NAMEPostboxAbsender-Name
rate_limitFEEDBACK_RATE_LIMIT30Max. Feedbacks pro User pro Stunde

google_api_usage.php​

Location: config/google_api_usage.php

KeyEnv-VariableDefaultBeschreibung
projectsGOOGLE_API_USAGE_PROJECTS''Kommaseparierte Google Cloud Projekt-IDs
serviceGOOGLE_API_USAGE_SERVICEyoutube.googleapis.comAPI Service Name fuer Monitoring
sync_interval_minutesGOOGLE_API_USAGE_SYNC_INTERVAL60Sync-Intervall in Minuten
credentials_pathGOOGLE_APPLICATION_CREDENTIALSnullPfad zu Service-Account Credentials JSON

Fester Scope: https://www.googleapis.com/auth/cloud-platform


server-monitoring.php​

Location: config/server-monitoring.php

Alert-Schwellwerte​

MetrikWarningCriticalLogik
cpu_percent7590Über dem Wert = Alarm
ram_percent8090Über dem Wert = Alarm
disk_percent8090Über dem Wert = Alarm
pg_connections500750Über dem Wert = Alarm (Gesamtzahl der Client-Verbindungen)
pg_cache_hit= Critical85Unter dem Wert = Alarm (invertiert). Beide Werte per .env, siehe unten

swap_percent wird erhoben, alarmiert aber nicht: Der Füllstand kann den Normalfall nicht vom Ernstfall trennen (Begründung in config/server-monitoring.php).

Für pg_cache_hit gibt es per Default keine eigene Warnstufe: Ohne SERVER_ALERT_PG_CACHE_HIT_WARNING fällt sie auf die kritische Schwelle. Eine Warnung ist nur zwischen Normalbetrieb und „kritisch“ sinnvoll, und geprüft wird ein Fensterwert über rund 15 Sekunden, der im laufenden Betrieb um 90 % liegen kann. Warnungen werden genauso gemailt wie kritische Alarme. Dieselben beiden Werte gelten für die Empfehlungen im DB-Monitoring (db_monitoring.thresholds.cache_hit_warning / cache_hit_critical).

Weitere Einstellungen​

KeyEnv-VariableDefaultBeschreibung
alerts.pg_cache_hit.critical_belowSERVER_ALERT_PG_CACHE_HIT_CRITICAL85Kritische Schwelle der Cache-Trefferquote (%). Leer oder keine Zahl = Default, 0 schaltet die Stufe ab
alerts.pg_cache_hit.warning_belowSERVER_ALERT_PG_CACHE_HIT_WARNING= CriticalWarnschwelle (%). Ohne eigenen Wert keine eigene Warnstufe
sustained_alert_cooldown_minutesSERVER_ALERT_SUSTAINED_COOLDOWN240Cooldown je Alarmstufe (Minuten). Eine Stufe meldet sich höchstens einmal pro Fenster, auch wenn sie zwischendurch verlassen und wieder betreten wird. Eine Eskalation auf eine Stufe, die im Fenster noch nicht gemeldet hat, kommt sofort.
active_users_minutesSERVER_ACTIVE_USERS_MINUTES5Schwelle für „aktive“ Session (Minuten)
pg_max_connectionsSERVER_PG_MAX_CONNECTIONS1000PostgreSQL max_connections für die Prozent-Berechnung. Fallback: Gelesen wird zuerst SHOW max_connections

vantage.php​

Location: config/vantage.php

KeyEnv-VariableDefaultBeschreibung
enabledVANTAGE_ENABLEDtrueMaster-Switch fuer das gesamte Package
store_payloadVANTAGE_STORE_PAYLOADtrueJob-Payloads fuer Debugging speichern
redact_keys--[password, token, secret, ...]Sensitive Keys im Payload werden redacted
retention_daysVANTAGE_RETENTION_DAYS14Aufbewahrungsdauer fuer Job-History (Tage)
routesVANTAGE_ROUTEStrueDashboard-Routen registrieren
route_prefixVANTAGE_ROUTE_PREFIXvantageURL-Prefix fuer das Dashboard
auth.enabledVANTAGE_AUTH_ENABLEDtrueGate-basierte Authentifizierung aktivieren
telemetry.enabledVANTAGE_TELEMETRY_ENABLEDtruePerformance-Telemetrie sammeln
telemetry.sample_rateVANTAGE_TELEMETRY_SAMPLE_RATE1.0Sample Rate (0.0 - 1.0)
telemetry.capture_cpuVANTAGE_TELEMETRY_CPUtrueCPU-Metriken erfassen
database_connectionVANTAGE_DATABASE_CONNECTIONnullSeparate DB-Connection (null = Default)
logging.enabledVANTAGE_LOGGING_ENABLEDtruePackage-Logging aktivieren
notify.emailVANTAGE_NOTIFY_EMAILnullE-Mail fuer Failure-Notifications (deaktiviert, Flare/Nightwatch wird genutzt)
notify.slack_webhookVANTAGE_SLACK_WEBHOOKnullSlack Webhook fuer Notifications

Vollstaendige .env-Referenz (Custom Variables)​

Authentifizierung​

WORKOS_CLIENT_ID=client_...
WORKOS_API_KEY=sk_...
WORKOS_REDIRECT_URL=https://app.postbox.so/authenticate
POSTBOX_ADMIN_EMAILS="admin@example.com, second@example.com"
LEGAL_PRIVACY_PAGE_ID=0
LEGAL_TERMS_PAGE_ID=0

YouTube API​

YOUTUBE_API_KEY=AIza...
YOUTUBE_API_KEYS=key1,key2,key3
YOUTUBE_API_VIDEO_KEYS=key4,key5
YOUTUBE_API_EXTENDED_KEYS=key6,key7
YOUTUBE_API_RESEARCH_KEYS=key8
POSTBOX_YOUTUBE_CUSTOM_URL_SEARCH_FALLBACK=false
POSTBOX_YOUTUBE_ROTATION_DAYS=3
POSTBOX_LEADERBOARD_CANDIDATE_LIMIT=200

Instagram​

POSTBOX_INSTAGRAM_ROTATION_DAYS=3
POSTBOX_INSTAGRAM_PRIORITY_DEFAULT=0
POSTBOX_INSTAGRAM_PRIORITY_CATCH_UP=10
POSTBOX_INSTAGRAM_PRIORITY_FAVORITE=20
POSTBOX_INSTAGRAM_PRIORITY_NEW_PROFILE=30
POSTBOX_INSTAGRAM_PRIORITY_CANDIDATE=40
POSTBOX_INSTAGRAM_PRIORITY_TOP_LEADER=50

Health Monitoring​

HEALTH_TOKEN=your-secret-health-token
HEALTH_FAILED_JOBS_THRESHOLD=100
HEALTH_COLLECTOR_HEARTBEAT_MINUTES=30
HEALTH_QUEUE_QUIET_START=4
HEALTH_QUEUE_QUIET_END=6

Queue​

DB_QUEUE_RETRY_AFTER=3600

Reverb (WebSockets)​

BROADCAST_CONNECTION=reverb
REVERB_APP_ID=postbox
REVERB_APP_KEY=your-random-key
REVERB_APP_SECRET=your-random-secret
REVERB_SERVER_HOST=127.0.0.1
REVERB_SERVER_PORT=8081
REVERB_HOST=app.postbox.so
REVERB_PORT=443
REVERB_SCHEME=https

Google API​

GOOGLE_API_USAGE_PROJECTS=project-1,project-2
GOOGLE_API_USAGE_SERVICE=youtube.googleapis.com
GOOGLE_API_USAGE_SYNC_INTERVAL=60
GOOGLE_APPLICATION_CREDENTIALS=/path/to/credentials.json

AI Enhancement​

AI_ENHANCER_HOURLY_LIMIT=100
AI_ENHANCER_RATE_PER_MINUTE=15
AI_ENHANCER_COOLDOWN_DAYS=30

Profile Retry​

POSTBOX_FAIL_STREAK_THRESHOLD=14
POSTBOX_INITIAL_RETRY_INTERVAL_DAYS=14
POSTBOX_INITIAL_RETRY_COUNT=3
POSTBOX_MONTHLY_RETRY_INTERVAL_DAYS=30
POSTBOX_ARCHIVE_AFTER_MONTHS=6
POSTBOX_RETRY_QUEUE=profile-retry

Profile Sanitizer​

POSTBOX_SANITIZER_MIN_AGE_DAYS=7
POSTBOX_SANITIZER_MIN_DATA_POINTS=3
POSTBOX_SANITIZER_YT_MIN_SUBSCRIBERS=100
POSTBOX_SANITIZER_YT_MIN_VIDEOS=1
POSTBOX_SANITIZER_IG_MIN_FOLLOWERS=100
POSTBOX_SANITIZER_IG_MIN_POSTS=1
POSTBOX_SANITIZER_RECHECK_MONTHS=3

Tag Consolidation​

POSTBOX_TAG_CHUNK_SIZE=50
POSTBOX_TAG_MAX_CHUNKS=100
POSTBOX_TAG_RATE_PER_MINUTE=10
POSTBOX_TAG_ESTABLISHED_THRESHOLD=5
POSTBOX_TAG_AUTO_EXECUTE=true
POSTBOX_TAG_MIN_LENGTH=3
POSTBOX_TAG_COOLDOWN_DAYS=90

Mail Flood Guard​

MAIL_FLOOD_CACHE_STORE=redis
MAIL_FLOOD_THRESHOLD_HOUR=200
MAIL_FLOOD_THRESHOLD_MINUTE=50
MAIL_FLOOD_AUTO_PAUSE=true
MAIL_FLOOD_ADMIN_NOTIFICATION=true

Server Monitoring​

SERVER_ALERT_SUSTAINED_COOLDOWN=240
SERVER_ACTIVE_USERS_MINUTES=5
SERVER_PG_MAX_CONNECTIONS=200

Collector​

COLLECTOR_LEASE_TTL_SECONDS=300

Feedback​

FEEDBACK_RECIPIENT=feedback@example.com
FEEDBACK_FROM_ADDRESS=hello@postbox.so
FEEDBACK_FROM_NAME=Postbox
FEEDBACK_RATE_LIMIT=30

Monitoring (Flare, Matomo, Nightwatch)​

FLARE_KEY=your-flare-key
MATOMO_URL=https://analytics.example.com
MATOMO_SITE_ID=1
MATOMO_TOKEN=your-token
MATOMO_ENABLED=true
NIGHTWATCH_TOKEN=your-token

Vantage​

VANTAGE_ENABLED=true
VANTAGE_STORE_PAYLOAD=true
VANTAGE_RETENTION_DAYS=14
VANTAGE_AUTH_ENABLED=true
VANTAGE_TELEMETRY_ENABLED=true
VANTAGE_TELEMETRY_SAMPLE_RATE=1.0