# AegisSight-Monitor

> OSINT-Lagemonitoring mit KI-gestützter Nachrichtenanalyse

## Übersicht

```yaml
projekt: AegisSight-Monitor
url: https://monitor.aegis-sight.de
server: ssh monitor (46.225.141.13, User: claude-dev)
pfad: /home/claude-dev/AegisSight-Monitor
quellcode: /home/claude-dev/AegisSight-Monitor/src/
datenbank: /mnt/gitea/osint-data/osint.db (SQLite WAL, geteilt mit Verwaltungsportal + Globe)
gitea: https://gitea-undso.aegis-sight.de/AegisSight/AegisSight-Monitor
service: osint-monitor.service (systemd, Port 8891, Nginx Reverse Proxy)
venv: /home/claude-dev/.venvs/osint/ (Python 3.12)
```

## Technologie-Stack

```yaml
backend:
  framework: FastAPI + Uvicorn
  datenbank: SQLite WAL (aiosqlite, async)
  auth: Magic-Link-Login per E-Mail (JWT HS256, 24h)
  scheduler: APScheduler (Auto-Refresh 1min, Cleanup 1h, Health-Check taeglich 04:00)
  websocket: FastAPI native (Echtzeit-Updates an Clients)
  ki: Claude CLI als Subprocess (WebSearch + WebFetch Tools)
  ki_modelle:
    schnell: CLAUDE_MODEL_FAST (Haiku) — Feed-Selektion, Geoparsing, Chat, QC
    mittel: CLAUDE_MODEL_MEDIUM (Sonnet) — Entity-Extraktion, Netzwerkanalyse
    standard: CLI-Default (Opus) — Recherche, Analyse, Faktencheck
  email: aiosmtplib (smtp.ionos.de:587 TLS)

frontend:
  typ: Vanilla JS (kein Framework, kein Build-Step)
  design: AegisSight Dark/Light Theme (Navy/Gold)
  fonts: Poppins (Titel), Inter (Body)
  layout: gridstack.js (Drag-and-Drop Dashboard-Kacheln)
  karte: Leaflet + MarkerCluster
  echtzeit: WebSocket mit Auto-Reconnect und Ping/Pong
```

## Projektstruktur

