12 KiB
Session Log — GuruRMM UI Gaps + Enrollment Audit
Date: 2026-06-07
Topic: GuruRMM UI/Dashboard — gap verification, batch implementation, hotfix, enrollment audit
User
- User: Mike Swanson (mike)
- Machine: GURU-BEAST-ROG
- Role: admin
Session Summary
Session started as a GuruRMM UI/Dashboard feedback thread via Discord bot. First pass verified all known UI gaps against the current dashboard source code (submodule was 50 commits behind origin/main at the time of the first coding pass, which caused a merge conflict scenario later). Eight gaps were confirmed open: enrollment management, AgentDetail client row not rendering, dashboard fleet counts computed client-side, tunnel session UI, fleet discovery page, install reporting page, BSOD crashes tab, and agent version history.
The main implementation batch addressed everything except tunnel (server skeleton is "not yet implemented" and needs xterm.js — deferred). A server-side Rust agent added six new/updated API endpoints: get_agent now returns AgentWithDetails (client_id + client_name via JOIN), GET /api/agents/:id/bsod-events, GET /api/agents/:id/version-history, GET /api/install-reports + /:id, GET /api/discovery/all-devices, and DELETE /api/agents/:id/key. A dashboard agent added two new pages (InstallReports, Discovery), a Crashes tab on AgentDetail, a version history table in the Updates tab, fleet stat wiring on Dashboard.tsx, and a Revoke Key button in SiteDetail. Code review approved all changes. The Gitea agent committed and pushed but the submodule was in detached HEAD 50 commits behind main, requiring a stash/pull/rebase merge that resolved five conflicts including Layout.tsx (upstream had switched to FunctionRail architecture) and Dashboard.tsx (upstream had switched to a Triage/ExceptionStream layout).
Immediately after the build deployed, Mike reported RMM was down. Diagnosis: the new get_agent_with_details_by_id SQL query was missing a.role_override — migration 054 had added role_override: String (required, non-optional) to AgentWithDetails and the existing get_all_agents_with_details already included it, but the new single-agent function was written from the pre-054 SQL. The list_agents endpoint continued working while GET /api/agents/:id returned 500. Hotfix was a one-line SQL change (a.role_override added), committed and pushed immediately (6faa382).
Enrollment work followed. Investigation revealed the enrolled_agents table (migration 012) already stored everything needed — enrolled_at, last_seen, ip_address, os_version, revoked — so no new migration was required. The original DELETE /api/agents/:id/key implementation only nulled agents.api_key_hash (legacy Mode 1/2 key), but WS auth checks enrolled_agents.agent_key_hash first (Mode 3 for agk_ keys), so revocation was incomplete for modern agents. Implementation added four new DB functions, GET /api/sites/:id/enrolled-agents with duplicate-hostname annotation, POST /api/enrolled-agents/:id/revoke, and fixed DELETE /api/agents/:id/key to cascade-revoke both key layers. Dashboard gained an Enrollment tab on SiteDetail with full audit table, status badges, duplicate warnings, per-row revoke with confirm. A second detached-HEAD merge was required; the Gitea agent resolved conflicts but incorrectly kept the old route pointer — caught and fixed manually (00129af).
Session closed with a sync that also deployed Phase 5c of sync.sh (skills mirroring to ~/.claude/skills/) per a coord message from GURU-5070.
Key Decisions
- Tunnel deferred: Server
tunnel.rsis a dead-code skeleton that logs "not yet implemented" — wiring the route would expose a non-functional endpoint. Excluded from this batch; needs proper design + xterm.js before implementation. - No migration for enrollment:
enrolled_agentstable (migration 012) already had all required fields. The gap was API exposure and UI, not missing data. - Dual-layer revoke:
DELETE /api/agents/:id/keywas redesigned to revoke bothagents.api_key_hash(legacy Mode 1/2) andenrolled_agents.revoked = TRUE(modern agk_ Mode 3). Originally only the legacy field was touched, leaving modern agents able to reconnect. - Duplicate hostname detection via two queries: Rather than a SQL window function,
list_site_enrollmentsdoes a second query forCOUNT(*) > 1grouped hostnames, then annotates in Rust. Simpler and safer with runtime sqlx (no compile-time macro). is_duplicate_hostnamescoped to active rows only: Only non-revoked enrollments count toward the duplicate flag. A revoked + active pair is not flagged — the revocation resolved the ambiguity.- Per-row revoke vs. bulk revoke: Added
POST /api/enrolled-agents/:id/revokefor targeted per-enrollment revocation (with cascade toagents.api_key_hash). The existingDELETE /api/agents/:id/keyis the bulk "revoke everything for this agent" path.
Problems Encountered
- Submodule detached HEAD (twice): The submodule was 50 commits behind
origin/maineach time the coding agents worked on it. Required stash → checkout main → pull → stash pop → conflict resolution. Upstream had refactored Dashboard.tsx to a Triage/ExceptionStream layout and Layout.tsx to FunctionRail/InfrastructureSpine — the coding agents worked on the old layout. Gitea agent resolved both merges. role_overridefield missing from new SQL query:get_agent_with_details_by_idwas copied from pre-migration-054 SQL. Migration 054 addedrole_override: String(non-optional) toAgentWithDetailsand updatedget_all_agents_with_detailsbut not our new function.sqlx::FromRowpanics at runtime when a required field has no column. Caused production 500s on allGET /api/agents/:idcalls. Fixed in hotfix commit6faa382.- Route pointer left on old handler after merge: The Gitea agent's conflict resolution for mod.rs kept the original
revoke_agent_keyroute (single-layer revoke) rather than switching torevoke_agent_key_handler(dual-layer). Caught by manual review, fixed in00129af. - SSH to production server unavailable from BEAST:
known_hostsfile issue prevented SSH key auth during incident diagnosis. Worked around by getting a JWT token via API and testing endpoints directly with curl.
Configuration Changes
GuruRMM submodule (projects/msp-tools/guru-rmm/):
Server files modified:
server/src/db/agents.rs—get_agent_with_details_by_id,revoke_agent_key(DB functions)server/src/db/enroll.rs—get_enrollment_by_id,revoke_single_enrollment,list_enrollments_for_site,duplicate_active_hostnames(4 new DB functions)server/src/db/discovery.rs—DiscoveredDeviceWithContext,list_all_discovered_devicesserver/src/db/updates.rs—Serializederive added toAgentUpdateRecordserver/src/db/bsod_events.rs— removed stale#[allow(dead_code)]server/src/api/agents.rs— updatedget_agenthandler; addedlist_bsod_events,get_agent_version_history,revoke_agent_key(old, kept),revoke_agent_key_handler(new dual-revoke)server/src/api/enroll.rs—EnrollmentRecordstruct,list_site_enrollments,revoke_enrollmenthandlersserver/src/api/install_report.rs—InstallReportstruct,list_install_reports,get_install_reporthandlersserver/src/api/discovery.rs—list_all_deviceshandlerserver/src/api/mod.rs— 10 new routes wired
Dashboard files modified:
dashboard/src/api/client.ts—AgentStats,BsodEvent,AgentUpdateRecord,InstallReport,DiscoveredDeviceWithContext,EnrollmentRecordinterfaces;agentsApiextensions;installReportsApi,discoveryFleetApi,enrollApinamespacesdashboard/src/pages/AgentDetail.tsx— Crashes tab, version history in Updates tabdashboard/src/pages/Dashboard.tsx— fleet stats wired to/agents/statsdashboard/src/pages/SiteDetail.tsx— Revoke Key button + Enrollment tabdashboard/src/components/Tabs.tsx— optionalbadgepropdashboard/src/components/FunctionRail.tsx— Install Reports + Fleet Discovery nav linksdashboard/src/App.tsx—/install-reports,/discoveryroutes
Dashboard files created:
dashboard/src/pages/InstallReports.tsxdashboard/src/pages/Discovery.tsx
ClaudeTools repo:
projects/msp-tools/guru-rmm/docs/UI_GAPS.md— updated to mark 8 items complete, detail enrollment partial status
Credentials & Secrets
- GuruRMM API admin:
claude-api@azcomputerguru.com/ vault:infrastructure/gururmm-server.sops.yamlfieldadmin-password - SSH:
guru@172.16.3.30— SSH key auth failing from BEAST (known_hosts issue); password in vaultinfrastructure/gururmm-server.sops.yamlfieldpassword
Infrastructure & Servers
- GuruRMM server:
172.16.3.30:3001(Rust/Axum, systemdgururmm-server) - Dashboard:
https://rmm.azcomputerguru.com(nginx,/var/www/gururmm/dashboard/) - Gitea:
https://git.azcomputerguru.com/azcomputerguru/gururmm(repo:azcomputerguru/gururmm) - Build pipeline webhook:
172.16.3.30:9000(not reachable from BEAST — Gitea triggers it) - Coord API:
http://172.16.3.30:8001/api/coord
Commands & Outputs
# Verified API server up during incident
curl -s "http://172.16.3.30:3001/api/auth/me"
# → "Missing authorization header" (server alive)
# Confirmed new build deployed (new endpoint returns 200)
curl -sv "http://172.16.3.30:3001/api/agents/<id>/bsod-events" -H "Authorization: Bearer $TOKEN"
# → HTTP/1.1 200 OK
# Confirmed bug: get_agent returning 500
curl -s "http://172.16.3.30:3001/api/agents/<id>" -H "Authorization: Bearer $TOKEN"
# → "Internal server error"
# Hotfix commits (gururmm submodule)
# 5854c63 — main UI gap batch
# 6faa382 — hotfix: role_override missing from get_agent_with_details_by_id
# 49a7109 — enrollment audit batch
# 00129af — fix: wire revoke_agent_key_handler to route
# Coord message handled: GURU-5070 skills empty, Phase 5c fix
# BEAST sync confirmed 19 skills deployed
Pending / Incomplete Tasks
- Tunnel Session Management (P2): Server skeleton "not yet implemented". Needs: xterm.js design, WS protocol spec for dashboard client, proper Phase 2 implementation. Estimated 3-5 days separate effort.
- Enrollment audit log (enrollment table) — key status column: The
enrolled_agentstable doesn't expose a "key_prefix" field (key hash is intentionally hidden). The UI shows hostname, dates, IPs but not a redacted key identifier. Could add first-8-chars of key hash as a non-sensitive display field in a future pass. - SSH from BEAST to production:
known_hostsfile issue prevents SSH key auth. Should be fixed for incident response capability. Likely needs the production server's host key added to BEAST's known_hosts. - Tunnel (deferred): See above.
- Documentation (P3): User guide, inline help tooltips — still not started.
revoke_agent_key(old handler): Dead code now that route points torevoke_agent_key_handler. Should be removed in a cleanup pass to avoid confusion.
Reference Information
- GuruRMM wiki:
wiki/projects/gururmm.md - UI gaps doc:
projects/msp-tools/guru-rmm/docs/UI_GAPS.md - enrolled_agents migration:
server/migrations/012_enrolled_agents.sql - Key commits:
5854c63— UI gap batch (7 server endpoints, 2 new pages, 5 dashboard enhancements)6faa382— hotfix role_override in get_agent SQL49a7109— enrollment audit batch00129af— fix route pointer to dual-revoke handler
- Coord message addressed: GURU-5070 skills Phase 5c fix (commit
62fed033)