Zurück zur Dokumentation
Guide 03

Konfiguration

Simmetric Chat hat zwei Konfigurationsebenen: Umgebungsvariablen, die beim Prozessstart mit Zod validiert werden, und eine Laufzeit-Konfigurationsebene in der Datenbank, die Admins über die Benutzeroberfläche bearbeiten können, ohne einen Dienst neu zu s…

3 Min. Lesezeit

Simmetric Chat hat zwei Konfigurationsebenen: Umgebungsvariablen, die beim Prozessstart mit Zod validiert werden, und eine Laufzeit-Konfigurationsebene in der Datenbank, die Admins über die Benutzeroberfläche bearbeiten können, ohne einen Dienst neu zu starten.

  1. Umgebungsvariablen (Prozessstart) - mit Zod validiert in packages/<pkg>/src/config/env.ts; process.exit(1) bei Ungültigkeit.
  2. Laufzeit-Konfiguration (DB `SystemConfig`) - über die Admin-UI ohne Neustart bearbeitbar.

Auflösung:

  • ALWAYS_READONLY-Schlüssel (JWT_SECRET, DATABASE_URL, SERVER_PORT, COLLECTOR_PORT, SERVER_URL, COLLECTOR_URL, LICENSE_SECRET): ENV > Default, nicht über die UI bearbeitbar.
  • Alle anderen: DB > ENV > Default, bearbeitbar via PUT /api/system/settings.

Die drei Pakete lesen .env via path.resolve(__dirname, "../../.env") (unabhängig vom Arbeitsverzeichnis). Das Referenzdokument ist .env.example im Repo-Root.

Umgebungsvariablen - server

Schema: packages/server/src/config/env.ts. Standardwerte gezeigt; PFL = Pflicht.

Core / Infrastruktur

VariableDefaultPfl.Beschreibung
NODE_ENVdevelopmentoptdevelopment/production/test
SERVER_PORT3000optServer-API-Port
COLLECTOR_PORT3210optCollector-Port (für die Standard-URL)
SERVER_URLhttp://localhost:3000optÖffentliche Server-URL
COLLECTOR_URLhttp://localhost:3210optCollector-URL
WIDGET_SERVICE_URLhttp://localhost:3211optWidget-Dienst-URL (Push-Cache-Bust)
WIDGET_API_KEY—optGeteiltes Widget-Push-Secret; fehlt es, ist Cache-Bust ein No-op (5 min TTL)
DATABASE_URLpostgresql://simmetricchat:[email protected]:5432/simmetricchatoptPostgreSQL-URL
JWT_SECRET—PFLJWT-Secret
ENCRYPTION_KEY—optBase64 von 32 Bytes für AES-256-GCM (data-at-rest). Nicht gesetzt → Fallback scryptSync(JWT_SECRET, salt)
SESSION_EXPIRY86400000 (24h ms)optJWT-Ablauf
ALLOWED_ORIGINShttp://localhost:5173,127.0.0.1:5173,localhost:3000optGlobale CORS-Allowlist (CSV). /api/internal/widget ausgenommen
LOG_LEVELinfooptdebug/info/warn/error
ALLOW_REGISTRATIONfalseoptSelbstregistrierungs-Schalter
DISABLE_TELEMETRYtrueoptDeaktiviert Telemetrie

Bootstrap-Admin (Seed)

VariableDefaultBeschreibung
SEED_BOOTSTRAP_ADMINtrueAuto-Seed des Admins beim ersten Start (custom transform: false/0/no/off/"" = deaktiviert)
SEED_ADMIN_USERNAMEadminBootstrap-Admin-Benutzername
SEED_ADMIN_PASSWORDadmin123Passwort (min 8); Konto mustChangePassword=true
SEED_ADMIN_EMAIL[email protected]Bootstrap-Admin-E-Mail

LLM

VariableDefaultBeschreibung
LLM_PROVIDERollamaopenai/anthropic/ollama/openrouter
LLM_MODELgemma4:latestStandardmodell
LLM_TEMPERATURE0.70–2
LLM_MAX_TOKENS4096Abschluss-Token-Obergrenze
LLM_TIMEOUT0Axios-Timeout ms (0 = keiner, für langsame lokale LLMs)
LLM_API_KEY / LLM_API_BASE_URL—Generische Overrides
OPENAI_API_KEY / OPENAI_MODEL—OpenAI
ANTHROPIC_API_KEY / ANTHROPIC_MODEL—Anthropic
OLLAMA_BASE_URLhttp://ollama:11434Ollama-Endpunkt
OLLAMA_MODEL / OLLAMA_API_KEY—Ollama-Override
OPENROUTER_API_KEY / OPENROUTER_BASE_URL / OPENROUTER_MODELhttps://openrouter.ai/api / —OpenRouter

Agent-Watchdog (Schleifen-/Stall-Verhinderung)

