# 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: /home/claude-dev/osint-data/osint.db (SQLite WAL, geteilt mit Verwaltungsportal + Globe; data/ im Projekt ist ein Symlink darauf) gitea: https://gitea-undso.aegis-sight.de/AegisSight/AegisSight-Monitor service: aegis-monitor.service (systemd, Port 8891, Nginx Reverse Proxy, EIN uvicorn-Prozess - Orchestrator und WebSockets halten Zustand im Speicher, niemals --workers setzen) 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 (PDF-Ingest 1min, Auto-Refresh 1min, Cleanup 1h, Health-Check taeglich 04:00, Telegram-Status taeglich 04:30) websocket: FastAPI native (Echtzeit-Updates an Clients) ki: Claude CLI als Subprocess (WebSearch + WebFetch Tools) ki_backend_eu: | EU-Umbau Phase 1 (seit 2026-07-29): zweiter Modellweg ueber AWS Bedrock (EU-Inferenzprofile Frankfurt, agents/bedrock_client.py). Umschaltbar je Lage (incidents.ai_backend) > Organisation (org_setting 'ai_backend') > global (ENV AI_BACKEND, Default 'cli'). Aufloesung je Refresh im Orchestrator via ContextVar _ai_backend_var. Phase 1 bedient nur werkzeuglose Aufrufe (tools=None); Aufrufe mit WebSearch/WebFetch laufen weiter uebers CLI, bis die staan-Recherche-Schleife (Phase 2) fertig ist. Kosten werden aus Token x BEDROCK_PRICING (config.py) berechnet, damit das Credits-System unveraendert funktioniert. AWS-Zugang + STAAN_API_KEY liegen in der Staging-.env. boto3 muss im venv installiert sein (beim Promote nach Live auch dort nachziehen!). ki_modelle: schnell: CLAUDE_MODEL_FAST (Haiku) — Feed-Selektion, Topic-Filter, Geoparsing, Uebersetzung, Chat, QC mittel: CLAUDE_MODEL_MEDIUM (Sonnet) — nur Netzwerkanalyse (entity_extractor hat aktuell keinen Aufrufer in der App) standard: CLAUDE_MODEL_STANDARD (Opus) — Recherche, Lagebild, 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: Reiter-Ansicht je Lage (layout.js; gridstack wurde abgeloest, nur Legacy-Stubs uebrig) karte: Leaflet + MarkerCluster (lokal in vendor/, Kacheln von tile.openstreetmap.de) 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" routes/: version_router.py: "GET /api/version + /api/release-notes (OHNE Auth, fuer das Update-Fenster im Frontend). Historischer Zweitordner neben routers/" agents/: orchestrator.py: "Queue-basierte Refresh-Steuerung (eine Lane je Organisation), Research Multi-Pass (3 Durchlaeufe), Retry, Cancel, Credits-Buchung. _run_refresh ist ~1450 Zeilen, Aenderungen dort nur mit Vorsicht" stage_runners.py: "Studio-Bausteine: analyze/factcheck einzeln auf vorhandenem Datenbestand, bewusst NEBEN _run_refresh gebaut" researcher.py: "WebSearch-Recherche (Standard + 4-Phasen-Tiefenrecherche), Feed-/Kanal-/Account-Selektion, Keyword-Extraktion, Google-News-Suchfeeds je Sprache" analyzer.py: "Analyse-Agent (Lagebild/Briefing, Erst- + inkrementell, Inline-Zitate, Topic-Filter, Neueste Entwicklungen, Stimmungsbild)" factchecker.py: "Faktencheck (Erst/Inkrementell/Zwei-Phasen mit Haiku-Triage ab 25 Fakten), Claim-Matching, Dedup, Schutz gegen Massen-Downgrades" geoparsing.py: "Haiku-basierte Ortsextraktion, Geocoding offline via geonamescache" entity_extractor.py: "Netzwerkanalyse (Sonnet). ACHTUNG: aktuell ohne Aufrufer in der App, nur regenerate_relations.py im Repo-Root nutzt es" translator.py: "Haiku-Uebersetzung in Batches a 5 (groessere Batches rissen den JSON-Output ab)" claude_client.py: "Shared Claude CLI Client, Usage-Tracking (Token, Kosten), Rate-Limit-Erkennung, Cancel via ContextVar. Routet je nach _ai_backend_var werkzeuglose Aufrufe zu bedrock_client" bedrock_client.py: "EU-Modellweg ueber AWS Bedrock Converse (nur eu.-Inferenzprofile, Token-zu-USD-Umrechnung ueber BEDROCK_PRICING, gleiche Fehlerkategorien wie das CLI). EU-Umbau Phase 1" feeds/: rss_parser.py: "RSS-Feed-Parsing (feedparser + httpx), adaptive Keyword-Schwelle, Frische-Bonus, Domain-Cap" telegram_parser.py: "Telethon-basierter Telegram-Parser (eine gemeinsame Session), Kanal-Validierung" x_parser.py: "X/Twitter via twscrape (Account-Store ~/.x-scraper/accounts.db, optional Mobilfunk-Proxy)" podcast_parser.py: "Podcast-Feeds inkl. Transkript-Extraktoren (transcript_extractors/)" services/: pipeline_tracker.py: "Die 11 Pipeline-Schritte (DE/EN, Laien-Tooltips), schreibt refresh_pipeline_steps + WebSocket-Events" post_refresh_qc.py: "Post-Refresh Quality Check: Faktencheck-Duplikate, Location-Korrektur, Umlaut-Normalisierung" fact_consolidation.py: "Periodisches Haiku-Clustering. ACHTUNG: als 6h-Job dokumentiert, aber NICHT im Scheduler eingeplant" source_health.py: "Quellen-Health-Checks (Erreichbarkeit, Feed-Validitaet, Stale, fetch_strategy-Logik)" source_suggester.py: "KI-Quellen-Vorschlaege via Haiku + Karteileichen-Heuristik" pdf_ingest.py: "Minutenjob: hochgeladene PDFs einlesen (pdfplumber + OCR-Fallback), uebersetzen, als Pool-Artikel ablegen" org_settings.py: "Key-Value-Einstellungen je Organisation (output_language etc.) mit 60s-Cache" license_service.py: "Lizenz-Pruefung, Credits-Buchung (charge_usage_to_tenant), Periodenwechsel, Budget-Warnung. expire_licenses() existiert, hat aber KEINEN Scheduler-Job" 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), JS inline" dashboard.html: "Hauptdashboard (Sidebar + Reiter-Ansicht + 8 Modals + Chat-Widget)" studio.html: "Studio (3-Spalten-Werkstatt, online nur fuer info@ sichtbar - Gating rein clientseitig)" css/: style.css: "AegisSight Design System (Dark/Light Theme, alle Komponenten)" studio.css: "Studio-spezifische Styles" js/: api.js: "REST-API-Client (fetch, Auth-Header, 30s Timeout, 403 -> Nur-Lese-Modus)" app.js: "Hauptlogik: ThemeManager, NotificationCenter, App-Objekt (181 KB)" components.js: "UI-Rendering: Sidebar, Faktenchecks, Toasts, Progress, Karte (geteilt mit Studio)" studio.js: "Kompletter Studio-Controller (109 KB)" chat.js: "Chat-Assistent Widget (Bedienfragen)" pipeline.js: "Grafische Analysepipeline (11 Schritte, Live-Animation)" layout.js: "Reiter-Umschalter je Lage (merkt letzten Reiter; enthaelt noch gridstack-Legacy-Stubs)" tutorial.js: "32-Schritte-Rundgang (140 KB). AKTUELL DEAKTIVIERT, alle Einstiege auskommentiert" a11y.js: "Barrierefreiheits-Panel (identische Datei wie im Verwaltungsportal)" update-system.js: "Was-ist-neu-Modal aus RELEASES.json + Update-Banner" ai-disclaimer.js: "KI-Haftungshinweis beim ersten Besuch" i18n.js: "Mini-Uebersetzung de/en (nur teilweise verdrahtet, Studio/Login komplett deutsch)" 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: hinweis: "Die 11 nutzersichtbaren Schritte definiert services/pipeline_tracker.py. Interner Ablauf in _run_refresh:" 1: "Feed-/Kanal-/Account-Selektion (Haiku) + dynamische Keywords je Sprache" 2: "Parallel sammeln: RSS + Google-News-Suchfeeds + WebSearch (Opus) + optional Telegram + X + Podcasts (nur adhoc)" 3: "URL-Verifizierung (HEAD-Requests, tote URLs werden zu site:-Suchlinks repariert)" 4: "Duplikaterkennung (URL + Headline, dann gegen DB-Bestand)" 5: "Relevanz-Scoring + semantischer Topic-Filter (Haiku)" 6: "Geoparsing (Haiku + geonamescache offline)" 7: "Faktencheck ZUERST (liefert Faktenkontext), DANN Lagebild/Briefing (Opus). Optional Stimmungsbild aus Foren-Quellen" 8: "Uebersetzung fehlender DE-Texte (Haiku, nur wenn TRANSLATOR_ENABLED) + Neueste Entwicklungen (nur adhoc)" 9: "Post-Refresh QC (Fakten-Dubletten, Karten-Kategorien, Umlaute)" 10: "Notifications (DB + E-Mail + WebSocket), Credits-Buchung (flat: adhoc 45 / research 40 je Durchlauf)" 11: "Background: Source-Discovery + Executive Summary" multi_tenancy: | Mandantentrennung ueber tenant_id-Filter je Abfrage (keine Middleware). Bei Lagen zentral ueber _check_incident_access. ACHTUNG, nicht alle Tabellen haben tenant_id (source_health_checks, source_suggestions, incident_subscriptions, billing_tariff), und der WebSocket-Broadcast filtert Stand 07/2026 NICHT nach Mandant. lage_reiter: - "Neueste Entwicklungen (research: Zusammenfassung)" - "Lagebild (research: Recherchebericht, Markdown + Inline-Zitate)" - "Ereignis-Timeline (horizontale Achse, Bucketing, Filter)" - "Geografische Verteilung (Leaflet, Kategorie-Marker, Legende)" - "Faktencheck (Status-Icons, Evidence, Filter)" - "Oeffentliche Stimmung (nur bei Foren-Quellen)" - "Analysepipeline (grafisch, 11 Schritte)" - "Quellenuebersicht (nach Domain gruppiert)" ``` ## Datenbank (30+ Tabellen) ```yaml kern: "organizations, licenses, users, magic_links, organization_settings, portal_admins (gehoert dem Portal)" lagen: "incidents, articles, incident_snapshots, fact_checks, incident_events, fact_check_runs" pipeline: "refresh_log, refresh_pipeline_steps" quellen: "sources, source_alignments, source_health_checks, source_suggestions, user_excluded_domains, podcast_transcripts" geo: "article_locations" netzwerk: "network_analyses, network_analysis_incidents, network_entities, network_entity_mentions, network_relations, network_generation_log (alle leer, kein Endpoint im Monitor)" abrechnung: "token_usage_monthly, billing_tariff, user_activity_days" system: "notifications, incident_subscriptions, system_status" hinweise: - "Es gibt KEINE feedback-Tabelle, Feedback geht direkt per E-Mail raus" - "Portal-Tabellen (portal_audit_log, portal_magic_links, source_health_history u.a.) liegen in derselben Live-DB, werden aber nur vom Verwaltungsportal angelegt/genutzt" - "users.is_global_admin/globe_access/network_access legt NUR das Portal an. Eine frisch per init_db erzeugte Monitor-DB kann daher keinen Login (no such column)" ``` ## 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 aegis-monitor (Staging startet der Auto-Deploy selbst neu)" - "Frontend-Aenderungen (HTML/JS/CSS) brauchen keinen Neustart" - "Backup-Dateien (.bak) nicht committen, vor Push loeschen" ``` ## UI-Sync mit dem Lokal-Fork (verbindlich) > Seit 2026-07-25. Die Oberflächen von Online-Monitor und Lokal-Fork bleiben angeglichen. ```yaml ui_sync: regel: "Jede Änderung unter src/static wird noch in derselben Sitzung ins jeweils andere Repo portiert." lokal_repo: "AegisSight/AegisSight-Monitor-Local (Branch main, läuft auf dem Windows-Rechner des Nutzers)" portieren: lokal_nach_online: | In einem temporären Klon dieses Repos (Branch develop) den Lokal-Fork als Remote hinzufügen, fetchen, git cherry-pick (gemeinsame Historie, Drei-Wege-Merge funktioniert), dann push origin develop. NIE auf main pushen, Live nur per Promote-UI. online_nach_lokal: "Im Lokal-Fork: git fetch online && git cherry-pick (Remote 'online' ist dort eingerichtet, fetch-only)." drift_check: "Im Lokal-Fork: bash scripts/ui-drift-check.sh (vergleicht src/static beider Repos, Zeilenenden ignoriert)" wortlaut: "Die Verbrauchseinheit heißt in BEIDEN Monitoren 'Credits' (Entscheidung Nutzer 2026-07-25). Nicht Guthaben, nicht Einheiten." gewollte_unterschiede: - "Online: Studio nur für info@aegis-sight.de freigegeben (Gating in studio.js init, Studio-Link im Dashboard-Header versteckt). Lokal ohne Gating, Knopf immer sichtbar." - "Lokal: X-Zugänge-Oberfläche (twscrape) in Sidebar/Modal/Quellenübersicht. Online bewusst nicht vorhanden (kein x-Router im Online-Backend)." - "Lokal: Auto-Login über /api/auth/dev-login (Demo-Modus). Online ausschließlich Magic-Link." - "Lokal: Kostenvorschau im Anlege-Dialog (updateIntervalCostHint, Fork-Commit 5b0b578). Online noch nicht portiert." bekannter_drift_stand_2026_07_26: - "Die Takt-Untergrenze ist inzwischen AUCH online (dort _getMinIntervalMinutes, 30 Min; lokal _intervalMinMinutes mit Vorgabe 12 Std). Ältere Angaben, sie fehle online, sind überholt." - "Online-only, noch nicht in den Fork portiert: Reiter 'Öffentliche Stimmung' (renderPublicMood) und die Fall-Chat-Rückfrage /clarify." - "Echter Inhalts-Drift in rund 11 Dateien (app.js, components.js, api.js, studio.js, style.css u.a.), weitere ~7 Dateien unterscheiden sich nur durch Umlaute in Kommentaren." stolperfalle_zeilenenden: | Die Frontend-Dateien (src/static) sind CRLF, die Python-Dateien dieses Repos sind LF (im Lokal-Fork teils anders). Beim Portieren keine Werkzeuge einsetzen, die Zeilenenden pauschal umschreiben (z.B. sed -i unter Git Bash), und den Diff vor dem Commit auf Plausibilität prüfen. Ein Riesen-Diff ist fast immer ein Zeilenenden-Unfall. ``` ## 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: /home/claude-dev/osint-data-staging/osint.db (per DB_PATH in .env gesetzt) achtung: "~/AegisSight-Monitor-staging/data/osint.db ist eine UNBENUTZTE Altkopie" initial: einmalige Kopie der Live-DB drift: gewollt - Aenderungen in Staging beeinflussen Live nicht reseed_von_live: | sudo systemctl stop aegis-monitor-staging cp /home/claude-dev/osint-data/osint.db /home/claude-dev/osint-data-staging/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) DB_PATH: /home/claude-dev/osint-data-staging/osint.db (Live setzt KEIN DB_PATH, dort greift der Default data/ = Symlink auf ~/osint-data) STAGING_MODE: 1 (unlimited_budget, kein Credits-Hard-Stop) TRANSLATOR_ENABLED: false TELEGRAM_SESSION_PATH: ~/.telegram-staging/ (eigene Session, dort ist NIEMAND eingeloggt - Telegram-Recherche auf Staging liefert nichts, die Status-Karte im Portal zeigt das ehrlich an) AI_BACKEND: "(optional) 'cli' oder 'bedrock', globaler Default fuer den EU-Modellweg. Ohne Eintrag gilt 'cli'" AWS_ACCESS_KEY_ID_usw: "AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION=eu-central-1 und STAAN_API_KEY liegen seit 2026-07-29 in der Staging-.env (IAM-Benutzer nur fuer Bedrock, staan-Suchschluessel fuer Phase 2)" 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 Der gueltige Ablauf steht unten unter "Vollstaendiger Workflow": develop pushen, Auto-Deploy aktualisiert Staging von selbst, auf Staging pruefen, Promote ueber https://deploy.aegis-sight.de, Live-Check. Manuelles git pull auf dem Server ist NICHT noetig und sollte unterbleiben (der Deploy-Listener macht git reset --hard, lokale Aenderungen in den Server-Verzeichnissen gehen dabei verloren). Stolperfalle bei der Warteschlangen-Pruefung: das Staging-Verzeichnis fetcht per Fetch-Regel NUR develop. Fuer "wie viele Commits warten auf Promote" immer erst git fetch origin main ausfuehren, sonst zeigt main..develop veraltete Zahlen. ### Bekannte Baustellen (Stand 2026-07-26) - WebSocket-Broadcast filtert nicht nach Mandant (main.py broadcast_for_incident bekommt tenant_id, nutzt ihn nicht) - POST /api/public/globe-ingest schreibt ohne Eigentuemer-Pruefung in beliebige Lagen, Artikel ohne tenant_id - fact_consolidation und expire_licenses existieren, haben aber keinen Scheduler-Job - entity_extractor/Netzwerkanalyse ohne Aufrufer in der App (nur regenerate_relations.py) - tutorial.js deaktiviert (Einstiege auskommentiert), laedt aber weiter bei jedem Login - dashboard.html laedt nicht existierende cluster-data.js (404) und ungenutztes d3.js vom CDN - Reiter "Oeffentliche Stimmung" fehlt in layout.js TAB_ORDER und ist dadurch nicht anklickbar - X-Schalter im Anlege-Dialog hat online keine Funktion (kein X-Backend online) - i18n unvollstaendig (Studio und Login nur deutsch), Begriffs-Mix Fall/Lage/Vorfall, zweimal "Token-Budget" statt "Credits" in app.js - Unter 768px verschwindet die Sidebar ersatzlos (mobil unbenutzbar) - Budget-Warnung geht nur in die Glocke, nicht per E-Mail (siehe docs/ABRECHNUNG.md) ## 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**