Files
NSCT---Neutral-Search-Crawl…/ADMIN-Backend.md

33 KiB
Raw Blame History

ADMIN-Handbook — NSCT Backend (API)

Produziert vom NSCT Development Team — Stand: 2025-09-06


1. Übersicht

NSCT (Neutral Search Crawler Tool) ist ein lokal betreibbares, containerisiertes Recherche- und Analyse-System. Es durchsucht automatisch Webquellen, extrahiert Behauptungen (Claims), bewertet Evidenz multi-dimensional und generiert neutrale, quellengestützte Berichte.

Architektur-Überblick

┌─────────────────────────────────────────────────────────────────────┐
│                         Docker Compose Stack                       │
│                                                                      │
│  ┌──────────┐     ┌──────────┐     ┌──────────┐                   │
│  │ nsct-api │────▶│ postgres │────▶│ searxng  │                   │
│  │ :8080    │     │ :5432    │     │ :8888    │                   │
│  │ FastAPI  │     │ PG 16    │     │ SearXNG  │                   │
│  └──────────┘     └──────────┘     └──────────┘                   │
│                                                                      │
│  User ───▶ LLM-Provider (:8030)  (extern, z.B. Ollama/VLLM)        │
│  User ───▶ Audio-Provider (:8030) (extern, STT/TTS)                │
│  User ───▶ Vision-Provider  (:8030) (extern, Bildanalyse)           │
└─────────────────────────────────────────────────────────────────────┘

Kernprinzipien:

  • Container-first — alles läuft in Docker Compose
  • Environment-only Configuration — keine Config-Dateien, alles über .env
  • Security-first — SSRF-Schutz, non-root, read-only FS, dropped capabilities
  • Provenance-Pflicht — jeder Schritt ist nachverfolgbar und reproduzierbar

Module:

Modul Path Beschreibung
API Layer src/nsct/api/ REST-Endpunkte (FastAPI)
Models src/nsct/models/ Pydantic v2 Schemas (DTOs)
Storage src/nsct/storage/ SQLAlchemy 2.0 + asyncpg
Providers src/nsct/providers/ LLM/Vision/Audio/Search Abstraktionen
Security src/nsct/security/ SSRF-Schutz, URL-Validierung
Config src/nsct/config.py Environment-only, Pydantic BaseSettings
Logging src/nsct/logging_config.py JSON-Formatter, per-request context
Metrics src/nsct/metrics.py Prometheus-kompatible Metriken
Orchestration src/nsct/orchestration/ State-Machine, Budget-Limits
Stages src/nsct/stages/ Pipeline-Stages 513
Provenance src/nsct/provenance.py Deterministische Hashes, Audit-Trail

2. Schnellstart

2.1 Voraussetzungen

  • Docker ≥ 24.0
  • Docker Compose ≥ 2.23
  • Mindestens 2 GB freier RAM
  • Zugang zu einem OpenAI-kompatiblen LLM-Endpunkt (Port 8030)
  • Git installiert

2.2 Repository klonen

git clone https://df918b20ee2da2c2f92f8dd9bdbdb42ee0c6f9da@git.frerkc.de/opencode/NSCT---Neutral-Search-Crawler-Tool.git
cd nsct

2.3 Environment-Datei erstellen und konfigurieren

cp .env.example .env

Trage die folgenden Werte in .env ein:

# ---- LLM (Hauptmodell, verpflichtend) ----
NSCT_LLM_BASE_URL=http://<LLM_HOST>:8030/openai/v1
NSCT_LLM_MODEL=Qwen3.6-35B
NSCT_LLM_MAX_CONCURRENCY=3
NSCT_LLM_API_KEY=<dein-api-key>

# ---- Vision (Bildanalyse, optional) ----
NSCT_VISION_BASE_URL=http://<LLM_HOST>:8030/openai/visual/v1
NSCT_VISION_MODEL=Qwen2.5-VL-3B
NSCT_VISION_API_KEY=<vision-api-key>

# ---- Audio (STT/TTS, optional) ----
NSCT_AUDIO_BASE_URL=http://<LLM_HOST>:8030/hermes-audio
NSCT_AUDIO_MODEL=default
NSCT_AUDIO_API_KEY=<audio-api-key>

# ---- PostgreSQL ----
POSTGRES_USER=nsct
POSTGRES_PASSWORD=<starkes-passwort>
POSTGRES_DB=nsct
NSCT_DB_URL=postgresql+asyncpg://nsct:<passwort>@postgres:5432/nsct

# ---- SearXNG (optional, interne Suche) ----
NSCT_SEARXNG_BASE_URL=http://searxng:8080

# ---- General ----
NSCT_DEBUG=false

Umgebungsvariablen-Referenz:

