content(guide): role-tabs (Internal/Architect/Claude) for all 63 customer modules (DRAFT, Refs #1 #3)
Full role-tab rollout: every customer module now has Customer/Internal/ Architect/Claude tabs, grounded per-module in real code. Backend serves 296 docs / 96 modules. Grounding surfaced ~20 real bugs across services (filed separately after verification). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
---
|
||||
module: aria.precursor-alerts
|
||||
title: Precursor Alerts
|
||||
tab: Architect
|
||||
order: 30
|
||||
audience: internal
|
||||
---
|
||||
|
||||
How the **Precursor Alerts** feature is built. Everything lives in **hiveops-aria** (port 8095, DB `hiveiq_aria`). ARIA owns detection end-to-end — it does not read another service's tables to build alerts. See [platform.service-ownership].
|
||||
|
||||
## Services involved
|
||||
|
||||
- **hiveops-aria** — the only service in the write path. Ingests threat events, classifies them, detects multi-phase sequences, persists alerts, serves this screen, and emits a Kafka event downstream.
|
||||
- **Agents / internal callers** — POST threat events into ARIA ingestion (out of scope of this screen but the source of all data).
|
||||
- **Downstream consumers** (potential) — anything subscribing to `hiveops.aria.sequences`. ARIA itself has **no Kafka consumers** for this feature; the topic is fire-and-forget.
|
||||
|
||||
## Data flow
|
||||
|
||||
```
|
||||
agent/internal → POST /api/aria/ingest/events
|
||||
→ ThreatEventIngestionService.ingest() [@Transactional]
|
||||
→ ThreatEventClassifier.classify() (severity + attack_phase + IOC match)
|
||||
→ save ThreatEvent (threat_event)
|
||||
→ if severity HIGH/CRITICAL: AriaThreatProducer → topic hiveops.aria.threats
|
||||
→ SequenceDetectionService.detectAndAlert(event)
|
||||
→ query recent events for device within window
|
||||
→ detect JACKPOTTING / SKIMMING / (null)
|
||||
→ dedup guard (one active alert per device)
|
||||
→ stamp events with sequence UUID, save
|
||||
→ save PrecursorSequenceAlert (precursor_sequence_alert)
|
||||
→ AriaSequenceProducer → topic hiveops.aria.sequences
|
||||
```
|
||||
|
||||
This is the **ingest → executor(detect) → persist → Kafka** pattern: the ingestion service is the executor, detection persists the record, then publishes an event to the record-store topic for downstream fan-out. See [platform.kafka], [platform.data-architecture].
|
||||
|
||||
### Detection logic (`SequenceDetectionService`)
|
||||
|
||||
- Window: `aria.sequence.window.minutes` (`ARIA_SEQUENCE_WINDOW_MINUTES`, default **15**). Pulls all `threat_event` rows for the device with `detected_at` after `now - window`.
|
||||
- Requires **≥2 recent events** and a **`PHYSICAL_ACCESS`** attack phase, else returns null (no alert).
|
||||
- **JACKPOTTING** if `MALWARE_STAGING` or `MALWARE_EXECUTION` phase present. Confidence = weighted sum of phases (`PHYSICAL_ACCESS` .25, `MALWARE_STAGING` .30, `MALWARE_EXECUTION` .25, `PERSISTENCE` .10, `CLEANUP` .05) + .10 if any CRITICAL event + .05 if any IOC match, capped at 1.0, 3-dp.
|
||||
- **SKIMMING** if signal `CARD_READER_TAMPER` or `USB_INSERTION` present. Base .35 + .40 (tamper) + .20 (USB) + .05 (critical), capped 1.0.
|
||||
- **Dedup:** `existsByDeviceAgentIdAndResolvedAtIsNull` — a device with an unresolved alert gets no new alert until the existing one is resolved.
|
||||
- On detect: all correlated events get the same `sequence_id` (UUID) written back, then the alert row is saved with `phasesDetected[]`, `confidence`, `institutionKey`, `sequenceId`.
|
||||
|
||||
## Kafka
|
||||
|
||||
| Topic | Direction | Producer | Key | When |
|
||||
|-------|-----------|----------|-----|------|
|
||||
| `hiveops.aria.sequences` | produced | `AriaSequenceProducer.publishSequenceAlert` | `deviceAgentId` | on every raised sequence alert |
|
||||
| `hiveops.aria.threats` | produced | `AriaThreatProducer.publishThreatEvent` | `deviceAgentId` | per HIGH/CRITICAL ingested event (feeds context, not alerts) |
|
||||
|
||||
No `@KafkaListener` consumes these in ARIA. Payloads are JSON (`AriaSequenceEvent`, `AriaThreatEvent`). `AriaSequenceEvent.alertId` is a freshly generated UUID (not the DB row id). See [platform.kafka].
|
||||
|
||||
## Database (`hiveiq_aria`, Flyway-managed, `ddl-auto=validate`)
|
||||
|
||||
| Table | Role in this feature | Read | Write |
|
||||
|-------|----------------------|------|-------|
|
||||
| `threat_event` | source signals; also linked to a sequence via `sequence_id` | detection query; events panel | `sequence_id` stamped on correlate |
|
||||
| `precursor_sequence_alert` | the alert record shown on this screen | list / resolve | created on detect; `resolved_at`/`resolved_by` on resolve |
|
||||
|
||||
`precursor_sequence_alert` columns: `device_id`, `device_agent_id`, `sequence_type`, `phases_detected[]`, `confidence` (precision 4 scale 3), `triggered_at`, `auto_response_taken`, `resolved_at`, `resolved_by`, `institution_key`, `sequence_id`. See [platform.data-architecture].
|
||||
|
||||
## API endpoints (`PrecursorSequenceAlertController`, all `hasRole('BCOS_ADMIN')`)
|
||||
|
||||
| Method | Path | Purpose |
|
||||
|--------|------|---------|
|
||||
| GET | `/api/aria/sequences` | paged list; params `resolved`, `sequenceType`, `deviceAgentId`, `page`, `size` (default size 25); sorted `triggeredAt` desc |
|
||||
| GET | `/api/aria/sequences/{id}/events` | threat events sharing the alert's `sequence_id` |
|
||||
| POST | `/api/aria/sequences/{id}/resolve` | idempotent resolve; stamps `resolved_at` + `resolved_by` (JWT email) |
|
||||
|
||||
Note the repository query precedence in `getSequences`: a non-blank `deviceAgentId` **overrides** the `resolved` and `sequenceType` filters (device query returns all statuses for that device). Only when no device filter is set do the resolved/type branches apply.
|
||||
Reference in New Issue
Block a user