---
module: aria.ioc-feeds
title: IOC Feeds
tab: Internal
order: 20
audience: internal
---

Ops/support view for the **IOC Feeds** screen in ARIA (HiveIQ threat-intelligence service, `hiveops-aria`, port **8095**). This screen pulls known-bad indicators (IOCs) from three external sources and upserts them into ARIA's IOC catalog.

## Who can use it

- **Every feed endpoint requires `hasRole('BCOS_ADMIN')`** — not the usual `hasAnyRole('MSP_ADMIN','BCOS_ADMIN')`. A plain `MSP_ADMIN` JWT gets **403** on status, run history, and every refresh button.
- Auth is stateless JWT (`JwtAuthenticationFilter`), shared `JWT_SECRET`. No session.
- The screen self-polls the **status** endpoint every 30s. It never auto-triggers a refresh; refreshes are always manual (buttons) or the nightly cron.

## What "Run" actually does

- **Run All Feeds** → `POST /api/aria/feeds/refresh` → controller spawns a background thread `ioc-feed-manual-all` and returns `202 accepted` immediately with `{"status":"refresh_started"}`. The UI does not wait; it reloads status/history after ~3s.
- **Run Feed** (single card) → `POST /api/aria/feeds/refresh/{feedName}` → thread `ioc-feed-manual-<FEED>`, same 202.
- Because it's fire-and-forget, a **202 does not mean the feed succeeded**. Always confirm the result in the run-history table or the card's status badge, not in the button response.
- The single-card **Run Feed** button is disabled when the feed is not `configured` (no API key) — the card shows the amber *API key required* tag.

## Feeds & what makes them "configured"

| Card | feedName | Configured when set |
|------|----------|---------------------|
| AlienVault OTX | `OTX` | `ARIA_FEED_OTX_API_KEY` non-blank |
| MalwareBazaar | `MALWARE_BAZAAR` | `ARIA_FEED_MALWARE_BAZAAR_API_KEY` non-blank |
| ThreatFox | `THREAT_FOX` | `ARIA_FEED_THREAT_FOX_API_KEY` non-blank |

All three require an API key. MalwareBazaar and ThreatFox keys come from `https://auth.abuse.ch/`. With no key, a run is **not** an error — the client logs "no API key configured, skipping" and returns 0 IOCs (a SUCCESS run with 0 added / 0 updated).

## First things to check when it misbehaves

1. **Buttons/tables return 403** → the caller's JWT is not `BCOS_ADMIN`. Confirm the role claim, not just that the user is logged in.
2. **Card stuck on "API key required" / Run Feed greyed out** → the matching `ARIA_FEED_*_API_KEY` env var is unset or blank on the ARIA container. Check the deployed `.env`, redeploy.
3. **Run finishes SUCCESS but 0 added / 0 updated** → almost always a missing/blank API key (feed skipped silently) OR the source returned no ATM-relevant indicators. Both feeds filter hard for ATM relevance (see below), so 0 is common and not automatically a fault.
4. **Status ERROR with a message in the Error column** → hover the cell for the full text. It's the exception message from the fetch/upsert. Usually an external-source outage, bad/expired API key, or an HTTP/parse error. Retry with the single-card **Run Feed**.
5. **Card stuck on "Running"** → a prior run row was left with `status='RUNNING'` (never completed — e.g. container killed mid-run). New runs of that same feed will short-circuit and just return the last row (see concurrency note). Inspect `ioc_feed_run` for that `feed_name` and clear/complete the stale RUNNING row.
6. **Nothing ever runs on its own** → the scheduled refresh cron is `ARIA_FEED_REFRESH_CRON` (default `0 0 3 * * *`, 03:00 daily). Check ARIA logs at that time for `IOC feed scheduled refresh starting`.
7. **No data at all / screen empty** → check ARIA is up (`/actuator/health`) and DB `hiveiq_aria` reachable; the table reads `ioc_feed_run`, cards read the same via `/status`.

## Common failure modes → fix

| Symptom | Likely cause | Fix |
|---------|-------------|-----|
| 403 on every action | non-`BCOS_ADMIN` JWT | grant BCOS_ADMIN / use an admin token |
| "API key required" tag, Run disabled | `ARIA_FEED_*_API_KEY` blank | set env var, redeploy ARIA |
| SUCCESS, 0/0 | key missing (skipped) or no ATM-relevant hits | verify key present; 0 hits can be normal |
| ERROR row | source outage / bad key / parse error | read Error column, retry single feed |
| Card wedged "Running" | stale RUNNING row after a crash | fix the `ioc_feed_run` row for that feed |
| Feeds never auto-run | cron disabled/changed, or ARIA down at 03:00 | check `ARIA_FEED_REFRESH_CRON`, logs |

## Concurrency gotcha (know before you spam the button)

`refreshFeed` guards against overlap with `countRunning(feedName)` (counts rows where `status='RUNNING'`). If a run is already RUNNING it skips and returns the existing row. Two caveats:
- A single manual **Run Feed** runs inside one `@Transactional` method, so the RUNNING row is **not committed until the whole fetch finishes** — a second concurrent click may not see it yet and can double-run. Don't rely on the guard as a hard lock.
- **Run All** and the nightly cron call `refreshFeed` via internal self-invocation, which **bypasses the `@Transactional` proxy** entirely (only the single-card path is transactional). Behaviour differs between "Run Feed" and "Run All" — flagged in uncertainties.

## `ARIA_FEED_ENABLED` does nothing

`aria.feed.enabled` / `ARIA_FEED_ENABLED` (default true) is defined in `application.properties` but **not referenced anywhere in Java** and the `/status` endpoint hardcodes `enabled: true`. Setting it to false will **not** stop the scheduled refresh. To actually stop automatic runs, change the cron or remove the API keys. Flagged in uncertainties.

Cross-links: the customer-facing feature is on the **Overview** tab; build/data-flow detail is on the **Architect** tab.
