Skip to content

IT Konfiguration

IT-Master Heizmann edited this page Sep 18, 2026 · 2 revisions

Konfiguration

Caution

Diese Seite richtet sich ausschliesslich an Informatikerinnen und Informatiker. Nicht für Laien geeignet: Falsche Werte können Anmeldungen verhindern, alle Personen abmelden, jede Anfrage sperren oder Schutzmechanismen ausser Kraft setzen. Ohne Fachkenntnisse bitte den Bereich Installation Schritt für Schritt verwenden.

Für wen ist diese Seite? Referenz aller Umgebungsvariablen und Konfigurationsdateien von SmallTime 2027. Quelle: backend/src/lib/config.ts, backend/src/lib/constants.ts, backend/.env.example, backend/config/*.json, frontend/vite.config.ts, scripts/package-dist.mjs.

Inhalt

  1. Grundregeln
  2. Umgebungsvariablen
  3. config/backend.json
  4. config/security.json
  5. frontend.json und Vite
  6. convert/convert.json
  7. Fest eingebaute Werte
  8. Empfohlene Produktionswerte

Grundregeln

  • Vorrang: Umgebungsvariable > Konfigurationsdatei > Standard im Code (Ausnahmen bei TRUST_PROXY, siehe unten).
  • .env wird mit dotenv aus dem Arbeitsverzeichnis des Prozesses geladen. Bereits gesetzte Umgebungsvariablen (z. B. aus systemd, Docker, Plesk) werden von .env nicht überschrieben.
  • Die JSON-Dateien werden beim Start mit zod validiert. Ein ungültiger Wert bricht den Start mit einer Meldung wie security.json ist ungueltig: … ab.
  • Konfigurationsänderungen wirken erst nach einem Neustart. Ausnahme: security.json, wenn sie über Administration → Sicherheit gespeichert wird (sofort wirksam).
  • Ablage der Dateien: Betriebspaket config/, Repository backend/config/ (Frontend: frontend/config/), siehe Pfadauflösung.

Umgebungsvariablen

Name Standard Bedeutung
NODE_ENV development production: Frontend wird ausgeliefert, SESSION_SECRET Pflicht, Kompression standardmässig an, morgan im Format combined. development: kein Frontend-Auslieferung, morgan dev.
PORT backend.json → port, sonst 55000 HTTP-Port des Backends. Viele Hostings (Plesk/Passenger) setzen ihn selbst.
SESSION_SECRET Dev-Standard SmallTime-dev-session-secret Schlüssel für JWT-Signatur und signierte Cookies. In production Pflicht; ist er leer oder gleich dem Dev-Standard, bricht der Start ab (SESSION_SECRET muss in Produktion explizit gesetzt werden.). Mind. 48 zufällige Bytes empfohlen. Ändern meldet alle Personen ab.
FRONTEND_ORIGIN http://localhost:<frontend.json port> = http://localhost:55001 Öffentliche Origin exakt wie im Browser: Schema, Host, optional Port, ohne Pfad und ohne / am Ende (wird entfernt). Muster ^https?://[^/\s]+$, sonst Startabbruch. Wird für CORS/Origin-Prüfung und für die Lizenz-Domain verwendet.
SECURE_COOKIE false true: Cookies immer mit Secure, HSTS-Header immer gesetzt. Hinter einem TLS-Proxy auf true.
TRUST_PROXY false (bzw. backend.json → trustProxy) true: Express trust proxy = 1 (genau ein Hop). request.ip aus X-Forwarded-For, request.secure aus X-Forwarded-Proto, Hostname aus X-Forwarded-Host. Länder-Header werden nur mit TRUST_PROXY ausgewertet. Aktiv, wenn Env oder Datei true ist.
ENABLE_COMPRESSION production: true, sonst false (bzw. backend.json → enableCompression) gzip/deflate über compression. Nur true/false erlaubt, sonst Startabbruch. /api/auth* und /api/csrf-token werden nie komprimiert (BREACH).
BCRYPT_COST security.json → bcryptCost, sonst 10 bcrypt-Kostenfaktor 4–15. Produktion: 12 (vom Paket so gesetzt), sofern die Login-Latenz passt.
REDIS_URL leer z. B. redis://redis:6379. Leer oder nicht erreichbar → In-Memory-Benutzer-Cache (Warnung im Log).
USER_CACHE_TTL_SECONDS 120 TTL des Benutzer-Caches (positive Ganzzahl).
LICENSE_SERVER_URL https://lizenz.small.li/info Endpunkt der Lizenzprüfung (POST, Formularfelder LizenzKey, Domain). Nur für Tests/Hersteller ändern.
SMALLTIME_AUTO_IMPORT – false schaltet den automatischen Import aus convert/ beim Start ab (zusätzlich zu autoImport in convert.json).
SMALLTIME_ORIGIN – Nur beim Paketbau (scripts/package-dist.mjs): Wert für FRONTEND_ORIGIN in der erzeugten .env. Ohne Angabe Platzhalter https://smalltime.example.ch.
FRONTEND_PORT frontend.json → port, sonst 55001 Nur Vite-Dev-Server (frontend/vite.config.ts).

Note

PORT wirkt in vite.config.ts zusätzlich als Ziel des Dev-Proxys (http://localhost:<PORT>).

backend/.env.example (Entwicklung):

PORT=55000
FRONTEND_ORIGIN=http://localhost:55000
SESSION_SECRET=change-me
NODE_ENV=development
SECURE_COOKIE=false
TRUST_PROXY=false
ENABLE_COMPRESSION=false
BCRYPT_COST=10

Vom Paketbau erzeugte .env (Produktion):

NODE_ENV=production
FRONTEND_ORIGIN=https://smalltime.example.ch
SESSION_SECRET=<48 zufällige Bytes, Base64>
# PORT=55000
SECURE_COOKIE=true
TRUST_PROXY=true
ENABLE_COMPRESSION=true
BCRYPT_COST=12

Warning

prod.cmd erzeugt bei jedem Lauf eine neue .env mit neuem SESSION_SECRET. Beim Update die .env auf dem Server nicht überschreiben.


config/backend.json

Schlüssel Standard Bedeutung
port 55000 HTTP-Port (von PORT übersteuert)
trustProxy false wie TRUST_PROXY
enableCompression abhängig von NODE_ENV wie ENABLE_COMPRESSION (Env hat Vorrang)
apiRateLimit { "limit": 300, "windowMinutes": 15 } globales Limit für /api pro Client-IP (express-rate-limit). Antwort 429 / E1007.
uploads.allowedExtensions Code: .bmp .csv .gif .jpeg .jpg .json .md .png .txt .webp; ausgelieferte Datei zusätzlich .zip .pdf .doc .docx .xls .xlsx .ppt .pptx erlaubte Endungen für Uploads. Leere Liste → Code-Standard. Punkt wird ergänzt, Kleinschreibung.

Ausgelieferte Datei:

{
  "port": 55000,
  "uploads": {
    "allowedExtensions": [".bmp", ".csv", ".gif", ".jpeg", ".jpg", ".json", ".md", ".png", ".txt",
      ".webp", ".zip", ".pdf", ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx"]
  }
}

Note

Das Frontend liest backend/config/backend.json beim Build (vite.config.ts) für die Dateiauswahl im Browser. Die verbindliche Prüfung (Endung, Dateiinhalt über file-type, Grösse) macht das Backend. Nach einer Änderung der Liste sollte das Frontend neu gebaut werden, damit die Auswahl übereinstimmt.

Important

Alle Personen hinter einer gemeinsamen öffentlichen IP (Büro mit NAT) teilen sich apiRateLimit. Bei vielen Personen oder Terminals hinter einer IP das Limit erhöhen.


config/security.json

Wird beim ersten Start mit den Standardwerten angelegt, falls sie fehlt (Registrierung aus). Über Administration → Sicherheit änderbar; dabei wird die Datei normalisiert neu geschrieben.

Schlüssel Standard im Code Repo-Datei Betriebspaket Bedeutung
rateLimits.login 5 / 5 min gleich gleich Login-Versuche pro Client-IP (auch erfolgreiche). 429 / E1025. Gilt auch für Terminal-Stempeln mit Passwort.
rateLimits.passwordChange 5 / 15 min gleich gleich Passwortwechsel pro IP
rateLimits.upload 20 / 10 min gleich gleich Uploads pro IP
allowedCountries [] DE AT CH IT LI [] ISO-3166-Alpha-2-Codes. Leere Liste = Länderfilter aus (IP-Listen wirken trotzdem).
countryHeaders CF-IPCountry, CloudFront-Viewer-Country, X-Country-Code, X-Geo-Country, X-Country gleich gleich Header, aus denen der Ländercode gelesen wird (erster gültiger gewinnt). Nur mit TRUST_PROXY. Leere Liste → Standard.
allowedIpRanges [] [] [] IP oder CIDR. Nicht leer → nur diese IPs zugelassen.
blockedIpRanges [] [] [] IP oder CIDR, immer gesperrt
allowRegistration false false false Selbstregistrierung. Für SmallTime ausgeschaltet lassen; Konten legt der Admin an.
allowLocalhost true true false Anfragen von Loopback (bzw. Host localhost + private IP) umgehen Länder-/IP-Filter und das globale API-Limit.
bcryptCost 10 10 10 4–15; BCRYPT_COST hat Vorrang

Auswertungsreihenfolge (getSecurityDecisionForRequest): Länderliste und beide IP-Listen leer → erlaubt. Sonst: allowLocalhost → blockedIpRanges → allowedIpRanges → (nur bei nicht leerer Länderliste) Ländercode aus Header. Ohne gültigen Ländercode: 403 «Kein gültiger Ländercode vom Proxy geliefert».

Warning

  • allowedIpRanges und blockedIpRanges werden auch bei leerer Länderliste ausgewertet.
  • Ist allowedCountries nicht leer und liefert der Proxy keinen Länder-Header (oder TRUST_PROXY ist aus), wird jede externe Anfrage mit 403 abgelehnt. Der Code-Standard ist deshalb [] (Länderfilter aus); so wird auch config/security.json beim ersten Start angelegt (z. B. leeres Docker-Volume).
  • Hinter einem Reverse-Proxy allowLocalhost auf false, sonst gelten bei falscher Proxy-Konfiguration alle Anfragen als lokal.

frontend.json und Vite

frontend/config/frontend.json (Betriebspaket: config/frontend.json, nicht ausgeliefert, optional):

{ "port": 55001 }
Wert Wirkung
port Port des Vite-Dev-Servers; im Backend Teil der Standard-FRONTEND_ORIGIN und der erlaubten Origins (http://localhost:<port>, http://127.0.0.1:<port>)

Beim Build definiert Vite __APP_FRONTEND_PORT__, __APP_BACKEND_PORT__ und __APP_ALLOWED_UPLOAD_EXTENSIONS__ (aus backend.json). Der Dev-Server leitet /api (mit WebSocket) und /custom.css an das Backend weiter.

Erlaubte Origins (CORS und WebSocket): FRONTEND_ORIGIN, http://localhost:<frontendPort>, http://127.0.0.1:<frontendPort>, http://localhost:<PORT>, http://127.0.0.1:<PORT>. Andere Origins erhalten 403 / E1006. Für den WebSocket gilt zusätzlich eine Origin gleich http(s)://<Host-Header> als erlaubt.


convert/convert.json

Optionen des Imports aus SmallTime PHP (conversion-options.ts, .strict() – unbekannte Schlüssel sind ein Fehler). Kommandozeile (npm run convert -- …, nur im Repository) > Datei > Standard.

Schlüssel Standard Bedeutung
cutoverDate erster Tag des Folgemonats Stichtag YYYY-MM-01
legacyTimeZone Europe/Paris Zeitzone der Unix-Zeitstempel der Altversion
timeZone aus Land (settings.txt [12]) fachliche Zeitzone (IANA)
calendarCode aus Land CH, DE, AT, LI
autoImport true automatischer Import bei leerer DB

Details: Umstieg von SmallTime PHP.


Fest eingebaute Werte

Nicht konfigurierbar (src/lib/constants.ts u. a.):

Wert Grösse
Access-Token (Cookie SmallTime.access) 15 Minuten
Refresh-Token (Cookie SmallTime.refresh, Pfad /api/auth) 2 Tage ohne Aktivität
CSRF-Cookie / -Header SmallTime.csrf / x-csrf-token
Upload max. 5 MB pro Datei, max. 10 Dateien pro Anfrage
JSON-/Formular-Body max. 2 MB
Terminal-Rate-Limit 120 Anfragen pro Minute und Gerätetoken
Kontosperre 30 Fehlversuche innerhalb 60 Minuten pro Konto
Altpasswörter aus SmallTime PHP 60 Tage gültig (Einstellung legacyPasswordValidDays)
Lizenzprüfung beim Start, dann alle 24 h, Timeout 10 s
Datenbank-Kopie täglich, data/backups/, alte Kopien werden automatisch rotiert
SQLite busy_timeout 5000 ms
WebSocket-Heartbeat 30 s, max. Nachricht 1 KiB
HSTS max-age=31536000; includeSubDomains

Empfohlene Produktionswerte

NODE_ENV=production
FRONTEND_ORIGIN=https://zeit.example.ch
SESSION_SECRET=<node -e "console.log(require('crypto').randomBytes(48).toString('base64'))">
SECURE_COOKIE=true
TRUST_PROXY=true
ENABLE_COMPRESSION=true
BCRYPT_COST=12
# REDIS_URL=redis://127.0.0.1:6379

Dazu security.json mit allowLocalhost: false, allowRegistration: false und – nur wenn ein vertrauenswürdiger Proxy/CDN einen Länder-Header setzt – einer Länderliste. Siehe Reverse-Proxy und HTTPS.


Weiter mit: Reverse-Proxy und HTTPS · Sicherheit und Betrieb · Übersicht

Clone this wiki locally