# Modul-15: Monitoring-Control **Status: FREIGEGEBEN** (20.08.2026) · Container `Modul-15-Monitoring-Control` · Image `monitoring-control:0.1.0` ## Zweck Zentrale **technische Überwachung und Sicherheits-/Kontrollebene** des Trading-Systems. Überwacht die Dienste M03–M14 (Health/Readiness/Erreichbarkeit) plus RabbitMQ-Queues/Consumer und PostgreSQL und leitet daraus deterministisch einen **globalen Trading-Status** (`ENABLED` / `PAUSED` / `HALTED` / `UNKNOWN`) ab. **Restriktionen (Nutzer-Vorgabe):** - **Keine KI/ML**, **keine Trading-Entscheidungen**, **keine Brokerorders**, **keine Shell-/Docker-Control-Rechte**. - **FAIL-CLOSED:** Bei unklarem/kritischem Zustand wird Trading sicher blockiert (`HALTED`). - M15 unerreichbar → M09 behandelt den Zustand als `HALTED` (FAIL-CLOSED, siehe M15→M09-Anbindung, FREIGEGEBEN). - **Keine Event-Schleifen:** Events nur bei Status-Änderung, dedupliziert. - Container wird **nicht** automatisch neu gestartet/gekillt — M15 beobachtet und entscheidet Status. - Port 55015 **nur Docker-intern** (`expose`, kein Host-Port). ## Architektur / Datenfluss ``` M03–M14 (health/ready) RabbitMQ (Queues) PostgreSQL │ │ │ └──────────┬────────────┴──────────────┘ ▼ Modul-15-Monitoring-Control ├── probe/ MonitoringProbe (HTTP-health + RMQ passive declare + PG) ├── rules/ MonitoringRules (deterministisch, FAIL-CLOSED, versioniert) ├── core/ MonitorService (Status-Engine, Single Source of Truth) ├── storage/ Persistenz (PostgreSQL: system_health/service_health/control_event) ├── publisher/ ControlPublisher (market.control, nur bei Status-Änderung) └── api/main.py REST-API (intern, Port 55015) ``` Status → `system_health` (append), Services → `service_health` (upsert), Änderungen → `control_event` (append-only Audit). ## API (Docker-intern, Port 55015, kein öffentliches Port-Mapping) ``` GET /health liveness GET /health/ready readiness (postgresql) GET /monitoring/status aktueller System-Status + Trading-Status (internes State-Modell) GET /monitoring/services aktueller Zustand aller überwachten Services/Queues GET /control/status Control-Status (manual_override, control_api_enabled) GET /control/trading-state autoritativer Trading-Status für M09 POST /control manuelle Control PAUSE/HALT/RESUME (nur wenn CONTROL_API_ENABLED=true) ``` ## Deterministische Regeln (Rules-Engine, Version 0.1.0) Präzedenz strikt von oben nach unten (FAIL-CLOSED): 1. **UNKNOWN** — PostgreSQL nicht erreichbar → nicht verlässlich → `HALTED`. 2. **UNHEALTHY/HALTED** — kritische Infrastruktur (PG/RabbitMQ) oder Execution-Pfad down. 3. **DEGRADED/PAUSED** — nicht-kritische Dienste down / stale. 4. **HEALTHY/ENABLED** — alles gut. | Bedingung | System | Trading | Reason-Code | |---|---|---|---| | PostgreSQL nicht erreichbar | UNHEALTHY | HALTED | `POSTGRESQL_UNREACHABLE` (+ `PERSISTENCE_FAILURE`) | | RabbitMQ nicht erreichbar | UNHEALTHY | HALTED | `RABBITMQ_UNREACHABLE` | | Kritischer Service down (M03–M09) | UNHEALTHY | HALTED | `CRITICAL_SERVICE_DOWN:` | | Market Data stale (> max_age) | UNHEALTHY | HALTED | `MARKET_DATA_STALE` | | Kritische Queue Consumer fehlt | — | HALTED | `NO_CONSUMER_` | | Kritischer Queue-Backlog ≥1000 | — | HALTED | `QUEUE_BACKLOG_CRIT::` | | Nicht-kritische Unhealthy (Analytics/Notification) | DEGRADED | PAUSED | `CRITICAL_SERVICE_DOWN`/`NO_CONSUMER_…` | | Queue-Backlog ≥100 / Warning | DEGRADED | PAUSED | `QUEUE_BACKLOG::` | | Alles healthy | HEALTHY | ENABLED | — | **FAIL-CLOSED-Hinweis:** `UNKNOWN` Trading-Status wird in der API auf `HALTED` gemappt (nie ENABLED aus unklarem Zustand). ## Events (market.control Exchange, keine Event-Schleife) Events werden **nur bei Status-Änderung** publiziert (Idempotenz) — nie bei jedem Poll: | Event-Typ | Routing-Key | Anlass | |---|---|---| | `system.degraded` | `system.degraded` | System → DEGRADED | | `system.halted` | `system.halted` | System → UNHEALTHY (HALT) | | `system.recovered` | `system.recovered` | System → HEALTHY | | `trading.state` | `trading.state` | Trading-Status-Änderung | | `service.unhealthy` | `service.unhealthy` | Einzelner Service UNHEALTHY | ## Persistenz / Audit (PostgreSQL Modul-01) - `system_health` — append-only Zeile pro Beobachtung (aktueller Status + reason_codes). - `service_health` — Upsert pro `service_id` (aktueller Zustand jedes Services/Queue). - `control_event` — **append-only Audit-Trail** jeder Status-/Trading-Änderung (`STATE_CHANGE`) und manuellen Control-Aktion (`MANUAL_*`). Idempotent — eine Änderung wird **genau einmal** auditiert. ## Manuelle Control (PAUSE/HALT/RESUME) - **Nur wenn `CONTROL_API_ENABLED=true`** (Default `false` → manuelle Control über API deaktiviert, nur Regel-getrieben). - **RESUME wird verweigert** (`RESUME_BLOCKED_CRITICAL_CAUSE`), solange eine kritische Ursache besteht. - Jede manuelle Aktion wird append-only in `control_event` auditiert. ## Sicherheit - **Keine Secrets in DB/Logs/Doku/Commits** — Zugangsdaten ausschließlich via Environment (Compose). - **Kein Docker-Socket**, **keine Shell-/Docker-Control-Rechte** (CMD=`python main.py`). - Port 55015 nur intern (`expose`), `restart: unless-stopped`, non-root (uid 1001). - Env: `POSTGRES_*`, `RABBITMQ_*`, `CONTROL_API_ENABLED`. ## Compose / Betrieb - Netzwerk `trading-modules`, DB-Host `Modul-01-PostgreSQL`, RabbitMQ `Modul-02-RabbitMQ`. - Env per Compose, Zugangsdaten nur via ENV (`POSTGRES_*`, `RABBITMQ_*`), nie hart kodiert. ## Unit-Tests - `tests/test_monitoring.py`: **19/19 grün** — deterministische Rules, FAIL-CLOSED (PG/RabbitMQ/Critical), stale, Queue-Consumer, Idempotenz, Audit genau einmal, Recovery, Reconnect, keine Secrets, `state_version`-Inkrementierung. ## E2E-Verifikation (VPS, 20.08.2026) — 17 Szenarien Alle 17 verifiziert (System-/Trading-Status via Loop, API `/control/trading-state` + `/monitoring/status` konsistent): 1. **Normalzustand:** `HEALTHY/ENABLED` — API, State-Modell, Loop identisch. 2. **PG-Ausfall:** `HALTED`, `POSTGRESQL_UNREACHABLE`+`PERSISTENCE_FAILURE`, Audit genau einmal. 3. **RabbitMQ-Ausfall:** `HALTED`, `RABBITMQ_UNREACHABLE`. 4. **Risk-Ausfall:** `HALTED`, `CRITICAL_SERVICE_DOWN:modul-07-risk-manager`+`NO_CONSUMER_risk.input`. 5. **Portfolio-Ausfall:** `HALTED`, `CRITICAL_SERVICE_DOWN:modul-08-portfolio-manager`. 6. **Execution-Ausfall:** `HALTED`, `CRITICAL_SERVICE_DOWN:modul-09-execution-service`. 7. **Analytics-Ausfall:** `DEGRADED` + `PAUSED` (Trading nicht vollständig kill). 8. **Notification-Ausfall:** `DEGRADED` + `PAUSED`. 9. **Market Data stale:** `HALTED`, `MARKET_DATA_STALE`; Recovery nach frischen Daten. 10. **Kritischer Consumer fehlt:** HALT-Severity korrekt (NO_CONSUMER auf kritischer Queue). 11. **Queue-Backlog:** Warning/HALT gemäß Schwelle (100/1000, Unit-getestet). 12. **Recovery:** Ursache erneut geprüft, kein Auto-RESUME solange Ursache, Status konsistent, kein Event-Spam. 13. **Manuelle Controls:** mit `CONTROL_API_ENABLED=false` sauber abgelehnt (`control_api_disabled`); Trading unangetastet. 14. **Idempotenz/Event-Flut:** stabiler Zustand → keine wiederholten SYSTEM_HALTED/SERVICE_UNHEALTHY (nur 1 Audit je Änderung). 15. **RabbitMQ-Reconnect:** automatisch (alle Recovery-Zyklen), keine Zombies, keine Eventverluste. 16. **Audit:** `control_event`/`system_health`/`service_health` vollständig + zeitlich nachvollziehbar. 17. **Security:** kein öffentlicher Port (PortBindings `{}`), kein Docker-Socket, keine Secrets im Compose/Logs. ## Bekannte Design-Hinweise (für separaten M15→M09-Vorschlag) - **M03–M06 (Market-Data-Pipeline) sind in `CRITICAL_SERVICES`** klassifiziert. Ausfall eines einzelnen Daten-Moduls führt so zu `HALTED` (über `CRITICAL_SERVICE_DOWN`), nicht ausschließlich über `MARKET_DATA_STALE`. Das ist fachlich konservativ aber zu klären (nur Data-Pipeline vs. Execution-Pfad). - **DEGRADED → `PAUSED`** (nicht `ENABLED`): bei Analytics/Notification-Ausfall wird Trading vorsichtig gestoppt, nicht vollständig killed. Design-Entscheidung, im Vorschlag benannt. - **PG-Ausfall ⇒ `PERSISTENCE_FAILURE`**: ohne PG kann `control_event`-Audit nicht geschrieben werden (fachlich korrekt FAIL-CLOSED), aber Audit-Lücke im Fehlerfenster. ## Freigabe - **FREIGEGEBEN** (20.08.2026, Nutzer-Bestätigung). Implementierung + E2E (17 Szenarien) grün. - M09-Anbindung **umgesetzt** (Variante C, Event-Cache + HTTP-Final-Check) — siehe `modul-15-m09-anbindung-implementierung.md`, FREIGEGEBEN. ## Nächste Module - **Modul-16 ff.** — noch Platzhalter (alpine), NICHT gestartet. --- ## Geändert ``` Geändert von: Rain Ocampo Datum: 20.08.2026 Grund: Modul-15-Monitoring-Control dokumentiert — implementiert + E2E-verifiziert (17 Szenarien grün), Rules/API/Events/Audit/Security dokumentiert, NICHT FREIGEGEBEN. ``` ``` Geändert von: Rain Ocampo Datum: 20.08.2026 Grund: Modul-15-Monitoring-Control auf FREIGEGEBEN gesetzt (Nutzer-Bestätigung 20.08.2026). M15→M09-Anbindung (Variante C) als umgesetzt/FREIGEGEBEN vermerkt; Unit-Tests 19/19. ```