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.tsxin 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
powershellpipeline, 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_commandsconvention.
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 --recursiveunconditionally. Fixed withsubmodule_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 --silenthung ~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 captured —
Start-Process -PassThrureturned.ExitCodenull, so every uninstall mapped to exit 0 / false "success" (a failed MSI 1603 would read as removed). Switched toSystem.Diagnostics.Processwith 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. Fixedbuild-server.shto also watchagent/scripts/uninstall-engine.ps1. - Git-Bash
curlstarted failing "Permission denied" (AV/EDR on the workstation after many calls). Pivoted RMM API calls to PowerShellInvoke-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 withjq/ extracting from already-valid JSON. Logged as friction. - PR auto-create failed —
vault.sh get-field services/gitea credentials.api-tokenmis-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, paginationserver/src/api/mod.rs— routesserver/src/db/software_removal.rs,server/src/db/software_knowledge.rs,db/mod.rsserver/migrations/061_software_removal_attempts.sql,062_software_knowledge.sqldashboard/src/api/client.ts,dashboard/src/components/SoftwareManager.tsx,dashboard/src/components/InventoryTab.tsx,dashboard/src/pages/AgentDetail.tsxdeploy/build-pipeline/build-server.sh— change-gate watches the embedded enginespecs/remote-software-uninstall/— plan, shape, references, standards, task1-results, bcu-research-and-tiers, knowledge-base-design, rip-and-replace-removal-toolsreports/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.yamlfieldscredentials.gururmm-api.admin-email/admin-password(used for API auth during testing). Gitea API token atservices/giteafieldcredentials.api-token(used for PR create/merge). Temp credential files written under.claude/tmp/during testing were shredded;.claude/tmpis gitignored.
Infrastructure & Servers
- GuruRMM API/server:
http://172.16.3.30:3001(prod; also the build host, userguru, 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(repoazcomputerguru/gururmm,azcomputerguru/guru-connect). Public hostgit.azcomputerguru.comis 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 commitc982352. - 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-outcomeserver/src/db/software_knowledge.rs—list()limit param,observe()log COALESCEagent/scripts/uninstall-engine.ps1—Get-PropStrictMode-safe accessor + guard fixdashboard/src/components/InventoryTab.tsx— Windows-only gating (read-only list on other OSs),isWindowspropdashboard/src/components/SoftwareManager.tsx,dashboard/src/api/client.ts— KB error surfacing, ASCII, empty-states, interface completenessdashboard/src/pages/AgentDetail.tsx— passisWindowsto InventoryTabreports/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-improvementshead0e8323b. - 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-rmmfeat/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 log86c789a.