Variable Typ Default Beschreibung
NSCT_LLM_BASE_URL string "" OpenAI-kompatibler LLM-Endpunkt
NSCT_LLM_MODEL string "" Modell-Name
NSCT_LLM_MAX_CONCURRENCY int 3 Max parallele LLM-Requests
NSCT_LLM_API_KEY string "" API-Key für LLM-Provider
NSCT_VISION_BASE_URL string "" Vision-Endpunkt
NSCT_VISION_MODEL string "" Vision-Modell-Name
NSCT_VISION_API_KEY string "" API-Key für Vision
NSCT_AUDIO_BASE_URL string "" Audio-Endpunkt (STT/TTS)
NSCT_AUDIO_MODEL string default Audio-Modell
NSCT_AUDIO_API_KEY string "" API-Key für Audio
POSTGRES_USER string nsct PostgreSQL-User
POSTGRES_PASSWORD string nsct_secret PostgreSQL-Passwort
POSTGRES_DB string nsct PostgreSQL-Name
NSCT_DB_URL string "" DB-Connection-String
NSCT_SEARXNG_BASE_URL string "" SearXNG-Instance-URL
NSCT_DEBUG bool false Debug-Modus (Swagger aktiv)
NSCT_LOG_LEVEL string INFO Log-Level (DEBUG/INFO/WARNING/ERROR/CRITICAL)
NSCT_METRICS_ENABLED bool true /metrics Endpoint aktivieren

2.4 Dienste starten

# Vollständiger Stack mit Build
docker compose up --build -d

# Oder nur API + PostgreSQL (ohne SearXNG)
docker compose up --build -d nsct-api postgres

2.5 Health-Check prüfen

# Grundlegender Liveness-Check (immer ok, wenn Prozess lebt)
curl -s http://localhost:8080/health | jq

# Readiness-Check (prüft LLM-Erreichbarkeit)
curl -s http://localhost:8080/ready | jq

# Provider-Status (zeigt konfigurierte Models)
curl -s http://localhost:8080/providers | jq

Erwartete Antwort /health:

{
  "status": "ok",
  "version": "0.1.0"
}

Erwartete Antwort /ready (bei voller Konfiguration):

{
  "status": "ready",
  "llm": "ok",
  "database": "unknown",
  "llm_model": "Qwen3.6-35B"
}

2.6 curl-Tests

# Provider-Status anzeigen
curl -s http://localhost:8080/providers | jq

# Prometheus-Metriken (falls aktiv)
curl -s http://localhost:8080/metrics

# Swagger UI (nur bei NSCT_DEBUG=true)
# http://localhost:8080/docs
# ReDoc: http://localhost:8080/redoc

3. PostgreSQL Setup

3.1 Verbindung einrichten

Die Standard-Verbindungsdaten aus .env:

Host:     postgres (intern im Docker-Netzwerk)
Port:     5432
Database: nsct
User:     nsct
Password: <POSTGRES_PASSWORD aus .env>

Shell-Zugriff auf die DB:

# In den Container verbinden
docker compose exec postgres psql -U nsct -d nsct

# Oder von der Host-Shell (wenn Port 5432 gemappt ist)
PGPASSWORD=<passwort> psql -h localhost -U nsct -d nsct -p 5432

3.2 Backup (pg_dump)

# Vollständiges Dump (SQL-Format)
docker compose exec postgres pg_dump -U nsct -d nsct > nsct_backup_$(date +%Y%m%d).sql

# Komprimiertes Dump
docker compose exec postgres pg_dump -U nsct -d nsct | gzip > nsct_backup_$(date +%Y%m%d).sql.gz

# Nur Struktur (ohne Daten)
docker compose exec postgres pg_dump -U nsct -d nsct --schema-only > nsct_schema.sql

# Nur Daten (ohne Struktur)
docker compose exec postgres pg_dump -U nsct -d nsct --data-only > nsct_data.sql

# Backup in ein eigenes Volume (für Offline-Storage)
docker compose exec postgres pg_dump -U nsct -d nsct > /tmp/nsct_backup.sql
docker cp nsct-postgres:/tmp/nsct_backup.sql ./nsct_backup.sql

3.3 Restore (pg_restore)

# Restore aus SQL-Datei (psql-Format)
docker compose exec -T postgres psql -U nsct -d nsct < nsct_backup.sql

# Restore aus komprimierter Datei
gunzip -c nsct_backup.sql.gz | docker compose exec -T postgres psql -U nsct -d nsct

# Restore aus pg_dumpall (wenn mit pg_dumpall gesichert)
docker compose exec -T postgres psql -U nsct -d nsct < nsct_full_backup.sql

3.4 Migrationen

