---
module: profile.inbox
title: Inbox
tab: Internal
order: 20
audience: internal
---

Ops/support view of the Profile **Inbox**. The UI lives in `hiveops-profile` (`Inbox.svelte`) but **all message data is owned by `hiveops-messaging`** (port 8086, DB `hiveiq_messaging`). If the inbox misbehaves, the profile SPA is almost never the cause — check messaging first.

## Who owns what
- **Frontend:** `hiveops-profile/frontend` → `src/components/Profile/Inbox.svelte`, API in `src/lib/api.ts`.
- **Backend:** `hiveops-messaging` → `MessageController` + `MessageService`. Everything under `/messaging/messages` proxies here.
- Recipient picker and institution dropdown are **cross-service** reads (auth + mgmt), not messaging — see below.

## Roles & admin-only actions
- **Reading / sending Direct / delete / mark-read** — any authenticated user (recipient, sender, or a broadcast to their institution).
- **Broadcast** — admin only. `MessageController.send()` rejects `type=BROADCAST` unless `MessagingPrincipal.isAdmin()`, which is true for role `ADMIN`, `MSP_ADMIN`, or `BCOS_ADMIN` (case-insensitive). Note: this is a manual principal check in the controller, **not** a `@PreAuthorize` annotation. Non-admins never see the Broadcast tab or Institution selector in the UI (`isAdmin` gate on `role ∈ {ADMIN, MSP_ADMIN, BCOS_ADMIN}`).
- **Institution-scoped broadcast** — admin picks an institution in Compose; blank = global (all institutions). Backend stores `institution_key` on the message; a user only sees a scoped broadcast if their JWT `institutionKey` claim matches (or the broadcast is global/NULL).
- **Compose recipient list** comes from `GET /auth/api/admin/users` — that endpoint is `authenticated()` (any logged-in user), so the picker works for non-admins too.
- **Compose institution list** comes from `GET /api/v1/msp/institutions` (mgmt) which **requires `MSP_ADMIN`/`BCOS_ADMIN`** — non-admins never call it.

## First things to check when it misbehaves
1. **Is `hiveiq-messaging` up?** `curl http://localhost:8086/actuator/health`. A dead messaging container = empty inbox / spinner, even though the Profile app itself loads.
2. **401/403 on `/messaging/messages`** — JWT problem. Messaging validates the token with the **shared `JWT_SECRET`** (same secret as auth + mgmt). A rotated/mismatched secret across services breaks messaging silently. Confirm `JWT_SECRET` matches auth.
3. **Empty inbox but user expects messages** — check `institution_key` scoping. Institution-scoped broadcasts only appear when the JWT `institutionKey` claim equals the message's `institution_key`. A user with a missing/blank claim sees only DIRECT messages + global broadcasts.
4. **Unread count wrong / stale** — the count is `@Cacheable(value="unreadCount", key=userId)`. It is evicted on mark-read / mark-all-read / delete. If a count looks stuck, the cache is per-userId; a rare desync self-corrects on next send/read.
5. **Action/system messages missing** — these arrive via Kafka (`hiveops.messages` topic) from external services. If ACTION messages stopped, check Kafka connectivity and the `hiveops-messaging` consumer group — not the Profile app.
6. **Compose "To" list empty** — `GET /auth/api/admin/users` failed. `loadRecipients()` swallows the error (non-fatal), so the picker just shows "No users found". Check auth service health.
7. **Institution dropdown empty for an admin** — `GET /api/v1/msp/institutions` (mgmt) failed or the user lacks `MSP_ADMIN`/`BCOS_ADMIN`. Frontend logs `[Inbox] loadInstitutions failed`.

## Common failure modes → fix
| Symptom | Likely cause | Fix |
|---|---|---|
| Whole inbox blank + toast "Load failed" | messaging down / 5xx | restart `hiveiq-messaging`, check logs |
| 401 on every messaging call | JWT_SECRET mismatch after redeploy | align `JWT_SECRET` across auth/mgmt/messaging |
| Scoped broadcast not seen by target users | JWT missing `institutionKey` claim | verify auth issues `institutionKey` in token |
| ACTION alerts not arriving | Kafka down / consumer lag | check broker + `hiveops-messaging` consumer group |
| Broadcast button missing for an admin | role not in {ADMIN,MSP_ADMIN,BCOS_ADMIN} | fix the user's role claim |
| Deleted message reappears | delete is **per-user soft-delete** only | expected — other users still see it |

## Data behaviors worth knowing
- **Delete is per-user and soft.** It writes a `message_deletes` row and also marks the message read; the underlying `messages` row is never removed and other recipients are unaffected.
- **Mark-read is per-user** via `message_reads`. "Sent" messages always render as read.
- **Sent tab** lists rows where `sender_user_id = you`, excluding your own soft-deletes.
- Inbox auto-refreshes every 60s (client-side timer, toggle persisted in `localStorage` key `profile_inbox_autoRefresh`).

Related: [profile.inbox] Overview (customer), [platform.service-ownership].
