---
module: fleet.dashboard
title: Fleet Dashboard
tab: Internal
order: 20
audience: internal
---

Ops/support view for the **Fleet Stats** screen (`FleetDashboard.svelte`, route inside `fleet.bcos.cloud`). It is a read-only dashboard: two stat bars (Task Statistics, Fleet Overview) plus two expandable tables (ATMs by Model, ATMs by Institution). It calls exactly **two** endpoints — everything on the screen comes from these:

- `GET /api/fleet/tasks/stats` (alias `/api/fleet/stats`) → the five task counters + Artifacts + Modules Reporting.
- `GET /api/fleet/devices` → the ATM rows, aggregated client-side by model/institution.

Owned end-to-end by **hiveops-fleet** (backend port 8097, DB `hiveiq_fleet`). No other service is called for this screen.

## Roles required

- Both endpoints: `@PreAuthorize("hasAnyRole('USER','CUSTOMER','ADMIN','MSP_ADMIN','BCOS_ADMIN')")`. This is **not** admin-only — any authenticated fleet user can view it.
- There are **no admin-only actions on this screen.** It is display-only (no create/cancel/approve here — those live on the Fleet Tasks screen).
- Data is **institution-scoped** for `CUSTOMER` and `MSP_ADMIN` tokens: `InstitutionContext.resolveScope()` reads the institution-key list from the JWT credentials and filters `device_summary`. `ADMIN`/`BCOS_ADMIN`/`USER` see everything (unrestricted scope).

## Refresh behaviour (know this before chasing "stale" reports)

- On mount the component loads **both** stats and ATMs once.
- The 60-second auto-refresh timer reloads **stats only** (`loadFleetStats`). The **ATMs by Model / by Institution tables do NOT auto-refresh** — they only update on a full page reload. A user reporting "device counts didn't update for an hour" is expected behaviour, not a bug.
- The accordion "N models · M ATMs" header count is derived from the same one-time ATM load.

## First things to check when it misbehaves

1. **Blank / all-zero task counters** → hit `GET /api/fleet/tasks/stats` directly with a real JWT. On failure the frontend silently swallows the error (`console.warn` only) and keeps the last values/zeros — there is no on-screen error banner for stats.
2. **"No ATM data available" in both tables** → `GET /api/fleet/devices` returned empty. Root cause is almost always an empty/stale `device_summary` table, not the dashboard. `device_summary` is a Kafka mirror — see failure modes below.
3. **Wrong institution split** → stale `device_summary` mirror. Fleet does not reconcile; it only trusts `hiveops.device.events`. If hiveops-devices stopped publishing, the split is stale. Cross-check against the devices service.
4. **403 on load** → token missing one of the five allowed roles, or an expired session (frontend maps 401→"session expired", 403→"no permission").
5. **Scoped user sees more/less than expected** → confirm the JWT's institution-key credentials list; `resolveScope()` returns `null` (unrestricted) for anyone lacking `ROLE_CUSTOMER`/`ROLE_MSP_ADMIN`.

## Known-quirks that look like bugs but are current behaviour

> These are grounded in `FleetTaskService.getStats(...)` as written today. Treat "the numbers look wrong" reports as product limitations, not incidents, until the service is changed.

- **Completed and Failed always show 0.** The service hard-codes `completed = 0` and `failed = 0`. The counters render, but never move.
- **Modules Reporting always shows 0.** The backend `FleetDashboardStats` DTO has no `totalModulesReporting` field, so the frontend default (0) is never overwritten. (The real module numbers live behind `GET /api/fleet/modules/summary`, a different screen.)
- **Pending counts only auto-update tasks.** `pendingTasks` = count of `AUTO_UPDATE` tasks in `PENDING` only — not all pending tasks of every kind.
- **Total tasks / Artifacts are NOT institution-scoped.** `getStats` ignores the scope argument and uses global `taskRepository.count()` / `artifactRepository.count()`. So a scoped MSP_ADMIN sees fleet-wide task and artifact totals but only their own ATMs in the tables — an intentional-looking mismatch that is actually the scope bug above.

## Failure modes + fixes

| Symptom | Likely cause | Fix |
|---------|--------------|-----|
| Both ATM tables empty | `device_summary` empty/stale | Check the `hiveops.device.events` consumer (`groupId hiveops-fleet-device-sync`) is running and lag is 0; confirm hiveops-devices is publishing |
| Stats stuck at last values | `/tasks/stats` failing silently | Curl the endpoint; check fleet backend logs (Loki), not the browser |
| 401/403 | expired/under-privileged JWT | Re-login; verify role + `fleetTaskApprover` not required here |
| Institution split wrong for one inst | stale mirror for that inst | Reconcile via devices service; fleet has no reconciler |
| "Operational/Offline" miscounts | status mapping | `IN_SERVICE`→operational; `OUT_OF_SERVICE`/`DOWN`/`INACTIVE`→offline; anything else counts toward Total only |
