CLAUDE.md aktualisiert + broadcast_for_incident tenant_id Fix
- CLAUDE.md komplett neu geschrieben mit aktueller Projektstruktur - broadcast_for_incident: tenant_id Parameter hinzugefuegt (TypeError Fix)
Dieser Commit ist enthalten in:
215
CLAUDE.md
215
CLAUDE.md
@@ -1,121 +1,194 @@
|
|||||||
# OSINT Lagemonitor
|
# AegisSight-Monitor
|
||||||
|
|
||||||
> Lokale Arbeitskopie für das OSINT-Monitor-Projekt auf Server `alt`
|
> OSINT-Monitoringsystem mit KI-gestützter Nachrichtenanalyse
|
||||||
|
|
||||||
## Übersicht
|
## Übersicht
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
projekt: osint-monitor
|
projekt: AegisSight-Monitor
|
||||||
url: https://osint.intelsight.de
|
url: https://osint.intelsight.de
|
||||||
beschreibung: "OSINT-basiertes Lagemonitoring mit Claude-KI-Agenten"
|
beschreibung: "OSINT-basiertes Lagemonitoring mit Claude-KI-Agenten"
|
||||||
server: alt (91.99.192.14, User: claude-dev)
|
server: alt (91.99.192.14, User: claude-dev)
|
||||||
pfad_server: /home/claude-dev/osint-monitor
|
pfad: /home/claude-dev/AegisSight-Monitor
|
||||||
pfad_lokal: C:\Users\Administrator\Desktop\OSINT-monitor
|
datenbank: /mnt/gitea/osint-data/osint.db (geteilt mit AegisSight-Monitor-Verwaltung)
|
||||||
status: aktiv (systemd service läuft)
|
gitea: https://gitea-undso.aegis-sight.de/AegisSight/AegisSight-Monitor
|
||||||
|
git_push_regel: "Jede Aenderung MUSS sofort committed und nach Gitea gepusht werden."
|
||||||
|
service: osint-monitor.service (systemd, Port 8891, Nginx Reverse Proxy)
|
||||||
|
venv: /home/claude-dev/.venvs/osint/
|
||||||
|
status: aktiv
|
||||||
```
|
```
|
||||||
|
|
||||||
## Technologie-Stack
|
## Technologie-Stack
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
backend:
|
backend:
|
||||||
framework: FastAPI (Python 3, venv /home/claude-dev/.venvs/osint/)
|
framework: FastAPI (Python 3.12)
|
||||||
datenbank: SQLite (WAL-Modus, aiosqlite) @ /mnt/gitea/osint-data/osint.db
|
datenbank: SQLite (WAL-Modus, aiosqlite)
|
||||||
auth: JWT (HS256, bcrypt, 24h Ablauf)
|
auth: Magic-Link-Login per E-Mail (JWT HS256, 24h Ablauf)
|
||||||
scheduler: APScheduler (Auto-Refresh jede Minute + Cleanup stündlich)
|
scheduler: APScheduler (Auto-Refresh jede Minute, Cleanup stuendlich)
|
||||||
websocket: FastAPI native
|
websocket: FastAPI native (Echtzeit-Updates)
|
||||||
ki_agenten: Claude CLI (WebSearch + WebFetch Tools)
|
ki_agenten: Claude CLI (WebSearch + WebFetch Tools)
|
||||||
|
email: aiosmtplib (Magic Links, Benachrichtigungen)
|
||||||
port: 8891 (localhost, Nginx Reverse Proxy)
|
port: 8891 (localhost, Nginx Reverse Proxy)
|
||||||
|
|
||||||
frontend:
|
frontend:
|
||||||
typ: Vanilla JS (kein Framework)
|
typ: Vanilla JS (kein Framework)
|
||||||
design: AegisSight Dark Theme (Navy/Gold)
|
design: AegisSight Dark/Light Theme (Navy/Gold)
|
||||||
fonts: Poppins (Titel), Inter (Body)
|
fonts: Poppins (Titel), Inter (Body)
|
||||||
echtzeit: WebSocket mit Auto-Reconnect
|
layout: gridstack.js (Drag-and-Drop Dashboard-Kacheln)
|
||||||
|
echtzeit: WebSocket mit Auto-Reconnect und Ping/Pong
|
||||||
```
|
```
|
||||||
|
|
||||||
## Projektstruktur
|
## Projektstruktur
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
osint-monitor/:
|
AegisSight-Monitor/:
|
||||||
CLAUDE.md: "Projektdokumentation"
|
CLAUDE.md: "Diese Datei"
|
||||||
requirements.txt: "Python-Abhängigkeiten"
|
requirements.txt: "Python-Abhaengigkeiten"
|
||||||
setup_users.py: "Nutzer-Initialisierung (rac00n, ch33tah)"
|
|
||||||
data/: "Symlink -> /mnt/gitea/osint-data/ (SQLite DB)"
|
data/: "Symlink -> /mnt/gitea/osint-data/ (SQLite DB)"
|
||||||
logs/: "Anwendungs-Logs"
|
logs/: "Anwendungs-Logs (osint-monitor.log)"
|
||||||
|
|
||||||
src/:
|
src/:
|
||||||
main.py: "FastAPI App, WebSocket-Manager, Scheduler, Lifespan"
|
main.py: "FastAPI App, WebSocketManager, Scheduler (lifespan), statische Routen"
|
||||||
config.py: "Konfiguration (JWT, Claude CLI, RSS-Feeds, Excluded Sources)"
|
config.py: "Konfiguration (JWT, Claude CLI Pfad/Timeout, SMTP, RSS-Default-Feeds, Excluded Sources, Zeitzone)"
|
||||||
auth.py: "JWT-Authentifizierung (bcrypt, HTTPBearer)"
|
auth.py: "JWT-Token erstellen/verifizieren, Magic-Link/Code generieren, get_current_user Dependency"
|
||||||
database.py: "SQLite Schema + Migrationen (8 Tabellen: users, incidents, articles, fact_checks, refresh_log, incident_snapshots, sources, notifications)"
|
database.py: "SQLite Schema (13 Tabellen), Migrationen, init_db()"
|
||||||
models.py: "Pydantic Request/Response-Schemas (inkl. Source CRUD + Notifications)"
|
models.py: "Pydantic Request/Response-Schemas"
|
||||||
source_rules.py: "Dynamische Quellen-Regeln aus DB (RSS-Feeds + Blacklist)"
|
source_rules.py: "Dynamische Quellen-Regeln aus DB, Domain-Kategorisierung, Feed-Discovery"
|
||||||
|
|
||||||
routers/:
|
routers/:
|
||||||
auth.py: "POST /api/auth/login, GET /api/auth/me"
|
auth.py: "Magic-Link-Login: POST /api/auth/magic-link, /verify, /verify-code, GET /api/auth/me"
|
||||||
incidents.py: "CRUD /api/incidents, /api/incidents/{id}/articles|factchecks|refresh"
|
incidents.py: "CRUD Lagen, Artikel, Snapshots, Faktenchecks, Refresh, Export, E-Mail-Subscriptions"
|
||||||
sources.py: "CRUD /api/sources, /api/sources/stats, /api/sources/refresh-counts"
|
sources.py: "CRUD Quellen, Discovery (Single/Multi), Domain sperren/entsperren, Stats"
|
||||||
notifications.py: "GET /api/notifications, GET /api/notifications/unread-count, PUT /api/notifications/mark-read"
|
notifications.py: "GET/PUT Benachrichtigungen (Liste, ungelesen, als gelesen markieren)"
|
||||||
feedback.py: "POST /api/feedback (Rate-Limited, HTML-E-Mail an feedback@aegis-sight.de)"
|
feedback.py: "POST /api/feedback (Rate-Limited, E-Mail an feedback@aegis-sight.de)"
|
||||||
|
|
||||||
agents/:
|
agents/:
|
||||||
claude_client.py: "Shared Claude CLI Client mit JSON-Output + Usage-Tracking (ClaudeUsage, UsageAccumulator)"
|
claude_client.py: "Shared Claude CLI Client (JSON-Output, Usage-Tracking: Token, Kosten)"
|
||||||
orchestrator.py: "AsyncQueue, koordiniert Agenten-Pipeline sequentiell, Token-Akkumulation + Snapshots + DB-Notifications"
|
orchestrator.py: "AsyncQueue, Agenten-Pipeline, Cancel, Snapshots, E-Mail-Benachrichtigungen, Quellen-Discovery"
|
||||||
researcher.py: "Claude WebSearch Agent (Ad-hoc + Deep Research Modus)"
|
researcher.py: "Claude WebSearch Agent (Ad-hoc + Deep Research Modus)"
|
||||||
analyzer.py: "Analyse-Agent (Zusammenfassung + Briefing-Format)"
|
analyzer.py: "Analyse-Agent (Zusammenfassung/Briefing mit Inline-Zitaten)"
|
||||||
factchecker.py: "Faktencheck-Agent (quellengebunden)"
|
factchecker.py: "Faktencheck-Agent (Claims gegen unabhaengige Quellen pruefen)"
|
||||||
|
|
||||||
feeds/:
|
feeds/:
|
||||||
rss_parser.py: "RSS-Feed Aggregation (dynamisch aus DB, Fallback auf config.py)"
|
rss_parser.py: "RSS-Feed Aggregation (dynamisch aus DB, Keyword-Matching)"
|
||||||
|
|
||||||
|
services/:
|
||||||
|
license_service.py: "Lizenzpruefung (check_license), Nutzer-Limit, Ablauf-Check"
|
||||||
|
|
||||||
|
middleware/:
|
||||||
|
license_check.py: "FastAPI Dependencies: require_active_license, require_writable_license"
|
||||||
|
|
||||||
|
migration/:
|
||||||
|
migrate_to_multitenancy.py: "Einmal-Migration: Single-Tenant zu Multi-Tenant"
|
||||||
|
|
||||||
|
email_utils/:
|
||||||
|
sender.py: "Async SMTP E-Mail-Versand (aiosmtplib, TLS)"
|
||||||
|
templates.py: "HTML-E-Mail-Templates (Magic-Link-Login, Incident-Benachrichtigungen)"
|
||||||
|
rate_limiter.py: "Rate-Limiting fuer Magic-Links und Code-Verifizierung"
|
||||||
|
|
||||||
static/:
|
static/:
|
||||||
index.html: "Login-Seite"
|
index.html: "Login-Seite (Magic-Link: E-Mail eingeben, Code eingeben)"
|
||||||
dashboard.html: "Hauptdashboard"
|
dashboard.html: "Hauptdashboard (Sidebar + Grid + Modals)"
|
||||||
css/style.css: "AegisSight Design System"
|
css/:
|
||||||
js/: "api.js, app.js, components.js, ws.js"
|
style.css: "AegisSight Design System (Dark/Light Theme, alle Komponenten)"
|
||||||
|
js/:
|
||||||
|
api.js: "REST-API-Client (fetch-basiert, 30s Timeout, Auto-Redirect bei 401)"
|
||||||
|
app.js: "Hauptlogik: ThemeManager, A11yManager, NotificationCenter, App-Objekt"
|
||||||
|
components.js: "UI-Rendering: Sidebar-Items, Faktenchecks, Evidence-Chips, Toasts, Fortschritt, Quellen"
|
||||||
|
layout.js: "gridstack.js Wrapper (Drag und Resize, localStorage-Persistenz)"
|
||||||
|
ws.js: "WebSocket-Client (Reconnect mit exponential Backoff, Ping/Pong)"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Architektur
|
## Architektur
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
auth:
|
||||||
|
methode: "Magic-Link per E-Mail (kein Passwort-Login)"
|
||||||
|
flow: "E-Mail eingeben, Code per E-Mail, Code eingeben oder Link klicken, JWT"
|
||||||
|
rate_limiting: "3 Magic-Links pro E-Mail/15min, 5 Fehlversuche Code/E-Mail"
|
||||||
|
multi_tenancy: "JWT enthaelt tenant_id, org_slug, role"
|
||||||
|
|
||||||
agenten_pipeline:
|
agenten_pipeline:
|
||||||
1_rss: "RSS-Feeds durchsuchen (nur Ad-hoc-Lagen)"
|
1_rss: "RSS-Feeds durchsuchen (nur Ad-hoc-Lagen)"
|
||||||
2_claude_recherche: "Claude CLI WebSearch (Ad-hoc oder Deep Research)"
|
2_claude_recherche: "Claude CLI WebSearch (Ad-hoc oder Deep Research)"
|
||||||
3_analyse: "Zusammenfassung/Briefing mit Inline-Zitaten [1][2] + sources_json"
|
3_deduplizierung: "URL-Normalisierung + Headline-Aehnlichkeit"
|
||||||
4_faktencheck: "Claims gegen unabhängige Quellen prüfen"
|
4_analyse: "Zusammenfassung/Briefing mit Inline-Zitaten [1][2]"
|
||||||
orchestrierung: "Sequentielle Queue (1 Auftrag gleichzeitig)"
|
5_faktencheck: "Claims gegen unabhaengige Quellen pruefen"
|
||||||
|
orchestrierung: "Sequentielle AsyncQueue (1 Auftrag gleichzeitig, 3 Retries)"
|
||||||
|
|
||||||
incident_typen:
|
incident_typen:
|
||||||
adhoc: "Breaking News -> RSS + WebSearch -> Plaintext-Summary mit Quellenreferenzen"
|
adhoc: "Breaking News: RSS + WebSearch, Fliesstext-Summary"
|
||||||
research: "Hintergrundrecherche -> Nur Deep Research -> Markdown-Briefing mit Quellenverzeichnis"
|
research: "Hintergrundrecherche: Deep Research, Markdown-Briefing"
|
||||||
|
|
||||||
internationale_quellen:
|
sidebar:
|
||||||
toggle: "Pro Lage konfigurierbar (Checkbox beim Anlegen/Bearbeiten)"
|
aktive_lagen: "Lagen mit type=adhoc und status=active"
|
||||||
international_true: "DE + internationale Feeds (Reuters, BBC, Al Jazeera) + mehrsprachige Claude-Recherche"
|
aktive_recherchen: "Lagen mit type=research und status=active"
|
||||||
international_false: "Nur deutschsprachige Quellen (DE, AT, CH), internationale RSS-Kategorie übersprungen"
|
archiv: "Alle Lagen mit status=archived (standardmaessig zugeklappt)"
|
||||||
db_feld: "international_sources INTEGER DEFAULT 1"
|
zaehler: "Anzahl pro Sektion in Klammern"
|
||||||
betroffene_agenten: "RSSParser (Kategorien-Filter), ResearcherAgent (Sprach-Prompts), Orchestrator (Weiterleitung)"
|
filter: "Alle / Eigene"
|
||||||
|
|
||||||
quellenanzeige:
|
|
||||||
inline_zitate: "Klickbare [1][2] Verweise im Lagebild → Quellenverzeichnis"
|
|
||||||
quellenverzeichnis: "Am Ende des Lagebilds, nummeriert mit Links"
|
|
||||||
quellenübersicht: "Aggregierte Ansicht aller Quellen pro Lage mit Sprach-Statistik"
|
|
||||||
timeline_expand: "Artikel klickbar → Inhaltsvorschau + Link zum Original"
|
|
||||||
sprach_badges: "EN/FR etc. Badge bei fremdsprachigen Artikeln"
|
|
||||||
evidence_text: "Faktencheck zeigt erklärenden Text + Quellen-Chips"
|
|
||||||
deduplizierung: "URL-Normalisierung + Headline-Ähnlichkeit (www, trailing slash, query params)"
|
|
||||||
|
|
||||||
benachrichtigungen:
|
benachrichtigungen:
|
||||||
persistenz: "DB-Tabelle notifications (pro Nutzer, 7 Tage Aufbewahrung)"
|
in_app: "NotificationCenter (Glocke + Badge, DB-persistent, 7 Tage)"
|
||||||
erzeugung: "Orchestrator schreibt nach refresh_summary in DB (öffentlich=alle Nutzer, privat=nur Ersteller)"
|
email:
|
||||||
frontend: "NotificationCenter lädt aus DB beim Init, optimistisches UI bei WebSocket-Events, Debounced DB-Sync"
|
einstellung: "Pro Lage konfigurierbar (3 Toggles im Lage-Modal)"
|
||||||
gelesen: "Als gelesen markieren (is_read=1) → visuell abgeblendet, nicht gelöscht"
|
optionen: "Neues Lagebild, Neue Artikel, Statusaenderung Faktencheck"
|
||||||
tab_badge: "document.title = '(N) IntelSight...' bei ungelesenen Notifications"
|
tabelle: "incident_subscriptions (pro User pro Lage)"
|
||||||
cleanup: "Stündlicher Job löscht Notifications älter als 7 Tage"
|
versand: "Nach jedem Refresh (ab dem 2.) basierend auf Subscriptions"
|
||||||
|
|
||||||
quellenverwaltung:
|
quellenverwaltung:
|
||||||
db_tabelle: "sources (id, name, url, domain, source_type, category, status, notes, added_by, article_count, last_seen_at, created_at)"
|
features: "Anlegen, Bearbeiten, Loeschen, Discovery (Multi-Feed), Domain sperren"
|
||||||
source_types: "rss_feed | web_source | excluded"
|
source_types: "rss_feed, web_source, excluded"
|
||||||
kategorien: "nachrichtenagentur, oeffentlich-rechtlich, qualitaetszeitung, behoerde, fachmedien, think-tank, international, regional, sonstige"
|
|
||||||
seeding: "Beim Start aus config.py RSS_FEEDS + EXCLUDED_SOURCES (wenn Tabelle leer)"
|
lizenz_anzeige:
|
||||||
dynamisch: "source_rules.py liest aktive Quellen aus DB, Fallback auf config.py"
|
header: "Org-Name + Lizenz-Badge (Trial/Annual/Permanent/Abgelaufen)"
|
||||||
frontend: "Modal mit Filter, Suche, Inline-Formular; Sidebar: 'Quellen verwalten' Button + Mini-Stats"
|
read_only: "Warnung wenn Lizenz abgelaufen"
|
||||||
|
|
||||||
|
dashboard_kacheln:
|
||||||
|
lagebild: "Markdown-Zusammenfassung mit klickbaren Zitaten"
|
||||||
|
faktencheck: "Status-Icons, Evidence-Chips, Filter"
|
||||||
|
quellenübersicht: "Aggregiert nach Quellen mit Sprach-Statistik"
|
||||||
|
timeline: "Interaktive Zeitleiste mit Bucketing, Filtern, Suche"
|
||||||
|
|
||||||
|
datenbank_tabellen:
|
||||||
|
organizations: "Multi-Tenancy Organisationen"
|
||||||
|
licenses: "Lizenzen pro Organisation (trial/annual/permanent)"
|
||||||
|
users: "Nutzer (E-Mail, Org, Rolle)"
|
||||||
|
magic_links: "Login-Tokens (10 Min. gueltig)"
|
||||||
|
portal_admins: "Admin-Zugaenge (genutzt von AegisSight-Monitor-Verwaltung)"
|
||||||
|
incidents: "Lagen/Recherchen"
|
||||||
|
articles: "Gesammelte Artikel (original + deutsche Uebersetzung)"
|
||||||
|
fact_checks: "Faktenchecks (claim, status, evidence)"
|
||||||
|
refresh_log: "Refresh-Protokoll (Token-Statistiken, Kosten)"
|
||||||
|
incident_snapshots: "Archivierte Lageberichte"
|
||||||
|
sources: "Quellen-Verwaltung (RSS-Feeds, Web-Quellen, Blacklist)"
|
||||||
|
notifications: "Persistente In-App-Benachrichtigungen"
|
||||||
|
incident_subscriptions: "E-Mail-Abo-Einstellungen pro User/Lage"
|
||||||
|
|
||||||
deployment:
|
deployment:
|
||||||
workflow: "scp Dateien -> ssh alt -> systemctl restart osint-monitor"
|
service: "systemd osint-monitor.service"
|
||||||
|
restart: "sudo systemctl restart osint-monitor"
|
||||||
|
logs: "tail -f ~/AegisSight-Monitor/logs/osint-monitor.log"
|
||||||
|
status: "systemctl status osint-monitor"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Verwandte Projekte
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
verwaltungsportal:
|
||||||
|
pfad: /home/claude-dev/AegisSight-Monitor-Verwaltung
|
||||||
|
beschreibung: "Admin-Portal fuer Organisationen, Lizenzen, Nutzer"
|
||||||
|
geteilte_db: /mnt/gitea/osint-data/osint.db
|
||||||
|
service: verwaltungsportal.service (Port 8892)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Regeln
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
regeln:
|
||||||
|
- "Jede Aenderung MUSS sofort committed und nach Gitea gepusht werden"
|
||||||
|
- "Echte Umlaute in UI-Texten verwenden, Umschreibungen in YAML/Code-Kommentaren OK"
|
||||||
|
- "Keine Passwoerter oder Secrets in den Code committen"
|
||||||
|
- "Service nach Backend-Aenderungen neustarten: sudo systemctl restart osint-monitor"
|
||||||
|
- "Frontend-Aenderungen brauchen keinen Neustart (statische Dateien)"
|
||||||
```
|
```
|
||||||
|
|||||||
In neuem Issue referenzieren
Einen Benutzer sperren