226 lines
14 KiB
Markdown
226 lines
14 KiB
Markdown
## 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 captured** — `Start-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 failed** — `vault.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.rs` — `list()` limit param, `observe()` log COALESCE
|
|
- `agent/scripts/uninstall-engine.ps1` — `Get-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`.
|