# Kopieren nach .env und mit echten Werten befuellen. .env ist gitignored. DATABASE_URL="postgresql://user:password@localhost:5432/apicore?schema=public" # Mindestens 32 Zeichen (server-seitig erzwungen, siehe # src/runtime/startup-checks.ts) - signiert jedes Access-/MFA-/SSO-Token. # TOTP- und Webhook-Secrets aus der Zeit vor den eigenen Schluesseln (unten) # sind noch damit verschluesselt: erst nach dem rekey-Job rotieren. # Erzeugen z.B. mit: openssl rand -hex 32 JWT_ACCESS_SECRET="change-me-to-a-random-value-at-least-32-characters-long" JWT_REFRESH_SECRET="change-me-refresh-secret" JWT_ACCESS_EXPIRES_IN="15m" JWT_REFRESH_EXPIRES_IN="7d" # Hard limit from login, however often the session is refreshed. SESSION_MAX_AGE="7d" # New passwords are checked against known breaches (Have I Been Pwned, # k-anonymity: only 5 hash characters leave the server). "false" turns the # check off for deployments without internet access. # PASSWORD_BREACH_CHECK=true PORT=3001 FRONTEND_ORIGIN="http://localhost:3000" # Reverse-proxy hops in front of the backend whose X-Forwarded-For is # trusted (client IP for rate limits and API-key IP allowlists). "1" for the # single K8s ingress; a list such as "loopback, 10.0.0.0/8" also works. # Behind Cloudflare + Traefik use "loopback, , " # (see deploy/k8s/base/backend/configmap.example.yaml); "1" would yield the # Cloudflare edge IP that many clients share. TRUST_PROXY=1 # Refresh-token cookie requires HTTPS by default. Set to "false" only for # local HTTP development - every real deployment must leave this unset (or # "true"). COOKIE_SECURE=false # Lokales Verzeichnis fuer hochgeladene Dokumente/Bilder (Modul 29). # Bei horizontaler Skalierung (>1 Replica) muss dies auf ein geteiltes # Volume (z.B. ReadWriteMany-PVC) zeigen, siehe ARCHITECTURE.md Annahme 7. DOCUMENTS_STORAGE_PATH="./uploads" # Automatische Backups (Modul Backup). Alle drei Werte sind auch ueber die # GUI (Verwaltung > Backup) konfigurierbar - eine hier gesetzte Env-Variable # hat aber IMMER Vorrang vor dem GUI-Wert, pro Feld einzeln. Ohne Pfad # (weder Env noch GUI) bleibt der Zeitplan deaktiviert (nur manueller # Export/Import ueber die UI verfuegbar). # Bei horizontaler Skalierung (>1 Replica) muss der Pfad auf ein geteiltes # Volume zeigen (wie DOCUMENTS_STORAGE_PATH), sonst sieht jede Replica nur # ihre eigenen Backup-Dateien und die Lauf-Erkennung zwischen Replicas greift # nicht. BACKUP_STORAGE_PATH="./backups" # Directory a backup path set in the admin UI must stay under (the env # variable above is not restricted). Default "./backups". # BACKUP_ROOT="./backups" # Cron-Ausdruck (Standard: taeglich um 03:00 Uhr). BACKUP_CRON_SCHEDULE="0 3 * * *" # Wie viele der neuesten Backup-Dateien behalten werden, aeltere werden nach # jedem Lauf geloescht (Standard: 14). BACKUP_RETENTION_COUNT=14 # Obergrenze fuer eine einzuspielende Backup-Datei in Bytes (Standard 50 MB, # hoechstens 256 MB, weil der Upload komplett im RAM verarbeitet wird). # Export und Zeitplan warnen, wenn eine Sicherung groesser wird. # BACKUP_MAX_IMPORT_BYTES=52428800 # Erster Administrator (prisma/seed.ts): wird angelegt, wenn es noch keinen # Benutzer mit dieser Adresse gibt. Ohne Passwort erzeugt der Seed ein # zufaelliges und gibt es einmalig im Log aus. # SEED_ADMIN_EMAIL=admin@apicore.local # SEED_ADMIN_PASSWORD= # Encryption keys, one per purpose (AES-256-GCM, key derived per purpose with # HKDF): TOTP secrets, webhook signing secrets, backup files. Required in # production: at least 32 characters each, all three different from each # other and from JWT_ACCESS_SECRET/JWT_REFRESH_SECRET, e.g. # "openssl rand -hex 32". Outside production a missing key falls back to one # derived from JWT_ACCESS_SECRET. # Rotation: move the old value to _PREVIOUS (still opens), set a new # , run the rekey job, then drop _PREVIOUS. Backup files are not # resealed: keep BACKUP_ENCRYPTION_KEY_PREVIOUS while older files must restore. # Set the same BACKUP_ENCRYPTION_KEY on another deployment to restore its # backups there (MFA_/WEBHOOK_ENCRYPTION_KEY too, to keep MFA and webhook # secrets). Keep the backup key outside the backup storage. # MFA_ENCRYPTION_KEY="" # WEBHOOK_ENCRYPTION_KEY="" # BACKUP_ENCRYPTION_KEY="" # Seals voice notes (audio, transcripts, suggestions); required in production. # VOICE_ENCRYPTION_KEY="" # MFA_ENCRYPTION_KEY_PREVIOUS="" # WEBHOOK_ENCRYPTION_KEY_PREVIOUS="" # BACKUP_ENCRYPTION_KEY_PREVIOUS="" # VOICE_ENCRYPTION_KEY_PREVIOUS="" # Entra ID SSO. Ohne alle vier Werte bleibt SSO deaktiviert # (GET /api/v1/auth/sso/status meldet enabled: false, kein Fehler beim Start). # OIDC_ISSUER_URL ist die v2.0-Metadaten-URL des Tenants, OIDC_REDIRECT_URI # muss exakt der in der App-Registrierung hinterlegten Redirect-URI entsprechen. # # Dieselbe App-Registrierung wird auch fuer die M365-Kalender-Synchronisation # (Modul Kalender) wiederverwendet - kein zweites Enterprise-App-Objekt noetig. # Dafuer in der App-Registrierung zusaetzlich die Application-Permission # "Calendars.ReadWrite" hinzufuegen (Admin-Consent noetig); die Tenant-ID wird # aus OIDC_ISSUER_URL abgeleitet. Eintraege (Aufgaben, Durchsichten, # Behandlungen, Fuetterungen, Ernten, Koeniginnen-Zuordnungen) werden # einseitig (ApiCore -> M365) in den Kalender einer Microsoft-365-Gruppe # uebertragen. Ohne diese drei Werte bleibt die Synchronisation deaktiviert; # zusaetzlich muessen die Synchronisation selbst UND die jeweilige Gruppen-ID # in der GUI (Verwaltung > Automatisierung > Kalender-Sync) gesetzt werden. OIDC_ISSUER_URL="https://login.microsoftonline.com//v2.0" OIDC_CLIENT_ID="change-me-entra-client-id" OIDC_CLIENT_SECRET="change-me-entra-client-secret" OIDC_REDIRECT_URI="http://localhost:3001/api/v1/auth/sso/callback" # Swagger-UI unter /api/v1/docs ist per Default deaktiviert, da sie das # gesamte API-Schema ungeschuetzt (kein JwtAuthGuard) preisgeben wuerde. # Nur fuer lokale Entwicklung/Debugging explizit aktivieren. ENABLE_SWAGGER=false # Laufrolle des Prozesses: api (nur HTTP), worker, job (CLI) oder all (API + # Worker in einem Prozess, nur fuer lokale Entwicklung und Tests). Ohne Wert # gilt der Standard des Einstiegspunkts (main: all, worker: worker, cli: job). # In Produktion Pflicht und "all" ist dort verboten. # APICORE_ROLE=api # Port des Health-Endpunkts des Worker-Prozesses. # WORKER_HEALTH_PORT=3002 # Wetterquelle fuer die Pflanzenentwicklung (Gradtagszahl) und die # Wetteranzeige. Einzige Quelle ist die freie Open-Meteo-API (bewusst # uebernommenes Lizenzrisiko, nicht-kommerzielle Bedingungen); der Adapter # (src/weather/history) erlaubt einen Wechsel. Nur https:-URLs; ein # unbekannter Quellenname oder eine http:-URL stoppt den Start. Der Schluessel # ist optional, gehoert in ein Secret und steht nie in Logs oder Fehlermeldungen. # WEATHER_HISTORY_SOURCE=open-meteo # OPEN_METEO_FORECAST_URL=https://api.open-meteo.com/v1/forecast # OPEN_METEO_ARCHIVE_URL=https://archive-api.open-meteo.com/v1/archive # OPEN_METEO_API_KEY= # Optional: Kontakt (E-Mail oder URL) fuer den User-Agent-Header der # Wetteranfragen ("ApiCore/ (+)"); ohne Wert # "ApiCore/ (self-hosted beekeeping app)". # WEATHER_USER_AGENT_CONTACT= # Telegram-Erinnerungen (optional, T4c). TELEGRAM_BOT_USERNAME (ohne @, 5-32 # Zeichen: Buchstaben, Ziffern, Unterstrich) steht in API und Worker und # erzeugt den Deep Link. TELEGRAM_BOT_TOKEN (BotFather) gehoert NUR dem Worker, # im Cluster in das eigene Secret apicore-telegram; der API-Pod darf es nicht # einbinden. Ein fehlerhaftes Token stoppt nichts: Telegram bleibt aus, der # Worker loggt einen Fehler (nie den Wert). Bei BotFather Gruppen ausschalten # (/setjoingroups) und keinen Webhook setzen. # Je Umgebung ein eigener Bot. Im Cluster steht der Name in der ConfigMap # (TELEGRAM_BOT_USERNAME, leer = Telegram in der App nicht eingerichtet); den # Zustand zeigt Verwaltung > System (Telegram-Karte, nur system:manage). # TELEGRAM_BOT_USERNAME= # TELEGRAM_BOT_TOKEN=