första UI/operatörs-pass

This commit is contained in:
larssand
2026-07-02 19:47:09 +02:00
parent 009c54b206
commit 7568767946
3 changed files with 181 additions and 14 deletions

View File

@@ -73,6 +73,71 @@ The UI also includes a `How To` tab with the same operational checklist. Use it
when adding new streams or when the dashboard has data but it is unclear what when adding new streams or when the dashboard has data but it is unclear what
needs attention next. needs attention next.
## Operating The Dashboard
Start in `Overview`, not in the long diagnostics tables. The intended daily flow
is:
1. Read `Operator Guidance` for the next action SignalScope thinks is most useful.
2. Check `Investigation Incidents` for grouped entity-level problems.
3. Use the correlation graph to see whether the same entity appears across
several streams.
4. Open `Findings` only after you have an entity or incident to inspect.
5. Expand evidence rows and use the Graylog query link to inspect the raw events.
6. Mark the finding as `Expected`, `False positive`, or `Confirmed`.
The top status badges are health indicators:
- `Graylog MCP: connected` means the last completed MCP query worked.
- `Graylog MCP: refreshing` means a poll is in progress and the UI is showing
previous counters until the poll completes.
- `Graylog MCP: error` means the UI may be showing cached data. Check the MCP
URL, token, DNS/TLS, and stream permissions.
- `Baseline: N sources ready` means historical source-IP baselines exist for
`N` source entities. Profile readiness is checked separately per stream field.
- `Ollama: cached` or `ok` means local LLM output is available. It is supporting
evidence, not the source of truth.
Use `Diagnostics -> Stream Coverage` to decide whether the monitor is healthy:
- `ready`: stream is enabled, profile exists, events are arriving, and profile
fields have enough baseline history.
- `learning`: stream is enabled and profiled, but baseline age or bucket count is
still too low.
- `missing_profile`: stream is enabled but no profile exists. Apply a
recommended profile or edit one manually.
- `no_events`: stream is enabled but no raw sample was returned in the last poll.
Check stream activity, query, range, or permissions.
- `partial_fetch`: Graylog returned only part of the raw sample. Use aggregate or
auto mode with a smaller raw sample on high-EPS streams.
- `sample capped`: aggregate mode counted the full window, but raw events were
intentionally capped to keep context queries manageable.
`Ready Fields` is shown as `ready/tracked`, for example `0/8`. The stream profile
tracks 8 fields, but none of those fields are mature yet. A field needs at least
12 five-minute buckets and the configured `Baseline training days` before it is
ready. With the default seven-day training window, new profiles can show `0/X`
for days even while data is being learned.
Interpret findings by maturity:
- During `learning`, findings are mostly profile-tuning signals.
- When fields are `ready`, high-score deviations are more meaningful.
- `new_relationship` means a new field pair appeared, such as
`username -> srcip` or `host -> process.name`.
- `rare_value` means a new value appeared for a profiled entity and field.
- `event_rate_burst`, `auth_failure_burst`, `dns_query_burst`, and
`deny_action_burst` compare the current window to historical buckets.
The review buttons are part of the detection loop:
- `Expected`: known behavior that should stay visible but not keep creating
noise for the same scope.
- `False positive`: weak or bad signal for this scoped pattern.
- `Confirmed`: real investigation item.
- Expiry should be used for temporary expected changes, such as maintenance or a
migration window.
## Primary Workflow: Graylog MCP ## Primary Workflow: Graylog MCP
Graylog 7.1 MCP is the primary log-source integration. In the dashboard, open Graylog 7.1 MCP is the primary log-source integration. In the dashboard, open

File diff suppressed because one or more lines are too long

View File

@@ -36,6 +36,14 @@ class DashboardTests(unittest.TestCase):
self.assertIn("Save streams", HTML) self.assertIn("Save streams", HTML)
self.assertIn("function saveStreamSelection", HTML) self.assertIn("function saveStreamSelection", HTML)
def test_dashboard_includes_operator_guidance(self):
self.assertIn("Operator Guidance", HTML)
self.assertIn("function buildActionPlan", HTML)
def test_dashboard_links_evidence_to_graylog(self):
self.assertIn("function graylogEvidenceLink", HTML)
self.assertIn("/search?rangetype=relative", HTML)
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()