sync: auto-sync from GURU-5070 at 2026-05-29 13:48:45
Author: Mike Swanson Machine: GURU-5070 Timestamp: 2026-05-29 13:48:45
This commit is contained in:
@@ -224,3 +224,106 @@ manifest versions now start at 0.2.2 (legitimate first signed release).
|
|||||||
### Reference Information
|
### Reference Information
|
||||||
- GC release commits: `e7f38ce`/`5727ccf` (jsign 7.1 + verify fix). Release run #22 green. Release `v0.2.2`.
|
- GC release commits: `e7f38ce`/`5727ccf` (jsign 7.1 + verify fix). Release run #22 green. Release `v0.2.2`.
|
||||||
- claudetools: `…70d2190` (submodule bump for verify fix).
|
- claudetools: `…70d2190` (submodule bump for verify fix).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Update: 20:47 PT — Birth Biologic SmartBadge correction, GuruRMM 0.3.36 deploy, alert re-routing, Rednour onboarding + onboarding diagnostic
|
||||||
|
|
||||||
|
## User
|
||||||
|
- **User:** Mike Swanson (mike)
|
||||||
|
- **Machine:** GURU-5070
|
||||||
|
- **Role:** admin
|
||||||
|
|
||||||
|
### Session Summary
|
||||||
|
|
||||||
|
Began with Birth Biologic: Kristin Steen (KSTEENBB2025) could not use the Datto SmartBadge Excel add-in. RMM recon against her machine and the working reference (EVO-X1) showed the 2026-05-28 fix had gone the wrong direction — it removed the newer Datto Workplace v10.53.4 (Workplace2, the fleet standard) and left the older Workplace Desktop v8.50.13. Corrected it: Mike Revo-uninstalled v8 (clean, swept the `{2B96EDC1}` CLSID + non-`_CC` add-in keys), then via RMM installed Workplace v10.53.4 from her Downloads, aligned the SmartBadge `_CC` CLSID/add-in to EVO-X1, and cleared her stuck per-user `LoadBehavior=2`. Verified byte-for-byte against EVO-X1. Filed Syncro #32339 public tech notes + 1hr warranty, and stood up a 7-day daily verification (scheduled task on GURU-5070 + coord todo).
|
||||||
|
|
||||||
|
GuruRMM Integration Center ("Store") planning: confirmed it is greenfield (MSP360 built, Syncro/catalog not). Locked five design decisions (partner-scoped, generic JSONB config storage with MSP360 migrated in, catalog + Syncro built together, code-defined plugin registry, AES-256-GCM reuse) and landed a revised SPEC-005 as gururmm PR #28. Parked per Mike; tracked in coord todo. Mike also questioned why site API keys are published when enrollment correlates by site_id — code review confirmed the modern `POST /enroll` gate is site_id-only and the site api_key is used only by the legacy WS path + install-info; filed as a security/arch decision.
|
||||||
|
|
||||||
|
Resolved Howard's blocker: the gururmm server on 172.16.3.30 was still running 0.3.32 despite an 18:51 restart. Root cause — the 0.3.36 binary had been copied to `/usr/local/bin/gururmm-server`, but the systemd `ExecStart` is `/opt/gururmm/gururmm-server`, which was still yesterday's 0.3.32. Copied 0.3.36 to the correct path, restarted, verified `/status` = 0.3.36 and command flow (test command to ACG-DC16 returned completed/exit 0). Replied to Howard on coord.
|
||||||
|
|
||||||
|
Re-routed bot alerts: RMM/Dev alerts (`[RMM]/[DEPLOY]/[DEV]/[BUILD]/[GURURMM]/[SMARTBADGE-WATCH]`) now auto-route to the new private #dev-alerts (Howard + Mike); Syncro/general stay in #bot-alerts. Onboarded Rednour Law Offices (client + site "Main", `GREEN-FALCON-7214`), vaulted the one-time enrollment key, and documented the whole flow as `/rmm onboard` in the skill. Three Rednour agents enrolled and online.
|
||||||
|
|
||||||
|
Built Phase 1 of an onboarding diagnostic (`/rmm diagnose`): a PS5.1/ASCII/SYSTEM-safe security+health probe dispatched via RMM, a triage layer grading RED/AMBER/GREEN, immutable per-client baselines, prior-baseline diff, and CRITICAL alerts to #dev-alerts. Code-reviewed (no blockers; folded in immutability guard, severity-independent finding ids, Defender-unknown sentinel, expanded competitor/backup detection). Baselined all three Rednour machines. Mike clarified the "foreign agents" it flagged (ScreenConnect/CW Control, Splashtop, Syncro, Datto RMM+EDR) are ACG's own stack — filed a detection-tuning todo and saved a memory so they aren't re-flagged. Real day-one findings surfaced: two machines on EOL Win10 22H2, RDP-without-NLA on REDNOURCARRIEVI, missing BitLocker, low disk, no backup agent on FrontDeskReception.
|
||||||
|
|
||||||
|
### Key Decisions
|
||||||
|
|
||||||
|
- **Reversed the 2026-05-28 SmartBadge fix** — the fleet standard is the *newer* Datto Workplace v10.53.4 (Workplace2); Workplace Desktop v8 is older despite the name. EVO-X1 used as the canonical reference.
|
||||||
|
- **Let Mike Revo-uninstall v8** rather than silent-uninstall via RMM — GUI uninstaller sweeps leftovers a silent uninstall leaves; coordinated to avoid both touching the live box at once.
|
||||||
|
- **SmartBadge daily watch via local scheduled task, not /schedule** — `/schedule` provisions cloud agents that can't reach the internal RMM API (172.16.3.30); only a LAN machine can.
|
||||||
|
- **Integration Center: partner-scoped, generic JSONB storage, catalog+Syncro together, code plugin registry, AES-256-GCM reuse** — corrected SPEC-005's internal-only, DB-catalog-table assumptions.
|
||||||
|
- **Server deploy to `/opt/gururmm/gururmm-server`** — that is the real `ExecStart`; the wiki's `/usr/local/bin` path is stale and caused the failed deploy.
|
||||||
|
- **Alert routing by message prefix** — auto-routes existing call sites with no edits; explicit `dev`/`bot` 2nd-arg override retained.
|
||||||
|
- **Onboarding diagnostic Phase 1 = script via /rmm now, Phase 3 = native GuruRMM feature later**; baselines stored in repo now, GuruRMM DB when the native feature lands ("both").
|
||||||
|
|
||||||
|
### Problems Encountered
|
||||||
|
|
||||||
|
- **Excel rewrote per-user add-in state on exit** — required Excel closed before clearing Kristin's `LoadBehavior=2`; the fix script guards on EXCEL.EXE.
|
||||||
|
- **sops encrypt failures** — needed `--config "$VR/.sops.yaml"` from outside the vault dir; bare YAML dates threw `Cannot walk value, unknown type: time.Time` (quote them); failed encrypt leaves plaintext on disk (verify with `grep -c 'ENC['`). Documented in `/rmm onboard`.
|
||||||
|
- **Server "rebuild" didn't take** — 0.3.36 binary was deployed to the wrong path (`/usr/local/bin` vs the service's `/opt/gururmm`). Fixed by copying to the correct path + restart.
|
||||||
|
- **Discord 400 on em-dash** — a unicode em-dash in an alert broke the JSON post (Windows Git Bash argv encoding); ASCII-only is already the rule.
|
||||||
|
- **Agent ~32KB command-size cap** — the 62KB diagnostic probe was rejected; runner base64-chunks the upload (<24KB chunks) then decodes/runs/cleans up.
|
||||||
|
- **Detection false positives** — substring matches (`cove`->Recovery, `ltsvc`->Vaultsvc) and PS5.1 scalar `.Count`; fixed with `\b` anchors and `@(...)` wrapping. Then ACG's own stack flagged as "foreign" — filed tuning todo + memory.
|
||||||
|
- **git push rejected (non-fast-forward)** twice — other instances had advanced origin; resolved with `pull --rebase` then push.
|
||||||
|
|
||||||
|
### Configuration Changes
|
||||||
|
|
||||||
|
- KSTEENBB2025 (via RMM): uninstalled Datto Workplace Desktop v8.50.13 (Revo); installed Datto Workplace v10.53.4; `_CC` CLSID `{3C639243}` -> Workplace2 x64/x86 DLLs; removed `{2B96EDC1}` + non-`_CC` add-in keys; per-user `Datto.SmartBadgeShim_CC` LoadBehavior 2->3, cleared Resiliency, set DoNotDisableAddinList.
|
||||||
|
- 172.16.3.30: `cp /usr/local/bin/gururmm-server /opt/gururmm/gururmm-server` (0.3.36), old saved `/opt/gururmm/gururmm-server.0.3.32.bak`, `systemctl restart gururmm-server`.
|
||||||
|
- `.claude/scripts/post-bot-alert.sh` — dev/bot channel routing by prefix + override arg.
|
||||||
|
- `.claude/scripts/ksteen-smartbadge-verify.ps1`, `ksteen-smartbadge-fix.ps1`, `check-ksteen-smartbadge.sh` — new (SmartBadge verify/fix/daily-runner).
|
||||||
|
- `.claude/scripts/onboarding-diagnostic.ps1`, `run-onboarding-diagnostic.sh` — new (onboarding diagnostic Phase 1).
|
||||||
|
- `.claude/commands/rmm.md` — added `/rmm onboard`, `/rmm diagnose`, dev-alerts routing notes.
|
||||||
|
- `.claude/memory/reference_acg_msp_stack.md` (+ MEMORY.md index) — new.
|
||||||
|
- `wiki/clients/birth-biologic.md` — agents table, dual-Workplace SmartBadge known issue + fleet standard, 5/28-5/29 history.
|
||||||
|
- Vault `D:/vault/clients/rednour/gururmm-site-main.sops.yaml` — new (Rednour enrollment key).
|
||||||
|
- Scheduled task "ClaudeTools - KSTEEN SmartBadge Daily" on GURU-5070 (daily 09:00, 2026-05-30..06-05).
|
||||||
|
- gururmm PR #28 (branch `feat/spec-005-integration-center`) — revised SPEC-005.
|
||||||
|
|
||||||
|
### Credentials & Secrets
|
||||||
|
|
||||||
|
- Rednour GuruRMM site API key (one-time, `grmm_...`): vaulted at `clients/rednour/gururmm-site-main.sops.yaml` (credentials.api_key). Site `GREEN-FALCON-7214`.
|
||||||
|
- Gitea API token field path correction: `services/gitea.sops.yaml` -> `credentials.api.api-token` (not `credentials.api-token`).
|
||||||
|
- Syncro API key: `msp-tools/syncro.sops.yaml` -> `credentials.credential` (unchanged).
|
||||||
|
- No new secrets created beyond the Rednour site key.
|
||||||
|
|
||||||
|
### Infrastructure & Servers
|
||||||
|
|
||||||
|
- GuruRMM server 172.16.3.30: `/status` -> 0.3.36; service `ExecStart=/opt/gururmm/gururmm-server` (NOT /usr/local/bin — wiki stale); build via `sudo /opt/gururmm/build-server.sh` (builds in `/home/guru/gururmm/server`, log `/var/log/gururmm-build.log`).
|
||||||
|
- Rednour Law Offices: client `85f7cff4-d4db-48a8-b477-b8788122a361`, site Main `c7f5787c-8e71-45b3-841f-fa52436f7d26` / `GREEN-FALCON-7214`. Agents: FrontDeskReception `04765560-...`, LegalAsst `18825ea7-...`, rednourcarrievirt `8e4e2221-...`.
|
||||||
|
- BB agents: BB-SERVER `6c02baa7`, KSTEENBB2025 `ee3c6aea`, EVO-X1 `9595f002`, BB-Office2 `48763401`.
|
||||||
|
- Discord channels: #bot-alerts `624710699771232265`, #dev-alerts `1509998508198068484` (private).
|
||||||
|
|
||||||
|
### Commands & Outputs
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Server fix (172.16.3.30)
|
||||||
|
sudo cp /opt/gururmm/gururmm-server /opt/gururmm/gururmm-server.0.3.32.bak
|
||||||
|
sudo systemctl stop gururmm-server
|
||||||
|
sudo cp /usr/local/bin/gururmm-server /opt/gururmm/gururmm-server
|
||||||
|
sudo systemctl start gururmm-server
|
||||||
|
curl -s localhost:3001/status | jq .version # -> "0.3.36"
|
||||||
|
|
||||||
|
# vault new entry (non-interactive)
|
||||||
|
sops --config "$VR/.sops.yaml" --encrypt --in-place <file> # quote dates; secrets under credentials:
|
||||||
|
|
||||||
|
# onboarding diagnostic
|
||||||
|
bash .claude/scripts/run-onboarding-diagnostic.sh <host> <client-slug>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pending / Incomplete Tasks
|
||||||
|
|
||||||
|
- **Rednour:** real findings to action — 2x EOL Win10 22H2 (LegalAsst, REDNOURCARRIEVI), RDP-without-NLA (REDNOURCARRIEVI), missing BitLocker, low disk (REDNOURCARRIEVI ~12%), no backup agent (FrontDeskReception). Mike to direct remediation.
|
||||||
|
- **KSTEENBB2025:** SmartBadge confirmed working; 7-day watch running (coord todo `4a5b09b3`).
|
||||||
|
- **Integration Center:** PR #28 open for review/merge; then /shape-spec + scaffold (coord todo `0198ba04`). PARKED.
|
||||||
|
- **GuruRMM enrollment security:** site_id-only enrollment gate decision (coord todo `00074cd8`).
|
||||||
|
- **Onboarding diagnostic:** Phase 3 native feature (coord todo `76c6050b`); single-element-array MD-table bug (`cc5dbdfa`); ACG-stack detection allowlist tuning (`3d886f1a`).
|
||||||
|
- **Wiki:** gururmm.md ExecStart path (/opt vs /usr/local/bin) should be corrected; no wiki article for Rednour yet.
|
||||||
|
|
||||||
|
### Reference Information
|
||||||
|
|
||||||
|
- Syncro #32339 (BB SmartBadge): public cmt 414607766, warranty li 42639366. Customer 17983014.
|
||||||
|
- gururmm PR #28: https://git.azcomputerguru.com/azcomputerguru/gururmm/pulls/28
|
||||||
|
- Coord: Howard reply f888b21c; broadcast bafae411; todos 4a5b09b3 / 0198ba04 / 00074cd8 / 76c6050b / cc5dbdfa / 3d886f1a.
|
||||||
|
- Rednour install page: https://rmm.azcomputerguru.com/install/GREEN-FALCON-7214
|
||||||
|
- Baselines: `clients/rednour/onboarding-baselines/` (FRONTDESKRECEPT, LEGALASST, REDNOURCARRIEVI).
|
||||||
|
|||||||
196
temp/SPEC-005-integration-catalog.md
Normal file
196
temp/SPEC-005-integration-catalog.md
Normal file
@@ -0,0 +1,196 @@
|
|||||||
|
# SPEC-005: Integration Catalog ("Integration Center")
|
||||||
|
|
||||||
|
**Status:** Approved for build (planning locked 2026-05-29)
|
||||||
|
**Priority:** P2 — built in tandem with SPEC-002 (Syncro PSA)
|
||||||
|
**Requested By:** Mike Swanson (2026-05-18; scope locked 2026-05-29)
|
||||||
|
**Estimated Effort:** Large (catalog + Syncro plugin + MSP360 migration)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The GuruRMM **Integration Center** is a centralized, **partner-scoped** surface for managing third-party integrations (Syncro PSA, MSP360 Managed Backup, and future providers). Each **partner (MSP)** browses the available catalog, configures the integrations they want for their own tenant, and monitors health/status — without per-tool fragmentation.
|
||||||
|
|
||||||
|
This is an **internal catalog**, not a public marketplace. App-store-style discovery, monetization/entitlement, and third-party-submitted integrations are explicitly **out of scope** (may be revisited as a separate "Store" initiative later).
|
||||||
|
|
||||||
|
**Audience:** Partner-level users (ADR-001 Dev -> Partner -> Client model). Partner-admins configure; partner-users have read-only status visibility. Dev/ACG (partner #1) sees all partners.
|
||||||
|
|
||||||
|
**User Benefits**
|
||||||
|
- Centralized, one-screen management of all integrations for a partner.
|
||||||
|
- One-click configure with validation; clear status (Not Configured / Configured / Active / Error).
|
||||||
|
- Per-partner health monitoring + audit trail.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Locked Decisions (2026-05-29)
|
||||||
|
|
||||||
|
1. **Partner-scoped.** Every configuration and audit row carries `partner_id NOT NULL REFERENCES partners(id)`. The API derives `partner_id` from the caller's JWT; it is never client-supplied. This mirrors the existing `mspbackups_config(partner_id, UNIQUE(partner_id))` pattern and enforces tenant isolation server-side (closing the same horizontal-privilege class as the known `credentials/:id/reveal` finding).
|
||||||
|
2. **Generic JSONB config storage.** A single `integration_configurations` table holds all per-partner configs in a `settings JSONB` column. MSP360's existing `mspbackups_config` is **migrated into this table** (see Migration). No per-plugin config tables.
|
||||||
|
3. **Catalog + Syncro built together.** MSP360 is wrapped as the first plugin (its config migrated in); Syncro (SPEC-002) is implemented as the first *new* plugin through the catalog pattern, proving the full configure flow. Both ship with the catalog.
|
||||||
|
4. **Available-integration metadata lives in code**, not the DB. The plugin registry (the `IntegrationPlugin` trait implementations) is the source of truth for the catalog listing (name, provider, category, required fields). The DB stores only per-partner configurations, status, and audit — no `integrations` catalog table to drift out of sync with shipped code.
|
||||||
|
5. **Reuse AES-256-GCM** (migration `016` credentials encryption) for secret-typed fields. Secret fields within `settings` are encrypted at rest and masked on read; non-secret fields stored plaintext in the JSONB.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
### Included (v1)
|
||||||
|
- Integration Center UI: browse the catalog (from the code registry), configure, and monitor — partner-scoped.
|
||||||
|
- Plugins: **MSP360** (migrated from standalone) and **Syncro PSA** (new, built alongside).
|
||||||
|
- One-click configuration flow with per-plugin field validation.
|
||||||
|
- Status tracking: Not Configured / Configured / Active / Error, with last-health-check + error message.
|
||||||
|
- Per-partner audit logging of all configuration changes.
|
||||||
|
- Plugin/extension architecture (trait + registry) so new integrations are additive.
|
||||||
|
|
||||||
|
### Out of Scope
|
||||||
|
- Public/marketplace discovery, third-party-submitted integrations.
|
||||||
|
- Monetization / licensing / entitlement.
|
||||||
|
- Multiple instances of the same integration per partner (v1 = one instance per integration per partner; multi-instance deferred to Phase 2).
|
||||||
|
- Non-IT integrations.
|
||||||
|
|
||||||
|
### Success Criteria
|
||||||
|
- A partner can configure Syncro and MSP360 from one screen; status reflects real health.
|
||||||
|
- MSP360 migration is transparent — the existing MSPBackups feature keeps working, no partner reconfiguration.
|
||||||
|
- Tenant isolation verified: no partner can read or affect another partner's integration config.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
### Plugin registry (code = source of truth for the catalog)
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// server/src/integrations/plugin_interface.rs
|
||||||
|
#[async_trait]
|
||||||
|
pub trait IntegrationPlugin: Send + Sync {
|
||||||
|
fn metadata(&self) -> IntegrationMetadata; // static catalog info
|
||||||
|
async fn validate(&self, settings: &serde_json::Value) -> Result<()>; // pre-save validation
|
||||||
|
async fn configure(&self, partner_id: Uuid, settings: serde_json::Value) -> Result<()>;
|
||||||
|
async fn health_check(&self, partner_id: Uuid) -> Result<HealthStatus>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct IntegrationMetadata {
|
||||||
|
pub key: String, // stable id, e.g. "syncro", "msp360"
|
||||||
|
pub name: String,
|
||||||
|
pub provider: String,
|
||||||
|
pub category: IntegrationCategory, // Psa, Backup, Rmm, Monitoring, ...
|
||||||
|
pub description: String,
|
||||||
|
pub fields: Vec<FieldSpec>, // drives the dynamic config form
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct FieldSpec {
|
||||||
|
pub key: String,
|
||||||
|
pub label: String,
|
||||||
|
pub kind: FieldKind, // Text | Secret | Select(Vec<String>) | Url | Bool
|
||||||
|
pub required: bool,
|
||||||
|
pub help: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum HealthStatus { Ok, Degraded(String), Error(String) }
|
||||||
|
```
|
||||||
|
|
||||||
|
A static `IntegrationRegistry` holds `HashMap<String /*key*/, Arc<dyn IntegrationPlugin>>`, built at startup. `GET /api/integrations` returns registry metadata joined with the calling partner's stored config status.
|
||||||
|
|
||||||
|
### Data model (generic, partner-scoped)
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- New: per-partner integration configuration (generic JSONB)
|
||||||
|
CREATE TABLE integration_configurations (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
partner_id UUID NOT NULL REFERENCES partners(id) ON DELETE CASCADE,
|
||||||
|
integration_key VARCHAR(64) NOT NULL, -- matches plugin registry key
|
||||||
|
settings JSONB NOT NULL DEFAULT '{}', -- secret-typed fields encrypted at rest
|
||||||
|
status VARCHAR(20) NOT NULL DEFAULT 'Configured'
|
||||||
|
CHECK (status IN ('Not Configured','Configured','Active','Error')),
|
||||||
|
last_health_check TIMESTAMPTZ,
|
||||||
|
error_message TEXT,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||||
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||||
|
CONSTRAINT uk_integration_partner_key UNIQUE (partner_id, integration_key)
|
||||||
|
);
|
||||||
|
CREATE INDEX idx_integration_cfg_partner ON integration_configurations(partner_id);
|
||||||
|
|
||||||
|
-- New: per-partner audit
|
||||||
|
CREATE TABLE integration_audit_logs (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
partner_id UUID NOT NULL REFERENCES partners(id) ON DELETE CASCADE,
|
||||||
|
user_id UUID REFERENCES users(id),
|
||||||
|
integration_key VARCHAR(64) NOT NULL,
|
||||||
|
action VARCHAR(100) NOT NULL, -- configured | reconfigured | enabled | disabled | health_error
|
||||||
|
details JSONB,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||||
|
);
|
||||||
|
CREATE INDEX idx_integration_audit_partner ON integration_audit_logs(partner_id);
|
||||||
|
CREATE INDEX idx_integration_audit_created ON integration_audit_logs(created_at DESC);
|
||||||
|
```
|
||||||
|
|
||||||
|
> Note: there is intentionally **no `integrations` catalog table** — available integrations come from the code registry (Decision #4).
|
||||||
|
|
||||||
|
### MSP360 migration (live integration — handle with care)
|
||||||
|
|
||||||
|
`mspbackups_config` is live and partner-scoped already. Migration:
|
||||||
|
1. Add the new tables (above).
|
||||||
|
2. Data migration: for each `mspbackups_config` row, insert an `integration_configurations` row with `integration_key='msp360'`, `status='Active'`, and `settings` = the MSP360 fields (secret fields re-encrypted via the credentials AES-256-GCM module).
|
||||||
|
3. Refactor the `mspbackups` module to read/write its config through the generic store (an accessor that serializes/deserializes the MSP360 settings shape), OR keep `mspbackups_config` as the system of record and have the `msp360` plugin **adapt** to it during a transition window. **Recommended: dual-write + read-from-new behind a flag, verify parity, then drop `mspbackups_config`** in a follow-up migration — never a hard cutover on a working integration.
|
||||||
|
4. The existing `MSPBackups.tsx` page stays; the Integration Center adds a unified entry that deep-links to it.
|
||||||
|
|
||||||
|
> This is the one genuinely risky step. It must ship behind a feature flag with a verified rollback (keep `mspbackups_config` until parity is confirmed in production).
|
||||||
|
|
||||||
|
### API (partner-scoped; keyed by plugin key)
|
||||||
|
|
||||||
|
- `GET /api/integrations` — catalog (registry metadata) + this partner's status per integration
|
||||||
|
- `GET /api/integrations/:key` — detail + field spec + current (masked) config + status
|
||||||
|
- `POST /api/integrations/:key/configure` — validate + store (partner from JWT); audit
|
||||||
|
- `POST /api/integrations/:key/reconfigure` — update existing
|
||||||
|
- `POST /api/integrations/:key/test` — run `health_check` on demand
|
||||||
|
- `DELETE /api/integrations/:key` — remove this partner's configuration
|
||||||
|
- `GET /api/integrations/:key/audit` — partner-scoped audit log
|
||||||
|
|
||||||
|
RBAC: configure/reconfigure/delete require partner-admin; reads require partner membership. `partner_id` always from JWT, never the body/path.
|
||||||
|
|
||||||
|
### Health checks
|
||||||
|
A scheduled job (every ~15 min) iterates each partner's configured integrations, calls `health_check(partner_id)`, updates `status`/`last_health_check`/`error_message`, and writes an audit row on any status transition. Error transitions may raise an alert via the existing alerting subsystem.
|
||||||
|
|
||||||
|
### Dashboard
|
||||||
|
- `pages/IntegrationCenter.tsx` — grid of `IntegrationCard` tiles (name, category, status badge), filterable by category.
|
||||||
|
- `pages/IntegrationDetail.tsx` — dynamic `ConfigurationForm` rendered from the plugin's `FieldSpec[]`, status panel, audit list, "Test connection" button.
|
||||||
|
- Reuse the existing status-badge helper pattern (explicit `getStatusBadgeClass()` function — not a `Record` const; see project anti-patterns).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Security
|
||||||
|
- **Tenant isolation:** every query filters by `partner_id` from JWT. Server-side enforced; covered by tests that attempt cross-partner access.
|
||||||
|
- **Secrets:** secret-typed fields encrypted at rest (AES-256-GCM, migration 016 module); masked in all API responses; never logged. Audit `details` must redact secrets.
|
||||||
|
- **Input validation:** each plugin validates `settings` against its `FieldSpec` before persist.
|
||||||
|
- **Audit:** all config mutations logged with user + partner + action.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Rollout (combined catalog + Syncro)
|
||||||
|
1. **Infra:** migrations (new tables), plugin trait + registry, health-check scheduler, encryption helpers.
|
||||||
|
2. **Plugins:** MSP360 adapter (dual-write/verify) + Syncro plugin (SPEC-002 functionality behind the plugin interface).
|
||||||
|
3. **API:** partner-scoped endpoints + RBAC + tests (incl. cross-partner isolation).
|
||||||
|
4. **Dashboard:** Integration Center + detail/config form + status/audit.
|
||||||
|
5. **Cutover:** behind `feature/integration-center` flag; verify MSP360 parity in prod; drop `mspbackups_config` in a follow-up migration.
|
||||||
|
|
||||||
|
Holistic-development rule applies: backend + API + dashboard + docs ship together (DESIGN.md).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
- **SPEC-002 (Syncro PSA)** — built in tandem; its connection/config logic implemented as the `syncro` plugin.
|
||||||
|
- **SPEC-004 (MSP360)** — existing; migrated into the catalog as the `msp360` plugin.
|
||||||
|
- **partners / multi-tenancy (ADR-001)** — already in place (`partners` table, `clients.partner_id`).
|
||||||
|
|
||||||
|
## Resolved Open Questions
|
||||||
|
- Multiple instances per partner? **No in v1** (UNIQUE(partner_id, integration_key)); Phase 2.
|
||||||
|
- Config storage? **Generic JSONB** (Decision #2).
|
||||||
|
- Build order? **Catalog + Syncro together** (Decision #3).
|
||||||
|
- Non-admin visibility? **Read-only status**, yes.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## References
|
||||||
|
- [SPEC-002 Syncro PSA](./SPEC-002-syncro-psa-integration.md), [SPEC-004 MSP360](./SPEC-004-mspbackups-integration.md)
|
||||||
|
- ADR-001 multi-tenancy (Dev/Partner/Client); migration `016` (credentials AES-256-GCM); `034/035/044` (MSP360)
|
||||||
|
- Patterns: plugin/registry, generic JSONB config, partner-scoped tenancy
|
||||||
65
temp/datto-fix.ps1
Normal file
65
temp/datto-fix.ps1
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
$ErrorActionPreference = 'SilentlyContinue'
|
||||||
|
$SID = 'S-1-12-1-4150293861-1139320743-1956584882-216650436' # kristinsteen
|
||||||
|
$wp2x64 = 'C:\Program Files\Datto\Workplace2\SmartBadge\DattoSmartBadgeShim_x64.dll'
|
||||||
|
$wp2x86 = 'C:\Program Files\Datto\Workplace2\SmartBadge\DattoSmartBadgeShim_x86.dll'
|
||||||
|
|
||||||
|
# --- Guard: Excel must be closed (it rewrites per-user add-in state on exit) ---
|
||||||
|
$xl = Get-Process EXCEL -ErrorAction SilentlyContinue
|
||||||
|
if ($xl) { Write-Output "[ABORT] EXCEL.EXE is running (pid $($xl.Id)). Close Excel before running the per-user fix."; exit 2 }
|
||||||
|
|
||||||
|
Write-Output "=== MACHINE-WIDE: remove v8 leftovers (match EVO-X1) ==="
|
||||||
|
# Drop the Workplace Desktop CLSID {2B96EDC1} (x64 + WOW64)
|
||||||
|
foreach ($k in @(
|
||||||
|
'HKLM:\Software\Classes\CLSID\{2B96EDC1-FDF3-47E1-B177-F205E7B98DF4}',
|
||||||
|
'HKLM:\Software\WOW6432Node\Classes\CLSID\{2B96EDC1-FDF3-47E1-B177-F205E7B98DF4}')) {
|
||||||
|
if (Test-Path $k) { Remove-Item $k -Recurse -Force; Write-Output " removed $k" } else { Write-Output " absent $k" }
|
||||||
|
}
|
||||||
|
# Drop the non-_CC Datto.SmartBadgeShim add-in keys (EVO-X1 only has _CC)
|
||||||
|
foreach ($k in @(
|
||||||
|
'HKLM:\Software\Microsoft\Office\Excel\Addins\Datto.SmartBadgeShim',
|
||||||
|
'HKLM:\Software\WOW6432Node\Microsoft\Office\Excel\Addins\Datto.SmartBadgeShim',
|
||||||
|
'HKLM:\Software\Microsoft\Office\Word\Addins\Datto.SmartBadgeShim',
|
||||||
|
'HKLM:\Software\Microsoft\Office\PowerPoint\Addins\Datto.SmartBadgeShim')) {
|
||||||
|
if (Test-Path $k) { Remove-Item $k -Recurse -Force; Write-Output " removed $k" } else { Write-Output " absent $k" }
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Output ""
|
||||||
|
Write-Output "=== MACHINE-WIDE: verify _CC CLSID -> Workplace2 DLLs ==="
|
||||||
|
$cc = '{3C639243-95A2-400D-B4B4-4384DA7F61D3}'
|
||||||
|
foreach ($pair in @(@("HKLM:\Software\Classes\CLSID\$cc\InprocServer32",$wp2x64), @("HKLM:\Software\WOW6432Node\Classes\CLSID\$cc\InprocServer32",$wp2x86))) {
|
||||||
|
$path = $pair[0]; $want = $pair[1]
|
||||||
|
$cur = (Get-Item $path -ErrorAction SilentlyContinue)
|
||||||
|
$val = if ($cur) { $cur.GetValue('') } else { $null }
|
||||||
|
if ($val -ne $want) {
|
||||||
|
if (-not (Test-Path $path)) { New-Item $path -Force | Out-Null }
|
||||||
|
Set-ItemProperty $path -Name '(default)' -Value $want
|
||||||
|
Set-ItemProperty $path -Name 'ThreadingModel' -Value 'Apartment'
|
||||||
|
Write-Output " set $path -> $want"
|
||||||
|
} else { Write-Output " ok $path -> $val" }
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Output ""
|
||||||
|
Write-Output "=== PER-USER ($SID): clear stuck disabled state ==="
|
||||||
|
$xlAddins = "Registry::HKEY_USERS\$SID\Software\Microsoft\Office\Excel\Addins"
|
||||||
|
# Remove non-_CC leftover in user hive (match EVO-X1 = no per-user Datto entries)
|
||||||
|
if (Test-Path "$xlAddins\Datto.SmartBadgeShim") { Remove-Item "$xlAddins\Datto.SmartBadgeShim" -Recurse -Force; Write-Output " removed HKCU Datto.SmartBadgeShim" }
|
||||||
|
# Reset _CC per-user override to 3 (Excel had set it to 2 = disabled)
|
||||||
|
if (Test-Path "$xlAddins\Datto.SmartBadgeShim_CC") {
|
||||||
|
Set-ItemProperty "$xlAddins\Datto.SmartBadgeShim_CC" -Name 'LoadBehavior' -Value 3 -Type DWord
|
||||||
|
Write-Output " set HKCU Datto.SmartBadgeShim_CC LoadBehavior=3"
|
||||||
|
}
|
||||||
|
# Clear Excel Resiliency DisabledItems + crash records
|
||||||
|
$res = "Registry::HKEY_USERS\$SID\Software\Microsoft\Office\16.0\Excel\Resiliency"
|
||||||
|
foreach ($sub in @('DisabledItems','CrashingAddinList','DocumentRecovery')) {
|
||||||
|
if (Test-Path "$res\$sub") {
|
||||||
|
$i = Get-Item "$res\$sub"
|
||||||
|
$i.GetValueNames() | ForEach-Object { Remove-ItemProperty "$res\$sub" -Name $_ -Force }
|
||||||
|
Write-Output " cleared values under Resiliency\$sub"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
# Keep add-in protected from auto-disable
|
||||||
|
$dnd = "$res\DoNotDisableAddinList"
|
||||||
|
if (-not (Test-Path $dnd)) { New-Item $dnd -Force | Out-Null }
|
||||||
|
Set-ItemProperty $dnd -Name 'Datto.SmartBadgeShim_CC' -Value 1 -Type DWord
|
||||||
|
Write-Output " ensured DoNotDisableAddinList Datto.SmartBadgeShim_CC=1"
|
||||||
|
Write-Output "=== FIX COMPLETE ==="
|
||||||
19
temp/datto-install-v10.ps1
Normal file
19
temp/datto-install-v10.ps1
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
$exe = 'C:\Users\kristinsteen\Downloads\DattoWorkplaceSetup_v10.5.3.4.exe'
|
||||||
|
if (-not (Test-Path $exe)) { Write-Output "[ERROR] installer missing: $exe"; exit 1 }
|
||||||
|
|
||||||
|
# Safety: confirm v8 is actually gone before installing v10
|
||||||
|
$v8 = Get-ItemProperty 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*','HKLM:\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*' -ErrorAction SilentlyContinue | Where-Object { $_.DisplayName -like '*Workplace Desktop*' }
|
||||||
|
if ($v8) { Write-Output "[ABORT] Workplace Desktop v8 still present - not installing yet:"; $v8 | ForEach-Object { Write-Output (" {0} v{1}" -f $_.DisplayName,$_.DisplayVersion) }; exit 2 }
|
||||||
|
|
||||||
|
Write-Output "[INFO] v8 confirmed absent. Starting silent install of v10..."
|
||||||
|
$p = Start-Process -FilePath $exe -ArgumentList '/install','/quiet','/norestart' -Wait -PassThru
|
||||||
|
Write-Output ("[INFO] Installer exit code: {0}" -f $p.ExitCode)
|
||||||
|
if ($p.ExitCode -ne 0 -and $p.ExitCode -ne 3010) { Write-Output "[WARNING] non-zero/non-3010 exit; check Bootstrap log" }
|
||||||
|
|
||||||
|
Start-Sleep -Seconds 8
|
||||||
|
Write-Output "=== Post-install product check ==="
|
||||||
|
Get-ItemProperty 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*','HKLM:\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*' -ErrorAction SilentlyContinue | Where-Object { $_.DisplayName -like '*Workplace*' } | ForEach-Object { Write-Output (" {0} v{1} {2}" -f $_.DisplayName,$_.DisplayVersion,$_.InstallLocation) }
|
||||||
|
Get-ChildItem 'C:\Program Files\Datto' -ErrorAction SilentlyContinue | ForEach-Object { Write-Output (" folder: {0}" -f $_.Name) }
|
||||||
|
Get-ChildItem 'C:\Program Files\Datto\Workplace2\SmartBadge' -Filter 'DattoSmartBadgeShim*.dll' -ErrorAction SilentlyContinue | ForEach-Object { Write-Output (" dll: {0}" -f $_.FullName) }
|
||||||
|
Write-Output "=== END ==="
|
||||||
27
temp/datto-prestage.ps1
Normal file
27
temp/datto-prestage.ps1
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
$ErrorActionPreference = 'SilentlyContinue'
|
||||||
|
Write-Output "=== Locate v10 installer (all user Downloads + common) ==="
|
||||||
|
$roots = @("C:\Users\kristinsteen\Downloads","C:\Users\Public\Downloads")
|
||||||
|
Get-ChildItem 'C:\Users\*\Downloads' -Filter 'DattoWorkplace*' -ErrorAction SilentlyContinue | ForEach-Object {
|
||||||
|
Write-Output (" {0} ({1:N1} MB, modified {2})" -f $_.FullName, ($_.Length/1MB), $_.LastWriteTime)
|
||||||
|
}
|
||||||
|
Get-ChildItem 'C:\Users\*\Downloads' -Filter '*Workplace*Setup*' -ErrorAction SilentlyContinue | ForEach-Object {
|
||||||
|
Write-Output (" {0} ({1:N1} MB, modified {2})" -f $_.FullName, ($_.Length/1MB), $_.LastWriteTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Output ""
|
||||||
|
Write-Output "=== Datto Workplace Desktop uninstall strings ==="
|
||||||
|
$paths = @('HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*','HKLM:\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*')
|
||||||
|
foreach ($p in $paths) {
|
||||||
|
Get-ItemProperty $p -ErrorAction SilentlyContinue | Where-Object { $_.DisplayName -like '*Workplace*' } | ForEach-Object {
|
||||||
|
Write-Output (" Name: {0} v{1}" -f $_.DisplayName, $_.DisplayVersion)
|
||||||
|
Write-Output (" Key: {0}" -f $_.PSChildName)
|
||||||
|
Write-Output (" Uninstall: {0}" -f $_.UninstallString)
|
||||||
|
Write-Output (" QuietUninstall: {0}" -f $_.QuietUninstallString)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Output ""
|
||||||
|
Write-Output "=== Datto Workplace Desktop process/service state ==="
|
||||||
|
Get-Process WorkplaceDesktop -ErrorAction SilentlyContinue | ForEach-Object { Write-Output (" proc WorkplaceDesktop pid {0}" -f $_.Id) }
|
||||||
|
Get-Service -ErrorAction SilentlyContinue | Where-Object { $_.DisplayName -like '*Workplace*' } | ForEach-Object { Write-Output (" svc {0} [{1}]" -f $_.Name, $_.Status) }
|
||||||
|
Write-Output "=== END ==="
|
||||||
73
temp/datto-recon.ps1
Normal file
73
temp/datto-recon.ps1
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
$ErrorActionPreference = 'SilentlyContinue'
|
||||||
|
Write-Output "=== HOST ==="
|
||||||
|
Write-Output $env:COMPUTERNAME
|
||||||
|
Write-Output "=== LOGGED-ON USER ==="
|
||||||
|
query user 2>$null
|
||||||
|
|
||||||
|
Write-Output ""
|
||||||
|
Write-Output "=== INSTALLED DATTO/WORKPLACE PRODUCTS (uninstall keys) ==="
|
||||||
|
$paths = @(
|
||||||
|
'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*',
|
||||||
|
'HKLM:\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
|
||||||
|
)
|
||||||
|
foreach ($p in $paths) {
|
||||||
|
Get-ItemProperty $p -ErrorAction SilentlyContinue |
|
||||||
|
Where-Object { $_.DisplayName -like '*Datto*' -or $_.DisplayName -like '*Workplace*' } |
|
||||||
|
ForEach-Object { Write-Output (" {0} | v{1} | {2}" -f $_.DisplayName, $_.DisplayVersion, $_.InstallLocation) }
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Output ""
|
||||||
|
Write-Output "=== DATTO PROGRAM FOLDERS ==="
|
||||||
|
Get-ChildItem 'C:\Program Files\Datto' -ErrorAction SilentlyContinue | ForEach-Object { Write-Output (" {0} (modified {1})" -f $_.Name, $_.LastWriteTime) }
|
||||||
|
Write-Output "--- SmartBadge DLLs present ---"
|
||||||
|
Get-ChildItem 'C:\Program Files\Datto' -Recurse -Filter 'DattoSmartBadgeShim*.dll' -ErrorAction SilentlyContinue | ForEach-Object { Write-Output (" {0}" -f $_.FullName) }
|
||||||
|
|
||||||
|
Write-Output ""
|
||||||
|
Write-Output "=== DATTO WORKPLACE SERVICES / PROCESSES ==="
|
||||||
|
Get-Service -ErrorAction SilentlyContinue | Where-Object { $_.Name -like '*Datto*' -or $_.DisplayName -like '*Workplace*' } | ForEach-Object { Write-Output (" svc {0} [{1}] {2}" -f $_.Name, $_.Status, $_.DisplayName) }
|
||||||
|
Get-Process -ErrorAction SilentlyContinue | Where-Object { $_.ProcessName -like '*Workplace*' -or $_.ProcessName -like '*Datto*' } | ForEach-Object { Write-Output (" proc {0} (pid {1}) {2}" -f $_.ProcessName, $_.Id, $_.Path) }
|
||||||
|
|
||||||
|
Write-Output ""
|
||||||
|
Write-Output "=== HKLM Excel Addins (Datto) ==="
|
||||||
|
foreach ($base in @('HKLM:\Software\Microsoft\Office\Excel\Addins','HKLM:\Software\WOW6432Node\Microsoft\Office\Excel\Addins')) {
|
||||||
|
Write-Output "[$base]"
|
||||||
|
Get-ChildItem $base -ErrorAction SilentlyContinue | Where-Object { $_.PSChildName -like '*Datto*' } | ForEach-Object {
|
||||||
|
Write-Output (" {0} LoadBehavior={1}" -f $_.PSChildName, (Get-ItemProperty $_.PSPath).LoadBehavior)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Output ""
|
||||||
|
Write-Output "=== CLSID InprocServer32 (SmartBadge shims) ==="
|
||||||
|
foreach ($clsid in @('{2B96EDC1-FDF3-47E1-B177-F205E7B98DF4}','{3C639243-95A2-400D-B4B4-4384DA7F61D3}')) {
|
||||||
|
foreach ($base in @("HKLM:\Software\Classes\CLSID\$clsid\InprocServer32","HKLM:\Software\WOW6432Node\Classes\CLSID\$clsid\InprocServer32")) {
|
||||||
|
$item = Get-Item $base -ErrorAction SilentlyContinue
|
||||||
|
if ($item) {
|
||||||
|
$def = $item.GetValue('')
|
||||||
|
$tm = $item.GetValue('ThreadingModel')
|
||||||
|
Write-Output (" {0}`n -> {1} [TM={2}]" -f $base, $def, $tm)
|
||||||
|
} else {
|
||||||
|
Write-Output (" {0}`n -> <MISSING>" -f $base)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Output ""
|
||||||
|
Write-Output "=== Active user hive: Excel addin LoadBehavior + Resiliency ==="
|
||||||
|
Get-ChildItem 'Registry::HKEY_USERS' -ErrorAction SilentlyContinue | Where-Object { $_.Name -match 'S-1-12-1-|S-1-5-21-' -and $_.Name -notmatch '_Classes$' } | ForEach-Object {
|
||||||
|
$sid = $_.PSChildName
|
||||||
|
$ua = "Registry::HKEY_USERS\$sid\Software\Microsoft\Office\Excel\Addins"
|
||||||
|
if (Test-Path $ua) {
|
||||||
|
Get-ChildItem $ua -ErrorAction SilentlyContinue | Where-Object { $_.PSChildName -like '*Datto*' } | ForEach-Object {
|
||||||
|
Write-Output (" [$sid] HKCU addin {0} LoadBehavior={1}" -f $_.PSChildName, (Get-ItemProperty $_.PSPath).LoadBehavior)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$rb = "Registry::HKEY_USERS\$sid\Software\Microsoft\Office\16.0\Excel\Resiliency"
|
||||||
|
if (Test-Path "$rb\DoNotDisableAddinList") {
|
||||||
|
(Get-ItemProperty "$rb\DoNotDisableAddinList").PSObject.Properties | Where-Object { $_.Name -notlike 'PS*' } | ForEach-Object { Write-Output (" [$sid] DoNotDisable {0}={1}" -f $_.Name, $_.Value) }
|
||||||
|
}
|
||||||
|
if (Test-Path "$rb\DisabledItems") {
|
||||||
|
$di = Get-Item "$rb\DisabledItems"
|
||||||
|
if ($di.ValueCount -gt 0) { Write-Output (" [$sid] DisabledItems has {0} entries (Excel has disabled an add-in)" -f $di.ValueCount) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Write-Output "=== END RECON ==="
|
||||||
103
temp/update.md
Normal file
103
temp/update.md
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Update: 20:47 PT — Birth Biologic SmartBadge correction, GuruRMM 0.3.36 deploy, alert re-routing, Rednour onboarding + onboarding diagnostic
|
||||||
|
|
||||||
|
## User
|
||||||
|
- **User:** Mike Swanson (mike)
|
||||||
|
- **Machine:** GURU-5070
|
||||||
|
- **Role:** admin
|
||||||
|
|
||||||
|
### Session Summary
|
||||||
|
|
||||||
|
Began with Birth Biologic: Kristin Steen (KSTEENBB2025) could not use the Datto SmartBadge Excel add-in. RMM recon against her machine and the working reference (EVO-X1) showed the 2026-05-28 fix had gone the wrong direction — it removed the newer Datto Workplace v10.53.4 (Workplace2, the fleet standard) and left the older Workplace Desktop v8.50.13. Corrected it: Mike Revo-uninstalled v8 (clean, swept the `{2B96EDC1}` CLSID + non-`_CC` add-in keys), then via RMM installed Workplace v10.53.4 from her Downloads, aligned the SmartBadge `_CC` CLSID/add-in to EVO-X1, and cleared her stuck per-user `LoadBehavior=2`. Verified byte-for-byte against EVO-X1. Filed Syncro #32339 public tech notes + 1hr warranty, and stood up a 7-day daily verification (scheduled task on GURU-5070 + coord todo).
|
||||||
|
|
||||||
|
GuruRMM Integration Center ("Store") planning: confirmed it is greenfield (MSP360 built, Syncro/catalog not). Locked five design decisions (partner-scoped, generic JSONB config storage with MSP360 migrated in, catalog + Syncro built together, code-defined plugin registry, AES-256-GCM reuse) and landed a revised SPEC-005 as gururmm PR #28. Parked per Mike; tracked in coord todo. Mike also questioned why site API keys are published when enrollment correlates by site_id — code review confirmed the modern `POST /enroll` gate is site_id-only and the site api_key is used only by the legacy WS path + install-info; filed as a security/arch decision.
|
||||||
|
|
||||||
|
Resolved Howard's blocker: the gururmm server on 172.16.3.30 was still running 0.3.32 despite an 18:51 restart. Root cause — the 0.3.36 binary had been copied to `/usr/local/bin/gururmm-server`, but the systemd `ExecStart` is `/opt/gururmm/gururmm-server`, which was still yesterday's 0.3.32. Copied 0.3.36 to the correct path, restarted, verified `/status` = 0.3.36 and command flow (test command to ACG-DC16 returned completed/exit 0). Replied to Howard on coord.
|
||||||
|
|
||||||
|
Re-routed bot alerts: RMM/Dev alerts (`[RMM]/[DEPLOY]/[DEV]/[BUILD]/[GURURMM]/[SMARTBADGE-WATCH]`) now auto-route to the new private #dev-alerts (Howard + Mike); Syncro/general stay in #bot-alerts. Onboarded Rednour Law Offices (client + site "Main", `GREEN-FALCON-7214`), vaulted the one-time enrollment key, and documented the whole flow as `/rmm onboard` in the skill. Three Rednour agents enrolled and online.
|
||||||
|
|
||||||
|
Built Phase 1 of an onboarding diagnostic (`/rmm diagnose`): a PS5.1/ASCII/SYSTEM-safe security+health probe dispatched via RMM, a triage layer grading RED/AMBER/GREEN, immutable per-client baselines, prior-baseline diff, and CRITICAL alerts to #dev-alerts. Code-reviewed (no blockers; folded in immutability guard, severity-independent finding ids, Defender-unknown sentinel, expanded competitor/backup detection). Baselined all three Rednour machines. Mike clarified the "foreign agents" it flagged (ScreenConnect/CW Control, Splashtop, Syncro, Datto RMM+EDR) are ACG's own stack — filed a detection-tuning todo and saved a memory so they aren't re-flagged. Real day-one findings surfaced: two machines on EOL Win10 22H2, RDP-without-NLA on REDNOURCARRIEVI, missing BitLocker, low disk, no backup agent on FrontDeskReception.
|
||||||
|
|
||||||
|
### Key Decisions
|
||||||
|
|
||||||
|
- **Reversed the 2026-05-28 SmartBadge fix** — the fleet standard is the *newer* Datto Workplace v10.53.4 (Workplace2); Workplace Desktop v8 is older despite the name. EVO-X1 used as the canonical reference.
|
||||||
|
- **Let Mike Revo-uninstall v8** rather than silent-uninstall via RMM — GUI uninstaller sweeps leftovers a silent uninstall leaves; coordinated to avoid both touching the live box at once.
|
||||||
|
- **SmartBadge daily watch via local scheduled task, not /schedule** — `/schedule` provisions cloud agents that can't reach the internal RMM API (172.16.3.30); only a LAN machine can.
|
||||||
|
- **Integration Center: partner-scoped, generic JSONB storage, catalog+Syncro together, code plugin registry, AES-256-GCM reuse** — corrected SPEC-005's internal-only, DB-catalog-table assumptions.
|
||||||
|
- **Server deploy to `/opt/gururmm/gururmm-server`** — that is the real `ExecStart`; the wiki's `/usr/local/bin` path is stale and caused the failed deploy.
|
||||||
|
- **Alert routing by message prefix** — auto-routes existing call sites with no edits; explicit `dev`/`bot` 2nd-arg override retained.
|
||||||
|
- **Onboarding diagnostic Phase 1 = script via /rmm now, Phase 3 = native GuruRMM feature later**; baselines stored in repo now, GuruRMM DB when the native feature lands ("both").
|
||||||
|
|
||||||
|
### Problems Encountered
|
||||||
|
|
||||||
|
- **Excel rewrote per-user add-in state on exit** — required Excel closed before clearing Kristin's `LoadBehavior=2`; the fix script guards on EXCEL.EXE.
|
||||||
|
- **sops encrypt failures** — needed `--config "$VR/.sops.yaml"` from outside the vault dir; bare YAML dates threw `Cannot walk value, unknown type: time.Time` (quote them); failed encrypt leaves plaintext on disk (verify with `grep -c 'ENC['`). Documented in `/rmm onboard`.
|
||||||
|
- **Server "rebuild" didn't take** — 0.3.36 binary was deployed to the wrong path (`/usr/local/bin` vs the service's `/opt/gururmm`). Fixed by copying to the correct path + restart.
|
||||||
|
- **Discord 400 on em-dash** — a unicode em-dash in an alert broke the JSON post (Windows Git Bash argv encoding); ASCII-only is already the rule.
|
||||||
|
- **Agent ~32KB command-size cap** — the 62KB diagnostic probe was rejected; runner base64-chunks the upload (<24KB chunks) then decodes/runs/cleans up.
|
||||||
|
- **Detection false positives** — substring matches (`cove`->Recovery, `ltsvc`->Vaultsvc) and PS5.1 scalar `.Count`; fixed with `\b` anchors and `@(...)` wrapping. Then ACG's own stack flagged as "foreign" — filed tuning todo + memory.
|
||||||
|
- **git push rejected (non-fast-forward)** twice — other instances had advanced origin; resolved with `pull --rebase` then push.
|
||||||
|
|
||||||
|
### Configuration Changes
|
||||||
|
|
||||||
|
- KSTEENBB2025 (via RMM): uninstalled Datto Workplace Desktop v8.50.13 (Revo); installed Datto Workplace v10.53.4; `_CC` CLSID `{3C639243}` -> Workplace2 x64/x86 DLLs; removed `{2B96EDC1}` + non-`_CC` add-in keys; per-user `Datto.SmartBadgeShim_CC` LoadBehavior 2->3, cleared Resiliency, set DoNotDisableAddinList.
|
||||||
|
- 172.16.3.30: `cp /usr/local/bin/gururmm-server /opt/gururmm/gururmm-server` (0.3.36), old saved `/opt/gururmm/gururmm-server.0.3.32.bak`, `systemctl restart gururmm-server`.
|
||||||
|
- `.claude/scripts/post-bot-alert.sh` — dev/bot channel routing by prefix + override arg.
|
||||||
|
- `.claude/scripts/ksteen-smartbadge-verify.ps1`, `ksteen-smartbadge-fix.ps1`, `check-ksteen-smartbadge.sh` — new (SmartBadge verify/fix/daily-runner).
|
||||||
|
- `.claude/scripts/onboarding-diagnostic.ps1`, `run-onboarding-diagnostic.sh` — new (onboarding diagnostic Phase 1).
|
||||||
|
- `.claude/commands/rmm.md` — added `/rmm onboard`, `/rmm diagnose`, dev-alerts routing notes.
|
||||||
|
- `.claude/memory/reference_acg_msp_stack.md` (+ MEMORY.md index) — new.
|
||||||
|
- `wiki/clients/birth-biologic.md` — agents table, dual-Workplace SmartBadge known issue + fleet standard, 5/28-5/29 history.
|
||||||
|
- Vault `D:/vault/clients/rednour/gururmm-site-main.sops.yaml` — new (Rednour enrollment key).
|
||||||
|
- Scheduled task "ClaudeTools - KSTEEN SmartBadge Daily" on GURU-5070 (daily 09:00, 2026-05-30..06-05).
|
||||||
|
- gururmm PR #28 (branch `feat/spec-005-integration-center`) — revised SPEC-005.
|
||||||
|
|
||||||
|
### Credentials & Secrets
|
||||||
|
|
||||||
|
- Rednour GuruRMM site API key (one-time, `grmm_...`): vaulted at `clients/rednour/gururmm-site-main.sops.yaml` (credentials.api_key). Site `GREEN-FALCON-7214`.
|
||||||
|
- Gitea API token field path correction: `services/gitea.sops.yaml` -> `credentials.api.api-token` (not `credentials.api-token`).
|
||||||
|
- Syncro API key: `msp-tools/syncro.sops.yaml` -> `credentials.credential` (unchanged).
|
||||||
|
- No new secrets created beyond the Rednour site key.
|
||||||
|
|
||||||
|
### Infrastructure & Servers
|
||||||
|
|
||||||
|
- GuruRMM server 172.16.3.30: `/status` -> 0.3.36; service `ExecStart=/opt/gururmm/gururmm-server` (NOT /usr/local/bin — wiki stale); build via `sudo /opt/gururmm/build-server.sh` (builds in `/home/guru/gururmm/server`, log `/var/log/gururmm-build.log`).
|
||||||
|
- Rednour Law Offices: client `85f7cff4-d4db-48a8-b477-b8788122a361`, site Main `c7f5787c-8e71-45b3-841f-fa52436f7d26` / `GREEN-FALCON-7214`. Agents: FrontDeskReception `04765560-...`, LegalAsst `18825ea7-...`, rednourcarrievirt `8e4e2221-...`.
|
||||||
|
- BB agents: BB-SERVER `6c02baa7`, KSTEENBB2025 `ee3c6aea`, EVO-X1 `9595f002`, BB-Office2 `48763401`.
|
||||||
|
- Discord channels: #bot-alerts `624710699771232265`, #dev-alerts `1509998508198068484` (private).
|
||||||
|
|
||||||
|
### Commands & Outputs
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Server fix (172.16.3.30)
|
||||||
|
sudo cp /opt/gururmm/gururmm-server /opt/gururmm/gururmm-server.0.3.32.bak
|
||||||
|
sudo systemctl stop gururmm-server
|
||||||
|
sudo cp /usr/local/bin/gururmm-server /opt/gururmm/gururmm-server
|
||||||
|
sudo systemctl start gururmm-server
|
||||||
|
curl -s localhost:3001/status | jq .version # -> "0.3.36"
|
||||||
|
|
||||||
|
# vault new entry (non-interactive)
|
||||||
|
sops --config "$VR/.sops.yaml" --encrypt --in-place <file> # quote dates; secrets under credentials:
|
||||||
|
|
||||||
|
# onboarding diagnostic
|
||||||
|
bash .claude/scripts/run-onboarding-diagnostic.sh <host> <client-slug>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pending / Incomplete Tasks
|
||||||
|
|
||||||
|
- **Rednour:** real findings to action — 2x EOL Win10 22H2 (LegalAsst, REDNOURCARRIEVI), RDP-without-NLA (REDNOURCARRIEVI), missing BitLocker, low disk (REDNOURCARRIEVI ~12%), no backup agent (FrontDeskReception). Mike to direct remediation.
|
||||||
|
- **KSTEENBB2025:** SmartBadge confirmed working; 7-day watch running (coord todo `4a5b09b3`).
|
||||||
|
- **Integration Center:** PR #28 open for review/merge; then /shape-spec + scaffold (coord todo `0198ba04`). PARKED.
|
||||||
|
- **GuruRMM enrollment security:** site_id-only enrollment gate decision (coord todo `00074cd8`).
|
||||||
|
- **Onboarding diagnostic:** Phase 3 native feature (coord todo `76c6050b`); single-element-array MD-table bug (`cc5dbdfa`); ACG-stack detection allowlist tuning (`3d886f1a`).
|
||||||
|
- **Wiki:** gururmm.md ExecStart path (/opt vs /usr/local/bin) should be corrected; no wiki article for Rednour yet.
|
||||||
|
|
||||||
|
### Reference Information
|
||||||
|
|
||||||
|
- Syncro #32339 (BB SmartBadge): public cmt 414607766, warranty li 42639366. Customer 17983014.
|
||||||
|
- gururmm PR #28: https://git.azcomputerguru.com/azcomputerguru/gururmm/pulls/28
|
||||||
|
- Coord: Howard reply f888b21c; broadcast bafae411; todos 4a5b09b3 / 0198ba04 / 00074cd8 / 76c6050b / cc5dbdfa / 3d886f1a.
|
||||||
|
- Rednour install page: https://rmm.azcomputerguru.com/install/GREEN-FALCON-7214
|
||||||
|
- Baselines: `clients/rednour/onboarding-baselines/` (FRONTDESKRECEPT, LEGALASST, REDNOURCARRIEVI).
|
||||||
Reference in New Issue
Block a user