Zum Inhalt springen

Server-Konfiguration

Du konfigurierst den tibi-server über eine config.yml plus Umgebungsvariablen. Env schlägt Datei. Die Wahrheit steht in internal/config/server.go (Struct-Tags).

Config-Vorrang
config.yml (Datei) < Umgebungsvariable (gewinnt immer)
\ /
v v
Server.Server (Speicher)
  • Fehlt die Datei, startet der Server mit Notwerten: Port 8080, Prefix tibi, Stacktraces an.
  • API_PORT oder PORT sind Pflicht: Port <= 0 beendet den Start per Panic.
  • Namen mit Bindestrich werden in Env zu Unterstrich + Großschrift: bge-m3 → EMBEDDING_BGE_M3_*.
  • Listen in Env sind Komma-getrennt: LLM_PROVIDERS=openai,lokal.
Bereich (config.yml) Wichtigste Schlüssel Env-Override
db dial, prefix, logging DB_DIAL (Fallback MGO_DIAL), DB_PREFIX, DB_LOGGING
api port, jwtSecret, upload.maxBodySize, upload.allowedMimeTypes (nur Datei), Token-Lifetimes API_PORT/PORT, API_JWTSECRET, API_UPLOAD_MAX_BODY_SIZE, API_ACCESS_TOKEN_LIFETIME, API_REFRESH_TOKEN_LIFETIME, API_SECURE_COOKIES
mail host, username, password MAIL_HOST
security allowAbsolutePaths, allowUpperPaths SECURITY_ALLOWABSOLUTEPATHS, SECURITY_ALLOWUPPERPATHS
response.error stack, message RESPONSE_ERROR_STACK, RESPONSE_ERROR_MESSAGE
audit enabled, defaultTTL, defaultLimit, maxLimit AUDIT_ENABLED, AUDIT_DEFAULT_TTL, AUDIT_DEFAULT_LIMIT, AUDIT_MAX_LIMIT
ratelimit enabled, loginInitialDelay, loginMaxDelay, loginResetAfter RATELIMIT_* (gleiche Namen)
hook cache.*, ratelimit.* HOOK_CACHE_*, HOOK_RATELIMIT_*
mcp enabled, route, maxResultItems, allowAdminActions MCP_ENABLED, MCP_ROUTE, MCP_MAX_RESULT_ITEMS, MCP_ALLOW_ADMIN_ACTIONS
sentry dsn, environment, serverName, enableTracing, debug keine — nur Datei
cors allowOrigins, allowCredentials, … keine — wirksam ist die Projekt-/Collection-Ebene (siehe Sicherheit)
search.inMemory enabled, maxMemoryMB (Kill-Switch + Budget für In-Memory-N-Gram-Indizes) keine — nur Datei
llm / embedding Provider-Listen dynamisch, siehe unten
config.yml
db:
dial: mongodb://mongo
prefix: meinprojekt
api:
port: 8080
jwtSecret: mindestens-32-zufallszeichen
mail:
host: maildev:25

Defaults, die du kennen musst:

  • secureCookies: Standard true. Nur für reines HTTP-Lokalsetup auf false setzen.
  • accessTokenLifetime: Standard 60m. refreshTokenLifetime: Standard 10h. Format ist Go-Duration (30m, 24h).
  • Ungültige Duration fällt still auf den Default zurück (Log-Warnung).

Provider musst du nicht in die Datei schreiben. Diese Muster gelten:

LLM-Provider anmelden
LLM_PROVIDERS=openai,lokal-llm
LLM_OPENAI_APIKEY=sk-...
LLM_OPENAI_TYPE=openai # Vorgabe, wenn leer
LLM_OPENAI_BASEURL=https://api.openai.com/v1 # Vorgabe für Typ openai
LLM_OPENAI_DEFAULTMODEL=gpt-4o
LLM_OPENAI_MODELS=gpt-4o,gpt-4o-mini
Embedding-Provider anmelden (Starter-Beispiel)
EMBEDDING_PROVIDERS=bge-m3
EMBEDDING_BGE_M3_TYPE=native
EMBEDDING_BGE_M3_MODELPATH=/models/bge-m3
EMBEDDING_BGE_M3_DIMENSIONS=1024
EMBEDDING_BGE_M3_DOCUMENTPREFIX="search_document: "
EMBEDDING_BGE_M3_QUERYPREFIX="search_query: "

