33 KiB
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 5–13 |
| 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://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
.envkorrekt ausgefüllt und nicht in Git committetPOSTGRES_PASSWORDstark genug (mind. 16 Zeichen, Sonderzeichen)NSCT_LLM_API_KEYgültig und zugreifbarNSCT_LLM_BASE_URLerreichbar (curl-Test vom Host aus)- Resource Limits in
docker-compose.ymlan Hardware angepasst NSCT_LOG_LEVELaufINFOfür Production gesetztNSCT_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
.dockerignoreenthä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