```yaml
src/:
  main.py: "FastAPI App, WebSocketManager, Scheduler, Lifespan, statische Routen"
  config.py: "Konfiguration (JWT, Claude-Modelle, SMTP, RSS-Feeds, Zeitzone)"
  auth.py: "JWT erstellen/verifizieren, Magic-Link/Code, get_current_user Dependency"
  database.py: "SQLite Schema (25+ Tabellen), Migrationen, init_db(), get_db()"
  models.py: "Pydantic Request/Response-Schemas"
  source_rules.py: "Domain-Kategorisierung, RSS-Feed-Discovery, Claude-Feed-Bewertung"
  report_generator.py: "PDF (WeasyPrint) + DOCX (python-docx) Export"

  routers/:
    auth.py: "Magic-Link-Login, Token-Verify, /api/auth/me"
    incidents.py: "CRUD Lagen, Refresh, Artikel, Snapshots, Faktenchecks, Export, E-Mail-Abos, Refresh-Log, Beschreibung generieren (Prompt Enhancement)"
    sources.py: "CRUD Quellen, Discovery (Single/Multi), Domain sperren, Telegram-Validierung"
    chat.py: "KI-Assistent (Haiku), Injection-Schutz, Tech-Leak-Filter"
    public_api.py: "API-Key Auth, Globe-Feed (GeoJSON), Globe-Ingest, Snapshot-Abruf"
    notifications.py: "CRUD Benachrichtigungen, Unread-Count, Mark-Read"
    feedback.py: "E-Mail-Feedback mit Bild-Anhaengen"
    tutorial.py: "Tutorial-Fortschritt pro User"

  agents/:
    orchestrator.py: "Queue-basierte Refresh-Steuerung, Research Multi-Pass (3 Durchlaeufe), Retry, Cancel, Credits-Tracking"
    researcher.py: "WebSearch-Recherche (Standard + 4-Phasen-Tiefenrecherche), Feed-Selektion, Keyword-Extraktion"
    analyzer.py: "Analyse-Agent (Lagebild/Briefing, Erst- + inkrementell, Inline-Zitate)"
    factchecker.py: "Faktencheck (Erst/Inkrementell/Zwei-Phasen mit Triage), Claim-Matching, Dedup"
    geoparsing.py: "Haiku-basierte Ortsextraktion, Geocoding via geonamescache"
    entity_extractor.py: "Netzwerkanalyse: Entity-Extraktion (Sonnet), Beziehungsanalyse, Dedup"
    claude_client.py: "Shared Claude CLI Client, Usage-Tracking (Token, Kosten), Rate-Limit-Erkennung"

  feeds/:
    rss_parser.py: "RSS-Feed-Parsing (feedparser + httpx), Keyword-Matching, Domain-Cap"
    telegram_parser.py: "Telethon-basierter Telegram-Parser, Kanal-Validierung"

  services/:
    post_refresh_qc.py: "Post-Refresh Quality Check: Faktencheck-Duplikate, Location-Korrektur"
    fact_consolidation.py: "Periodisches Haiku-Clustering, Auto-Resolve veralteter Fakten"
    source_health.py: "Quellen-Health-Checks (Erreichbarkeit, Feed-Validitaet, Stale)"
    source_suggester.py: "KI-Quellen-Vorschlaege via Haiku"
    license_service.py: "Lizenz-Pruefung (Org, Ablauf, Nutzer-Limit)"

  middleware/:
    license_check.py: "Dependencies: require_active_license, require_writable_license"

  email_utils/:
    sender.py: "Async SMTP Versand"
    templates.py: "HTML-Templates (Magic-Link, Benachrichtigungen)"
    rate_limiter.py: "Rate-Limiting Magic-Links"

  migration/:
    migrate_to_multitenancy.py: "Einmal-Migration Single->Multi-Tenant"

  report_templates/:
    report.html: "HTML-Template fuer PDF/DOCX-Export"

  static/:
    index.html: "Login-Seite (Magic-Link)"
    dashboard.html: "Hauptdashboard (Sidebar + GridStack + Modals)"
    css/:
      style.css: "AegisSight Design System (Dark/Light Theme, alle Komponenten)"
    js/:
      api.js: "REST-API-Client (fetch, Auth-Header, 30s Timeout)"
      app.js: "Hauptlogik: ThemeManager, NotificationCenter, App-Objekt"
      components.js: "UI-Rendering: Sidebar, Faktenchecks, Toasts, Progress-Bar, Karte"
      chat.js: "Chat-Assistent Widget"
      layout.js: "gridstack.js Wrapper (Drag/Resize, localStorage)"
      tutorial.js: "Interaktiver 32-Schritte Rundgang mit Animationen"
      ws.js: "WebSocket-Client (Reconnect, Ping/Pong)"
    vendor/:
      leaflet.js: "Karten-Bibliothek"
      leaflet.markercluster.js: "Marker-Clustering"
```

## Architektur

```yaml
incident_typen:
  adhoc:
    label: "Live-Monitoring"
    quellen: "RSS + WebSearch + optional Telegram"
    analyse: "Fliesstext-Lagebild"
    faktencheck_status: "confirmed/unconfirmed/contradicted/developing"
    refresh: "Manuell oder automatisch (Intervall konfigurierbar)"
  research:
    label: "Recherche"
    quellen: "Nur WebSearch 4-Phasen-Tiefenrecherche (kein RSS)"
    analyse: "Strukturiertes Briefing (Ueberblick, Hintergrund, Akteure, Lage, Einschaetzung, Quellenqualitaet)"
    faktencheck_status: "established/unverified/disputed/developing"
    refresh: "Immer manuell, erster Refresh automatisch 3 Durchlaeufe (Multi-Pass)"
    multi_pass:
      durchlaeufe: 3
      labels: ["Breite Erfassung", "Vertiefung", "Konsolidierung"]
      bedingung: "Nur beim ersten Refresh (kein Summary vorhanden)"
      cancel: "Zwischen und innerhalb der Durchlaeufe moeglich"

refresh_pipeline:
  1: "Feed-Selektion (Haiku) + dynamische Keywords"
  2: "Parallel: RSS + WebSearch + optional Telegram"
  3: "URL-Verifizierung (HEAD-Requests)"
  4: "Duplikaterkennung (URL + Headline)"
  5: "Relevanz-Scoring + DB-Dedup"
  6: "Geoparsing (Haiku + geonamescache)"
  7: "Parallel: Analyse + Faktencheck"
  8: "Post-Refresh QC"
  9: "Notifications (DB + E-Mail + WebSocket)"
  10: "Credits-Tracking (Token auf Lizenz buchen)"
  11: "Background: Source-Discovery"

multi_tenancy: "Volle Mandantentrennung (tenant_id auf allen Tabellen)"

dashboard_kacheln:
  - "Lagebild (Markdown + Inline-Zitate)"
  - "Faktencheck (Status-Icons, Evidence, Filter)"
  - "Quellenübersicht (nach Domain gruppiert)"
  - "Timeline (horizontale Achse, Bucketing, Filter)"
  - "Karte (Leaflet, Kategorie-Marker, Legende)"
```

