---
module: aria.device-posture
title: Device Posture
tab: Internal
order: 20
audience: internal
---

> **Internal ops/support view.** Grounded in `hiveops-aria` (`DevicePostureController`, `DevicePostureService`) and the agent `hiveops-module-aria-signals`. Verify against code before acting on production.

## Who owns it

- **Service:** `hiveops-aria` (port **8095**, DB `hiveiq_aria`, table `device_security_posture`). This screen reads/writes ARIA only — no incident/journal/devices involvement.
- **Data source:** the ATM agent's **`aria-signals`** module POSTs a posture report on a slow schedule (default every **24h**, fires once immediately on module start). There is no Kafka in this path — it is a direct HTTP upsert.

## Roles required

- **Viewing the screen** (`GET /api/aria/posture`, `GET /api/aria/posture/{deviceId}`) requires **`hasRole('BCOS_ADMIN')`**.
- ⚠ Note this is **BCOS_ADMIN only** — NOT the usual `hasAnyRole('MSP_ADMIN','BCOS_ADMIN')` you see elsewhere. An `MSP_ADMIN` who can see other HiveIQ screens will get **403** here. This is by design in ARIA, not a bug — do not "fix" it without a ticket.
- **Posture ingest** (`POST /api/aria/posture/report`) is `permitAll()` at the security-filter level but gated inside the controller by the **`X-Service-Secret`** header matching `aria.internal.service-secret` (`ARIA_SERVICE_SECRET`).

## First things to check when it misbehaves

**"Empty table / No posture data yet"**
1. Confirm the caller is a **BCOS_ADMIN** — an MSP admin gets 403 and the UI shows a load error, not empty data.
2. Confirm **`ARIA_SERVICE_SECRET` is set (non-blank)** on the `hiveops-aria` service. If it is blank, **every** posture report is rejected `401` (see failure modes) — the table stays empty forever with no obvious error.
3. Confirm agents actually run `aria-signals`: the module is inactive unless `aria.endpoint` is configured in `hiveops.properties`, and it needs `aria.service.secret` to match the backend secret.
4. Remember the report cadence is **24h by default** (`aria.posture.report.interval.sec=86400`). A freshly deployed fleet can take a day to populate. It does fire once on agent start, so a restart is a fast way to force a report.

**"Device shows no Disk Enc / Audit / Whitelist (all `—`)"**
- Those three checks are **Windows-only** (BitLocker `manage-bde`, `auditpol`, McAfee Solidcore `sadmin` / AppLocker fallback). Linux ATMs report only `osVersion` + `osEolStatus`; the boolean columns stay null and render as `—`. Expected.

**"Image always shows `—` (never Match/Drift)"**
- Image drift only populates when the agent has **both** `aria.gold.image.hash` and `aria.image.path` set. Without them, `goldImageHash` is null and the Image column is `—`.

**"Score looks lower than the penalties shown in the detail panel"**
- The detail panel lists penalties for encryption (−25), audit (−20), whitelist (−10), OS EOL (−20) and image drift (−20). The backend **also** subtracts a **staleness penalty** (−5/day beyond 7 days stale, capped at −15) that the panel does **not** display. A device that stopped reporting will keep dropping in score without a visible reason on the panel. See uncertainties.

**"Device I expect is missing / duplicated"**
- Records are keyed by **`deviceAgentId`** (upsert via `findByDeviceAgentId`). A device re-imaged with a different agent ID creates a **new** posture row; the old one lingers until manually cleaned. Institution grouping uses `institutionKey` from the report — a blank key groups under **"Unknown"**.

## Common failure modes + fixes

| Symptom | Likely cause | Fix |
|---------|-------------|-----|
| Table empty, no agents landing | `ARIA_SERVICE_SECRET` blank on backend → all reports `401` | Set the secret to a non-blank value on `hiveops-aria`, redeploy, and set matching `aria.service.secret` on agents |
| Reports `401` from one ATM only | Agent's `aria.service.secret` mismatches backend | Push a `CONFIG_UPDATE` with the correct secret |
| MSP admin sees load error | BCOS_ADMIN-only gate | Expected — use a BCOS_ADMIN JWT |
| Bool checks all `—` on many devices | Linux fleet (checks are Windows-only) | Expected, not a defect |
| Stale `Last Check` timestamps fleet-wide | `aria-signals` disabled or `aria.endpoint` unset | Verify agent config; restart forces an immediate report |
| Score drifting down with no failing check | Hidden staleness penalty for stopped reporters | Investigate why the device stopped reporting |

## Manual data checks (BCOS_ADMIN JWT)

- List: `GET /api/aria/posture?page=0&size=500` (UI pulls up to 500 at once).
- Filters: `scoreFilter=low` (<50) or `scoreFilter=medium` (<80); `osEolStatus=EOL|SUPPORTED|UNKNOWN`. `osEolStatus` takes precedence over `scoreFilter` in the controller (only one is applied).
- Single device: `GET /api/aria/posture/{deviceId}` — note `{deviceId}` is matched against the **`device_id`** column, not the row primary key.