Die Datenbank wird aktuell manuell über SQLAlchemy-Alchemy-Reflection initialisiert. Migrationen werden über Alembic in zukünftigen Stages implementiert.

# Aktuelle Tabellenliste
docker compose exec postgres psql -U nsct -d nsct -c "\dt"

# Tabellenschema anzeigen
docker compose exec postgres psql -U nsct -d nsct -c "\d+ sources"

# Datenbankgröße
docker compose exec postgres psql -U nsct -d nsct -c "SELECT pg_database_size('nsct');"

# Tabellen-Größen (Byte)
docker compose exec postgres psql -U nsct -d nsct -c "\l+"
docker compose exec postgres psql -U nsct -d nsct -c "SELECT relname, pg_size_pretty(pg_total_relation_size(quote_ident(schemaname) || '.' || quote_ident(tablename))) FROM pg_tables WHERE schemaname = 'public';"

3.5 Troubleshooting

# PostgreSQL ist nicht bereit — Connection refused
# Lösung: Warte bis health check bestanden ist
docker compose ps postgres

# Prüfe ob Container läuft
docker compose logs postgres

# Manueller health-check
docker compose exec postgres pg_isready -U nsct -d nsct

# Wenn Password-Auth fehlschlägt:
# .env prüfen: POSTGRES_PASSWORD muss mit dem Wert übereinstimmen,
# den du in NSCT_DB_URL verwendest.

# Connection-String validieren:
echo "$NSCT_DB_URL" | python3 -c "import sys; print(sys.stdin.read())"

# PostgreSQL-Fehler in Container-Logs
docker compose logs --tail=50 postgres

Häufige Fehler:

Fehler Ursache Lösung
FATAL: database "nsct" does not exist POSTGRES_DB in .env falsch gesetzt Prüfe POSTGRES_DB und NSCT_DB_URL
FATAL: password authentication failed Password mismatch .env korrigieren, docker compose down -v && up
could not connect to server: Connection refused PostgreSQL noch nicht ready Warte, prüfe docker compose ps
FATAL: too many connections Pool exhausted (default: 100) max_connections erhöhen oder Poolgröße senken
relation "xxx" does not exist Tabellen nicht initialisiert Prüfe, ob API korrekt gestartet ist

4. LLM-Endpunkte konfigurieren

Das NSCT-Backend erwartet OpenAI-kompatible Endpunkte (Standard: Ollama, vLLM, LMStudio).

4.1 Qwen3.6-35B (Hauptmodell)

Das primäre LLM für Claim-Extraktion, Synthese, Evidence-Scoring und alle LLM-gesteuerten Stages.

NSCT_LLM_BASE_URL=http://<LLM_HOST>:8030/openai/v1
NSCT_LLM_MODEL=Qwen3.6-35B
NSCT_LLM_MAX_CONCURRENCY=3
NSCT_LLM_API_KEY=<dein-api-key>

Endpoint-Validierung:

# Modellliste abfragen
curl -s http://<LLM_HOST>:8030/openai/v1/models | jq

# Erwartete Antwort:
# {
#   "data": [
#     { "id": "Qwen3.6-35B", "object": "model", "owned_by": "qwen" }
#   ]
# }

# Chat-Completion testen
curl -s http://<LLM_HOST>:8030/openai/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <dein-api-key>" \
  -d '{
    "model": "Qwen3.6-35B",
    "messages": [{"role": "user", "content": "Hello"}],
    "max_tokens": 10
  }' | jq

4.2 Qwen2.5-VL-3B (Vision)

Für Bild- und Dokumentenanalyse (Screenshots, PDFs, Charts).

NSCT_VISION_BASE_URL=http://<LLM_HOST>:8030/openai/visual/v1
NSCT_VISION_MODEL=Qwen2.5-VL-3B
NSCT_VISION_API_KEY=<vision-api-key>

Endpoint-Validierung:

# Models-Endpoint
curl -s http://<LLM_HOST>:8030/openai/visual/v1/models | jq

# Chat mit Bild (Base64) testen
curl -s http://<LLM_HOST>:8030/openai/visual/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <vision-api-key>" \
  -d '{
    "model": "Qwen2.5-VL-3B",
    "messages": [
      {
        "role": "user",
        "content": [
          {"type": "image_url", "image_url": {"url": "data:image/png;base64,..."}},
          {"type": "text", "text": "Was siehst du?"}
        ]
      }
    ]
  }' | jq

4.3 Audio-Modell (STT/TTS)

Für Audio-Transkription (Stage 11) und Text-to-Speech.

NSCT_AUDIO_BASE_URL=http://<LLM_HOST>:8030/hermes-audio
NSCT_AUDIO_MODEL=default
NSCT_AUDIO_API_KEY=<audio-api-key>