VariableDefaultBeschreibung
AGENT_WALLCLOCK_TIMEOUT_MS600000 (10 min)Absolute Obergrenze für eine ReAct-Anfrage
AGENT_MAX_TOTAL_TOKENS200000Prompt+Completion-Gesamt pro Anfrage
AGENT_MAX_CONTEXT_BYTES500000 (500 KB)Obergrenze der Kontext-Array-Größe
AGENT_MAX_TOOL_OUTPUT_LENGTH5000Abschneidung der Ausgabe pro Skill
AGENT_MAX_SKILL_EXECUTION_MS60000Timeout pro Skill
AGENT_LOOP_DETECTION_WINDOW3Abbruch nach N identischen aufeinanderfolgenden tool+input
CHAT_MAX_CONCURRENT_PER_USER5Chat-Gleichzeitigkeitslimit pro Benutzer

Embedding / Vector DB / OCR / Synthesis

VariableDefaultBeschreibung
EMBEDDING_PROVIDERlocallocal/openai/ollama
EMBEDDING_MODELXenova/all-MiniLM-L6-v2Embedding-Modell
EMBEDDING_API_KEY—Embedding-API-Key
VECTOR_DB_PROVIDERlancedblancedb/qdrant
VECTOR_DB_URL / VECTOR_DB_API_KEY—Für entferntes Qdrant
OCR_MODELglm-ocr:latestVision-OCR-Modell (Format model:version)
OCR_TIMEOUT600000Vision-OCR-Timeout
SYNTHESIS_LLM_MODELgemma4:latestModell der Synthese-Pipeline
PUPPETEER_EXECUTABLE_PATH—Chromium für PDF-Export von Archiven

Lizenz, Push, E-Mail, Collector

VariableDefaultBeschreibung
LICENSE_KEY—Enterprise-Lizenz-JWT (HS256). Fehlt → Community
LICENSE_SECRET—JWT-Verifikationsgeheimnis (empfohlen); Fallback auf LICENSE_KEY
COLLECTOR_SECRET—PFL — geteiltes Geheimnis server↔collector
VAPID_PUBLIC_KEY / VAPID_PRIVATE_KEY / VAPID_SUBJECT—Web Push; nicht gesetzt → ephemere Schlüssel in dev
SMTP_HOST / SMTP_PORT / SMTP_USER / SMTP_PASS / SMTP_FROM—SMTP für Passwort-Reset + Backup-Fehlerbenachrichtigungen
LEGACY_PREVIOUS_ENCRYPTION_KEYS—CSV base64 32 Byte für Schlüsselrotation
Variablen in .env.example, aber als DB-Settings verwaltet (nicht in envSchema): OCR_ENABLED, OCR_PRECHECK_CHARS, STORAGE_PATH (collector), RESET_PASSWORD_STRATEGY.

Umgebungsvariablen - collector

Schema: packages/collector/src/config/env.ts.

VariableDefaultPfl.Beschreibung
COLLECTOR_PORT3210optHTTP-Port
COLLECTOR_URLhttp://localhost:3210optEigene URL
SERVER_URLhttp://localhost:3000optServer-URL (Callback)
EMBEDDING_PROVIDERlocaloptlocal/openai/ollama
EMBEDDING_MODEL—optEmbedding-Modell
EMBEDDING_API_KEY—optAPI-Key
VECTOR_DB_PROVIDERlancedboptlancedb/qdrant
VECTOR_DB_URL / VECTOR_DB_API_KEY—optQdrant
OLLAMA_BASE_URLhttp://ollama:11434optOllama
STORAGE_PATH./storageoptDateisystem-Speicher
COLLECTOR_SECRET—PFLMuss mit dem Server übereinstimmen

Umgebungsvariablen - widget

Schema: packages/widget/src/config/env.ts.

VariableDefaultPfl.Beschreibung
NODE_ENVdevelopmentoptUmgebung
WIDGET_PORT3211optHTTP-Port
SERVER_URLhttp://localhost:3000optServer-URL
WIDGET_API_KEY—PFLGeteiltes Geheimnis widget↔server (muss übereinstimmen)
LOG_LEVELinfooptLog-Level
Frontend: keine VITE_*-Variablen und keine .env-Datei. Das Frontend liest Branding/Konfiguration vom Server (GET /api/system/settings, GET /api/license/info).

Feature-Flags & Lizenz

Definiert in packages/shared/src/constants/license.ts (FEATURE_FLAGS + Defaults). Verifiziert in packages/server/src/services/licenseService.ts.

Wie man von Community zu Enterprise wechselt

  1. Setzen Sie LICENSE_KEY (HS256-JWT) und, empfohlen, LICENSE_SECRET.
  2. jwt.verify → Payload gegen licensePayloadSchema validiert (tier, iss, sub, iat, exp, features).
  3. tier=enterprise → startet von ENTERPRISE_FEATURE_DEFAULTS + die features-Overrides des JWT.
  4. Abgelaufen/ungültig → Community-Fallback mit Warnung.

Graceful Degradation: getLicenseInfo() prüft den Ablauf zur Laufzeit; läuft Enterprise während der Nutzung ab, degradiert es ohne Neustart zu Community. requireFeature(flag) → HTTP 402, wenn deaktiviert; requireFeatureLimit(flag, model) → 402, wenn count >= limit (Enterprise = Infinity).

