The 162 Architect/Claude docs were tagged 'audience: dev'. The 'dev' tier resolves to isInternal() = ROLE_MSP_ADMIN || ROLE_BCOS_ADMIN, so every MSP_ADMIN could read full internal architecture: source paths, DB tables, service ports, NGINX routes. Verified live on bcos.dev as msp_a@msp1.bcos.dev. Retag them to 'audience: bcos' (isBcos, BCOS_ADMIN only). Content-only change; the 'dev' tier mapping is deliberately left alone so the 15 Internal, 35 Overview and 66 Testing docs stay MSP-visible. Also add platform.guide-access documenting the audience tiers and the per-role visibility matrix, so the access model is queryable from the Guide instead of re-derived from source each time. Verified: MSP module count unchanged (116 across 17 apps); no module loses all its tabs. Guide is bcos.dev only, not deployed to production. Closes #16 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
4.7 KiB
module, title, tab, order, audience
| module | title | tab | order | audience |
|---|---|---|---|---|
| fleet.dashboard | Fleet Dashboard | Architect | 30 | bcos |
How the Fleet Stats dashboard is built. It is a thin read-only view over hiveops-fleet's own database — no cross-service HTTP at request time. Cross-link [platform.service-ownership], [platform.data-architecture], [platform.kafka].
Services involved
| Concern | Owner | Role for this screen |
|---|---|---|
| Dashboard UI | hiveops-fleet/frontend (Svelte, port 5176) |
FleetDashboard.svelte, aggregates ATMs client-side |
| Stats + device list API | hiveops-fleet/backend (Spring Boot 3.4, port 8097) |
FleetApiController → FleetTaskService.getStats(...) + DeviceSummaryRepository |
| Device state feed | hiveops-devices |
Publishes hiveops.device.events; fleet mirrors it |
Only hiveops-fleet is on the request path. hiveops-devices contributes asynchronously via Kafka, never a live call. This is the deliberate no-cross-service-HTTP pattern for fleet.
Data flow
Task/artifact counters (Task Statistics + Fleet Overview bars):
FleetDashboard onMount / 60s timer
→ GET /api/fleet/tasks/stats
→ FleetTaskService.getStats(InstitutionContext.resolveScope())
→ fleet_tasks (count / countByTaskKindAndStatus / findByStatus)
→ fleet_artifacts (count)
→ FleetDashboardStats DTO
ATMs by Model / by Institution tables:
FleetDashboard onMount (once, not on the 60s timer)
→ GET /api/fleet/devices
→ InstitutionContext.resolveScope() (null = unrestricted; else List<institutionKey>)
→ deviceSummaryRepository.findAll() OR findByInstitutionKeyIn(scope)
→ List<Map> {atmId, institution(Key), status, model, ...}
→ frontend groups by model & institution, counts operational/offline
The device rows are populated out-of-band by the Kafka consumer, so the dashboard reads a local mirror, decoupled from device-service availability.
Kafka (async, off the request path) — [platform.kafka]
Consumed by fleet (feeds this screen's device tables):
| Topic | Consumer | Effect |
|---|---|---|
hiveops.device.events |
DeviceEventConsumer (@KafkaListener, groupId hiveops-fleet-device-sync, auto-offset-reset=earliest) |
Upserts device_summary rows read by GET /api/fleet/devices |
Produced by fleet (NOT read by this screen — listed for completeness):
| Topic | Producer | Notes |
|---|---|---|
hiveops.fleet.task.events |
FleetTaskEventPublisher via FleetTaskService |
created/status/archived events for downstream consumers; the dashboard reads fleet_tasks directly, not this topic |
There is no reconciler — if devices stops publishing, device_summary (and therefore the ATM tables) goes stale silently. Cross-link [platform.data-architecture].
DB tables (all in hiveiq_fleet)
| Table | Access for this screen | Notes |
|---|---|---|
fleet_tasks |
READ (counts) | active tasks (PENDING/QUEUED/RUNNING) |
fleet_artifacts |
READ (count) | uploadable/CDN-imported artifacts |
device_summary |
READ | Kafka-synced mirror of device state; the only source for the ATM tables |
fleet_task_history, patch_windows, atm_captures exist in fleet but are not touched by the dashboard.
Key API endpoints (this feature)
| Method Path | Handler | Auth |
|---|---|---|
GET /api/fleet/tasks/stats (alias /api/fleet/stats) |
FleetApiController.getStats → FleetTaskService.getStats |
hasAnyRole('USER','CUSTOMER','ADMIN','MSP_ADMIN','BCOS_ADMIN') |
GET /api/fleet/devices |
FleetApiController.listDevices |
same |
Base default in frontend: http://localhost:8097/api/fleet; prod https://api.bcos.cloud/fleet.
Executor → Kafka → record-store pattern (context)
The broader fleet task lifecycle follows executor → hiveops.fleet.task.events → devices record-store, and terminal tasks archive to fleet_task_history. This dashboard sits outside that loop — it only aggregates current-state reads (task/artifact counts + the device mirror). Worth knowing so you don't wire the dashboard into the task-event pipeline.
Notable implementation facts (verify before relying on displayed numbers)
getStatscurrently hard-codescompletedTasks/failedTasksto0, scopespendingTaskstoAUTO_UPDATE+PENDINGonly, and ignores itsinstitutionKeysargument for the globalfleet_tasks/fleet_artifactscounts.- The
FleetDashboardStatsDTO has nototalModulesReportingfield, so the frontend's "Modules Reporting" always renders its 0 default. - Device status mapping is client-side:
IN_SERVICE→operational;OUT_OF_SERVICE/DOWN/INACTIVE→offline.