diff --git a/README.md b/README.md index a85a604..09bea09 100644 --- a/README.md +++ b/README.md @@ -202,8 +202,12 @@ Graylog under `System -> Configurations -> MCP` and use stream IDs internally; the fgAI stream picker resolves titles in the UI. For live monitoring, `graylog_range_seconds` controls how far back each MCP poll -searches. The default is one hour, which keeps findings and correlations more -stable than a very short window while still limiting MCP query cost. +searches. The default is 300 seconds, so each poll re-checks the last five +minutes. `graylog_max_events_per_stream` caps raw events fetched from each +stream during that window. If a stream hits the cap, the dashboard marks the +window as truncated because high EPS means SignalScope sampled only part of the +Graylog result set. For very high-volume streams, prefer aggregate baselines and +targeted drill-down queries over trying to pull every raw event through MCP. ## Monitoring Export diff --git a/src/fgai/config.py b/src/fgai/config.py index 3e607df..78de764 100644 --- a/src/fgai/config.py +++ b/src/fgai/config.py @@ -12,7 +12,8 @@ DEFAULT_CONFIG: dict[str, object] = { "graylog_streams": [], "graylog_stream_profiles": [], "graylog_query": "*", - "graylog_range_seconds": 3600, + "graylog_range_seconds": 300, + "graylog_max_events_per_stream": 5000, "baseline_retention_days": 14, "baseline_value_retention_days": 7, "baseline_max_values_per_field": 2000, @@ -65,7 +66,7 @@ class ConfigStore: current[key] = bool(value) elif key == "log_source" and value in {"local_syslog", "graylog_mcp"}: current[key] = value - elif key in {"graylog_range_seconds", "baseline_retention_days", "baseline_value_retention_days", "baseline_max_values_per_field", "baseline_training_days", "profile_advisor_timeout", "threat_intel_daily_limit", "threat_intel_ttl_seconds", "threat_intel_error_ttl_seconds", "abuseipdb_max_age_days"}: + elif key in {"graylog_range_seconds", "graylog_max_events_per_stream", "baseline_retention_days", "baseline_value_retention_days", "baseline_max_values_per_field", "baseline_training_days", "profile_advisor_timeout", "threat_intel_daily_limit", "threat_intel_ttl_seconds", "threat_intel_error_ttl_seconds", "abuseipdb_max_age_days"}: try: minimum = 60 if key == "graylog_range_seconds" else 1 current[key] = max(minimum, int(value)) diff --git a/src/fgai/dashboard.py b/src/fgai/dashboard.py index 6156ecd..51c4939 100644 --- a/src/fgai/dashboard.py +++ b/src/fgai/dashboard.py @@ -93,8 +93,8 @@ HTML = """ .review-actions button { border: 1px solid #39709a; background: #0b2944; color: #d9e8f7; padding: 6px 8px; cursor: pointer; } .review-actions button[data-status="false_positive"] { border-color: #b7823a; color: #ffd36e; } .review-actions button[data-status="confirmed"] { border-color: #2a9b6e; color: #7be3ae; } - .chart { width: 100%; height: 220px; background: #04182d; border: 1px solid #163b59; } - .graph { width: 100%; height: 300px; background: #04182d; border: 1px solid #163b59; } + .chart { width: 100%; height: 280px; background: #04182d; border: 1px solid #163b59; } + .graph { width: 100%; height: 360px; background: #04182d; border: 1px solid #163b59; } .sort-button { border: 0; background: transparent; color: #83bce9; cursor: pointer; font: inherit; font-weight: 600; padding: 0; } .sort-button:hover { color: #d9e8f7; } .model-list { display: flex; flex-wrap: wrap; gap: 8px; } @@ -116,7 +116,7 @@ HTML = """