sync: auto-sync from HOWARD-HOME at 2026-06-22 18:54:25
Author: Howard Enos Machine: HOWARD-HOME Timestamp: 2026-06-22 18:54:25
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
# Runbook — Cascades Planned Power Outage (2026-06-23, 05:30–09:00)
|
||||
|
||||
- **Client:** Cascades of Tucson
|
||||
- **Event:** Building-wide electrical work; **no power 05:30–09:00 MST, 2026-06-23**.
|
||||
- **Authored:** 2026-06-22 by Howard Enos (Howard-Home).
|
||||
- **Why this runbook exists:** Avoid a repeat of the **2026-06-17 unplanned outage**, where a
|
||||
*dirty* pfSense power-loss caused duplicate dhcpd + a 2nd-floor switch with one-way L2 forwarding
|
||||
+ a Cox-modem-reboot scramble. This outage is **planned**, so we shut down **clean** and skip all of it.
|
||||
Incident basis: `clients/cascades-tucson/reports/2026-06-17-power-outage-incident.md`.
|
||||
|
||||
## Roles
|
||||
- **Howard (remote):** graceful shutdowns at ~05:20; monitors + remediates bring-up from 09:00.
|
||||
- **John Trozzi (onsite):** physical power-on of CS-SERVER + Synology at ~09:00 once power is confirmed stable.
|
||||
- **Standing rule:** no Cascades prod change without explicit per-change go (memory `feedback_cascades` #4).
|
||||
|
||||
## Decision: CLEAN graceful shutdown, do NOT let the UPS drain
|
||||
- CS-SERVER has a **DEGRADED RAID-1 OS mirror on a single old spindle** — a dirty power-loss is the
|
||||
worst-case data-integrity event. Graceful shutdown is mandatory.
|
||||
- Once everything is soft-off the UPS load collapses, so it may **not** drain in 3.5h → devices won't
|
||||
see a power-loss→restore event → they **stay off**. So we plan on **John powering devices on at 09:00**;
|
||||
AC-recovery/auto-restart settings are only a backstop.
|
||||
|
||||
---
|
||||
|
||||
## PRE-FLIGHT — VERIFIED 2026-06-22 ~18:50 (read-only)
|
||||
|
||||
| Check | Result |
|
||||
|---|---|
|
||||
| CS-SERVER cloud backup last-run | **[OK] Success @ 2026-06-22 00:11** — MSP360 FFI file backup, Full consistency check PASSED, 578.2 GB scanned, 6.81 GB uploaded, **0 failed / 0 errors**. (File-level FFI, not bare-metal image — image-based remains a separate roadmap item.) |
|
||||
| CS-SERVER iDRAC "AC Power Recovery" (backstop) | **[OK] On** (verified via OMSA `omreport chassis biossetup` → "AC Power Recovery Mode: On" — self-boots when AC returns) |
|
||||
| Synology "Restart automatically after power failure" (backstop) | **[OK] Enabled** (DSM API `SYNO.Core.Hardware.PowerRecovery` → `rc_power_config: true`) |
|
||||
| CS-SERVER GuruRMM agent (resolve live) | **[OK]** `c39f1de7-d5b6-45ae-b132-e06977ab1713`, online (last seen 01:46Z 6/23) |
|
||||
| Remote access paths (for the 05:20 shutdowns) | **[OK] pfSense** via OpenVPN tunnel (up 4d, single clean dhcpd). **[OK] Synology** DSM API over tunnel (auth OK, reachable). **[OK] CS-SERVER** via RMM cloud. |
|
||||
| What is on battery-backed UPS outlets (pfSense was on surge-only 6/17 — Mike moved it) | **TODO — John/onsite** confirm pfSense + core/PoE switches on battery side |
|
||||
|
||||
---
|
||||
|
||||
## SHUTDOWN SEQUENCE (start 05:28) — dependents first, gateway last
|
||||
> Start at **05:28**. All three target devices are on the UPS, so the battery carries them through the
|
||||
> 05:30 cut — the graceful shutdowns complete on UPS power even if they run slightly past 5:30. Kick off
|
||||
> CS-SERVER FIRST (slowest to come down); pfSense last.
|
||||
|
||||
1. **CS-SERVER** (192.168.2.254) — graceful Windows shutdown via `rmm` (backup already verified).
|
||||
DC/DNS/DHCP-role/Hyper-V/file+print server — the fragile box (degraded RAID). Best-effort stop
|
||||
Hyper-V VMs first, then shut down:
|
||||
```powershell
|
||||
Get-VM | Where-Object State -eq 'Running' | Stop-VM -Force -ErrorAction SilentlyContinue
|
||||
Stop-Computer -Force
|
||||
```
|
||||
(Dispatch via RMM `command_type: powershell`. Confirm the agent goes offline = down.)
|
||||
2. **Synology cascadesDS** (192.168.0.120) — graceful DSM shutdown via API (auth verified tonight):
|
||||
`SYNO.API.Auth login` → `SYNO.Core.System method=shutdown` (over the OpenVPN tunnel).
|
||||
3. **UniFi switches/APs** — drop with building power (resilient; no special action). Note UPS-backed ones.
|
||||
4. **pfSense** (192.168.0.1) — clean shutdown **last**, so DHCP/routing stays up while the rest drain:
|
||||
`bash .claude/skills/unifi-wifi/scripts/pfsense-ssh.sh cascades-tucson run "shutdown -p now"`
|
||||
|
||||
---
|
||||
|
||||
## POWER-ON SEQUENCE (~09:00, John presses buttons, Howard monitors) — bottom-up
|
||||
|
||||
1. **pfSense first.** Verify single dhcpd and WAN up:
|
||||
- `pgrep -f "dhcpd -user" | wc -l` → must be **1** (not 2 — the 6/17 failure).
|
||||
- WAN up: dpinger WAN_DHCP + WANCOAX_DHCP healthy.
|
||||
- **If WAN does NOT establish → reboot the Cox modem** (the missed post-restore step on 6/17).
|
||||
2. **Switching/APs re-adopt** (core → distribution → APs). UniFi is SLOW here — watch the UOS controller
|
||||
(172.16.3.29, site `va6iba3v`) until **12/12 switches + 77/77 APs** report connected.
|
||||
3. **CS-SERVER** boots → verify AD/DNS, DHCP role, Hyper-V VMs, file + print shares. Then **Synology**.
|
||||
4. **Straggler sweep:** power-cycle any printer/POS/IoT that booted into a DHCP-down window and cached a
|
||||
disconnected state. **Known: kitchen thermal printer (iPad POS ticket printer).** These are invisible
|
||||
to a DHCP scan (they stop requesting) — expect a few "won't connect" reports, each fixed by a power-cycle.
|
||||
|
||||
---
|
||||
|
||||
## WATCH-LIST (the 2026-06-17 casualties — confirm each recovers)
|
||||
- **Switch 2nd Floor #2** (USL24PB, `192.168.2.193`) — broke one-way L2 forwarding last time; floors 2/3/4
|
||||
hang off it. If those floors don't come back → **reset + re-adopt** the switch.
|
||||
- **Duplicate dhcpd** on pfSense — a clean shutdown should prevent it, but verify (step 1 above).
|
||||
- **Cox modem / WAN** — reboot if WAN doesn't re-establish.
|
||||
|
||||
## Access reference
|
||||
- pfSense: `clients/cascades-tucson/pfsense-firewall` · `pfsense-ssh.sh cascades-tucson run "<cmd>"` (logs PLAIN TEXT, not clog).
|
||||
- CS-SERVER: GuruRMM (`rmm`, resolve agent live) / ScreenConnect. iDRAC `192.168.2.65`.
|
||||
- Synology DSM: `http://192.168.0.120:5000` — vault `clients/cascades-tucson/synology-cascadesds`.
|
||||
- UOS controller: `https://172.16.3.29:11443`, site `va6iba3v` / `685f39068e65331c46ef6dd2`.
|
||||
</content>
|
||||
</invoke>
|
||||
@@ -0,0 +1,398 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Dataforth | Shared Drives Reorganization Plan</title>
|
||||
<style>
|
||||
:root{
|
||||
/* warm-paper engineering-report palette, OKLCH, tinted neutrals */
|
||||
--paper: oklch(0.985 0.004 85);
|
||||
--surface: oklch(0.998 0.003 85);
|
||||
--ink: oklch(0.255 0.014 70);
|
||||
--ink-soft: oklch(0.420 0.016 70);
|
||||
--muted: oklch(0.540 0.016 72);
|
||||
--hair: oklch(0.905 0.006 78);
|
||||
--hair-soft:oklch(0.945 0.005 80);
|
||||
--accent: oklch(0.420 0.052 235); /* deep petrol, not corporate royal-blue */
|
||||
--accent-d: oklch(0.330 0.050 238);
|
||||
--ochre: oklch(0.560 0.090 70); /* warm emphasis */
|
||||
|
||||
/* sensitivity roles (meaningful color, kept muted + harmonious) */
|
||||
--dept: oklch(0.470 0.062 165); --dept-bg: oklch(0.965 0.022 165);
|
||||
--restr: oklch(0.480 0.110 30); --restr-bg: oklch(0.960 0.030 35);
|
||||
--common: oklch(0.520 0.080 72); --common-bg:oklch(0.965 0.030 80);
|
||||
--user: oklch(0.460 0.072 330); --user-bg: oklch(0.965 0.022 330);
|
||||
--arch: oklch(0.500 0.010 70); --arch-bg: oklch(0.955 0.005 75);
|
||||
|
||||
--serif: Georgia, "Times New Roman", "Iowan Old Style", serif;
|
||||
--sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||
}
|
||||
*{box-sizing:border-box}
|
||||
html{-webkit-text-size-adjust:100%}
|
||||
body{
|
||||
margin:0; background:var(--paper); color:var(--ink);
|
||||
font:16px/1.62 var(--sans);
|
||||
-webkit-font-smoothing:antialiased; text-rendering:optimizeLegibility;
|
||||
}
|
||||
.wrap{max-width:820px; margin:0 auto; padding:0 30px 84px;}
|
||||
|
||||
/* ---- masthead ---- */
|
||||
.masthead{border-bottom:2px solid var(--ink); padding:40px 0 22px; margin-bottom:8px;}
|
||||
.firm{
|
||||
font:600 12.5px/1 var(--sans); letter-spacing:.26em; text-transform:uppercase;
|
||||
color:var(--accent);
|
||||
}
|
||||
.masthead h1{
|
||||
font:400 35px/1.12 var(--serif); margin:18px 0 0; color:var(--ink);
|
||||
letter-spacing:-.01em; max-width:18ch;
|
||||
}
|
||||
.docline{
|
||||
display:flex; flex-wrap:wrap; gap:6px 22px; margin-top:20px;
|
||||
font-size:13.5px; color:var(--muted);
|
||||
}
|
||||
.docline b{color:var(--ink-soft); font-weight:600;}
|
||||
|
||||
/* ---- lead ---- */
|
||||
.lead{margin:30px 0 14px;}
|
||||
.lead p{font-size:17px; line-height:1.62; color:var(--ink-soft); max-width:68ch;}
|
||||
.lead p.first{font-size:19px; line-height:1.55; color:var(--ink);}
|
||||
.lead strong{color:var(--ink); font-weight:600;}
|
||||
|
||||
.note{
|
||||
background:var(--surface); border:1px solid var(--hair); border-radius:10px;
|
||||
padding:15px 18px; margin:22px 0; max-width:70ch;
|
||||
}
|
||||
.note .lbl{
|
||||
display:block; font:700 11px/1 var(--sans); letter-spacing:.12em; text-transform:uppercase;
|
||||
color:var(--accent); margin-bottom:7px;
|
||||
}
|
||||
.note p{margin:0; font-size:14.5px; color:var(--ink-soft);}
|
||||
|
||||
/* ---- sections ---- */
|
||||
section{margin-top:52px;}
|
||||
.shead{display:flex; align-items:baseline; gap:16px; margin-bottom:6px;}
|
||||
.shead .n{font:400 30px/1 var(--serif); color:var(--accent); min-width:32px;}
|
||||
.shead h2{font:600 22px/1.2 var(--sans); margin:0; letter-spacing:-.01em;}
|
||||
section > p{max-width:68ch; color:var(--ink-soft);}
|
||||
section > p.intro{margin-top:4px;}
|
||||
|
||||
/* ---- tiers (structure) ---- */
|
||||
.tiers{margin-top:24px; border-top:1px solid var(--hair);}
|
||||
.tier{
|
||||
display:grid; grid-template-columns:190px 1fr; gap:26px;
|
||||
padding:20px 0; border-bottom:1px solid var(--hair-soft);
|
||||
}
|
||||
.tier:last-child{border-bottom:1px solid var(--hair);}
|
||||
.tlabel{padding-top:2px;}
|
||||
.tname{display:flex; align-items:center; gap:9px; font:600 16.5px/1.2 var(--sans);}
|
||||
.dot{width:11px; height:11px; border-radius:50%; flex:0 0 auto;}
|
||||
.d-dept{background:var(--dept)} .d-restr{background:var(--restr)}
|
||||
.d-common{background:var(--common)} .d-user{background:var(--user)} .d-arch{background:var(--arch)}
|
||||
.trule{
|
||||
display:inline-block; margin-top:9px; font:700 10.5px/1 var(--sans);
|
||||
letter-spacing:.08em; text-transform:uppercase; padding:4px 9px; border-radius:999px;
|
||||
}
|
||||
.r-dept{color:var(--dept); background:var(--dept-bg)}
|
||||
.r-restr{color:var(--restr); background:var(--restr-bg)}
|
||||
.r-common{color:var(--common); background:var(--common-bg)}
|
||||
.r-user{color:var(--user); background:var(--user-bg)}
|
||||
.r-arch{color:var(--arch); background:var(--arch-bg)}
|
||||
.tdesc{margin:0 0 10px; color:var(--ink-soft); font-size:15px; max-width:60ch;}
|
||||
.chips{display:flex; flex-wrap:wrap; gap:6px;}
|
||||
.chip{
|
||||
font:500 13px/1.3 var(--sans); color:var(--ink-soft);
|
||||
background:var(--surface); border:1px solid var(--hair); border-radius:7px; padding:4px 10px;
|
||||
}
|
||||
.infra{margin-top:22px; color:var(--muted); font-size:13.5px; font-style:italic; max-width:70ch;}
|
||||
|
||||
/* ---- access model ---- */
|
||||
.levels{display:grid; grid-template-columns:1fr 1fr; gap:18px; margin:22px 0 26px; max-width:62ch;}
|
||||
.level h3{font:600 15px/1.2 var(--sans); margin:0 0 4px; display:flex; align-items:center; gap:8px;}
|
||||
.level p{margin:0; font-size:14px; color:var(--muted);}
|
||||
.pill{
|
||||
display:inline-block; font:700 10.5px/1 var(--sans); letter-spacing:.05em;
|
||||
padding:4px 8px; border-radius:6px;
|
||||
}
|
||||
.pill-rw{background:var(--accent); color:var(--paper);}
|
||||
.pill-ro{background:transparent; color:var(--accent); box-shadow:inset 0 0 0 1.5px var(--accent);}
|
||||
|
||||
table{width:100%; border-collapse:collapse; margin:14px 0 8px; font-size:13.5px;}
|
||||
caption{caption-side:top; text-align:left; font-size:13.5px; color:var(--ink-soft);
|
||||
max-width:66ch; margin-bottom:10px;}
|
||||
th,td{padding:9px 6px; border-bottom:1px solid var(--hair-soft); text-align:center;}
|
||||
thead th{
|
||||
font:700 11px/1.3 var(--sans); letter-spacing:.02em; text-transform:uppercase;
|
||||
color:var(--muted); border-bottom:1.5px solid var(--hair); vertical-align:bottom;
|
||||
}
|
||||
tbody th{text-align:left; font:600 13.5px/1.3 var(--sans); color:var(--ink); white-space:nowrap;}
|
||||
tbody tr:hover td, tbody tr:hover th{background:var(--surface);}
|
||||
.cell-rw{color:var(--accent-d); font-weight:700;}
|
||||
.cell-ro{color:var(--muted); font-weight:600;}
|
||||
.cell-no{color:var(--hair);}
|
||||
.cell-x{color:var(--restr); font-weight:600; font-size:12px;}
|
||||
.legend{font-size:12.5px; color:var(--muted); margin-top:4px;}
|
||||
.legend b{color:var(--ink-soft);}
|
||||
|
||||
/* ---- asks ---- */
|
||||
.asks{margin-top:24px; counter-reset:ask; max-width:70ch;}
|
||||
.ask{
|
||||
display:grid; grid-template-columns:34px 1fr; gap:14px; padding:15px 0;
|
||||
border-top:1px solid var(--hair-soft);
|
||||
}
|
||||
.ask:first-child{border-top:1px solid var(--hair);}
|
||||
.ask::before{
|
||||
counter-increment:ask; content:counter(ask);
|
||||
font:400 20px/1 var(--serif); color:var(--ochre); padding-top:2px;
|
||||
}
|
||||
.ask p{margin:0; font-size:15px; color:var(--ink-soft);}
|
||||
.ask b{color:var(--ink); font-weight:600;}
|
||||
.closing{
|
||||
margin-top:26px; padding:18px 20px; background:var(--surface);
|
||||
border:1px solid var(--hair); border-radius:10px; max-width:70ch;
|
||||
}
|
||||
.closing p{margin:0; font-size:15px; color:var(--ink-soft);}
|
||||
.closing strong{color:var(--ink); font-weight:600;}
|
||||
|
||||
footer{
|
||||
margin-top:54px; padding-top:18px; border-top:1px solid var(--hair);
|
||||
font-size:12.5px; color:var(--muted); line-height:1.7;
|
||||
}
|
||||
footer b{color:var(--ink-soft); font-weight:600;}
|
||||
|
||||
a{color:var(--accent); text-underline-offset:2px;}
|
||||
:focus-visible{outline:2px solid var(--accent); outline-offset:2px; border-radius:3px;}
|
||||
|
||||
@media (max-width:660px){
|
||||
.wrap{padding:0 20px 60px;}
|
||||
.masthead h1{font-size:28px;}
|
||||
.tier{grid-template-columns:1fr; gap:12px;}
|
||||
.levels{grid-template-columns:1fr;}
|
||||
.table-scroll{overflow-x:auto;}
|
||||
table{min-width:560px;}
|
||||
}
|
||||
@media (prefers-reduced-motion:no-preference){
|
||||
tbody tr td, tbody tr th{transition:background .18s cubic-bezier(.2,.7,.3,1);}
|
||||
}
|
||||
@media print{
|
||||
body{background:#fff;}
|
||||
.wrap{max-width:none; padding:0;}
|
||||
.note,.closing,.chip,.surface{-webkit-print-color-adjust:exact; print-color-adjust:exact;}
|
||||
section{break-inside:avoid;}
|
||||
.tier,.ask{break-inside:avoid;}
|
||||
a{color:var(--ink);}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrap">
|
||||
|
||||
<header class="masthead">
|
||||
<div class="firm">Arizona Computer Guru</div>
|
||||
<h1>Shared Drives Reorganization & Access Plan</h1>
|
||||
<div class="docline">
|
||||
<span>Prepared for <b>Dataforth Corporation</b></span>
|
||||
<span><b>Draft</b> for review</span>
|
||||
<span>June 2026</span>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="lead">
|
||||
<p class="first">Today, every shared drive at Dataforth is open to every employee. Anyone
|
||||
who logs in can open, change, or delete anything, including Payroll, OSHA records,
|
||||
Purchase Orders, and the accounting files.</p>
|
||||
<p>This plan reorganizes those drives so each <strong>department</strong> sees only what it
|
||||
needs, the sensitive areas are locked down, and access stays simple to manage. The
|
||||
encouraging part: your drives are <strong>already arranged by department</strong>. We are
|
||||
largely tidying that structure and adding the access controls that should have been there
|
||||
all along.</p>
|
||||
</div>
|
||||
|
||||
<div class="note">
|
||||
<span class="lbl">How to read this</span>
|
||||
<p>Section 1 is the proposed folder layout. Section 2 is who would get access. Section 3 is
|
||||
the short list of things we need from you to finalize it. Nothing on your systems changes
|
||||
until you approve the plan.</p>
|
||||
</div>
|
||||
|
||||
<!-- 1 -->
|
||||
<section>
|
||||
<div class="shead"><span class="n">1</span><h2>Proposed folder layout</h2></div>
|
||||
<p class="intro">Everything would sit under one clear, consistent structure. You still reach
|
||||
your files the same way (your familiar mapped drives can stay). This is about how folders
|
||||
are grouped, and who can open them.</p>
|
||||
|
||||
<div class="tiers">
|
||||
|
||||
<div class="tier">
|
||||
<div class="tlabel">
|
||||
<div class="tname"><span class="dot d-dept"></span>Departments</div>
|
||||
<span class="trule r-dept">Team access</span>
|
||||
</div>
|
||||
<div class="tcontent">
|
||||
<p class="tdesc">Each team's working files. People see their own department; another
|
||||
department is added only when there is a reason to.</p>
|
||||
<div class="chips">
|
||||
<span class="chip">Engineering & Test Engineering</span>
|
||||
<span class="chip">Manufacturing / Production</span>
|
||||
<span class="chip">Quality / Calibration</span>
|
||||
<span class="chip">Sales & Marketing</span>
|
||||
<span class="chip">Shipping / Receiving</span>
|
||||
<span class="chip">Purchasing</span>
|
||||
<span class="chip">IT</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tier">
|
||||
<div class="tlabel">
|
||||
<div class="tname"><span class="dot d-restr"></span>Restricted</div>
|
||||
<span class="trule r-restr">Named people only</span>
|
||||
</div>
|
||||
<div class="tcontent">
|
||||
<p class="tdesc">Sensitive data, walled off from general staff. Only specific people
|
||||
(HR, Finance, management) are granted access.</p>
|
||||
<div class="chips">
|
||||
<span class="chip">Accounting & Finance (Sage, QuickBooks, invoices)</span>
|
||||
<span class="chip">Payroll</span>
|
||||
<span class="chip">HR</span>
|
||||
<span class="chip">OSHA / Safety records</span>
|
||||
<span class="chip">Purchase Orders</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tier">
|
||||
<div class="tlabel">
|
||||
<div class="tname"><span class="dot d-common"></span>Company-Wide</div>
|
||||
<span class="trule r-common">Everyone: view</span>
|
||||
</div>
|
||||
<div class="tcontent">
|
||||
<p class="tdesc">Shared resources everyone can read, with editing limited to the
|
||||
owners so nothing gets changed by accident.</p>
|
||||
<div class="chips">
|
||||
<span class="chip">Forms</span>
|
||||
<span class="chip">Policies</span>
|
||||
<span class="chip">Templates</span>
|
||||
<span class="chip">Scanned Documents</span>
|
||||
<span class="chip">General Documents</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tier">
|
||||
<div class="tlabel">
|
||||
<div class="tname"><span class="dot d-user"></span>User Folders</div>
|
||||
<span class="trule r-user">Private</span>
|
||||
</div>
|
||||
<div class="tcontent">
|
||||
<p class="tdesc">A private home folder per employee. Only that person and IT can see
|
||||
inside. This replaces the loose person-named folders scattered across the drives today.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tier">
|
||||
<div class="tlabel">
|
||||
<div class="tname"><span class="dot d-arch"></span>Archive</div>
|
||||
<span class="trule r-arch">Read-only history</span>
|
||||
</div>
|
||||
<div class="tcontent">
|
||||
<p class="tdesc">Old engineering archives and material from former staff, kept for
|
||||
reference, read-only, out of everyone's daily view.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<p class="infra">Behind-the-scenes systems stay exactly where they are so nothing breaks:
|
||||
the DOS test stations, the website datasheet system, the IT software library, and the live
|
||||
Sage accounting database. We handle those separately.</p>
|
||||
</section>
|
||||
|
||||
<!-- 2 -->
|
||||
<section>
|
||||
<div class="shead"><span class="n">2</span><h2>Who would get access</h2></div>
|
||||
<p class="intro">Access is granted by <strong>department group</strong>, not person by
|
||||
person. We add an employee to their department group and they immediately get the right
|
||||
folders; if they change teams, we move the membership. There are two simple levels:</p>
|
||||
|
||||
<div class="levels">
|
||||
<div class="level">
|
||||
<h3><span class="pill pill-rw">RW</span> Read / Write</h3>
|
||||
<p>Open, edit, and save files in their department's folders.</p>
|
||||
</div>
|
||||
<div class="level">
|
||||
<h3><span class="pill pill-ro">RO</span> Read-Only</h3>
|
||||
<p>View files another department owns, without changing them.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-scroll">
|
||||
<table>
|
||||
<caption>Our <b>starting assumption</b>: each department owns its own area, and the
|
||||
sensitive folders are restricted. This is the grid we would like you to confirm or
|
||||
correct in Section 3.</caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="text-align:left">Department</th>
|
||||
<th>Engr</th><th>Mfg</th><th>Quality</th><th>Sales</th>
|
||||
<th>Shipping</th><th>Purch</th><th>Company-Wide</th><th>Restricted</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr><th>Engineering</th><td class="cell-rw">RW</td><td class="cell-ro">RO</td><td class="cell-ro">RO</td><td class="cell-no">·</td><td class="cell-no">·</td><td class="cell-no">·</td><td class="cell-ro">RO</td><td class="cell-no">·</td></tr>
|
||||
<tr><th>Manufacturing / Prod.</th><td class="cell-ro">RO</td><td class="cell-rw">RW</td><td class="cell-ro">RO</td><td class="cell-no">·</td><td class="cell-ro">RO</td><td class="cell-no">·</td><td class="cell-ro">RO</td><td class="cell-no">·</td></tr>
|
||||
<tr><th>Quality / Calibration</th><td class="cell-ro">RO</td><td class="cell-ro">RO</td><td class="cell-rw">RW</td><td class="cell-no">·</td><td class="cell-no">·</td><td class="cell-no">·</td><td class="cell-ro">RO</td><td class="cell-no">·</td></tr>
|
||||
<tr><th>Sales & Marketing</th><td class="cell-no">·</td><td class="cell-no">·</td><td class="cell-no">·</td><td class="cell-rw">RW</td><td class="cell-ro">RO</td><td class="cell-no">·</td><td class="cell-ro">RO</td><td class="cell-no">·</td></tr>
|
||||
<tr><th>Shipping / Receiving</th><td class="cell-no">·</td><td class="cell-ro">RO</td><td class="cell-no">·</td><td class="cell-ro">RO</td><td class="cell-rw">RW</td><td class="cell-no">·</td><td class="cell-ro">RO</td><td class="cell-no">·</td></tr>
|
||||
<tr><th>Purchasing</th><td class="cell-no">·</td><td class="cell-no">·</td><td class="cell-no">·</td><td class="cell-no">·</td><td class="cell-no">·</td><td class="cell-rw">RW</td><td class="cell-ro">RO</td><td class="cell-x">PO only</td></tr>
|
||||
<tr><th>Accounting / Finance</th><td class="cell-no">·</td><td class="cell-no">·</td><td class="cell-no">·</td><td class="cell-no">·</td><td class="cell-no">·</td><td class="cell-ro">RO</td><td class="cell-ro">RO</td><td class="cell-rw">RW</td></tr>
|
||||
<tr><th>HR / Payroll</th><td class="cell-no">·</td><td class="cell-no">·</td><td class="cell-no">·</td><td class="cell-no">·</td><td class="cell-no">·</td><td class="cell-no">·</td><td class="cell-ro">RO</td><td class="cell-rw">RW</td></tr>
|
||||
<tr><th>IT</th><td class="cell-ro">RO</td><td class="cell-ro">RO</td><td class="cell-ro">RO</td><td class="cell-ro">RO</td><td class="cell-ro">RO</td><td class="cell-ro">RO</td><td class="cell-rw">RW</td><td class="cell-x">by request</td></tr>
|
||||
<tr><th>Management / Exec</th><td class="cell-ro">RO</td><td class="cell-ro">RO</td><td class="cell-ro">RO</td><td class="cell-ro">RO</td><td class="cell-ro">RO</td><td class="cell-ro">RO</td><td class="cell-ro">RO</td><td class="cell-x">as needed</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<p class="legend"><b>RW</b> read & write · <b>RO</b> read-only
|
||||
· <b>·</b> no access. Restricted covers Payroll, OSHA, Accounting,
|
||||
and Purchase Orders.</p>
|
||||
</section>
|
||||
|
||||
<!-- 3 -->
|
||||
<section>
|
||||
<div class="shead"><span class="n">3</span><h2>What we need from you</h2></div>
|
||||
<p class="intro">A few answers let us finalize the plan and build it. A short call works
|
||||
too if that is easier.</p>
|
||||
|
||||
<div class="asks">
|
||||
<div class="ask"><p><b>Confirm the departments</b> in Section 1. Add, remove, or rename
|
||||
any that are off.</p></div>
|
||||
<div class="ask"><p><b>Confirm or correct the access grid</b> in Section 2 (who gets
|
||||
Read/Write, Read-Only, or no access for each area).</p></div>
|
||||
<div class="ask"><p><b>Name the people for the sensitive areas.</b> Exactly who should
|
||||
see Payroll, OSHA records, Purchase Orders, and Accounting? This usually needs HR and
|
||||
Finance sign-off.</p></div>
|
||||
<div class="ask"><p><b>Department rosters.</b> Which employees are in which department.
|
||||
An existing org chart or staff list is perfect.</p></div>
|
||||
<div class="ask"><p><b>Cleanup.</b> Are there folders (the "do not use" ones, old
|
||||
per-person folders) you already know are safe to archive or remove?</p></div>
|
||||
<div class="ask"><p><b>Exceptions.</b> Anyone who needs cross-department access, plus any
|
||||
contractors or outside parties.</p></div>
|
||||
</div>
|
||||
|
||||
<div class="closing">
|
||||
<p>Once we have this, we send back a final "who sees what" map for your sign-off, then
|
||||
implement it <strong>in stages</strong> so nobody loses access unexpectedly.
|
||||
<strong>Nothing changes until you approve the plan.</strong></p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<footer>
|
||||
<b>Arizona Computer Guru</b> · Prepared for Dataforth Corporation
|
||||
· Draft, June 2026<br>
|
||||
Questions? Reply to our email or call. We are glad to walk through it on a quick call.
|
||||
</footer>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,198 @@
|
||||
# Dataforth — Proposed Target Folder Structure (DRAFT / strawman)
|
||||
|
||||
**By:** ACG (Howard) · **Date:** 2026-06-22 · **Status:** DRAFT — pre-client-input
|
||||
**Inputs:** inferred from existing shares + folder contents in
|
||||
[current-state-2026-06-10.md](./current-state-2026-06-10.md),
|
||||
[acl-audit-detail-2026-06-10.md](./acl-audit-detail-2026-06-10.md), and the ENGR
|
||||
exploration notes. Refine against Dataforth's access matrix (Phase 1 reply) before sign-off.
|
||||
|
||||
> Purpose: lay out as much of the Phase 2 target-state design as we can **from the data
|
||||
> we already have** — the way Dataforth has their shares arranged today already tells us
|
||||
> their departments and data domains. This maps the current sprawl onto the common
|
||||
> departmental-share pattern. Nothing here is implemented; it is the proposal we hand the
|
||||
> client (simplified) for confirmation.
|
||||
|
||||
---
|
||||
|
||||
## 1. What today's layout tells us (departments inferred from the data)
|
||||
|
||||
Their existing shares/folders are effectively **organized by department already** — just
|
||||
spread across eight shares with no access control. Reading the structure backwards gives us
|
||||
a strong starting department list:
|
||||
|
||||
| Evidence in current shares/folders | Implied department / domain |
|
||||
|---|---|
|
||||
| `Engineering` (B:), `e-drive` ENGR/ECO'S/FMEA/TE, `archive` (Y:), ATE/DESIGN/Project Reports | **Engineering** (+ Test Engineering sub) |
|
||||
| c-drive Manufacturing / Production Control / SMT; e-drive MANUFACT | **Manufacturing / Production** |
|
||||
| FMEA, ECO'S, Test Equipment, calibration/ATE | **Quality / Calibration** |
|
||||
| `sales` (W:) — marketing, contacts, RMAs, shipping handoffs | **Sales & Marketing** |
|
||||
| c-drive Shipping; sales shipping handoffs | **Shipping / Receiving** |
|
||||
| c-drive Purchasing, **Purchase Orders** | **Purchasing** |
|
||||
| `sage` (S:), e-drive **QBfiles**, invoices, financial reports | **Accounting / Finance** (restricted) |
|
||||
| c-drive **Payroll** | **Payroll / HR** (restricted) |
|
||||
| c-drive **OSHA 300 / OSHA Safety Training** | **HR / Safety** (restricted) |
|
||||
| `itsvc`, `webshare` (datasheet automation) | **IT** (+ app/infra) |
|
||||
| Person-named + "Do not use" folders across c-drive/sales | legacy → **Archive / cleanup** |
|
||||
|
||||
Departments we can confidently propose: **Engineering, Manufacturing/Production,
|
||||
Quality/Calibration, Sales & Marketing, Shipping/Receiving, Purchasing, Accounting/Finance,
|
||||
HR/Payroll, IT, Management/Exec.** (Matches the discovery-email starter list — the existing
|
||||
data corroborates it.)
|
||||
|
||||
---
|
||||
|
||||
## 2. Target structure — the "north star" (consolidated departmental share)
|
||||
|
||||
The standard pattern: **one logical tree**, departmental subfolders, a broken-inheritance
|
||||
**Restricted** branch for sensitive data, a read-mostly **Company-Wide** area, per-user
|
||||
**Users** home folders, and a read-only **Archive**. Access-Based Enumeration (ABE) on so
|
||||
people only see what they can open.
|
||||
|
||||
```
|
||||
Company\ (one tree; can stay multi-drive-letter mapped — see §4)
|
||||
|
|
||||
+-- Departments\
|
||||
| +-- Engineering\ ENGR, ECO'S, FMEA, DESIGN, Project Reports, MTBF, LABEL
|
||||
| | +-- Test-Engineering\ ATE, Test Equipment, TESTLOGS, Tester Notebooks
|
||||
| | +-- Custom-Products\
|
||||
| +-- Manufacturing\ Production Control, SMT, MANUFACT, Scanned (mfg travelers)
|
||||
| +-- Quality\ FMEA (quality copy), Calibration, Test Equipment records
|
||||
| +-- Sales-Marketing\ contacts, RMAs, videos, weekly updates, marketing assets
|
||||
| +-- Shipping-Receiving\ shipping handoffs, packing/labels
|
||||
| +-- Purchasing\ vendor files, (Purchase Orders -> see Restricted)
|
||||
| +-- IT\ tools/notes (software depot stays in ITSvc, see §5)
|
||||
|
|
||||
+-- Restricted\ (inheritance BROKEN; no Domain Users; per-area groups)
|
||||
| +-- Accounting-Finance\ Sage data refs, invoices, financial reports, QBfiles
|
||||
| +-- Payroll\ (from c-drive Payroll)
|
||||
| +-- HR\ personnel, policies-confidential
|
||||
| +-- OSHA\ OSHA 300, Safety Training records
|
||||
| +-- Purchase-Orders\ (from c-drive — finance-sensitive)
|
||||
|
|
||||
+-- Company-Wide\ (all staff: Read; limited Write groups)
|
||||
| +-- Forms\
|
||||
| +-- Policies\ (non-confidential, published)
|
||||
| +-- Templates\
|
||||
| +-- Scanned-Documents\ (general intake; mfg-specific -> Manufacturing)
|
||||
| +-- Documents\ (general company docs from c-drive)
|
||||
|
|
||||
+-- Users\ (per-user home folders; only owner + admins)
|
||||
|
|
||||
+-- Archive\ (read-only historical; legacy + "Do not use" landing zone)
|
||||
+-- Engineering-Archive\ (current Y: archive)
|
||||
+-- Former-Staff\ (person-named folders pending cleanup decision)
|
||||
```
|
||||
|
||||
**App / infra shares stay OUT of this tree** and are handled case-by-case (§5).
|
||||
|
||||
---
|
||||
|
||||
## 3. Where each current share/folder lands (migration map)
|
||||
|
||||
| Today | Target location | Notes |
|
||||
|---|---|---|
|
||||
| Q: c-drive \ Documents | `Company-Wide\Documents` | confirm any dept-specific subfolders |
|
||||
| Q: c-drive \ Manufacturing, Production Control, SMT | `Departments\Manufacturing` | |
|
||||
| Q: c-drive \ Shipping | `Departments\Shipping-Receiving` | |
|
||||
| Q: c-drive \ Purchasing | `Departments\Purchasing` | |
|
||||
| Q: c-drive \ Scanned Documents | `Company-Wide\Scanned-Documents` | split mfg travelers to Manufacturing if needed |
|
||||
| Q: c-drive \ **Payroll** | `Restricted\Payroll` | broken inheritance, HR/Payroll group only |
|
||||
| Q: c-drive \ **OSHA 300 / OSHA Safety Training** | `Restricted\OSHA` | HR/Safety group only |
|
||||
| Q: c-drive \ **Purchase Orders** | `Restricted\Purchase-Orders` | Purchasing + Finance only |
|
||||
| Q: c-drive \ person-named / "Do not use" | `Archive\Former-Staff` | after migration-gap audit clears |
|
||||
| T: e-drive \ ENGR, ECO'S, FMEA | `Departments\Engineering` | |
|
||||
| T: e-drive \ Test Engineering (TE) | `Departments\Engineering\Test-Engineering` | |
|
||||
| T: e-drive \ MANUFACT | `Departments\Manufacturing` | dedupe vs c-drive Manufacturing |
|
||||
| T: e-drive \ **QBfiles** (QuickBooks) | `Restricted\Accounting-Finance` | get it off the open eng drive |
|
||||
| S: sage (Sage ERP) | `Restricted\Accounting-Finance` (refs) | **app paths stay put — see §5 caution** |
|
||||
| W: sales | `Departments\Sales-Marketing` | shipping handoffs -> Shipping-Receiving subfolder or shared |
|
||||
| Y: archive (ENGR archive) | `Archive\Engineering-Archive` | read-only |
|
||||
| B: Engineering (ENGR: ATE/DESIGN/etc.) | `Departments\Engineering` (+ Test-Engineering) | **largest store; AD1 C: ~90% full — destination decision needed** |
|
||||
| itsvc | stays `ITSvc` (IT depot) | not in dept tree; §5 |
|
||||
| X: webshare | stays `webshare` | app/automation; preserve `svc_testdatadb`; §5 |
|
||||
| test | stays `test` | DOS/SMB1 — untouched, excluded |
|
||||
|
||||
---
|
||||
|
||||
## 4. Drive-letter strategy (keep habits, change permissions)
|
||||
|
||||
Two ways to deliver the structure above:
|
||||
|
||||
- **Option A — Keep current drive letters (recommended for phase 1 of rollout).** Leave
|
||||
Q/S/T/W/Y/B mapped where they are; reorganize folders *within* each share and apply
|
||||
department groups. Lowest disruption, no app/path breakage, no retraining. The
|
||||
"Company / Departments / Restricted" tree is realized *logically* across the existing
|
||||
shares rather than physically consolidated on day one.
|
||||
- **Option B — Consolidate to one mapped drive** (e.g. one `Company` share, ABE on, single
|
||||
letter) once apps and muscle-memory allow. Cleaner long-term, but risks hard-coded UNC
|
||||
paths (DOS, Sage, datasheet pipeline, GageTrak/Epicor shortcuts) and user retraining.
|
||||
|
||||
**Recommendation:** ship **Option A** structure + groups first (safe, reversible), hold
|
||||
**Option B** consolidation as a later optional phase after the app-path audit. Either way the
|
||||
*permission model is identical* — only the physical/mapping layout differs.
|
||||
|
||||
---
|
||||
|
||||
## 5. Excluded app / infra shares (do NOT fold into the dept tree)
|
||||
|
||||
- `test` (AD2) — DOS test stations, SMB1 + Guest:Read. **Leave exactly as-is.**
|
||||
- `webshare` (AD2) — datasheet automation. **Preserve `svc_testdatadb:Full`**; restrict
|
||||
human access to IT/Engineering; do not move paths.
|
||||
- `ITSvc` (AD1) — IT software depot. Keep `Domain Computers:Read` (deployment); IT-RW.
|
||||
- `sage` app data (SAGE-SQL) — Sage ERP reads/writes here; **do not relocate the live data
|
||||
path.** Restrict via group at the share, but keep the UNC stable for the app/SQL.
|
||||
- `NETLOGON` / `SYSVOL` — never touch.
|
||||
|
||||
---
|
||||
|
||||
## 6. AD security groups this implies (naming `SG-<Resource>-<RW|RO>`)
|
||||
|
||||
Derived directly from the structure above — RW for the owning dept, RO where another dept
|
||||
needs visibility (confirm RO grants with the client matrix):
|
||||
|
||||
```
|
||||
SG-Engineering-RW SG-Engineering-RO
|
||||
SG-Manufacturing-RW SG-Manufacturing-RO
|
||||
SG-Quality-RW SG-Quality-RO
|
||||
SG-Sales-RW SG-Sales-RO
|
||||
SG-Shipping-RW SG-Shipping-RO
|
||||
SG-Purchasing-RW SG-Purchasing-RO
|
||||
SG-IT-RW
|
||||
SG-Accounting-RW SG-Accounting-RO (Restricted\Accounting-Finance)
|
||||
SG-Payroll-RW (Restricted\Payroll)
|
||||
SG-HR-RW (Restricted\HR, OSHA)
|
||||
SG-PurchaseOrders-RW SG-PurchaseOrders-RO (Purchasing + Finance)
|
||||
SG-CompanyWide-RW (everyone = RO by default via Authenticated Users:Read)
|
||||
```
|
||||
|
||||
- Users get **Modify** via the RW group (never Full); SYSTEM/Administrators keep Full.
|
||||
- Restricted branch: **no `Domain Users`**, inheritance broken, only the named group.
|
||||
- Management/Exec cross-access handled by adding execs to the RO groups they need (not by
|
||||
re-opening shares).
|
||||
|
||||
---
|
||||
|
||||
## 7. What still needs the client (gates Phase 2 sign-off)
|
||||
|
||||
This draft fills in everything inferable from the existing layout. Still **must come from
|
||||
Dataforth** before build:
|
||||
|
||||
1. **Confirm the department list** (we inferred it; they validate).
|
||||
2. **The access matrix** — for each department, RW / RO / none per area (the grid in the
|
||||
discovery email). Our map above assumes "owning dept RW, others none" except where noted.
|
||||
3. **Sensitive-data named access** — exactly who sees Payroll, OSHA, POs, Accounting (likely
|
||||
HR/Finance sign-off, not just Dan).
|
||||
4. **Rosters** — who is in each department (to populate groups).
|
||||
5. **Cleanup approval** — which person-named / "Do not use" folders archive vs delete.
|
||||
6. **Engineering destination** — AD1 C: ~90% full; the big ENGR store needs a target volume
|
||||
before any restructure/consolidation.
|
||||
|
||||
---
|
||||
|
||||
## 8. Sequencing note
|
||||
|
||||
This slots into **Phase 2 (Target-state design)** of [roadmap.md](./roadmap.md). It is the
|
||||
strawman to (a) sanity-check internally and (b) simplify into the client sign-off doc once
|
||||
the Phase 1 matrix arrives. Build order stays lowest-risk-first
|
||||
(archive -> sales -> c-drive/e-drive -> Engineering -> Restricted last), additive groups
|
||||
first, remove `Everyone`/`Domain Users` only after pilot validation.
|
||||
@@ -17,6 +17,12 @@ Categories (the `[type]` tag): _(none)_ = skill/command execution failure ·
|
||||
|
||||
<!-- Append entries below this line -->
|
||||
|
||||
2026-06-22 | Howard-Home | gururmm/uninstall-engine | [correction] assumed AnyDesk needs remote removal; it has UninstallString '...AnyDesk.exe --uninstall' and supports --silent, so it is silently removable -- added vendor rule
|
||||
|
||||
2026-06-22 | Howard-Home | bash/json | [friction] hand-built JSON literal with C: backslashes collapsed to single backslash in Git-Bash (invalid JSON, ConvertFrom-Json failed); fix: build JSON with jq --arg / extract from existing valid json
|
||||
|
||||
2026-06-22 | Howard-Home | gitea/pr-api | PR create returned 'invalid token'; vault.sh get-field services/gitea credentials.api-token returned 4 chars (wrong field resolution) [ctx: repo=gururmm endpoint=172.16.3.20:3000]
|
||||
|
||||
2026-06-22 | Howard-Home | sync/submodules | [friction] Phase-3 'git submodule update --init --recursive' reset guru-rmm submodule to pinned commit, discarding feature branch + commits mid-build; fix: submodule_update_safe() skips branch/dirty submodules [ctx: ref=sync.sh:525 fixed]
|
||||
|
||||
2026-06-22 | Howard-Home | save/wiki-compile | [friction] /save Phase 3 emits 'project:guru-rmm' (from submodule dir name) but canonical wiki article is 'gururmm'; guru-rmm.md is a tombstone redirect. Map guru-rmm -> gururmm in the slug derivation. [ctx: ref=wiki-slug-tombstone proj=guru-rmm]
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
## 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")`.
|
||||
Reference in New Issue
Block a user