## User - **User:** Howard Enos (howard) - **Machine:** Howard-Home - **Role:** tech ## Session Summary Expanded and end-to-end tested the `/syncro` skill from PSA-only to full Syncro API coverage, including asset/RMM management and a working Syncro-agent deployment path. Started by auditing the live OpenAPI 3.0 spec (`api-docs.syncromsp.com/swagger.json`) against the existing skill and discovered the skill deliberately avoided asset writes based on a memory (`reference-syncro-rmm-api-gui-only`) that turned out to be a token-scope symptom, not an API limit. Pulled the complete inventory (~180 operations across 38 resource types), built a new verified reference doc, and probed the live tenant with Howard's per-user token to map exactly what works vs. what needs scope. Corrected the record: the entire asset/RMM surface (create/update, patches, installed_applications, rmm_alerts, contacts, contracts, leads, vendors, POs, products, payments, wiki) is API-drivable; only policy-folder operations were 401-gated by a missing "Assets - Policy Change" token scope. Mike upgraded Howard's token to full scope mid-session; verified the policy-folder move works via flip-and-restore on a test asset. Also checked Mike's and Winter's keys — Winter full, Mike's vaulted token (…ebbeb3) still 401 (needs re-vault). Ran a full live test campaign on a new sandbox customer ("Howard Test", 36118743). Phase 1 exercised the complete PSA + breadth write surface (tickets, comments, line items, invoices, estimates, appointments, schedules, contracts, contacts, leads, vendors, products, purchase orders, worksheets) and captured every response shape and gotcha. Phase 2 proved the deploy path end-to-end: pushed the per-policy-folder Syncro MSI installer to RMM-TEST-MACHINE via GuruRMM (`/rmm`), the box auto-enrolled as a Syncro asset in the correct client + policy folder, then tested patches, installed_applications, policy-folder create/move/delete, and rmm_alerts on the real device. Decoded the installer URL structure and confirmed it is a per-client secret (not constructible), so harvested links are vaulted per client (4 seeded). Confirmed Syncro's public API has NO remote device-control/exec endpoint — execution stays on GuruRMM. Finished by cleaning up the sandbox (API-deleted the deletable records, uninstalled the Syncro agent from RMM-TEST-MACHINE) and recording all findings in a living `test-findings.md` plus the skill's hard rules. ## Key Decisions - Kept the high-frequency PSA workflows in `syncro.md` and put the full breadth in a new `api-reference.md` rather than bloating the incident-scarred core. - Installer-push (`deploy-agent`) lives in `/syncro` (Howard's call) but uses GuruRMM as the execution arm — Syncro has no installer-download or exec API. - Chose lazy harvest of installer URLs (grab+vault+wiki-pointer at first deploy) over proactive harvesting of all ~69 clients. - Vaulted installer URLs (they embed an unguessable per-client secret token) rather than storing plaintext in the wiki; wiki gets a pointer. - Built `move-asset` with a capability preflight + mandatory post-write verify because an under-scoped token returns HTTP 200 and silently no-ops the folder change. - Did not probe undocumented endpoints (e.g. guessed exec routes) per the skill's hard rule. ## Problems Encountered - **Emails not delivered despite 200 "Email sent".** Invoice/estimate email endpoints take no recipient param and send to nobody when `customer.email` is null. `customer.email` is unique tenant-wide (Howard's real email was "already taken"). Fixed with a plus-addressed unique email (`howard+synctest@`), which delivered — resolved. - **Null write responses looked like failures.** POST /contacts (flat `.id`), POST /contracts (no echo) returned null on the wrapped parse though the writes succeeded. GET-verify caught it; a blind retry would have duplicated. Documented the three response-shape families. - **Policy-folder 401 then 404.** Old token 401'd on `/policy_folders`; upgraded token 404s on the bare route but 200s with `?customer_id=N`. `POST /policy_folders` requires `parent_id`. - **PO line item rejected** until the product had `maintain_stock:true`. - **Customer would not hard-delete** — `DELETE /customers` disables instead when associated data (asset/lead) exists. Expected; noted GUI-only leftovers. - **jq `--arg` path lookup quirk** on the swagger file — switched to single non-parameterized walks / hardcoded paths. - **/tmp write blocked** by the block-tmp-path hook (working as intended) — used repo-relative `.claude/tmp/`. ## Configuration Changes Created: - `.claude/standards/syncro/api-reference.md` — full verified Syncro endpoint catalog + worked templates. - `.claude/standards/syncro/test-findings.md` — living log of verified behaviors/gotchas. - `session-logs/2026-07/2026-07-10-howard-syncro-skill-expansion.md` — this log. Modified: - `.claude/commands/syncro.md` — asset read/write/move, RMM alerts, deploy-agent, capability preflight helper, email-delivery hard rule, expanded Verified Response Shapes table, reference pointer. - `.claude/memory/reference_syncro_rmm_api_gui_only.md` + `.claude/memory/MEMORY.md` — corrected the "RMM is GUI-only" belief to the verified token-scope reality. - `errorlog.md` — two `--correction` entries (asset-API assumption; email 200 != delivered). Vault (separate repo, pushed): - `clients/{cascades-tucson,grabb-durando,reliant,howard-test}/syncro-agent-installer.sops.yaml` — Syncro RMM MSI installer URLs (encrypted). ## Credentials & Secrets - Syncro per-user API keys (unchanged): `msp-tools/syncro` (Mike, uid 1735, …ebbeb3 — still lacks Policy-Change scope), `msp-tools/syncro-winter` (Winter, uid 1737, …5edb3a — full), `msp-tools/syncro-howard` (Howard, uid 1750, …680e18 — full, upgraded this session). - Syncro installer URLs vaulted at `clients//syncro-agent-installer` (`credentials.installer_url`). Structure: `base64(v1-{customer_id}-{A:secret}-48753-{policy_folder_id})`; `48753` = ACG Syncro account id; `A` is an unguessable per-client secret (not API-derivable). - GuruRMM API creds unchanged: `infrastructure/gururmm-server.sops.yaml`. ## Infrastructure & Servers - Syncro PSA API: `https://computerguru.syncromsp.com/api/v1` (query-param auth, 180 req/min). - Syncro RMM installer host: `https://rmm.syncromsp.com/dl/msi/` (Kabuto, separate from PSA API). - GuruRMM: `http://172.16.3.30:3001` (JWT auth). - RMM-TEST-MACHINE (GuruRMM agent id `99d6d692-99e0-4359-9f9c-f43be89f49e5`, online, v0.6.66, client "AZ Computer Guru", site "Howard-VM"). A second stale enrollment exists (`7d3456f5-…`, offline). Syncro agent MSI product code `{B7F56D3D-2AD3-4021-9D36-3B9E9C9FBE33}`. - Sandbox: Syncro customer "Howard Test" 36118743 (now DISABLED), policy folder 5092561, enrolled asset 12676769. ## Commands & Outputs - Decode installer token: `echo | base64 -d` → `v1-{customer_id}-{A}-48753-{policy_folder_id}`. - Capability preflight (policy scope): `GET /policy_folders?customer_id=N` → 200 has scope, 401 lacks. - Deploy (GuruRMM): download MSI, `Start-Process msiexec.exe -ArgumentList @('/i',$msi,'/qn','/norestart') -Wait` → service `Syncro` Running, asset enrolls in ~1 min. - Uninstall (GuruRMM): find Uninstall reg entry `DisplayName -match 'Syncro'`, `msiexec /x {ProductCode} /qn /norestart` → service/process/dir/reg all removed. - `DELETE /customers/{id}` on customer with associated data → `{"message":"Customer was disabled instead of deleted because they have associated data."}`. ## Pending / Incomplete Tasks - **Mike's Syncro token (…ebbeb3) still lacks Assets-Policy-Change** — re-vault after he confirms/upgrades the correct token, so `move-asset` works in Mike-attributed sessions. - **GUI-only sandbox leftovers** (no API DELETE): Howard Test asset 12676769 (archive), lead 43492446, vendor 809317, product 23639304, PO 2101221; customer 36118743 stays DISABLED until asset+lead cleared. - Optional: add a `deploy-agent --uninstall` action (msiexec /x method verified). - Optional: grant Policy-Change scope to all per-user tokens for consistent `move-asset`. - Lazy-harvest installer URLs for other clients at first deploy. ## Reference Information - Spec: `https://api-docs.syncromsp.com/swagger.json` (OpenAPI 3.0, 116 paths / ~180 ops / 38 tags). - Skill: `.claude/commands/syncro.md`; reference `.claude/standards/syncro/api-reference.md`; findings `.claude/standards/syncro/test-findings.md`. - Commits this session: `1f56098`, `12bb9f1`, `555e1ff`, `1fa1a42`, `ae2ee34`, `269449d`, `8afd7ec` (+ this save). - Vault installer entries: `clients/{cascades-tucson,grabb-durando,reliant,howard-test}/syncro-agent-installer`.