Immer-aktive Commodity-Flags: seit der EPA-02-Entfernung existieren die 8 Commodity-Flags nicht mehr in FEATURE_FLAGS - webhooks, push_notifications, web search, memory, lead_export, widget_analytics, auto_title, synthesis_rate_limit sind in Community immer aktiv (Commodity-Routen laufen ohne Gating). priority_support ist kein Flag: es ist ein kommerzieller/SLA-Vertrag (EPA-08). Es bleiben 11 Enterprise-Flags, darunter widget_credits_editing (Widget-Credits-Bearbeitung, Community false / Enterprise true) und custom_agents als numerisches Limit (Community 3, Enterprise Infinity).

Flag-Tabelle (11)

FlagTypCommunityEnterpriseWas es gateet
sso_enabledboolfalsetrueEnterprise-SSO
audit_log_immutableboolfalsetrueUnveränderbarkeit des Audit-Logs
white_labelboolfalsetrueBranding BRANDING_*
max_workspacesnum3InfinityWorkspace-Erstellung
max_projectsnum3InfinityProjekterstellung
custom_agentsnum3InfinityCustom-Agenten (Limit)
widget_enabledboolfalsetrueEinbettbares Widget
max_widgetsnum1InfinityWidget-Erstellung
backup_enabledboolfalsetrueBackup-System
max_backup_destinationsnum1InfinityBackup-Ziele
widget_credits_editingboolfalsetrueWidget-Credits-Bearbeitung

Laufzeit-Konfiguration (DB SystemConfig)

Bearbeitbar via GET/PUT /api/system/settings (bulk) und PUT /api/system/chat-retention (spezifisch, auditiert). Die Defaults leben in CONFIG_DEFAULTS (packages/shared/src/constants/permissions.ts).

Schlüsselgruppen: LLM (LLM_PROVIDER, LLM_MODEL, LLM_TEMPERATURE, ...), Embedding/Vector, Server-Ports/URLs, Auth (SESSION_EXPIRY), Branding (BRANDING_APP_NAME, BRANDING_PRIMARY_COLOR, BRANDING_APP_SUBTITLE, BRANDING_APP_ICON_URL - gated white_label), Feature (DISABLE_TELEMETRY, ALLOW_REGISTRATION, DLP_ENABLED), OCR (OCR_DEFAULT_MODEL, OCR_DEFAULT_MODE, OCR_DEFAULT_CUSTOM_INSTRUCTIONS, OCR_ENABLED, OCR_PRECHECK_CHARS), Synthesis (SYNTHESIS_LLM_PROVIDER_ID, SYNTHESIS_LLM_MODEL), Retention (upload_draft_retention_days, ALLOW_NON_ADMIN_UPLOAD, chat_message_retention_days).

PUT /api/system/settings gibt immer 200 mit { updated: [...], rejected: [...] } zurück (readonly-Schlüssel werden abgelehnt, während die anderen angewendet werden).

Chat-Nachrichtenaufbewahrung (Reaper)

Service: packages/server/src/services/chatMessageReaperJob.ts. DB-Knob: chat_message_retention_days.

  • Pass 1 (Soft-Delete): Tombstones Nachrichten AKTIVER Chats jenseits von retentionDays. No-op, wenn der Wert null/""/<=0 ist.
  • Pass 2 (Hard-Purge): Löscht tombstonierte Zeilen jenseits einer 7-Tage-Gnadenfrist (hardcodiert). Läuft immer, unabhängig von der Aufbewahrungskonfiguration (Anti-PII-Leak).
  • Kadenz: 24h-setInterval, erster Lauf um 03:00 Ortszeit. isRunning-Mutex.
  • Nur PUT /api/system/chat-retention (auditiert, confirmDataLoss: true, retentionDays: integer|null, min 1); der Bulk-Settings-Endpunkt lehnt diesen Schlüssel ab.

Backup-Aufbewahrung

Pro Job (das retentionDays-Feld auf BackupJob, Standard 30). backupRetentionService.ts bereinigt Logs/Dateien jenseits von cutoffDate. Nicht global.

Verschlüsselungsschlüssel-Rotation

packages/server/src/services/encryptionService.ts (AES-256-GCM, 32-Byte-Schlüssel, 16-Byte-IV, Salt simmetric-chat-encryption-salt).

  • Aktueller Schlüssel: ENCRYPTION_KEY (base64 32 Bytes), wenn gesetzt; sonst scryptSync(JWT_SECRET, salt, 32).
  • LEGACY_PREVIOUS_ENCRYPTION_KEYS (CSV base64 32 Bytes) → Entschlüsselungs-Schlüsselkette [current, ...previous, scryptLegacy?].
  • encrypt() nutzt chain[0]. Es gibt keinen automatischen Re-Encryption-Job: die Prozedur ist manuell decrypt-mit-alt → re-encrypt-mit-neu.