diff --git a/.claude/SKILL_ROUTING.md b/.claude/SKILL_ROUTING.md index a35e83d9..8c36d367 100644 --- a/.claude/SKILL_ROUTING.md +++ b/.claude/SKILL_ROUTING.md @@ -23,11 +23,7 @@ | Run a command / investigate / script on an RMM agent | `/rmm` (find the host first with `rmm-search`) | | M365 investigation/remediation, breach check, mailbox/inbox-rule/forwarding audit, tenant sweep | `remediation-tool` | | Onboard a new M365 tenant to the remediation app suite | `onboard365` | -| Is client X backing up? backup status/failures/stale plans; MSP360 backup user/company/license provisioning | `msp360` (authoritative backup layer — check here, not bucket contents) | -| Backblaze / B2 storage, buckets, backup storage cost (destination side) | `b2` | -| Who backs up to Seafile / ownCloud / Datto Workplace (per-backend inventory + endpoint detection) | `seafile` / `owncloud` / `datto-workplace` | -| Provision/admin Seafile (SeaCloud) — create/deactivate/delete account, set quota, create/transfer/delete library, share/unshare | `seafile` (writes preview by default; `--confirm` to apply) | -| Provision/admin ownCloud (cloud.acghosting) — create/delete/enable/disable user, reset password, set quota, groups, apps/config, maintenance mode, files scan/transfer, trashbin/versions cleanup | `owncloud` (writes gated `--confirm`; `--all-users` purge also needs `--force-all-users`) | +| Anything about a customer's **BACKUP** — is X backing up, who backs up where, backup status/failures, or set up / provision a backup | **Backups → wiki-first** (see the "Backups" block right below this table) | | Bitdefender / GravityZone AV — endpoints, sweeps, policies, quarantine, EDR | `bitdefender` | | Datto EDR / Datto AV — detections, isolate, scan, agent deploy | `datto-edr` | | VoIP — PacketDial / OITVOIP / NetSapiens domains, users, DIDs, queues, CDRs | `packetdial` | @@ -50,6 +46,31 @@ > Not listed? If no skill fits, do it directly — and say so. New recurring need → `skill-creator`. +### Backups (wiki-first routing) + +A customer can back up through **any** of several backends, so a backup request is a +two-step route: **first find out which backend THIS customer uses, then use that skill.** +Never assume B2/MSP360 — several clients are on Seafile, ownCloud, or Datto Workplace. + +1. **Identify the backend for this customer (don't guess).** Read the client wiki + (`wiki/clients/.md`) and the backup map (`projects/gps-rmm-audit/backup-map.md`) — + both record each customer's real destination. If it isn't recorded, confirm with + `msp360 monitoring --company ` plus the per-backend inventory skills, then write the + answer back to the client wiki. +2. **Route to that backend's skill:** + + | Need | Skill | + |---|---| + | Is X backing up? status / last run / failures / stale plans (MSP360 + B2 Cloud plans) | `msp360` — **authoritative**, check here not bucket contents | + | Who backs up to Seafile / ownCloud / Datto Workplace (inventory + endpoint detection) | `seafile` / `owncloud` / `datto-workplace` | + | Storage / bucket / cost on the destination side | `b2` (Backblaze) | + | **Set up / provision a backup** — create account/user, quota, library, license, destination | the **target backend's** skill: `msp360` (MBS user/company/license), `seafile`, `owncloud` (all writes gated `--confirm`); `b2` for a new bucket/key | + +- **Which machine gets backed up is a per-client decision** — report mismatches (billed vs + running), don't provision jobs off a billing count. See [[feedback_backup_targets_never_guessed]]. +- Full per-customer destination + health map: `projects/gps-rmm-audit/backup-map.md`; + how backup-status works: memory `[[reference_msp360_backup_monitoring]]`. + --- ## Check-skills (work-type → the gate run BEFORE "done") diff --git a/projects/gps-rmm-audit/session-logs/2026-07/2026-07-05-howard-gps-backup-map-msp360.md b/projects/gps-rmm-audit/session-logs/2026-07/2026-07-05-howard-gps-backup-map-msp360.md index 5465ec1e..e692f45a 100644 --- a/projects/gps-rmm-audit/session-logs/2026-07/2026-07-05-howard-gps-backup-map-msp360.md +++ b/projects/gps-rmm-audit/session-logs/2026-07/2026-07-05-howard-gps-backup-map-msp360.md @@ -155,3 +155,48 @@ Tedards DESKTOP-SUFJR0J last Success 2025-07-12 (~12 mo); Glaz-Tech GTI-INV-VMHO `https://acg.workplace.datto.com/api/v1/openapi.json` - Datto WP API scope docs: help.workplace.datto.com API Integrations (Super Administrator access enables full-team project visibility on the file API). + +## Update: 21:23 PT — built the msp360 skill (full CRUD) + consolidated backup routing + +Built the `msp360` skill (Howard asked for full management, not read-only), then reviewed, +fixed, and published it; consolidated all backup-related skill routing behind a wiki-first +rule. + +**MSP360 API surface** — confirmed authoritative endpoint list against +`help.mspbackups.com/mbs-api-specification/methods` and live `Allow`-header probing: full +CRUD on Users, Companies, Licenses (Grant/Release/Revoke), Destinations, Accounts, +Packages, Administrators; read-only Monitoring/Billing. `/api/Computers` returns HTTP 400 +"Remote Management API methods are not enabled" (feature off on this account). Pulled live +User/Company/License schemas so write payloads use real field names, not guesses. + +**Skill built** (`.claude/skills/msp360/`): `msp360_client.py` (Bearer login + typed +helpers, reuses `backup_common.py`), `msp360.py` (CLI), `SKILL.md`, `.gitignore`. +Reads: status, monitoring (`--failed`/`--company`/`--stale`/`--json`), companies, users, +licenses, billing, raw GET. Writes (all `--confirm`-gated, dry-run preview otherwise): +create/delete company, create/delete user (`--keep-data`), grant/release/revoke license, +raw passthrough. Errors -> `errorlog.md`; expected conditions (feature-gate, no-match) not. + +**Code review** (workflow, high effort, 18 agents): 10 findings, all fixed + re-verified. +Notable: (1) CREDENTIAL LEAK — create-user dry-run echoed plaintext `--password`; now +redacted. (2) main() caught only `Msp360Error`, not parent `bc.BackupError`, so vault/HTTP +failures were mislabeled "unexpected"; now catches `bc.BackupError`. (3) non-list 2xx body +crashed reads; added `_as_list` guard. (4) licenses `Number` format crash on None; coalesced. +(5) `monitoring --failed` hid never-run/Unknown plans (the exact dead-plan case); now +included. Plus stale-date parse robustness, feature-gate substring broadening, dead +`--json` on billing, dead `update_*` client methods, and `_root()` now uses +`bc.resolve_root()`. Refuted 1 (SKILL.md does not document passwords on CLI). + +**Dry-test sweep:** 14/14 reads OK; 8/8 writes correctly gated (nothing executed without +`--confirm`); password-redaction confirmed. + +**Routing consolidation** (`.claude/SKILL_ROUTING.md`): seafile + owncloud had each written +their own provisioning rows. Collapsed all five backup rows (msp360/b2/seafile/owncloud/ +datto) into ONE pointer row + a dedicated **"Backups (wiki-first routing)"** block: a backup +request is a two-step route — (1) identify which backend THIS customer uses via the client +wiki + `backup-map.md` (don't assume B2/MSP360), then (2) route to that backend's skill; +provisioning uses the target backend's skill (all writes gated). Encodes both behaviors +Howard asked for (check-the-wiki-for-status, and use-the-skills-to-set-up). + +**Published:** commits `976bfe7` (skill) + `a8e76ba` (routing/memory) pushed to origin; +Phase 5c installed msp360 to global. Memory `reference_msp360_backup_monitoring` updated +(skill now exists, full not read-only).