Files
claudetools/session-logs/2026-06/2026-06-22-howard-gururmm-software-uninstall.md
Howard Enos 48b6c94b4a sync: auto-sync from HOWARD-HOME at 2026-06-22 19:15:51
Author: Howard Enos
Machine: HOWARD-HOME
Timestamp: 2026-06-22 19:15:51
2026-06-22 19:16:22 -07:00

14 KiB

User

  • User: Howard Enos (howard)
  • Machine: Howard-Home
  • Role: tech

Session Summary

Built the GuruRMM remote software-uninstall feature (SPEC-030) end to end, starting from Howard's reference to dUninstaller.exe (a closed-source Codejock GUI binary — nothing to lift). Confirmed the agent already inventories installed software and has a robust command pipeline, so the gap was: capture uninstall metadata, an uninstall engine, and a dashboard. Shaped the feature via /shape-spec into projects/msp-tools/guru-rmm/specs/remote-software-uninstall/, then prototyped a standalone PowerShell engine (agent/scripts/uninstall-engine.ps1) implementing a silent-first tier ladder (MSI /qn, QuietUninstallString, detected NSIS/InnoSetup switch, winget), validated by dry-run across 67 real programs and a live -List on test box DESKTOP-MS42HNC.

Chose "Route B" (server-orchestrated): the server embeds the engine via include_str! and dispatches it over the existing powershell command pipeline — no agent rebuild or redeploy. Built server/src/api/software.rs (GET /software, POST /software/uninstall)

  • dashboard SoftwareManager.tsx in the Inventory tab (multi-select, confirm-gated bulk uninstall, per-program results). Merged to main (PR #47, #48) and deployed; verified live by removing Everything and FastStone on DESKTOP-MS42HNC through the real endpoints.

Then layered the removal knowledge loop: a per-agent tracking table (migration 061) and, after Howard refined the model, a fleet knowledge catalog (migration 062, software_knowledge) with three classifications — silent / requires_ui / unknown — keyed by exact DisplayName, logs kept only for unknowns, dashboard promotion of unknowns. Added BCU (Bulk Crap Uninstaller, Apache-2.0) informed engine upgrades: NSIS detection by binary signature, MSI REBOOT=ReallySuppress + WiX Package Cache, fail-fast 120s timeout, and a critical exit-code-capture fix (Start-Process -PassThru left .ExitCode null → everything falsely reported success; switched to System.Diagnostics.Process). Ran a multi-round live test battery (MSI, NSIS x2, quiet, vendor Firefox/OneDrive, interactive AnyDesk, MSI-1605 path) — all correct, removals verified.

Captured three follow-on designs as specs: GuruConnect SPEC-019 (private Backstage GUI desktop for interactive uninstall), rip-and-replace Tier 1.4 (vendor AV/RMM removal tools for client takeovers), and Tier 1.5 (BCU-style headless UI automator). Fixed a fleet-wide sync.sh bug that was repeatedly clobbering submodule branch work. Finished with a scoped 3-pass Opus audit (/rmm-audit) of the SPEC-030 code against GuruRMM standards; fixed all HIGH + MEDIUM findings. The engine/catalog work remains on branch feat/engine-bcu-improvements (pushed, NOT merged — Howard wants more validation first).

Key Decisions

  • Route B (server-orchestrated) over a native agent command type. The server embeds the validated engine and dispatches it via the existing powershell pipeline, so the feature works on the currently-deployed agent with no rebuild/redeploy. The native command-type port is a later internal refactor behind the same REST shape.
  • Windows-only for now; show-only on other OSs. Removal is gated to Windows agents (server returns 501 for non-Windows; dashboard shows the live uninstall UI only on Windows and a read-only installed-software list elsewhere). Linux/macOS removal is a tracked follow-on.
  • Knowledge catalog keyed by exact DisplayName, three states (silent/requires_ui/ unknown); logs saved ONLY for unknown (undocumented) installers; promotion of unknowns done via a dashboard action (silent methods still added in engine code, not data-driven).
  • Use vendors' own removal tools for AV/RMM rip-and-replace (Avast clear, McAfee MCPR, etc.) rather than reverse-engineering; host vetted checksummed copies on our infra.
  • GuruConnect owns interactive (Tier-2) removal — silent removal is figured out first; SPEC-019 extends the existing SPEC-013 backstage from terminal to a private GUI desktop.
  • Engine exits 0 on per-target failures (failures reported in JSON, not exit code) so one failed program can't fail a whole bulk batch.
  • Fleet knowledge endpoints are admin-only (cross-tenant logs + shared writes), matching the list_commands convention.

Problems Encountered

  • sync.sh repeatedly reset the guru-rmm submodule, discarding committed branch work mid-build (HEAD jumped to a stale pinned commit twice). Root cause: Phase-3 post-rebase ran git submodule update --init --recursive unconditionally. Fixed with submodule_update_safe() that skips any submodule on a branch or with uncommitted changes; pushed to parent main so the whole fleet gets it. Recovered orphaned commits via cherry-pick onto a feature branch.
  • AnyDesk --uninstall --silent hung ~5+ min (silent flag not honored on the tested build). Dropped the AnyDesk vendor rule → it now classifies as needs_remote instantly (interactive tier, no launch). Logged as a correction.
  • Exit codes were not capturedStart-Process -PassThru returned .ExitCode null, so every uninstall mapped to exit 0 / false "success" (a failed MSI 1603 would read as removed). Switched to System.Diagnostics.Process with async stream reads; verified 1605 + exit-0 now captured.
  • Engine embedded in server but the server build change-gate only watched server/ — an engine-only change would silently ship the old engine. Fixed build-server.sh to also watch agent/scripts/uninstall-engine.ps1.
  • Git-Bash curl started failing "Permission denied" (AV/EDR on the workstation after many calls). Pivoted RMM API calls to PowerShell Invoke-RestMethod.
  • Hand-built JSON with C:\\ backslashes was mangled in Git-Bash (collapsed to single backslash → invalid JSON, ConvertFrom-Json failed). Fixed by building targets JSON with jq / extracting from already-valid JSON. Logged as friction.
  • PR auto-create failedvault.sh get-field services/gitea credentials.api-token mis-resolved (returned 4 chars). Worked around by parsing the api-token line directly; validated against the Gitea API before use.

Configuration Changes

Branch feat/engine-bcu-improvements (guru-rmm submodule, pushed, NOT merged):

  • agent/scripts/uninstall-engine.ps1 — new engine (tiers, vendor table, binary-NSIS, Package Cache, fail-fast, exit-code fix, hardened self-uninstall guard)
  • server/src/api/software.rs — endpoints (list/uninstall/removal-status/resolve/ knowledge/classify), os_type gate, admin gating, error-leak fixes, pagination
  • server/src/api/mod.rs — routes
  • server/src/db/software_removal.rs, server/src/db/software_knowledge.rs, db/mod.rs
  • server/migrations/061_software_removal_attempts.sql, 062_software_knowledge.sql
  • dashboard/src/api/client.ts, dashboard/src/components/SoftwareManager.tsx, dashboard/src/components/InventoryTab.tsx, dashboard/src/pages/AgentDetail.tsx
  • deploy/build-pipeline/build-server.sh — change-gate watches the embedded engine
  • specs/remote-software-uninstall/ — plan, shape, references, standards, task1-results, bcu-research-and-tiers, knowledge-base-design, rip-and-replace-removal-tools
  • reports/2026-06-22-spec030-software-uninstall-audit.md

Already on guru-rmm main (deployed): base inventory+uninstall + per-device tracking (PR #47 merge 42681f2c, PR #48 merge c4c0ea7).

guru-connect submodule: docs/specs/SPEC-019-private-backstage-session.md + docs/FEATURE_ROADMAP.md on branch feat/spec-019-backstage-uninstall (pushed, off main).

Parent claudetools (pushed to main): .claude/scripts/sync.sh (submodule_update_safe), .claude/memory/feedback_submodule_autosync_discipline.md, errorlog.md.

Credentials & Secrets

  • No new credentials created. RMM admin creds read from vault infrastructure/gururmm-server.sops.yaml fields credentials.gururmm-api.admin-email / admin-password (used for API auth during testing). Gitea API token at services/gitea field credentials.api-token (used for PR create/merge). Temp credential files written under .claude/tmp/ during testing were shredded; .claude/tmp is gitignored.

Infrastructure & Servers

  • GuruRMM API/server: http://172.16.3.30:3001 (prod; also the build host, user guru, repo /home/guru/gururmm). Beta dashboard: https://rmm-beta.azcomputerguru.com (built from main, talks to prod API). Prod dashboard: https://rmm.azcomputerguru.com.
  • Gitea internal API: http://172.16.3.20:3000 (repo azcomputerguru/gururmm, azcomputerguru/guru-connect). Public host git.azcomputerguru.com is behind Cloudflare (blocks curl).
  • Test box: DESKTOP-MS42HNC — AZ Computer Guru / Howard-VM, Windows, agent id 0de89b88-b21d-4647-ab64-96157ba87cc5.

Commands & Outputs

  • Run engine standalone: powershell -NoProfile -ExecutionPolicy Bypass -File agent/scripts/uninstall-engine.ps1 -List (JSON inventory) / ... -TargetsJson <file> [-DryRun].
  • Server build check: SQLX_OFFLINE=true cargo check -p gururmm-server (clean).
  • Dashboard: npx tsc -p tsconfig.app.json --noEmit + npm run build (clean).
  • Live results: classification 104/120 silent-capable on DESKTOP-MS42HNC; removed Everything, FastStone, HandBrake, ImgBurn, Paint.NET, GIMP, Firefox, OneDrive, AIMP (verified gone); AnyDesk correctly retained as needs_remote; fake-GUID MSI → exit 1605 "not installed".
  • Gitea PR+merge via Invoke-RestMethod / token from vault api-token line.

Pending / Incomplete Tasks

  • Merge + deploy feat/engine-bcu-improvements (engine improvements + knowledge catalog + audit fixes). Not merged per Howard ("keep testing before live"). Post-deploy: verify catalog populates + promote an unknown live; the catalog/dashboard cannot be exercised end-to-end until deployed (live server still runs the old engine).
  • Audit LOW items (tracked in the report): warn! on audit-write failure, randomized temp filename, TS interface completeness, empty-states, ASCII em-dash/ellipsis cleanup.
  • GuruConnect SPEC-019 (private Backstage GUI desktop) — branch pushed, not merged.
  • Rip-and-replace Tier 1.4 (AV/RMM vendor removal tools) — spec written, not built.
  • Tier 1.5 (headless UI automator) — spec written, not built.
  • Linux/macOS removal — Windows-only today; tracked follow-on.

Reference Information

  • Branch: feat/engine-bcu-improvements (guru-rmm) — latest commit c982352.
  • Merged to guru-rmm main: PR #47 (42681f2c), PR #48 (c4c0ea7).
  • guru-connect branch: feat/spec-019-backstage-uninstall; SPEC-019.
  • Parent commits: 9108f94 (sync fix), 7ad4353 (memory).
  • Specs: projects/msp-tools/guru-rmm/specs/remote-software-uninstall/ (8 docs).
  • Audit report: projects/msp-tools/guru-rmm/reports/2026-06-22-spec030-software-uninstall-audit.md.
  • BCUninstaller (Apache-2.0): https://github.com/BCUninstaller/Bulk-Crap-Uninstaller
  • Engine embed path: server include_str!("../../../agent/scripts/uninstall-engine.ps1").

Update: 19:15 PT — standards audit, fixes, and re-check

Ran a scoped /rmm-audit (3 parallel Opus passes: Rust/API/migrations, TypeScript, engine/platform/security) against the actual GuruRMM standards. Core was compliant (runtime sqlx, idempotent migrations, no unwrap/panic, injection-safe base64 dispatch, audit logging, confirm-gated UI, ASCII engine markers). Fixed all HIGH + MEDIUM: platform-parity (server gates on os_type → 501 non-Windows; dashboard shows live uninstall only on Windows, read-only inventory list elsewhere — per Howard's Windows-only-for-now call); admin-gated the fleet knowledge endpoints; stopped agent-stderr error leakage; paginated the catalog; hardened the self-uninstall guard; surfaced the dashboard knowledge-query error. Then fixed all LOW: warn! on audit-write failure (via an audit() helper), randomized temp filenames, complete TS interfaces, stable list keys + empty-states, ASCII-only strings.

Per Howard's request, ran a fresh Opus re-check of the post-fix branch. It caught a CRITICAL regression introduced by my own round-1 fix: the hardened self-uninstall guard read $Target.install_location, a field the server's UninstallTarget never sends → under Set-StrictMode that throws → engine exit 3 → every bulk uninstall failed, while -List/inventory kept working and masked it. Round-1 live tests passed only because they used -List-shaped targets (which include install_location). Fixed with a StrictMode-safe Get-Prop accessor routed through all $Target.* reads; verified live with a server-shaped target (no install_location): 7-Zip removed, exit 0; GuruRMM self still refused. Re-check also found + fixed a MEDIUM (knowledge classified on attempted tier not outcome → a failed silent attempt mislabeled silent and its log discarded; now classifies on outcome) and a LOW (observe() could null an existing unknown's log → COALESCE). Logged the testing-shape lesson to errorlog (friction): always re-test the destructive path with the actual serialized caller shape.

Update — Configuration Changes (additional, all on feat/engine-bcu-improvements)

  • server/src/api/software.rs — os_type gate, admin-gating, error-leak fixes, pagination, audit() helper, random temp filenames, knowledge-class-by-outcome
  • server/src/db/software_knowledge.rslist() limit param, observe() log COALESCE
  • agent/scripts/uninstall-engine.ps1Get-Prop StrictMode-safe accessor + guard fix
  • dashboard/src/components/InventoryTab.tsx — Windows-only gating (read-only list on other OSs), isWindows prop
  • dashboard/src/components/SoftwareManager.tsx, dashboard/src/api/client.ts — KB error surfacing, ASCII, empty-states, interface completeness
  • dashboard/src/pages/AgentDetail.tsx — pass isWindows to InventoryTab
  • reports/2026-06-22-spec030-software-uninstall-audit.md — audit report + round-2 re-check

Update — Pending / Incomplete Tasks (current)

  • Still NOT merged (prod untouched). Branch feat/engine-bcu-improvements head 0e8323b.
  • Merge + deploy when ready; post-deploy verify the catalog populates + promote an unknown.
  • Follow-ons unchanged: GuruConnect SPEC-019, rip-and-replace Tier 1.4, Tier 1.5, Linux/macOS.

Update — Reference (current)

  • Branch head: 0e8323b (guru-rmm feat/engine-bcu-improvements).
  • Key commits this update: 584c3bc (HIGH/MED), c3e6259 (LOW), 6c8cbb1 (re-audit CRITICAL/MED/LOW), 0e8323b (report round-2). Earlier parent sync log 86c789a.