docs(migration): RMM-to-Breeze migration guides and toolkit - #3250
Open
ToddHebebrand wants to merge 7 commits into
Open
docs(migration): RMM-to-Breeze migration guides and toolkit#3250ToddHebebrand wants to merge 7 commits into
ToddHebebrand wants to merge 7 commits into
Conversation
Adds a "Migrating to Breeze" docs section: a vendor-neutral seven-phase playbook, a toolkit of ready-to-run API recipes, and per-vendor guides for Datto RMM, NinjaOne, ConnectWise Automate, Kaseya VSA, N-able N-central, Atera and Syncro, plus shorter notes for Pulseway, Action1, Automox, Level, Tactical RMM and ScreenConnect. The guides are built around two things the product already does well: the incumbent RMM's own script engine is the best vehicle for deploying the Breeze agent (it has SYSTEM-level reach on 100% of the fleet), and mgmtdetect/Management Posture already fingerprints 11 competing RMMs, which makes Breeze — not the incumbent's console — the authoritative source for whether a machine is safe to decommission. The toolkit documents the current API accurately rather than aspirationally: tenancy writes need a user JWT with MFA satisfied (X-API-Key is accepted only on the MCP, dev-push and device custom-field surfaces), enrollment keys take maxUsage up to 100k and ttlMinutes up to 365 days, and POST /scripts takes osTypes + language rather than a shell field. Gaps found while writing are filed as #3242-#3248 under epic #3249 and listed in the toolkit's "Known Rough Edges" table. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Deploying breeze with
|
| Latest commit: |
805609b
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://84c1364d.breeze-9te.pages.dev |
| Branch Preview URL: | https://docs-rmm-migration-guides.breeze-9te.pages.dev |
Design for the first phase of the RMM migration epic (#3249). Introduces organization_external_links, a one-to-many external-system linkage replacing the single-valued accounting_provider/accounting_external_id pair that shipped with the QuickBooks importer. Single-valued linkage breaks as soon as a second importer exists: an org sourced from a Datto CSV and also linked to ConnectWise and QuickBooks can only hold one link, and the second importer silently overwrites the first, breaking its idempotency. partner_id is denormalised onto the link table so uniqueness can be scoped per partner, and held honest by a composite FK to organizations(id, partner_id) -- the same mechanism used for users. The table deliberately has no jsonb column, since any open container must be classified excludedOpen and would then be dropped from tenant export. Also defines the shared orgImport preview/commit seam that #3246 plugs into, and records the deferred phases: QuickBooks migration onto the seam, the accounting_* column drop, custom-field backfill (#3257) and first-class contacts (#3258). The linkage decision is marked open pending the independent advisor review, which could not run (expired Codex auth). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Marks the spec ready for review and records why the one-to-many organization_external_links table wins over generalising the shipped single-valued accounting_* columns. The deciding point is that generalising is not the cheap option it looks like: renaming shipped columns still needs a migration, a reader sweep, and an update to the organizations entry in CORE_TENANT_EXPORT_POLICY, since removing or renaming a column on a registered table breaks the export-policy test exactly as adding one does. It pays most of the cost and still buys a one-link-per-org ceiling whose failure mode is silent duplicate-tenant creation. Also records the reversal-cost asymmetry that justifies the choice, and notes that the decision was made without an independent advisor leg. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…#3245) Two more specs for the RMM migration epic (#3249). Fleet migration report (#3244): aggregate devices.management_posture on the fly rather than denormalising detections into a table. The jsonb is rewritten by the agent on every scan, so a denormalised copy that misses a sync would report a competing agent as removed while it is still installed -- which for a decommission report is exactly the failure that strands endpoints. Notes the LEFT JOIN LATERAL + COALESCE as load-bearing: a CROSS JOIN silently drops never-scanned devices and makes the report read better than reality. Script bundles (#3245): versioned JSON bundle carrying no tenancy identifiers, with isSystem stripped unconditionally on import -- stricter than POST /scripts, which permits it at system scope, because a bundle is untrusted input whoever uploads it. Automations are excluded from bundles deliberately; a bundle that could carry a schedule binding would make import arbitrary scheduled RCE across a fleet. Loose .ps1/.sh files are converted to a bundle client-side, so the API keeps one JSON intake path. Both defer cleanly and neither adds a table, so no RLS/cascade/export registration. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
An adversarial review of the three migration-epic specs found that each one's flagship guarantee was contradicted by the concrete artifact in the same document. The mechanical contracts (RLS shape, cascade lists, export policy) were right; the end-to-end data flow was not. #3242 -- the spec deferred migrating the QuickBooks WRITER to a later phase while moving its READER to the link table in this PR. Every org QuickBooks created after that would have had no link row, so the next import would not match it and would mint a duplicate organization: precisely the silent duplicate-tenant creation the link table exists to prevent. Now requires dual-write plus a union read until the columns are dropped. Also records that soft-deleted orgs keep the unique slot occupied, corrects the cascade-ordering rationale (the delete order is computed topologically from pg_constraint, not taken from the alphabetical list), and cites deployment_invites as the shipped prior art for the composite FK. #3244 -- neverScannedCount was not computable from the spec's own query. A single GROUP BY collapsed three distinct populations (never scanned, scanned with category absent, scanned with empty array) into one NULL-product row, so a never-scanned device was indistinguishable from a verified-clean one. Split into two queries: detections, and coverage denominators computed without the lateral join. Also count(DISTINCT d.id) so a duplicated product entry cannot double-count a device. #3245 -- the security section missed three fields. parameters is z.any() with no intake bound (the 64KB cap is execute-time only); an all-null exitCodeSeverityMapping ships a script pre-configured never to alert; and availability:'partner' rides a path with no canManagePartnerWidePolicies check (#3262). Import now defaults to 'org' and depends on #3262 landing first. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…cle (#3261) specs + implementation plans Two approved design specs plus their ready-to-implement plans: - Proxy access (#3199): fix both 5-minute session death mechanisms (never- refreshed cookie + legacy tunnel_open TCP socket reaped agent-side), honest server-side status with lazy expiry, idempotent single-port proxy-connect replacing Enable Proxy Access, single entry point on /devices/network/:id ("Proxy to {ip}:{port} through agent"), in-place self-signed retry, locale fixes x7, live-target verification. - Asset linking (#3261): hidden-and-automatic identity linking — durable unlink via auto_link_suppressed_at, auto-links unlinkable, modal loses all link controls, network device page becomes the single override surface, "Same device as {name}" function-first wording. States the privilege-free link invariant that makes MAC/IP auto-matching safe. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Five plans in the open work queue, ordered by dependency. 2026-08-08-scripts-partner-wide-gate (#3262) -- do first. Live authz gap: POST /scripts gates partner-wide creation on scope alone, so a 'selected' access partner user can push SYSTEM-level code to every org under the partner. Includes a sweep task for sibling dual-ownership tables. 2026-08-08-org-external-links-and-bulk-import (#3242) -- new organization_external_links table plus the shared orgImport seam. Task 4 (QuickBooks dual-write) is called out as non-deferrable: moving the reader without the writer would mint duplicate organizations. 2026-08-08-partner-api-provisioning-writes (#3243) -- write scopes and three POST routes on the Partner API, whose machine principal already exists. The non-GET allowlist test is written first so its first run proves it catches the new routes. 2026-08-08-fleet-migration-posture-report (#3244) -- two aggregate queries over the existing management_posture jsonb, no new table. Flags up front that one GROUP BY collapses never-scanned devices into verified-clean. 2026-08-08-script-bundle-import-export (#3245) -- blocked on #3262 for the partner-wide path; the rest can proceed. Bounds parameters at intake (the schema is z.any() and the 64KB cap is execute-time only) and rejects an all-null exitCodeSeverityMapping. Each is registered in the open/ inbox table with its status tag. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds a Migrating to Breeze docs section (
apps/docs/src/content/docs/migration/) and wires it into the Starlight sidebar.overviewtoolkitdatto-rmm,ninjaone,connectwise-automate,kaseya-vsa,n-central,atera,syncroother-rmmsThe two ideas the guides are built on
mgmtdetectalready fingerprints 11 competing RMMs. That makes Breeze — not the incumbent's console — the authoritative decommission report, because it can see machines whose incumbent agent is already broken. Those are exactly the endpoints that strand and need a site visit.Accuracy
The toolkit documents the API as it is, verified against the source rather than the existing reference docs:
X-API-Keyis accepted only on the MCP server, dev-push, and device custom-field-value surfaces —authMiddleware(middleware/auth.ts:425) hard-requiresBearer. The API reference currently claims otherwise; filed as [Docs] API reference wrongly documents X-API-Key as general-purpose auth — the /devices example returns 401 #3247.maxUsageup to 100,000 andttlMinutesup to 525,600,.strict(), andttlMinutesXORexpiresAt. The raw key is returned once, on create.POST /scriptstakesosTypes+language(not ashellfield), andavailability: "partner"— the right default for a migrated MSP toolkit.All internal doc links verified to resolve against existing slugs. MDX scanned for unescaped JSX braces/angle brackets outside code fences — clean.
Gaps filed
Writing these surfaced where the product makes migration harder than it needs to be. Filed under epic #3249: #3242 (no bulk org/site import), #3243 (no M2M provisioning credential), #3244 (no fleet migration report from Management Posture), #3245 (no script import/export), #3246 (PSA
getCompanies()implemented everywhere, called nowhere), #3247 (API-key docs bug), #3248 (no GPO/Intune guide). Each is listed in the toolkit's "Known Rough Edges" table with its documented workaround, so the docs are honest about current friction rather than quietly routing around it.Not verified locally
The Astro build was not run —
apps/docsdependencies are not installed in this environment and local Node is v20 against the repo's required v22. Link resolution and MDX-unsafe syntax were checked by script instead; the build check in CI is the real gate.🤖 Generated with Claude Code