## Datenbank (25+ Tabellen)

```yaml
kern: "organizations, licenses, users, magic_links, portal_admins"
lagen: "incidents, articles, incident_snapshots, fact_checks, refresh_log"
quellen: "sources, source_health_checks, source_suggestions, user_excluded_domains"
geo: "article_locations"
netzwerk: "network_analyses, network_analysis_incidents, network_entities, network_entity_mentions, network_relations, network_generation_log"
system: "notifications, incident_subscriptions, feedback, token_usage_monthly"
```

## Verwandte Projekte (gleicher Server)

```yaml
verwaltungsportal:
  pfad: /home/claude-dev/AegisSight-Monitor-Verwaltung
  url: https://monitor-verwaltung.aegis-sight.de
  service: verwaltungsportal.service (Port 8892)
  geteilte_db: ja

globe:
  pfad: /home/claude-dev/AegisSight-Globe
  url: https://globe.aegis-sight.de
  service: globe.service (Port 8890)
  geteilte_db: ja

netzwerkanalyse:
  pfad: /home/claude-dev/AegisSight-Netzwerkanalyse
  url: https://netzwerkanalyse.aegis-sight.de
  service: netzwerkanalyse.service (Port 8893)
```

## Regeln

```yaml
regeln:
  - "Jede Aenderung MUSS sofort committed und nach Gitea gepusht werden"
  - "Echte Umlaute in UI-Texten (ue, ae, oe, ss), keine Umschreibungen"
  - "Keine Passwoerter oder Secrets in den Code committen"
  - "Service nach Backend-Aenderungen: sudo systemctl restart osint-monitor"
  - "Frontend-Aenderungen (HTML/JS/CSS) brauchen keinen Neustart"
  - "Backup-Dateien (.bak) nicht committen, vor Push loeschen"
```

## Changelog-Workflow

Bei JEDER Aenderung am Monitor muessen zwei Dinge passieren:

1. **TaskMate Wissensdatenbank** (Kategorie: "Changelog Monitor", category_id=31):
   

2. **Git Commit + Push zu Gitea**

Changelog-Kategorien in TaskMate:
- 31 = Changelog Monitor
- 32 = Changelog Globe
- 33 = Changelog Netzwerkanalyse
- 34 = Changelog Verwaltung
- 35 = Changelog Website
- 36 = Changelog TaskMate

## Staging-Umgebung

```yaml
staging:
  url: https://staging.monitor.aegis-sight.de
  server: 46.225.141.13 (gleicher Host wie Live)
  pfad: /home/claude-dev/AegisSight-Monitor-staging
  branch: develop
  port: 18891 (Live: 8891)
  service: aegis-monitor-staging.service (systemd)
  venv: /home/claude-dev/AegisSight-Monitor-staging/venv (eigenes venv)
  zugriff: Magic-Link-Login an info@aegis-sight.de (Cookie 30 Tage)

  datenbank:
    pfad: ~/AegisSight-Monitor-staging/data/osint.db
    initial: einmalige Kopie der Live-DB
    drift: gewollt - Aenderungen in Staging beeinflussen Live nicht
    reseed_von_live: |
      sudo systemctl stop aegis-monitor-staging
      cp ~/AegisSight-Monitor/data/osint.db ~/AegisSight-Monitor-staging/data/osint.db
      sudo systemctl start aegis-monitor-staging

  besonderheiten_env:
    JWT_SECRET: eigener fuer Staging (nicht Live-JWT)
    MAGIC_LINK_BASE_URL: https://staging.monitor.aegis-sight.de (sonst leitet App zu Live)
    TELEGRAM_API_ID: 0  # deaktiviert - verhindert Doppel-Login mit Live
    TELEGRAM_API_HASH: 0
    DB-Pfad: relative aus config.py (nutzt automatisch ~/AegisSight-Monitor-staging/data/)

  auth_service:
    pfad: /opt/aegis-staging-auth
    service: aegis-monitor-staging-auth.service
    port: 127.0.0.1:8095
    cookie_domain: staging.monitor.aegis-sight.de
    cookie_name: aegis_monitor_staging_auth
    code_quelle: identisch zum Service auf 46.225.225.49 (eigene Konfig)
```