Endpoint-Validierung:

# Test-Request (STT)
curl -s http://<LLM_HOST>:8030/hermes-audio/transcribe \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <audio-api-key>" \
  -d '{
    "model": "default",
    "language": "de"
  }' | jq

4.4 Endpoint-Validierung aus der API

# Alle Provider auf einmal prüfen
curl -s http://localhost:8080/providers | jq

# Readiness-Check (LLM-Erreichbarkeit)
curl -s http://localhost:8080/ready | jq

4.5 Troubleshooting

Fehler Ursache Lösung
LLM connectivity check failed Endpunkt nicht erreichbar Prüfe Netzwerk, Firewall, .env
error:ConnectionRefused LLM-Server down Starte LLM-Server neu
error:401 API-Key falsch .env prüfen, Key regenerieren
error:404 Falscher Endpoint-Path Prüfe Base-URL (z.B. /openai/v1 vs /v1)
timeout LLM zu langsam NSCT_LLM_MAX_CONCURRENCY senken, Timeout erhöhen

LLM-Server-Status von innen prüfen:

# Vom nsct-api Container aus zum LLM-Host ping'en
docker compose run --rm nsct-api curl -s http://<LLM_HOST>:8030/openai/v1/models | jq

5. API-Key-Verwaltung

Hinweis: In der aktuellen Version (Stage 22+) ist die Authentifizierung noch nicht implementiert. Die Struktur ist vorbereitet für zukünftige API-Key-Authentifizierung.

5.1 User anlegen (vorbereitete SQL-Struktur)

Die Datenbank-Struktur für User/API-Key-Management:

-- User-Tabelle (vorbereitet für Stage 22+)
CREATE TABLE IF NOT EXISTS users (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    username VARCHAR(255) UNIQUE NOT NULL,
    email VARCHAR(255) UNIQUE,
    is_active BOOLEAN DEFAULT TRUE,
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW()
);

-- API-Key-Tabelle
CREATE TABLE IF NOT EXISTS api_keys (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id UUID REFERENCES users(id) ON DELETE CASCADE,
    key_hash VARCHAR(255) NOT NULL,
    name VARCHAR(255),
    is_active BOOLEAN DEFAULT TRUE,
    created_at TIMESTAMP DEFAULT NOW(),
    expires_at TIMESTAMP,
    last_used_at TIMESTAMP
);

-- Indexes
CREATE INDEX idx_api_keys_key_hash ON api_keys(key_hash);
CREATE INDEX idx_api_keys_user_id ON api_keys(user_id);
CREATE INDEX idx_api_keys_is_active ON api_keys(is_active);

5.2 User und API-Key anlegen

-- User erstellen
INSERT INTO users (username, email, is_active)
VALUES ('admin', 'admin@example.com', TRUE)
ON CONFLICT (username) DO NOTHING;

-- API-Key generieren (Hash speichern)
-- 1. Generiere einen sicheren Key (von der Admin-Shell)
python3 -c "import secrets, hashlib; key = secrets.token_hex(32); print('KEY:', key); print('HASH:', hashlib.sha256(key.encode()).hexdigest())"

-- 2. Hash in der DB speichern (behalte den Original-Key sicher!)
INSERT INTO api_keys (user_id, key_hash, name, is_active)
SELECT u.id, '<key-hash>', 'admin-key', TRUE
FROM users u WHERE u.username = 'admin';

5.3 API-Key-Hashes verwalten

-- Alle aktiven Keys mit User
SELECT ak.name, ak.is_active, ak.created_at, ak.expires_at, u.username, u.email
FROM api_keys ak
JOIN users u ON ak.user_id = u.id
WHERE ak.is_active = TRUE
ORDER BY ak.created_at DESC;

-- Key-Verwendung (last_used_at)
UPDATE api_keys SET last_used_at = NOW() WHERE key_hash = '<key-hash>';

-- Ablaufende Keys prüfen
SELECT ak.name, ak.expires_at, u.username
FROM api_keys ak
JOIN users u ON ak.user_id = u.id
WHERE ak.expires_at < NOW() AND ak.is_active = TRUE;

-- Inaktive Keys deaktivieren
UPDATE api_keys SET is_active = FALSE WHERE is_active = FALSE;

5.4 Debugging von Auth-Problemen

-- API-Key existiert und ist aktiv?
SELECT * FROM api_keys WHERE key_hash = '<key-hash>' AND is_active = TRUE;

-- User existiert?
SELECT * FROM users WHERE username = '<username>';

-- Hash-Korrelation prüfen
SELECT ak.key_hash, u.username, ak.is_active
FROM api_keys ak
JOIN users u ON ak.user_id = u.id
WHERE ak.key_hash = '<key-hash>';

