Integrations overview
Lockstep VM unifies data from every tool an MSP runs — EDR, RMM, MDM, a vuln scanner, a directory, and tools with no API at all (via CSV/Excel import) — into one per-tenant inventory. Each API integration follows the same lifecycle and the same code shape, so once you understand one you understand them all.
The shared integration lifecycle
Every API integration (SentinelOne, Rapid7, Intune, ConnectWise Automate, Entra ID) is implemented as a FastAPI router plus a background sync task, and every one of them moves through these five stages:
- Env seed — on backend startup,
app/main.pyruns a_seed_*_configcoroutine for each source. If the relevant environment variables are set and no config yet exists for thedefaulttenant, it inserts a config document keyedtenant_id: "default". This lets an operator wire credentials once (in the environment) and have the UI ready immediately, with no manual setup. - Adopt-on-read config — each router has a
_resolve_config(tenant_id, db)helper. It looks up the tenant’s own config first; if none exists and the tenant is notdefault, it copies the env-seededdefaultconfig into that tenant on first access (anupsertwith$setOnInsert). After adoption, per-tenant edits and deletes win — the tenant owns its copy. Config lives in a per-source collection keyed bytenant_id(sentinelone_config,rapid7_config,intune_config,connectwise_automate_config,entra_config). - Test —
POST .../test-connectionbuilds a client via_build_clientand makes one live API call to validate credentials before you rely on a sync. - Sync —
POST .../syncenqueues a background_run_sync(tenant_id, db)task (FastAPIBackgroundTasks). All blocking HTTP runs in a thread-pool executor so the event loop (and login/auth) stays responsive. The task setssync_status: running, pulls data, upserts it, deletes stale rows (anything whose live ID is no longer returned), and finally recordssync_status: success(orerrorwith a message). PollGET .../sync-statusfor progress;GET .../statsfor summary counts. - Rollup — after writing rows, the sync calls
rebuild_applications(tenant_id, db)so installed software from that source rolls up into the shared per-tenant Software inventory alongside every other source.
Because config is per-tenant and clients are rebuilt from stored config on every call, the whole system is multi-tenant with no shared client state.
source tagging into inventory_items
Installed-software rows from every source are written into one shared
collection, inventory_items, each row tagged with a source field
(sentinelone, rapid7, intune, connectwise_automate, plus csv for
imported data). This is what lets:
- the Software inventory roll up the same app seen by multiple tools,
- CVE-to-device matching work uniformly regardless of which tool reported the app, and
- stale cleanup stay scoped — each sync only deletes
inventory_itemsrows for its ownsource.
Rows carry device_id / device_name, application_name, vendor, version,
os_platform and (where known) user_email / user_name, so devices and users
reconcile across sources. Sources cross-reference each other by hostname — e.g.
SentinelOne and Rapid7 look up matching Intune devices to adopt the Intune device
UUID and owner, so a machine is one record, not one per tool.
All sources at a glance
| Source | What it is | Auth | What it pulls | Collections written |
|---|---|---|---|---|
| SentinelOne | EDR / endpoint protection | API token + console URL | Agents/devices, threats, installed apps, app-risk CVEs | sentinelone_devices, sentinelone_threats, sentinelone_app_risks, inventory_items (source=sentinelone), vulnerabilities |
| Rapid7 InsightVM | Vulnerability scanner | API key + region | Assets, per-asset vulnerability findings, per-asset software | rapid7_assets, rapid7_findings, inventory_items (source=rapid7) |
| Microsoft Intune | MDM / device management | MSAL (cert thumbprint+PEM, or client secret) | Managed devices, detected apps, per-device apps | intune_devices, intune_apps, inventory_items (source=intune) |
| ConnectWise Manage (PSA) | Ticketing / PSA | Basic auth (company+public key : private key) + clientId | Service tickets, notes, time entries, configurations | connectwise_tickets, cw_ticket_notes, cw_ticket_cache, cw_ticket_assets, cw_bulk_pull_jobs |
| ConnectWise Automate | RMM | Integrator token (username/password + optional clientId) | Computers, per-computer software — scoped to selected client(s) | connectwise_automate_devices, inventory_items (source=connectwise_automate) |
| Microsoft Entra ID | Identity / directory | MSAL (reuses the Intune app registration) | Users (+ aliases), groups, group memberships | entra_users, entra_groups |
| CSV / Excel providers | Tools with no API | File upload | Devices, software and/or users from an uploaded sheet | inventory_items (source=csv), csv_users, provider-specific |
Two ConnectWise integrations — don’t confuse them
There are two distinct ConnectWise products wired in, and they are separate integrations:
- ConnectWise Manage (PSA/ticketing) — module
connectwise.py, mounted at/api/v1/connectwise. It creates and syncs service tickets (including vulnerability tickets), notes, time entries and configuration links. It uses ConnectWise REST Basic auth (company+publicKey:privateKeyplus aclientIdheader) and reads env vars or a DB config. It does not feed the inventory. - ConnectWise Automate (RMM) — module
connectwise_automate.py, mounted at/api/v1/connectwise-automate. It pulls computers and installed software into the inventory, and follows the standard integration lifecycle above. See its dedicated page.
Where to go next
- SentinelOne · Rapid7 InsightVM · Microsoft Intune · ConnectWise Automate · Microsoft Entra ID
- The Software inventory and Assets guides show where synced data surfaces.
- The durable-workflows design covers where the current
BackgroundTaskssync model is heading.