Hermes 284313f908
Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Initial import of AlgaPSA codebase from PSA server
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz

Source: /opt/alga-psa on psa.joliet.tech
2026-06-22 16:12:17 -05:00

10 KiB
Raw Permalink Blame History

PRD — Tactical RMM Integration

  • Slug: tacticalrmm-integration
  • Date: 2026-02-13
  • Status: Draft

Summary

Add a Tactical RMM integration that works in both Community Edition (CE) and Enterprise Edition (EE). The integration should:

  • Connect to a Tactical RMM instance using either API key auth or username/password (Knox token) auth (including optional TOTP).
  • Sync Tactical inventory into Alga Assets (clients/sites/agents -> clients/assets + mappings).
  • Support near-real-time updates via Tactical alert-action webhooks (alerts only), plus optional alert backfill via the Tactical alerts API.
  • Fit the existing multi-provider RMM platform patterns established by the NinjaOne integration (shared DB tables, org mapping, device sync, asset fields).

Problem

Alga currently has an RMM integration pattern (NinjaOne) that syncs devices and supports webhooks, but it is EE-oriented and provider-specific. We want to support Tactical RMM as an additional provider, available in both CE and EE, and integrate it cleanly into the existing Assets system and the “RMM” Integrations settings tab.

Goals

  1. CE + EE: Tactical RMM appears in Settings -> Integrations -> RMM and can be configured in both editions.
  2. Connection management:
    • Support Tactical API key auth.
    • Support Tactical username/password auth with TOTP handling (checkcreds + login).
    • Store credentials securely (tenant secrets) and show masked status in UI.
  3. Inventory sync:
    • Sync Tactical Clients into rmm_organization_mappings.
    • Map Tactical Clients to Alga Clients (companies) via UI.
    • Sync Tactical Agents into Alga Assets using the beta API at fleet scale.
  4. Asset enrichment:
    • Populate core RMM fields on assets (rmm_provider, rmm_device_id, rmm_organization_id, agent_status, last_seen_at, last_rmm_sync_at).
    • Populate cached “RMM vitals” fields when available (current user, uptime, LAN/WAN IP).
    • Add support for Tacticals “overdue” state (distinct from offline).
  5. Realtime (alerts):
    • Provide a webhook endpoint for Tactical alert actions with shared-secret header validation.
    • On webhook receive, upsert rmm_alerts and trigger a targeted “sync single agent” refresh.
  6. Optional: alerts backfill via Tactical alerts API for historical visibility.

Non-goals

  • Remote access (MeshCentral / remote background): explicitly deferred.
  • Full “device created/updated/deleted” webhooks (Tactical doesnt provide them natively; only alerts).
  • Patch deployment / remediation (scan + display only, if we even include scan hooks).
  • Deep ticket-automation parity with NinjaOne (auto ticket creation/rules) unless explicitly requested later.

Users and Primary Flows

Primary user: MSP admin / system admin configuring integrations.

Flows:

  1. Configure Tactical:
    • On the Tactical server, enable the Beta API: set BETA_API_ENABLED = True and restart Tactical.
    • Open Settings -> Integrations -> RMM -> Tactical RMM.
    • Enter the Tactical API host as the instance URL: the api. subdomain (e.g. https://api.example.com), not the dashboard (rmm.) URL.
    • Choose auth mode:
      • API key: paste key, save, verify.
      • Username/password: enter creds, if TOTP required prompt for code, login, store token.
  2. Sync Tactical Clients:
    • Click “Sync Clients” (organizations).
    • Map Tactical Clients to Alga Clients (companies) in an org mapping table.
  3. Sync Assets:
    • Click “Sync Devices”.
    • Alga upserts Assets and extension records and creates external mappings.
  4. Realtime alerts:
    • Admin copies webhook URL + secret, configures Tactical alert-action webhook with X-Alga-Webhook-Secret.
    • Alerts trigger/resolve -> Alga receives webhook -> stores alert -> refreshes affected asset.
  5. Optional: “Sync Alerts” to backfill alerts from Tactical.

UX / UI Notes

  • Tactical RMM should appear next to NinjaOne under the Integrations “RMM” category tab.
  • Settings card should match the “feel” of NinjaOne settings:
    • Status panel (connected/disconnected, last sync, counts).
    • Credential management section (masked + update).
    • Sync buttons.
    • Organization mapping section.
    • Webhook section (URL, header name, secret, payload template).
  • Since Tactical is CE + EE, the Tactical settings UI must not rely on EE-only dynamic imports.

Requirements

Functional Requirements

  1. Connection status
    • Show whether Tactical is configured and reachable.
    • Show instance URL and auth mode.
  2. Credential storage
    • Store and retrieve per-tenant credentials via secret provider.
    • Mask secrets in UI (show only last 4 chars).
  3. Sync clients (organizations)
    • Fetch Tactical Clients via beta API.
    • Upsert rmm_organization_mappings for provider tacticalrmm.
  4. Org mapping management
    • Allow selecting an Alga Client for each Tactical Client mapping.
    • Persist auto_sync_assets toggles.
  5. Sync devices (agents)
    • For each mapped Tactical Client:
      • fetch sites (paged) and agents (paged and filtered by client_id).
      • upsert assets + extension data.
      • upsert tenant_external_entity_mappings for the agent.
      • mark missing agents as inactive (optional; see Open Questions).
  6. Agent status mapping
    • Compute online|offline|overdue using the provided deterministic rules from last_seen, offline_time, overdue_time.
    • Persist status to assets.agent_status.
  7. Realtime alerts via webhooks
    • Provide POST /api/webhooks/tacticalrmm.
    • Validate X-Alga-Webhook-Secret.
    • Accept a small JSON contract (required: agent_id; optional: alert_id, event, severity, message, alert_time, client_id, site_id).
    • Upsert rmm_alerts and associate to an asset when possible.
    • Trigger “sync single agent” after alert receives/resolves.
  8. Alert backfill (optional but planned)
    • Use Tactical alerts endpoint to fetch active/recent alerts and upsert into rmm_alerts.
  9. Software inventory ingestion (bulk, cached)
    • Use Tactical GET /software/ to ingest cached software inventory without per-agent refresh.

Non-functional Requirements

  • Fleet-scale pagination: use beta endpoints with page_size=1000 and loop pages.
  • Resilience:
    • Handle 401 by re-authing (username/password mode) or failing with actionable error (API key mode).
    • Avoid per-agent “refresh” calls by default (software refresh is expensive).
  • Multi-provider safety: Tactical must not break NinjaOne; providers must co-exist in the same tenant.

Data / API / Integrations

Point the integration at the Tactical API host (the api. subdomain), for example https://api.example.com. Do not use the dashboard host (rmm.). The dashboard serves the single-page app and returns its HTML with a 200 for every path, so sync reads zero records and silently does nothing.

Tactical's Beta API must be enabled for inventory sync. Set BETA_API_ENABLED = True in Tactical and restart it. While it is off, the /beta/v1/* endpoints return 404 and both the connection test and sync fail.

Tactical endpoints (base URL https://<tactical-api-host>, served at the root with no /api prefix):

  • Auth (username/password):
    • POST /v2/checkcreds/ { username, password } -> { totp: true } or short-lived token
    • POST /v2/login/ { username, password, twofactor? } -> Knox token response
    • Auth header: Authorization: Token <knox_token>
  • Auth (api key):
    • Auth header: X-API-KEY: <key>
  • Inventory (beta API, requires BETA_API_ENABLED):
    • GET /beta/v1/client/
    • GET /beta/v1/site/?page_size=1000&page=N
    • GET /beta/v1/agent/?client_id=<client_pk>&page_size=1000&page=N
  • Alerts:
    • PATCH /alerts/ with filter body for backfill/top-N
  • Software:
    • GET /software/ bulk cached software inventory
    • PUT /software/<agent_id>/ per-agent refresh (non-goal by default)

Alga DB tables involved:

  • rmm_integrations (one row per tenant+provider; store instance_url, is_active, settings JSON including webhook secret and auth mode).
  • rmm_organization_mappings (Tactical Client PK mapped to Alga Client).
  • assets + extension tables (workstation/server fields; cached “vitals” fields already exist).
  • tenant_external_entity_mappings (agent_id crosswalk; store client/site in metadata).
  • rmm_alerts for alert ingestion and association.

Security / Permissions

  • Configuration actions require settings permissions (same posture as NinjaOne).
  • Secrets stored in tenant secret store (never in plaintext in DB tables).
  • Webhook endpoint is unauthenticated but must require shared-secret header and be explicitly enabled/configured by admin.

Observability

  • Minimal: structured logs for sync start/finish/errors and webhook receives.
  • (Optional) publish existing event-bus events where appropriate (RMM_WEBHOOK_RECEIVED, RMM_DEVICE_UPDATED, etc.) to align with NinjaOne patterns.

Rollout / Migration

  • Add tacticalrmm provider to RMM provider unions in Typescript.
  • Potential migration if we choose to add new agent status value (overdue) to UI filters and any code expecting only online/offline/unknown.
  • Ensure /api/webhooks/tacticalrmm is added to API auth middleware skip paths.

Open Questions

  • Should we treat Tactical “overdue” as a first-class agent_status enum value everywhere (recommended), or map it to offline?
  • Do we support marking assets as inactive when agents disappear from Tactical (requires a “full inventory snapshot” delete detection)?
  • Asset type mapping: do we classify Tactical agents as workstation vs server based on OS string heuristics, or introduce a Tactical-specific mapping override?
  • Should alert backfill be limited to “active only” or allow recent resolved (time window)?

Acceptance Criteria (Definition of Done)

  1. Tactical RMM is visible and configurable in CE and EE under Integrations -> RMM.
  2. Admin can connect using API key or username/password (with TOTP when required), and connection status is shown.
  3. Admin can sync Tactical Clients and map them to Alga Clients.
  4. Admin can sync Tactical Agents into Alga Assets; assets show provider + agent status + last seen where available.
  5. Tactical alert-action webhook can be configured with custom headers; Alga validates X-Alga-Webhook-Secret, records alerts, and refreshes the affected asset.
  6. Optional: alerts backfill and bulk software inventory ingestion work without per-agent refresh calls.