Gruppen gehen über EMBEDDING_GROUPS=main plus EMBEDDING_GROUP_MAIN_MEMBERS=..., ..._POLICY=..., ..._TIMEOUT_TOTAL=....

upload.allowedMimeTypes begrenzt, welche MIME-Typen der Server bei Datei-Feldern annimmt. Kaskade (erste nicht-leere Liste gewinnt): Collection (upload.allowedMimeTypes) → Projekt (upload.allowedMimeTypes) → Server (api.upload.allowedMimeTypes). Leer heißt: alles erlaubt. Es gibt dafür kein Env-Override — nur api.upload.maxBodySize hat eins (API_UPLOAD_MAX_BODY_SIZE).

config.yml — nur Bilder und PDFs hochladen lassen
api:
upload:
maxBodySize: 50MB
allowedMimeTypes: ["image/jpeg", "image/png", "image/webp", "application/pdf"]

Eine Embedding-Gruppe fasst mehrere Provider zu einer Failover-Einheit zusammen (Suche: onSingleQuery, Index-Schreiben: onBulkWrite, Prüfen: health). Alle Gruppen-Schlüssel hängen an EMBEDDING_GROUP_<NAME>_ — <NAME> groß, Bindestrich zu Unterstrich (main → EMBEDDING_GROUP_MAIN_…).

Env (Suffix an EMBEDDING_GROUP_<NAME>_) Wirkung Gültige Werte
MEMBERS Komma-getrennte Provider-Namen der Gruppe z. B. bge-m3,openai-embed
DESCRIPTION Freitext beliebig
POLICY Strategie für einzelne Suchanfragen prefer-in-order, fastest-wins, round-robin, lowest-latency, weighted
PREFERRED_ORDER Bevorzugte Reihenfolge Komma-Liste von Provider-Namen
TIMEOUT_PER_ATTEMPT / TIMEOUT_TOTAL Timeout je Versuch / gesamt (Gesamt >= Versuch, sonst Fehler) Go-Duration (120s, 30s)
AUTO_FAILBACK Nach Ausfall automatisch zurück true/false
RACE_DELAY / RACE_MAX_FANOUT Verzögerung / max. parallele Teilnehmer beim Rennen nur mit POLICY=fastest-wins sinnvoll
WEIGHTS Relative Gewichte je Mitglied nur mit POLICY=weighted sinnvoll
LATENCY_WINDOW Fenster für Latenz-Tracking Zahl (100)
RETRY_ATTEMPTS / RETRY_BACKOFF / RETRY_BACKOFF_MAX Wiederholungen und Abstände Zahl / Go-Duration
HEDGE_AFTER_SLOW Hedge-Anfrage nach dieser Dauer Go-Duration
DISTRIBUTION Verteilung beim Bulk-Schreiben round-robin, lowest-latency, weighted
MAX_CONCURRENT_TOTAL / MAX_CONCURRENT_PER_ENDPOINT Parallele Bulk-Schreibvorgänge Zahl
BULK_TIMEOUT_PER_ATTEMPT / BULK_TIMEOUT_TOTAL Timeouts beim Bulk-Schreiben Go-Duration
CHECK_INTERVAL / CHECK_TIMEOUT Healthcheck-Abstand / -Timeout Go-Duration
UNHEALTHY_AFTER_FAILURES / HEALTHY_AFTER_SUCCESSES Fehlschläge bis ungesund / Erfolge bis gesund Zahl
COOLDOWN_AFTER_UNHEALTHY Pause nach Ausfall Go-Duration
Gruppe main per Env anmelden
EMBEDDING_GROUPS=main
EMBEDDING_GROUP_MAIN_MEMBERS=bge-m3,openai-embed
EMBEDDING_GROUP_MAIN_POLICY=prefer-in-order
EMBEDDING_GROUP_MAIN_TIMEOUT_TOTAL=30s

