diff --git a/README.md b/README.md index b2c0c16..96582df 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ SignalScope is a local multi-source security analytics agent. Its primary mode connects to Graylog through MCP, discovers the streams and fields already available in your environment, and uses stream profiles to normalize events, build baselines, correlate entities, and explain anomalies with a local LLM. +Its running only locally and if using LLM it's running also locally so no data is sent or exposed outside. + FortiGate is one supported example. The same workflow applies to DNS/AdGuard, Windows Event Logs, Sysmon, Nginx, Squid, VPN, Proxmox, Filebeat-collected logs, and future Graylog streams. The Python module and legacy `fgai` command remain available for compatibility. New installations can use `signalscope`. diff --git a/images/FGinspectionagent.png b/images/FGinspectionagent.png index c473d46..2366ce3 100644 Binary files a/images/FGinspectionagent.png and b/images/FGinspectionagent.png differ diff --git a/src/fgai/baseline.py b/src/fgai/baseline.py index 3f1b559..c629faf 100644 --- a/src/fgai/baseline.py +++ b/src/fgai/baseline.py @@ -134,11 +134,17 @@ class BaselineStore: rows = connection.execute("select events, numeric_sum from profile_buckets where stream_id=? and entity=? and field=? order by bucket_start desc limit 25", (stream, entity, field)).fetchall() if len(rows) < 12: continue - averages = [row[1] / row[0] if row[0] else 0 for row in rows] - baseline = mean(averages) - deviation = abs((values[1] / values[0] if values[0] else 0) - baseline) - if deviation > (pstdev(averages) or 1.0) * 3: - output[entity].append({"field": field, "stream_id": stream, "score": 15, "reason": f"{field} deviates from its stream baseline"}) + if values[1] == 0: + history = [row[0] for row in rows] + current_value = values[0] + reason = f"{field} event rate deviates from its stream baseline" + else: + history = [row[1] / row[0] if row[0] else 0 for row in rows] + current_value = values[1] / values[0] if values[0] else 0 + reason = f"{field} value deviates from its stream baseline" + deviation = abs(current_value - mean(history)) + if deviation > (pstdev(history) or 1.0) * 3: + output[entity].append({"field": field, "stream_id": stream, "score": 15, "reason": reason}) return output def profiles(self, source_ips: set[str]) -> dict[str, dict[str, object]]: