---
module: aria.threat-events
title: Threat Events
tab: Internal
order: 20
audience: internal
---

Ops/support/admin view of the ARIA **Threat Events** screen. This is a read-only list rendered from the `hiveops-aria` service (`ThreatEvents.svelte`); there are no per-row admin actions on this screen — the only controls are filters, pagination, and auto-refresh. Grounded in `hiveops-aria` source.

## Who can see it

- The screen calls **`GET /api/aria/events`**, which is gated **`@PreAuthorize("hasRole('BCOS_ADMIN')")`**.
- ⚠️ **BCOS_ADMIN only** — this is *not* the usual `hasAnyRole('MSP_ADMIN','BCOS_ADMIN')`. An MSP_ADMIN JWT will get **403** and the list will show the "Could not load threat events" toast. If a customer/MSP admin reports an empty or failing screen, this is expected — they are not authorized. Same gate applies to the stats endpoint (`GET /api/aria/stats`).

## What feeds the list

- Rows come from the `threat_event` table in the **`hiveiq_aria`** database, newest first (`ORDER BY detectedAt DESC`).
- Rows are written by the **ingestion pipeline**, not by anyone using this screen. Agents/services POST batches to **`POST /api/aria/ingest/events`**; `ThreatEventIngestionService` classifies each item (severity, matched IOC, attack phase) and saves a `threat_event` row.
- The **IOC Match** column is populated only when the classifier matched the signal against an active row in `threat_ioc`. No match → `—`.

## First things to check when it misbehaves

1. **403 / "Could not load threat events"** — confirm the caller's JWT role is `BCOS_ADMIN`. MSP_ADMIN is rejected.
2. **Screen loads but is empty** — check whether events are actually being ingested:
   - Are agents/services POSTing to `/api/aria/ingest/events`? Look for `ARIA event ingested: device=… signal=… severity=… phase=…` INFO logs in `hiveops-aria`.
   - `401` on ingest means the `X-Service-Secret` header did not match `aria.internal.service-secret` (`ARIA_SERVICE_SECRET`). Look for `Rejected ingestion request from … — invalid service secret` WARN logs.
   - ⚠️ If `ARIA_SERVICE_SECRET` is **blank/unset**, the ingest endpoint accepts *any* request (no auth). A blank secret means the header is never checked.
3. **Device filter seems ignored** — see "Known quirk" below. Searching a Device (Agent) ID **while a severity is also selected** returns severity-filtered results ignoring the device. Clear the severity filter to search by device.
4. **IOC Match always empty** — the IOC catalog may be empty/inactive. IOCs live in `threat_ioc`; check via `GET /api/aria/ioc` (also BCOS_ADMIN). If the feed refresh hasn't run or seed migration (V2, FBI FLASH) is missing, nothing will match.
5. **List looks stale** — auto-refresh runs client-side every **60s** (toggle persisted in `localStorage` key `aria_threats_autoRefresh`). The `↻` button forces a reload. Staleness is a data/ingest problem, not a refresh problem — the UI just re-calls `GET /api/aria/events`.
6. **Wrong severity/phase on a row** — classification is done at ingest time by `ThreatEventClassifier`; rows are not re-classified. A bad label means the classifier or the IOC catalog was wrong when the event arrived.

## Common failure modes + fixes

| Symptom | Likely cause | Fix |
|---------|-------------|-----|
| 403 loading events | Caller is MSP_ADMIN, not BCOS_ADMIN | Use a BCOS_ADMIN account/JWT |
| Empty list, no ingest logs | Agents not posting, or wrong service URL | Verify agent/service target + `ARIA_SERVICE_SECRET` match |
| Ingest returns 401 | `X-Service-Secret` mismatch | Align caller header with `ARIA_SERVICE_SECRET` |
| Ingest accepts anything | `ARIA_SERVICE_SECRET` unset/blank | Set the secret in the ARIA env |
| IOC Match column all `—` | No active IOCs matched | Check `threat_ioc` / feed refresh (`GET /api/aria/feeds/status`) |
| Device search returns severity results | Severity + device combined (see quirk) | Clear severity, then search device |
| Downstream (incident, etc.) missing an event | Only CRITICAL/HIGH are published to Kafka | Expected — MEDIUM/LOW/INFO are stored but not published |

## Known quirk (verified in code)

`ThreatEventController.getEvents` checks `severity` **before** `deviceAgentId` and returns early. So when the UI sends both (severity dropdown + device search active at once), the **device filter is silently ignored**. Workaround: filter by only one at a time. Track/report as a backend bug if it causes support confusion.

## Related admin surfaces (same service, BCOS_ADMIN)

- `GET /api/aria/stats` — dashboard counters (total events, critical/high 24h, active IOCs, active sequences, low-posture/EOL devices).
- `GET /api/aria/ioc`, `POST/PUT/DELETE /api/aria/ioc` — IOC catalog management.
- `GET /api/aria/sequences` — precursor-sequence alerts (multi-phase attack chains).

See the **Architect** and **Claude** tabs for data flow and exact endpoint/table/topic references.
