---
module: reports.builder
title: Report Builder
tab: Internal
order: 20
audience: internal
---

Ops/support view of the HiveIQ Report Builder. Owning service: **hiveops-reports** (`hiveiq-reports`, internal `:8088`, external `:8011`; frontend `report.bcos.cloud` @ `:5182`). Route: `api.bcos.cloud/reports/` → `hiveiq-reports:8088/api/reports/`.

## Who can do what
- **No `MSP_ADMIN`/`BCOS_ADMIN` gate on report actions.** `SecurityConfig` is `anyRequest().authenticated()` behind `JwtAuthenticationFilter` — *any* logged-in HiveIQ user with a valid JWT can create/run/download reports. The one method-level guard is `GET /api/devices/atms` (`@PreAuthorize("hasAnyRole('USER','CUSTOMER','ADMIN','MSP_ADMIN','BCOS_ADMIN')")`).
- **Templates and runs are per-user, not per-institution.** Listing uses `principal.userId()` (`listForUser`, `getForUser`). A user sees only their own templates plus ones explicitly **Shared** with them. There is no admin "see everyone's reports" endpoint.
- **Scheduled runs impersonate.** The scheduler mints its own JWT (see below) — a scheduled report can read data the human owner might not, because it runs as a service `MSP_ADMIN`/`ADMIN` principal scoped to the template's `institutionKey`.

## First things to check when it misbehaves
1. **A run is stuck PENDING/RUNNING** → check `hiveiq-reports` logs. Execution is `@Async("reportExecutor")` with a **small pool (core=2, max=5)**; a burst of runs queues behind it. `report_runs.status` never leaving RUNNING usually means a downstream fetch hung.
2. **A run FAILED** → the reason is stored on the run and shown on hover in Run History. Reports **fetch live** from other services at run time; a downstream outage = FAILED run, not empty data. Map the data source to the service:
   - INCIDENTS / SOFTWARE_ERRORS / AGENT_MODULES → **hiveops-incident**
   - FLEET_TASKS → **hiveops-fleet** (`hiveiq-fleet:8097`)
   - TRANSACTIONS → **hiveops-journal** (+ incident for ATM list)
   - ATM_HEALTH / AGENT_VERSIONS → **local `device_summary` mirror** (no HTTP call)
3. **Download returns 404/500** → CSV files live on disk under `REPORT_UPLOAD_DIR` (default `/app/uploads/reports/{uuid}.csv`; bind-mounted to `/data/hiveiq/reports/` in OpenMetal), **not** in the DB. If the volume is missing/rotated, the DB row exists but the file is gone.
4. **ATM Health / Agent Versions show stale or missing devices** → `device_summary` is a **Kafka-fed mirror with no reconciler**. If `hiveops.device.events` stops flowing (consumer group `hiveops-reports-device-sync` lagging/down), device data silently goes stale. Check consumer lag and restart the service to rejoin.
5. **Scheduled report never fires** → scheduler polls every 60s (`findScheduledDue`). Verify the template has a valid cron and that a query is marked scheduled (`set-scheduled`). An invalid cron is logged as a warning and skipped, not surfaced to the user.
6. **Scheduled report fires but downstream calls 401/403** → the self-minted service JWT is signed with `jwt.secret`. If `hiveops-reports`' `JWT_SECRET` drifts from the shared platform secret, scheduled (but not user-triggered) runs fail auth downstream.

## Common failure modes → fix
| Symptom | Likely cause | Fix |
|---|---|---|
| Run FAILED, "No fetcher for data source" | Template `data_source` value has no matching fetcher | Data source enum drift; check template row in `report_templates` |
| Device data stale in reports | `hiveops.device.events` consumer stopped | Restart `hiveiq-reports`; check Kafka lag on `hiveops-reports-device-sync` |
| Download 404 with COMPLETED run | CSV file missing on disk | Check `REPORT_UPLOAD_DIR` volume mount; re-run the report |
| Scheduled runs fail auth, manual runs fine | `JWT_SECRET` mismatch | Align `hiveops-reports` secret with platform `JWT_SECRET` |
| Distributed report never lands in recipient inbox | Kafka `hiveops.reports.completed` not consumed | Check `hiveops-messaging` consumer; see `report_distribution_log` for per-recipient rows |
| Slow runs under load | 2–5 async threads saturated | Expect queueing; not a bug |

## Data locations (DB `hiveiq_reports`)
`report_templates` (definitions, cron, sharing, institutionKey) · `report_runs` (status, row count, `csv_path`, `uuid`, triggered_by) · `report_queries` (saved date-range + ATM sets, scheduled flag) · `report_distribution_log` (one row per recipient) · `device_summary` (Kafka mirror). Connection is split `DB_HOST`/`DB_PORT`/`DB_NAME`/`DB_USERNAME`/`DB_PASSWORD` (not `DB_URL`).