6. Docker Compose Konfiguration

6.1 docker-compose.yml erklärt

# NSCT — docker-compose.yml
# Services: nsct-api, postgres, optional searxng

6.2 Services

Service Image Port Beschreibung
nsct-api build: . 8080:8080 FastAPI-Server
postgres postgres:16-alpine 5432:5432 PostgreSQL 16
searxng searxng/searxng:latest 8888:8080 Interne Suche (optional)

6.3 Service-Details

nsct-api

nsct-api:
  build:
    context: .
    dockerfile: Dockerfile
  container_name: nsct-api
  ports:
    - "8080:8080"
  environment:
    NSCT_DB_URL: postgresql+asyncpg://...@postgres:5432/nsct
    NSCT_LLM_MAX_CONCURRENCY: "3"
  depends_on:
    postgres:
      condition: service_healthy
    searxng:
      condition: service_started
  volumes:
    - nsct_data:/app/data
  healthcheck:
    test: ["CMD-SHELL", "python -c \"import urllib.request; urllib.request.urlopen('http://localhost:8080/health')\" || exit 1"]
    interval: 30s
    timeout: 5s
    retries: 3
    start_period: 15s
  read_only: true
  tmpfs:
    - /tmp
  cap_drop:
    - ALL

Umgebungsvariablen im Container:

  • NSCT_DB_URL — DB-URL (Host: postgres)
  • NSCT_LLM_MAX_CONCURRENCY — LLM-Concurrency (erbt aus .env)
  • Alle anderen vars aus .env (env_file: .env)

postgres

postgres:
  image: postgres:16-alpine
  container_name: nsct-postgres
  environment:
    POSTGRES_USER: ${POSTGRES_USER:-nsct}
    POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-nsct_secret}
    POSTGRES_DB: ${POSTGRES_DB:-nsct}
  ports:
    - "5432:5432"
  volumes:
    - postgres_data:/var/lib/postgresql/data
  healthcheck:
    test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-nsct} -d ${POSTGRES_DB:-nsct}"]
    interval: 10s
    timeout: 5s
    retries: 5
    start_period: 10s
  deploy:
    resources:
      limits:
        memory: 512M
        cpus: "0.5"

searxng (optional)

searxng:
  image: searxng/searxng:latest
  container_name: nsct-searxng
  ports:
    - "8888:8080"
  volumes:
    - searxng_settings:/etc/searxng
  environment:
    - SEARXNG_BASE_URL=http://localhost:8888/
    - SEARXNG_SECRET_KEY=nsct_searxng_secret_key_change_me
  read_only: true
  tmpfs:
    - /tmp
  cap_drop:
    - ALL

6.4 Volumes

Volume Pfad Beschreibung
postgres_data /var/lib/postgresql/data PostgreSQL-Datenbankdateien
nsct_data /app/data NSCT-Runtime-Daten (Cache, etc.)
searxng_settings /etc/searxng SearXNG-Konfiguration

6.5 Networks

Standard docker compose Netzwerk (bridge). Alle Services sind über Service-Namen erreichbar:

postgres     → postgres:5432
searxng      → searxng:8080
nsct-api     → nsct-api:8080

Für isolierte Netzwerke:

networks:
  nsct-internal:
    driver: bridge

6.6 Resource Limits

Service Memory Limit CPU Limit Memory Reservation
nsct-api 1G 1.0 256M
postgres 512M 0.5
searxng 512M 0.5

Anpassen: In docker-compose.yml unter deploy.resources.limits editieren.

6.7 Health Checks

Service Endpoint Interval Timeout Retries
nsct-api GET /health 30s 5s 3
postgres pg_isready 10s 5s 5

7. Health-Check, Logs, Troubleshooting

7.1 curl-Befehle

# Basic health — Liveness
curl -s http://localhost:8080/health | jq

# Readiness — prüft LLM-Connectivity
curl -s http://localhost:8080/ready | jq

# Provider-Info
curl -s http://localhost:8080/providers | jq

# Prometheus-Metriken
curl -s http://localhost:8080/metrics

# Swagger Docs (nur bei NSCT_DEBUG=true)
curl -s -I http://localhost:8080/docs

7.2 Docker Logs

# Alle Logs (live)
docker compose logs -f

# Nur API-Container
docker compose logs -f nsct-api

# Letzte 100 Zeilen
docker compose logs --tail=100 nsct-api

# Errors nur
docker compose logs -f nsct-api | grep ERROR

# PostgreSQL Logs
docker compose logs -f postgres

# Logs mit Zeitstempeln
docker compose logs -f --timestamp nsct-api

7.3 Container-Status prüfen

# Container-Status
docker compose ps