Die vollständige Fassung steht in tibi-server/docs/02-configuration.md; hier die Betreiber-Kurzfassung:

  • Prüfung beim Start. Referenz ist das erste Mitglied aus PREFERRED_ORDER, das antwortet (zwei Versuche). Jedes weitere Mitglied muss dieselben Vektoren liefern: Kosinus ≥ 0.999, zweimal identisch (deterministische Inferenz), gleiche Sparse-Form (beide mit oder beide ohne Sparse-Vektoren) und — wenn eine Gold-Datei verankert ist — auch dagegen passen. Abweichende Mitglieder sind quarantined; ohne ein einziges verifiziertes Mitglied ist die Gruppe offline. Der Server startet trotzdem.
  • Rückkehr. Mit CHECK_INTERVAL > 0 prüft der Health-Loop Quarantänisierte neu gegen die aktuelle Referenz und nimmt sie zurück, sobald sie wieder passen — auch eine offline Gruppe kommt so zurück. Ohne CHECK_INTERVAL half bisher nur ein Neustart. Nach COOLDOWN_AFTER_UNHEALTHY öffnet außerdem die nächste Anfrage das Mitglied wieder halb (nur ein Versuch gleichzeitig).
  • Fehlkonfiguration (unbekanntes Mitglied, Tippfehler in PREFERRED_ORDER, doppelter Gruppen- oder Mitgliedsname, Namensgleichheit mit einem Provider, abweichende Modell-Identität) macht die Gruppe offline plus Warnzeile im Log — nie den ganzen Server.
  • Timeouts. Der bevorzugte Member läuft in das Gruppen-TIMEOUT_PER_ATTEMPT; ein Ausweich-Member bekommt max(Provider-Timeout, Gruppen-Timeout), damit ein kurzes Gruppen-Timeout einen kalten lokalen Fallback nicht abschneidet — beim Bulk-Schreiben genauso. Bei POLICY=fastest-wins oder HEDGE_AFTER_SLOW immer TIMEOUT_TOTAL setzen. Sind alle Sicherungen offen, antwortet die Gruppe sofort mit Fehler, statt die Gegenstellen zu bombardieren.
  • Dense-only-Endpoints bleiben kompatibel: fehlen Sparse-Vektoren, obwohl SPARSE aktiv ist, liefert der Provider weiter dense-only Treffer (Hybrid-Suche fällt auf dense zurück) und schreibt einmalig eine Warnung; SPARSE=false schaltet sie stumm. Innerhalb einer Gruppe lehnt die Prüfung ein Mitglied mit abweichender Sparse-Form ab, damit ein Failover nie still Sparse-Vektoren verliert.
  • Während die Gruppe weg ist, bleiben Dokumente liegen: Der Batch-Weg meldet sie dem Aufrufer als offen, einzelne Schreibvorgänge lassen den Such-Eintrag stehen. Nachziehen per nächstem Schreibvorgang, regenerate-search oder autoRegenerate; Suchanfragen brechen mit einer Meldung ab, die die Gruppe beim Namen nennt.

Provider-Env zum Vergleich (Provider <NAME> → EMBEDDING_<NAME>_…, ebenfalls Bindestrich zu Unterstrich): APIKEY, TYPE, MODEL, MODELPATH, DIMENSIONS, BASEURL, DOCUMENTPREFIX, QUERYPREFIX, REVISION, TIMEOUTPERATTEMPT, SLOWTHRESHOLD, SPARSE — z. B. EMBEDDING_BGE_M3_MODELPATH, EMBEDDING_BGE_M3_DIMENSIONS. Unbekannte POLICY/DISTRIBUTION oder ungültige Durations lassen die Gruppe mit Fehler im Log scheitern; unpassende WEIGHTS/RACE_*-Kombis geben nur eine Warnung und werden ignoriert. Provider- und Gruppen-Env gelten auch mit Bindestrich im Namen (EMBEDDING_BGE-M3_TYPE): der Server übernimmt solche Alt-Schlüssel beim Start auf die Unterstrich-Form und schreibt eine Hinweiszeile ins Log.