### Workflow Staging -> Live

1. **Aenderung in develop machen** (im Staging-Verzeichnis):
   ```bash
   cd ~/AegisSight-Monitor-staging
   git checkout develop
   # Aenderung
   git add . && git commit -m ... && git push origin develop
   ```

2. **Staging aktualisieren** (aktuell manuell):
   ```bash
   ssh claude-dev@46.225.141.13 'cd ~/AegisSight-Monitor-staging && git pull && sudo systemctl restart aegis-monitor-staging'
   ```

3. **In https://staging.monitor.aegis-sight.de testen**

4. **Promote zu Live**: Pull Request develop -> main in Gitea, dann:
   ```bash
   ssh claude-dev@46.225.141.13 'cd ~/AegisSight-Monitor && git pull'
   # Live laeuft als loser uvicorn-Prozess (kein systemd) - manueller Restart
   # bei Backend-Aenderungen noetig
   ```

### Offen (noch nicht implementiert)

- Auto-Deploy bei Push auf develop (Webhook-Listener)
- Promote-UI mit Ein-Klick-Button
- Live-Monitor auf systemd umstellen (~10s Downtime einmalig)

## Auto-Deploy + Promote-UI

```yaml
auto_deploy:
  listener_service:
    pfad: /opt/aegis-staging-deploy
    service: aegis-staging-deploy.service
    port: 127.0.0.1:8096
    deployments:
      staging: develop -> ~/AegisSight-Monitor-staging (restartet aegis-monitor-staging)
      live:    main    -> ~/AegisSight-Monitor          (restartet aegis-monitor)
    endpoints:
      "POST /__deploy":      staging via Gitea-Webhook (HMAC)
      "POST /__deploy/live": live via Promote-UI (HMAC)
    secrets: /opt/aegis-staging-deploy/.env (nicht im Repo)

  gitea_webhook:
    repo: AegisSight/AegisSight-Monitor
    url: https://staging.monitor.aegis-sight.de/__deploy
    branch_filter: develop

live_systemd:
  service: aegis-monitor.service
  hinweis: |
    Live-Monitor laeuft seit 2026-04-26 als systemd-Service (vorher loser
    uvicorn-Prozess). Manueller Restart bei Backend-Aenderungen:
      sudo systemctl restart aegis-monitor
    Beim Promote via UI passiert das automatisch.

promote_ui:
  url: https://deploy.aegis-sight.de
  laeuft_auf: 46.225.225.49 (zentral fuer alle Services)
  zugriff: Magic-Link-Login an info@aegis-sight.de
  funktion: |
    Live- vs. Staging-Stand pro Service inkl. Liste der ausstehenden Commits.
    Promote-Knopf -> Gitea-PR develop->main wird auto-gemerged + Live-Listener
    pullt main + restartet aegis-monitor.
```

### Vollstaendiger Workflow (Aenderung am Monitor)

1. **Entwickeln in develop**:
   ```bash
   cd ~/AegisSight-Monitor-staging
   git checkout develop
   # Aenderung
   git add . && git commit -m "..." && git push origin develop
   # Auto-Deploy pullt automatisch + restartet aegis-monitor-staging
   ```

2. **Auf https://staging.monitor.aegis-sight.de pruefen**

3. **Promoten via https://deploy.aegis-sight.de** (Klick auf Monitor-Karte)
   → Gitea merged develop→main → Listener pullt main → `systemctl restart aegis-monitor`

4. **Live-Check auf https://monitor.aegis-sight.de**