# Container-Details (JSON)
docker inspect nsct-api | jq '.[0].State'

# Container-Logs (letzte 50 Zeilen, nicht-live)
docker compose logs --tail=50 nsct-api

# Resource-Verbrauch
docker stats nsct-api nsct-postgres nsct-searxng

7.4 Restart-Prozedur

# Soft restart (alle Services)
docker compose restart

# Nur API neu starten
docker compose restart nsct-api

# Vollständiger Reload (neuer Build)
docker compose down
docker compose up --build -d

# Container komplett entfernen und neu erstellen
docker compose down -v
docker compose up --build -d

7.5 Common Errors und Lösungen

Error Lösung
Connection refused auf /health Warte 15s (start_period), prüfe docker compose ps
LLM: error:Connection refused LLM-Server prüfen, .env Base-URL validieren
LLM: error:ConnectionRefused Netzwerk-Verbindung prüfen: docker compose run --rm nsct-api curl -v <LLM_URL>
HTTP 503 bei Research-Requests Readiness prüfen: curl localhost:8080/ready
no space left on device docker system prune -f ausführen
Container OOMKilled Memory-Limit in docker-compose.yml erhöhen
relation "xxx" does not exist API neu starten (Schema wird automatisch initialisiert)
asyncpg.errors.InvalidCatalogName POSTGRES_DB in .env prüfen
CORS-Fehler im Browser ALLOWED_ORIGINS in main.py anpassen

8. SSL/TLS für externen Zugriff

8.1 Reverse-Proxy mit Caddy (empfohlen)

