syncro: finalize deploy-agent (installer URL = per-client vaulted secret, MSI)
Decoded the Syncro RMM installer URL from 4 client samples:
base64(v1-{customer_id}-{A}-48753-{policy_folder_id}). 48753 = our account id (constant);
customer_id + policy_folder_id are API-readable; A is an unguessable per-client security token
NOT exposed anywhere in the API -> the URL is NOT constructible and must be harvested per client
and vaulted. Installer is an MSI -> deploy uses msiexec /qn /norestart.
deploy-agent now reads clients/<slug>/syncro-agent-installer (credentials.installer_url) from
the vault (cascades-tucson, grabb-durando, reliant, howard-test seeded) and pushes via GuruRMM.
Findings recorded in test-findings.md.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -741,46 +741,51 @@ curl -s "${BASE}/rmm_alerts?per_page=50&api_key=${API_KEY}" | tr -d '\000-\037'
|
|||||||
|
|
||||||
#### Agent Deployment (`/syncro deploy-agent`) — hybrid via GuruRMM
|
#### Agent Deployment (`/syncro deploy-agent`) — hybrid via GuruRMM
|
||||||
|
|
||||||
Push the Syncro RMM agent to a machine. Syncro's API has **no** installer-download endpoint,
|
Push the Syncro RMM agent to a machine. Syncro's API has **no** installer-download endpoint
|
||||||
so this is a **hybrid**: the per-policy-folder installer (which embeds the customer enrollment
|
(verified: 0 of 116 paths), so this is a **hybrid**: the per-policy-folder installer URL is
|
||||||
token) is obtained once from the Syncro GUI and vaulted; **GuruRMM (`/rmm`)** does the actual
|
harvested once from the Syncro GUI and **vaulted**; **GuruRMM (`/rmm`)** does the push (runs as
|
||||||
push (it runs PowerShell as SYSTEM on the endpoint). `/rmm diagnose` already *detects* a
|
SYSTEM). `/rmm diagnose` already *detects* a Syncro agent — this is the deploy half.
|
||||||
Syncro agent as a competitor-RMM check — this is the deploy half.
|
|
||||||
|
|
||||||
**Prerequisites (one-time per customer):**
|
**The installer URL is a per-client SECRET and is NOT constructible.** Decoded (2026-07-10):
|
||||||
1. In Syncro: **Assets & RMM → Downloads** (or the target Policy Folder) → copy the
|
`https://rmm.syncromsp.com/dl/msi/<b64>` where `<b64>` = base64 of
|
||||||
`SyncroSetup-<company>-<token>.exe` **download URL**. It is per-policy-folder — the token
|
`v1-{customer_id}-{A}-48753-{policy_folder_id}`. `48753` is OUR account id (constant across all
|
||||||
binds the installed agent to that customer + policy. Do NOT reuse one customer's URL for
|
clients); `customer_id` and `policy_folder_id` are API-readable — but **`A` is an unguessable
|
||||||
another.
|
~1.8-billion per-client security token that appears nowhere in the API** (checked customer +
|
||||||
2. Vault it via the `vault` skill at `clients/<slug>/syncro-agent-installer.sops.yaml`
|
policy-folder objects). So you cannot build the URL; it must be harvested per client. Note the
|
||||||
(`credentials.installer_url`). Never hardcode the token URL.
|
link is an **MSI** (`/dl/msi/`) → silent install is `msiexec /qn`, not the `.exe --console` path.
|
||||||
|
|
||||||
|
**Prerequisites (one-time per client):**
|
||||||
|
1. In Syncro GUI: the client's Policy Folder → **Downloads** → copy the `.../dl/msi/<token>` URL.
|
||||||
|
2. Vault it (it embeds the secret `A`): `bash .claude/skills/vault/scripts/vault-helper.sh new
|
||||||
|
clients/<slug>/syncro-agent-installer --kind generic --name "Syncro RMM Installer - <Client>"
|
||||||
|
--set installer_url=<URL> --set customer_id=<N> --set policy_folder_id=<N>`. Already vaulted:
|
||||||
|
`cascades-tucson`, `grabb-durando`, `reliant`, `howard-test`.
|
||||||
|
|
||||||
**Deploy workflow:**
|
**Deploy workflow:**
|
||||||
1. Resolve the target host in GuruRMM (`/rmm` — resolve hostname → UUID; confirm `os_type`
|
1. Read the URL: `bash .claude/scripts/vault.sh get-field clients/<slug>/syncro-agent-installer
|
||||||
is `windows` and it is `is_connected`). Confirm the customer↔host mapping with the user.
|
credentials.installer_url`. If the client has no entry, STOP and have the URL harvested first.
|
||||||
2. Preview the exact install command + target, get explicit confirmation.
|
2. Resolve the target host in GuruRMM (`/rmm` — hostname → UUID; confirm `os_type` windows +
|
||||||
3. Dispatch via GuruRMM (`command_type: powershell`, SYSTEM context). The agent installs
|
`is_connected`). Confirm the client↔host mapping with the user.
|
||||||
**silently by default**; `--console` runs it non-interactively and `--allow-force-reboot`
|
3. Preview the exact command + target; get explicit confirmation. **deploy-agent is
|
||||||
permits the reboot Syncro triggers if it must update .NET first:
|
outward-facing (installs software, may reboot) — always confirm first.**
|
||||||
|
4. Dispatch via GuruRMM (`command_type: powershell`, SYSTEM). Download the MSI and install silent:
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
$url = '<vaulted SyncroSetup URL>'
|
$url = '<vaulted installer_url>'
|
||||||
$exe = "$env:TEMP\SyncroSetup.exe"
|
$msi = "$env:TEMP\SyncroSetup.msi"
|
||||||
Invoke-WebRequest -Uri $url -OutFile $exe -UseBasicParsing
|
Invoke-WebRequest -Uri $url -OutFile $msi -UseBasicParsing
|
||||||
Start-Process -FilePath $exe -ArgumentList '--console','--allow-force-reboot' -Wait
|
Start-Process msiexec.exe -ArgumentList '/i',"`"$msi`"",'/qn','/norestart' -Wait
|
||||||
```
|
```
|
||||||
(Refs: Syncro deploy docs `docs.syncrosecure.com/agents-alerts-automations/deploy-an-endpoint`;
|
(`.exe` variant, if a client's link is `/dl/exe/`: `Start-Process $exe -ArgumentList
|
||||||
community "Command Line install".) `--allow-force-reboot` CAN reboot the endpoint — treat
|
'--console','--allow-force-reboot' -Wait`.)
|
||||||
deploy-agent as an outward-facing action: confirm reboot tolerance with the user first.
|
5. **Verify enrollment:** after ~2-3 min, confirm the new asset appears —
|
||||||
4. **Verify** the agent enrolled: after ~2-3 min, either re-query GuruRMM output, or confirm
|
`GET /customer_assets?customer_id=N&query=<host>` — and/or "Syncro" shows in the endpoint's
|
||||||
the new asset appears under the customer via `GET /customer_assets?customer_id=N&query=<host>`
|
`installed_applications`.
|
||||||
and/or that "Syncro" shows in the endpoint's `installed_applications`.
|
6. Bot alert (RMM write → `[RMM]`/`[DEPLOY]` routes to #dev-alerts).
|
||||||
5. Bot alert (RMM write → `[RMM]`/`[DEPLOY]` routes to #dev-alerts; Syncro-side note to
|
|
||||||
#bot-alerts if an asset record was created).
|
|
||||||
|
|
||||||
**[VERIFY before first production use]** Confirm `--console --allow-force-reboot` against the
|
**[VERIFY before first production use]** Confirm the `msiexec /qn /norestart` silent behavior on
|
||||||
current Syncro installer on one ACG-internal test machine before rolling to a customer — the
|
RMM-TEST-MACHINE (Howard Test) before rolling to a customer. Full detail:
|
||||||
switches are stable historically but the installer is versioned.
|
`.claude/standards/syncro/test-findings.md`.
|
||||||
|
|
||||||
#### Customers
|
#### Customers
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,30 @@ Verified behaviors/gotchas discovered while testing `/syncro` against the live t
|
|||||||
Each entry is a rule the skill must not re-learn. Critical ones are also folded into
|
Each entry is a rule the skill must not re-learn. Critical ones are also folded into
|
||||||
`syncro.md` (operating manual) and `api-reference.md` (breadth reference).
|
`syncro.md` (operating manual) and `api-reference.md` (breadth reference).
|
||||||
|
|
||||||
|
## 2026-07-10 — Phase 2 prep: RMM installer URL (structure decoded)
|
||||||
|
|
||||||
|
**The Syncro RMM installer URL is a base64 token that partly encodes API-derivable IDs.**
|
||||||
|
A working install link is `https://rmm.syncromsp.com/dl/msi/<b64>` where `<b64>` base64-decodes
|
||||||
|
to `v1-{customer_id}-{A}-{B}-{policy_folder_id}`. For Howard Test:
|
||||||
|
`djEt...` → `v1-36118743-1819842526-48753-5092561` (customer_id 36118743, policy_folder_id
|
||||||
|
5092561 — both pulled from the API; `A=1819842526`, `B=48753` are NOT on the customer or
|
||||||
|
policy-folder objects). Host is `rmm.syncromsp.com` (Kabuto/RMM), separate from the PSA API.
|
||||||
|
|
||||||
|
- **RESOLVED (4-client sample):** `B=48753` is CONSTANT (our Syncro account id). `A` VARIES per
|
||||||
|
client (1819842526 / 1610167344 / 1552910968 / 1520216784) and is **not** `kabuto_customer_id`
|
||||||
|
nor anywhere in the customer/policy-folder objects — an unguessable per-client security token
|
||||||
|
(prevents URL forgery from sequential customer_ids). **Therefore the URL is NOT constructible**
|
||||||
|
— it must be harvested per client and vaulted as a secret. `customer_id` + `policy_folder_id`
|
||||||
|
ARE API-readable, but `A` is the blocker. Vaulted so far: cascades-tucson, grabb-durando,
|
||||||
|
reliant, howard-test at `clients/<slug>/syncro-agent-installer.sops.yaml`
|
||||||
|
(`credentials.installer_url`). `deploy-agent` reads from there.
|
||||||
|
- **Installer is an MSI** (`/dl/msi/`), so silent deploy uses `msiexec /i <file>.msi /qn
|
||||||
|
/norestart` (NOT the `.exe --console` path). Syncro also offers a `.exe` (`--console
|
||||||
|
--allow-force-reboot`); the download link determines which. Verify the exact MSI silent
|
||||||
|
behavior on RMM-TEST-MACHINE before any customer push.
|
||||||
|
- The token embeds an enrollment secret → it is a **credential**: vault it (per client), never
|
||||||
|
commit plaintext. `policy_folder_id` comes from `GET /policy_folders?customer_id=N`.
|
||||||
|
|
||||||
## 2026-07-10 — Phase 1 (PSA CRUD)
|
## 2026-07-10 — Phase 1 (PSA CRUD)
|
||||||
|
|
||||||
**Emailing an invoice/estimate returns 200 "Email sent" even when it delivered to NOBODY.**
|
**Emailing an invoice/estimate returns 200 "Email sent" even when it delivered to NOBODY.**
|
||||||
|
|||||||
Reference in New Issue
Block a user