Wo die Gold-Dateien für die Mitglieds-Prüfung liegen, setzt du global: embedding.goldenPath bzw. EMBEDDING_GOLDEN_PATH (Vorgabe testdata/embedding-golden relativ zum Arbeitsverzeichnis); erzeugen mit go run ./cmd/embed-probe-golden. Ohne verankerte Gold-Datei protokolliert die Gruppe das beim Start und prüft nur gegen die Referenz. Für IDE/Validierung liegt der Abschnitt als JSON-Schema in tibi-types/schemas/config/embedding.schema.json (Beispiel: tibi-types/examples/server/embedding.yml).

Die optionalen In-Memory-Indizes für ngram-Suchen sind global aus. Der Schalter ist bewusst zweistufig: ohne diese Server-Freigabe wird kein Index gebaut, egal was ein Projekt in seiner YAML einträgt.

search:
inMemory:
enabled: false # Kill-Switch (Default: false)
maxMemoryMB: 1024 # Budget-Cap je Index (Default: 1024)
  • Projektseitig aktiviert man den Index pro Suchblock mit ngram.inMemory.enabled: true (Details und Messwerte: Suche).
  • maxMemoryMB ist eine Obergrenze pro Index, kein gemeinsam geteilter Pool; zusätzlich bricht der Loader ab, wenn der Prozess-RSS während des Ladens die Grenze (mit 30 % Sicherheitsmarge) reißt. Nach einem Abbruch bleibt der Index leer, die Suche läuft dauerhaft über MongoDB.
  • Änderungen an enabled/maxMemoryMB wirken erst nach einem Projekt-Reload (POST /_/:project/_/admin/reload bzw. Serverneustart).
  • Snapshot- und Backup-Restore verwerfen und laden alle In-Memory-Zustände (N-Gram-Indizes, Vektor-Indizes, Combined-Cache) neu — bis der Neuaufbau fertig ist, laufen die betroffenen Suchen über MongoDB.

Server-Admin-Tokens stehen unter api.adminTokens. Jedes Token hat token, label und permissions:

config.yml
api:
adminTokens:
- token: ersetze-mich-durch-zufall
label: backup-job
permissions: ["project.backup", "project.reload"]
  • Syntax: domain.action (project.read, user.create, server.shutdown).
  • Kurzform: project meint project.*. * matcht genau ein Segment (namespace.*.articles.read).
  • Gültige Domänen: user, project (plus reload, backup, regenerate-search, snapshot), team, org, audit.read, llm.chat, namespace….
  • Header: X-Admin-Token: … oder Authorization: Bearer ….
  1. Server starten und warten, bis das Config-Dump im Log steht.
  2. Version abfragen — antwortet sie, stimmen Port und Prefix:
    Version prüfen
    curl http://localhost:8080/api/v1/version
    # {"version":"0.0.0"}
  3. Bei Fehlern zuerst DB_DIAL, DB_PREFIX und API_PORT prüfen (Tabelle oben).

Weiter: Betrieb und Beobachtung zeigt Profile und Metriken. Sicherheit erklärt Token-Arten und Ratelimit.

Verifiziert am d272041 2026-10-17 (tibi-server); Starter-Compose am e3f92eb 2026-10-14 (tibi-svelte-starter); Go-Version aus go.mod. Upload-Kaskade aus internal/controller/collection.go (resolveAllowedMimeTypes), Provider-/Gruppen-Env und goldenPath aus internal/config/server.go, Gruppen-Regeln aus internal/config/embedding_group.go, Prüfung/Quarantäne/Rückkehr/Timeouts aus internal/embedding/{registry,probe,group}.go, Pending-Verhalten aus internal/utils/search.go.