Caddy automatisiert TLS-Zertifikate (Let's Encrypt).

# Caddyfile — /etc/caddy/Caddyfile

# HTTPS mit automatischem TLS
nsct.example.com {
    reverse_proxy nsct-api:8080

    # Header-Sicherheit
    header {
        Strict-Transport-Security "max-age=31536000; includeSubDomains"
        X-Content-Type-Options "nosniff"
        X-Frame-Options "DENY"
    }

    # Rate-Limiting
    request_body {
        size 10MB
    }
}

Caddy in Docker Compose:

services:
  caddy:
    image: caddy:2-alpine
    container_name: nsct-caddy
    ports:
      - "443:443"
      - "80:80"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - caddy_data:/data
      - caddy_config:/config
    networks:
      - nsct-internal
    restart: unless-stopped

8.2 Reverse-Proxy mit Nginx

# /etc/nginx/sites-available/nsct
server {
    listen 443 ssl http2;
    server_name nsct.example.com;

    ssl_certificate     /etc/letsencrypt/live/nsct.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/nsct.example.com/privkey.pem;

    # TLS-Konfiguration
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;

    # Security Headers
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-Frame-Options "DENY" always;

    # Rate Limiting
    limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
    limit_req zone=api burst=20 nodelay;

    location / {
        proxy_pass http://nsct-api:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Request-ID $request_id;

        # Timeouts für lange Research-Pipelines
        proxy_read_timeout 300s;
        proxy_connect_timeout 10s;
        proxy_send_timeout 300s;

        # Body-Größe
        client_max_body_size 10M;

        # Buffering deaktivieren für SSE/Streaming
        proxy_buffering off;
    }

    # Swagger UI nur whitelisted
    location /docs {
        allow 10.0.0.0/8;
        deny all;
        proxy_pass http://nsct-api:8080/docs;
    }
}

8.3 HTTPS-Zertifikate

Mit certbot (Nginx):

# Certificate anfordern
sudo certbot --nginx -d nsct.example.com

# Certificate renew test
sudo certbot renew --dry-run

# Manuell renew
sudo certbot renew

Mit Caddy (automatisch):

Caddy holt und renewt Zertifikate automatisch — keine manuelle Konfiguration nötig.

8.4 DNS-Konfiguration

# A-Record erstellen (IPv4)
# nsct.example.com. 300 IN A  <SERVER_IP>

# AAAA-Record (IPv6, optional)
# nsct.example.com. 300 IN AAAA  <SERVER_IPV6>

# Test DNS-Auflösung
dig nsct.example.com
nslookup nsct.example.com

9. Monitoring

9.1 Prometheus-Metriken

Der GET /metrics Endpunkt (Standard: aktiv) liefert Prometheus-kompatible Metriken:

curl -s http://localhost:8080/metrics

Metriken-Übersicht:

Metrik Typ Beschreibung
search_queries_total counter Anzahl Suchanfragen
sources_fetched_total counter Anzahl abgerufener Quellen
claims_extracted_total counter Extrahierte Claims gesamt
contradictions_detected_total counter Festgestellte Widersprüche
research_completed_total counter Abgeschlossene Research-Läufe
research_failed_total counter Fehlgeschlagene Research-Läufe
research_duration_seconds histogram Dauer pro Research-Lauf
llm_request_duration_seconds histogram Dauer pro LLM-Anfrage
active_research_runs gauge Laufende Research-Läufe

Steuerung über Environment:

# Metriken-Endpoint aktivieren/deaktivieren
NSCT_METRICS_ENABLED=true   # default
NSCT_METRICS_ENABLED=false  # deaktivieren

9.2 Log-Ebenen

# Log-Level konfigurieren
NSCT_LOG_LEVEL=INFO    # DEBUG | INFO | WARNING | ERROR | CRITICAL

Log-Format (JSON):

{
  "timestamp": "2025-09-06T12:00:00",
  "level": "INFO",
  "module": "nsct.api.main",
  "message": "HTTP GET /health 200 2.34ms",
  "request_id": "a1b2c3d4-...",
  "method": "GET",
  "path": "/health",
  "status_code": "200",
  "duration_ms": 2.34
}

Log-Ausgabeort:

# Logs nach stdout (default)
NSCT_LOG_FILE=

# Logs in Datei schreiben
NSCT_LOG_FILE=/app/data/nsct.log

9.3 Alerting-Empfehlungen

# Prometheus Alerting Rules (prometheus.yml)
# Beispiel: Research-Fehler-Rate alert
groups:
  - name: nsct
    rules:
      - alert: HighResearchFailureRate
        expr: rate(research_failed_total[5m]) > 0.1
        for: 2m
        labels:
          severity: warning
        annotations:
          summary: "Hohe Research-Fehler-Rate"
          description: "Mehr als 10% Research-Läufe fehlschlagen."

      - alert: LLMUnreachable
        expr: ready{status="not_ready"} == 1
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: "LLM nicht erreichbar"
          description: "Readiness-Check schlägt fehl."

      - alert: HighErrorRate
        expr: rate(http_requests_total{status_code=~"5.."}[5m]) > 0.05
        for: 2m
        labels:
          severity: warning
        annotations:
          summary: "Hohe 5xx-Fehler-Rate"
          description: "Mehr als 5% aller Requests enden mit 5xx."

Prometheus-Target-Konfiguration:

# scrape_configs
- job_name: 'nsct-api'
  static_configs:
    - targets: ['nsct-api:8080']
  metrics_path: '/metrics'
  scrape_interval: 15s

9.4 Grafana-Dashboard (empfohlene Panels)

Panel Metrik Typ
Research-Rate rate(research_completed_total[5m]) Gauge
Fehlerrate rate(research_failed_total[5m]) Gauge
LLM-Latenz histogram_quantile(0.95, research_duration_seconds_bucket) Histogram
Aktive Runs active_research_runs Gauge
Quellen pro Lauf Quellen-Counter / Research-Counter Ratio
DB-Größe pg_database_size('nsct') Gauge

10. Deployment-Checkliste

10.1 Pre-Deploy-Checkliste

  • .env korrekt ausgefüllt und nicht in Git committet
  • POSTGRES_PASSWORD stark genug (mind. 16 Zeichen, Sonderzeichen)
  • NSCT_LLM_API_KEY gültig und zugreifbar
  • NSCT_LLM_BASE_URL erreichbar (curl-Test vom Host aus)
  • Resource Limits in docker-compose.yml an Hardware angepasst
  • NSCT_LOG_LEVEL auf INFO für Production gesetzt
  • NSCT_DEBUG=false (Swagger deaktiviert)
  • Firewall-Regeln: nur Port 443 (HTTPS) offen, nicht 8080
  • Backup-Policy für PostgreSQL-Volume eingerichtet
  • SSL/TLS-Zertifikat bereitgestellt (Caddy oder certbot)
  • DNS-Einträge verifiziert (dig nsct.example.com)
  • CORS-Origins an Production-URL angepasst
  • .dockerignore enthält .env, .venv, __pycache__
  • Git-Status sauber (keine uncommitted Änderungen)
  • Reverse-Proxy konfiguriert und getestet
  • Rate-Limiting aktiviert (Proxy-Ebene)

10.2 Post-Deploy-Verifikation

# 1. Container laufen?
docker compose ps
# Alle 3 Services sollten "healthy" sein

# 2. Health-Check
curl -s http://localhost:8080/health | jq
# Erwartet: {"status": "ok", "version": "0.1.0"}

# 3. Readiness-Check
curl -s http://localhost:8080/ready | jq
# Erwartet: {"status": "ready", "llm": "ok", ...}

# 4. Provider-Status
curl -s http://localhost:8080/providers | jq
# Alle konfigurierten Provider sollten available=true zeigen

# 5. Logs prüfen
docker compose logs --tail=20 nsct-api
# Keine ERROR-Level Einträge nach Startup

# 6. Research-Test (vollständig)
curl -s http://localhost:8080/v1/research \
  -H "Content-Type: application/json" \
  -d '{
    "query": "Test recherche",
    "language": "de",
    "depth": "quick"
  }' | jq

