---
module: msp.module-allocations
title: Module Allocations
tab: Architect
order: 30
audience: dev
---

How the **Module Allocations** feature is built. See also [platform.data-architecture], [platform.kafka], [platform.service-ownership].

## Services involved
- **`hiveops-msp` (frontend only, port 5186)** — Svelte 4 SPA. No backend. `AllocationsView.svelte` orchestrates everything client-side. Two axios instances (`api`, `authApi`) both point at the same gateway base and attach `Authorization: Bearer <token>` from `localStorage`.
- **`hiveops-mgmt` (owner of module data)** — serves the module catalogue, per-user allocations, and the MSP institution list. DB: `hiveiq_mgmt`.
- **`hiveops-auth` (owner of user data)** — serves the MSP-scoped user list. DB: `hiveiq_auth`.

There is **no Kafka** on this path — no producers or consumers touch module allocation. It is a synchronous REST + relational-write feature. (No executor→Kafka→record-store pattern applies here.)

## Data flow (single screen load + save)
1. On mount the SPA fires three parallel GETs:
   - `GET /mgmt/api/v1/msp/modules` → `MspController.getModules()` → `AppModuleService.getAllModules()` filtered to `enabled && !isDefault`.
   - `GET /auth/api/msp/users?size=200` → `MspUserController.listUsers()` (auth), scoped by MSP.
   - `GET /mgmt/api/v1/msp/institutions` → `MspController.getInstitutions()`.
2. On row hover / panel open: `GET /mgmt/api/v1/msp/modules/users/{authUserId}` → `AppModuleService.getModulesForAuthUser()` returns **defaults + user allocations** combined; the SPA strips defaults to derive the editable set.
3. On save: `PUT /mgmt/api/v1/msp/modules/users/{authUserId}` with `{ moduleIds: [...] }` → `AppModuleService.setModulesForAuthUser()` → **delete-all-then-insert** into `user_module_allocations`, validating each id against the module catalogue.

## MSP scoping logic (mgmt)
- `BCOS_ADMIN` → `institutionAccessRepository.findAll()` (all institutions).
- `MSP_ADMIN` → derive email domain → `customerRepository.findByDomain(domain)` → `findByCustomerIdOrderByInstitutionNameAsc(customerId)`. **DB-fresh, not JWT-cached** — a deliberate choice to avoid stale institution-key claims. Licenses, contacts, and an appended "MSP group" entry are joined on top.
- Auth's `MspUserController` scopes users by the resolved institution keys (BCOS_ADMIN = all non-SYSTEM users); pageable capped at `size ≤ 200`, sorted by `name`.

## DB tables
| Table | DB | Read | Write | Role here |
|---|---|---|---|---|
| `app_modules` | `hiveiq_mgmt` | ✔ | — (this screen) | Module catalogue (`enabled`, `is_default`, `is_core`, `display_order`) |
| `user_module_allocations` | `hiveiq_mgmt` | ✔ | ✔ | Per-user grants (`auth_user_id` → `module_id`) |
| `institution_module_allocations` | `hiveiq_mgmt` | ✔ (launcher only) | — (this screen) | Institution gate applied at launcher time |
| `customer_institution_access` | `hiveiq_mgmt` | ✔ | — | MSP → institution scope |
| `customers` | `hiveiq_mgmt` | ✔ | — | Domain-based MSP resolution, contact/license join |
| `users` | `hiveiq_auth` | ✔ | — | User list (owned by auth) |

## The gate: allocation ≠ visibility
Writing a `user_module_allocations` row does **not** guarantee the user sees the module. The browser launcher calls `GET /api/v1/users/me/modules` → `AppModuleService.getEffectiveModulesForUser(authUserId, institutionKeys, bypassGate)`, which returns:
`defaults` (always) + user-allocated modules that are **core OR allowed by `institution_module_allocations` for the user's institution keys** — unless `bypassGate` (BCOS_ADMIN). So institution-level allocation is a second, independent layer that must also be set for a non-core module to appear.

## Key endpoints
- mgmt: `GET /api/v1/msp/modules`, `GET /api/v1/msp/institutions`, `GET/PUT /api/v1/msp/modules/users/{authUserId}`
- auth: `GET /auth/api/msp/users`
- launcher (not this screen): mgmt `GET /api/v1/users/me/modules`

## Ownership
Per [platform.service-ownership]: module definitions and allocations belong to **mgmt**; user identity belongs to **auth**. The MSP SPA is a pure client composing the two — never persists state itself.