# 7. Metriken prüfen
curl -s http://localhost:8080/metrics | head -20

# 8. Reverse-Proxy Test (HTTPS)
curl -s -I https://nsct.example.com/health | head -5

# 9. DB-Verbindung
docker compose exec postgres pg_isready -U nsct -d nsct

# 10. Resource-Usage
docker stats --no-stream nsct-api nsct-postgres

10.3 Rollback-Prozedur

# --- Rollback auf vorherige Version ---

# 1. Vorheriges Image taggen (vor dem Update gemacht?)
# docker tag nsct-api nsct-api:rollback-$(date +%Y%m%d)

# 2. Alte Version deployen
git checkout <commit-hash>
docker compose down
docker compose up --build -d

# 3. Datenbank-Backup vor Rollback?
# Falls Migrationen rückgängig gemacht werden müssen:
docker compose exec postgres pg_dump -U nsct -d nsct > pre_rollback_backup.sql

# 4. Health-Check nach Rollback
sleep 20
curl -s http://localhost:8080/health | jq
curl -s http://localhost:8080/ready | jq

# 5. Logs auf Fehler prüfen
docker compose logs --tail=50 nsct-api | grep -i error

# 6. Restore aus Backup (nur falls benötigt)
# gunzip -c nsct_backup.sql.gz | docker compose exec -T postgres psql -U nsct -d nsct

10.4 Disaster Recovery

# --- Komplette Wiederherstellung ---

# 1. Neue Instanz aufsetzen (frisches System)
# Docker, Docker Compose, Git-Clone etc.

# 2. .env aus Backup wiederherstellen
cp /backup/.env ./nsct/.env

# 3. PostgreSQL-Datenbank wiederherstellen
gunzip -c /backup/nsct_backup.sql.gz | docker compose exec -T postgres psql -U nsct -d nsct

# 4. API starten
docker compose up -d nsct-api

# 5. Verifizierung
curl -s http://localhost:8080/health | jq
curl -s http://localhost:8080/ready | jq
docker compose exec postgres psql -U nsct -d nsct -c "SELECT count(*) FROM research_runs;"

10.5 Wartungsplan

# --- Regelmäßige Wartung ---

# Monatlich:
# 1. PostgreSQL-Bereinigung (alte Research-Läufe)
docker compose exec postgres psql -U nsct -d nsct -c "
SELECT relname, pg_size_pretty(pg_total_relation_size(quote_ident(schemaname) || '.' || quote_ident(tablename)))
FROM pg_tables WHERE schemaname = 'public' ORDER BY pg_total_relation_size(quote_ident(schemaname) || '.' || quote_ident(tablename)) DESC;"

# 2. Log-Rotation prüfen
# journalctl --vacuum-time=7d  (falls systemd-logs verwendet)

# 3. Zertifikat-Ablauf prüfen
openssl x509 -in /etc/ssl/certs/nsct.pem -noout -enddate

# 4. Docker-Prune (Platz freimachen)
docker system prune -f --filter "until=168h"  # alte Images löschen

Anhang: Schnelle Referenz

Wichtige Ports

Dienst Port Zugriff
NSCT API 8080 Intern (Docker) / 443 extern (Reverse-Proxy)
PostgreSQL 5432 Intern (Docker)
SearXNG 8888 Intern (Docker)

Wichtige Pfade

Pfad Beschreibung
/.env Environment-Konfiguration
/docker-compose.yml Container-Definition
/Dockerfile Container-Build
/src/nsct/api/ API-Router
/src/nsct/config.py Config-Module
/src/nsct/logging_config.py Logging-Setup
/src/nsct/metrics.py Prometheus-Metriken
/src/nsct/security/policy.py SSRF-Schutz

Kommando-Zusammenfassung

# Deployment
docker compose up --build -d          # Deploy
docker compose down                    # Stop
docker compose restart                 # Restart

# Debugging
docker compose logs -f nsct-api        # Logs
docker compose exec postgres psql ...  # DB-Zugriff
docker compose exec nsct-api ...       # API-Container-Shell

# Health
curl localhost:8080/health             # Liveness
curl localhost:8080/ready              # Readiness
curl localhost:8080/metrics            # Prometheus

# Backup
docker compose exec postgres pg_dump -U nsct -d nsct > backup.sql
docker compose exec postgres pg_restore ...

# Monitoring
docker stats                           # Resource Usage
docker compose ps                      # Container Status