The SOC Real-Time Triage Console is a real-time MSP operations dashboard that surfaces the health of every managed platform, all active security incidents, and the full ticket queue in a single browser-based interface. It is designed for NOC/SOC engineers who need to monitor across multiple clients and platforms simultaneously without switching between vendor portals.
The dashboard auto-refreshes every 30 seconds. When a live API endpoint is reachable it augments simulated data with real values. When the API is unreachable it falls back to a realistic simulation seamlessly — the indicator in the top rail shows which mode is active.
The dashboard has two operating modes that coexist at runtime. On every refresh cycle fetchData() attempts to call the live API. If the call succeeds the response is merged into the simulated payload. If it fails the simulation runs entirely on its own. The UI is identical in both modes.
When the API is unavailable, buildPayload() generates a full realistic dataset on each refresh using randomized values seeded from four static arrays: PLATS (9 monitored platforms), CLIENTS (10 managed clients), APOOL (12 alert archetypes), and TICKET_DATA (7 ticket records). Each refresh produces new health scores, incident assignments, and alert combinations — making the dashboard appear live even without a backend.
/api/msp/soc-dashboard with an 8-second timeout. On success merges the API response into the simulated payload via Object.assign(). On failure returns pure simulation. Updates the API / SIM badge in the rail.| MODE | API STATUS | BADGE | DATA SOURCE | TICKET DATA |
|---|---|---|---|---|
| Simulation | Down or unreachable | ◌ SIM | buildPayload() — randomized each refresh | Static TICKET_DATA array |
| Live (merged) | API returns 200 | ● API | buildPayload() + Object.assign(json) | From API if provided, else TICKET_DATA |
The rail runs across the very top of the page and is always visible regardless of which console is active. It shows six live KPI values that update on every refresh cycle. Each value is clickable and opens the detail pane filtered to that category.
critAlerts across all nine monitored platforms. Clicking opens the detail pane showing only CRIT incidents.warnAlerts across all platforms. Clicking opens the pane filtered to WARN incidents.Console 1 is the command overview. It gives a single-screen picture of every managed platform, all clients, the full ticket queue, and system-wide health metrics. Load this console during morning standups or any time leadership asks for an environment status summary.
A nine-cell grid across the top of Console 1, one cell per monitored platform. Each cell shows the platform icon, name, operational status, critical alert count, and API latency. A color stripe at the top of each cell indicates health: green for Operational, orange for Degraded, red for Critical. Clicking any platform cell opens the detail pane with that platform's full status.
Seven KPI tiles below the platform grid: Malware alerts, Firewall criticals, Network down, Backup failures, Open tickets, Clients at risk, and SLA compliance percentage. Each tile shows a directional flag (▲ if above 5, — if zero). Clicking any tile opens the detail pane in kpi mode.
All ten managed clients listed with sector, device count, alert count, health score, and a colored dot (red/yellow/green risk indicator). Clicking a client row opens its full detail in the pane including per-platform scores, patch compliance, and backup failure count.
Open ticket count and closed-today count at the top, SLA compliance percentage (green above 95%, orange 85–94%, red below 85%), followed by the full ticket list. Each ticket row shows priority badge (P1 P2 P3 P4), subject, client, assignee, SLA status, and age. Clicking a ticket row opens its detail in the pane.
A semicircular gauge in the right column shows the overall ENV score. Below it, four health bars (Security, Network, Backup, Endpoints) show the breakdown. The gauge animates on each refresh with a 1.2-second stroke transition. Color rules: green ≥85%, orange 70–84%, red below 70%.
The API status panel lists all nine platforms with their current latency in milliseconds and a color-coded status badge. The 7-day chart below shows a stacked bar chart of Critical (red), Warning (orange), and Informational (blue) alert volumes per day for the rolling week.
Console 2 is the active triage view. It surfaces every incoming alert and incident in real time and is the primary view for on-call engineers during an active incident. When an incident escalates from warning to critical, engineers switch to Console 2 to assess and then jump to Console 3 to respond.
The left column shows a scrolling stream of active critical alerts, each with a severity badge, title, source (tool and device), timestamp, and client name. New alerts on the current refresh cycle are marked with a NEW badge. Clicking any alert item loads that incident into Console 3's context panel.
Four animated counters at the top of the left column: Malware (SentinelOne criticals), Firewall (FortiGate criticals), Network (Auvik criticals), and Backup failures. These match the values shown in Console 1's KPI strip and animate via the an() helper on every refresh.
The center column shows the active incident feed — up to 10 incidents combining live-injected events from the session (LIVE array) and the simulated pool. Each incident card shows severity, ID, title, detail text, device, platform, client, and timestamp. Clicking an incident card switches to Console 3 with that incident pre-loaded as context.
The right column shows the same ticket queue as Console 1 — all open tickets sorted by priority with SLA status coloring. P1 tickets with SLA breach are highlighted red. Use this column to cross-reference whether a P1 ticket already exists for an active incident before creating a duplicate.
Console 3 is the response console. It keeps the full incident context visible on the left while exposing every available action on the right — security actions, network actions, ticket actions, and automation scripts. Engineers drive the full incident response lifecycle without leaving this view.
The left column shows the currently active incident: severity badge, incident ID, timestamp, title, detail text, and a fields grid showing Client, Device, Platform, Ticket link (or Unlinked), Status, and Assigned engineer. Below this is the incident selector list showing up to 8 active incidents — clicking any one loads it into the context panel without leaving Console 3.
When switching from Console 2 by clicking an incident card, Console 3 pre-loads that incident automatically and calls switchConsole(3).
Six one-click ticket operations scoped to the currently active incident: Create P1 Ticket (full ConnectWise workflow), Assign to Engineer, Escalate Incident, Clear Alert, Send Client Update, and Close Ticket. These appear in a compact action-row layout with colored Run buttons matching their severity implication.
Six pre-built scripts that run multi-step automated workflows: Remediation Script, Force Patch Cycle, Malware Cleanup, Emergency Backup, Bulk Password Reset, and Generate SOC Report. Each script displays its target systems and a ▶ Run button. All scripts use the same runAction() modal system as the action buttons above.
A stacked bar chart at the bottom of Console 3 showing critical (red) and warning (orange) alert volumes by hour over the last 24 hours. Rendered with Chart.js. The chart re-renders each time Console 3 becomes active to ensure it draws correctly after being hidden.
The detail pane is a 440px slide-in panel that opens from the right side of the screen. It is triggered by clicking any stat value in the top rail, any platform cell, any client row, or any ticket row. It overlays all three consoles and can be closed by clicking outside it or pressing the close button.
openPane('critical')openPane('warnings')openPane('incidents')openPane('tickets')openPane('clients')openPane('kpi')Every action button and script in Console 3 uses the same animated modal system. When an action is triggered, a full-screen overlay appears with the action title, a list of steps, a progress bar, and step-by-step status indicators.
- 1Action is triggeredby clicking a button or script. The modal opens immediately with all steps in "waiting" state.
- 2Steps execute sequentially.Each step transitions waiting → running (with animated label) → ✓ done. The progress bar advances proportionally.
- 3On completionthe progress bar fills to 100%, a green "✓ Completed successfully." message appears, and the Close button becomes available.
Step durations are defined in each action's steps array (e.g. {id:'a', icon:'🔒', label:'Apply isolation', dur:1300}). In simulation mode all steps always succeed. When wired to real APIs, individual step failures can be surfaced by adding error handling inside the next() closure in runAction().
secActs, netActs, or scripts array inside renderC3(). Define icon, lbl, sub, and a fn string calling runAction("Title", [steps]). The modal handles the rest automatically.
A slim 20px ticker bar runs below the console switcher. It continuously scrolls all active incidents from left to right, color-coded by severity: red for critical, orange for warning, green for informational. Each item shows the timestamp, incident title, and client name.
The ticker content is duplicated in the DOM so the scroll animation loops seamlessly. Hovering over the ticker pauses it — useful for reading a specific entry without switching consoles. The ticker updates on every refresh cycle via renderTicker(d).
The dashboard monitors nine platforms across six categories. Each platform appears in the Console 1 health grid and the API status panel, and is the source of specific alert types in Consoles 2 and 3.
In simulation mode, platform health states are randomly drawn from the pool: ok · ok · ok · ok · warn · warn · crit · deg. At least one platform is always forced to Critical to ensure realistic triage conditions.
| ACTION | CATEGORY | TARGET PLATFORM | STEPS | DESTRUCTIVE |
|---|---|---|---|---|
| Isolate Endpoint | Security | SentinelOne | Locate → Isolate → Snapshot → Confirm | Yes |
| Kill Process | Security | SentinelOne | Find → Terminate → Verify | Yes |
| Run EDR Scan | Security | Huntress | Init → Scan → Analyze → Report | No |
| Reset Credentials | Security | Azure AD | Auth → Invalidate tokens → Notify | Partial |
| Block IP / Domain | Security | FortiGate | Connect → Push rule → Verify | Yes |
| Memory Snapshot | Security | SentinelOne | Capture → Upload → Hash verify | No |
| Restart Device | Network | Ninja RMM | Heartbeat → Stop services → Reboot → Await | Partial |
| Disable Switch Port | Network | Auvik | Connect → Disable → Verify | Yes |
| Pull Device Logs | Network | Ninja RMM | Connect → Fetch → Index | No |
| Bounce VPN Tunnel | Network | FortiGate | Connect → Flush → Re-establish → Confirm | Partial |
| Network Topology | Network | Auvik | Trigger discovery → Map → Update | No |
| Test Connectivity | Network | Ninja RMM | Ping sweep → Trace route → Report | No |
| Create P1 Ticket | Ticket | ConnectWise | Fetch client → Populate → Submit → Notify | No |
| Remediation Script | Script | Ninja RMM (all) | Load → Validate → Deploy → Complete | Partial |
| Force Patch Cycle | Script | Ninja RMM (unpatched) | Find unpatched → Queue → Push → Verify | No |
| Malware Cleanup | Script | SentinelOne | Enumerate → Remove → Harden policy | Partial |
| Emergency Backup | Script | Backup Systems | Init job → Upload → Verify integrity | No |
| Bulk Password Reset | Script | Azure AD | Auth → Enumerate → Reset all → Notify | Partial |
| Generate SOC Report | Script | All platforms | Gather data → Security summary → Render PDF → Send | No |
| LEVEL | LABEL | COLOR | TYPICAL SOURCES |
|---|---|---|---|
| CRIT | Critical | Red | Malware detected, ransomware indicator, DC unreachable, VPN down, RAID degraded |
| WARN | Warning | Orange | Backup job failed, high CPU, switch port offline, firewall rule spike, patch compliance low, auth anomaly |
| INFO | Informational | Blue | SSL cert expiring, scheduled maintenance, policy updates |
| TICKET PRIORITY | SLA — RESPONSE | SLA — RESOLVE | EXAMPLE |
|---|---|---|---|
| P1 | 15 minutes | 4 hours | VPN down, malware isolation, DC unreachable |
| P2 | 1 hour | 8 hours | Server disk full, firewall policy mismatch |
| P3 | 4 hours | 24 hours | Printer offline, email quarantine review |
| P4 | Next business day | 72 hours | New user onboarding, routine requests |
SLA status coloring in the ticket queue: breach = SLA already missed (red), warn = SLA at risk within 30 minutes (orange), ok = within SLA (green).
The dashboard runs in simulation mode with zero prerequisites — open the HTML file in any modern browser and it works immediately. Connecting a live API or wiring real platform actions requires the following.
C:\ path works for simulation only — browser security policies block cross-origin fetch from file:// origins. Any static host, IIS, SharePoint document library, or CDN works./api/msp/soc-dashboard returning a JSON object. The file sends a GET with header X-Console: three-console-soc. The response is merged into the simulation using Object.assign() — you only need to return the fields you want to override, not the full payload.The dashboard calls one endpoint on every refresh cycle. You control how much real data it injects by deciding which fields your API returns. Everything you don't return stays simulated.
The platform array in the API response is matched to the static PLATS array by the id field. Use these exact IDs or the merge will not apply: ninja · auvik · forti · s1 · huntress · backup · cw · email · azure
If your API lives at a different path, find fetchData() in the script and update the fetch URL. The path must be relative to the host serving the HTML file to avoid CORS issues.
Four static arrays at the top of the script control the simulation engine. Update these to reflect your actual MSP environment — even in simulation mode the dashboard will then show your real clients, tools, and incident patterns.
Nine objects. Each needs id (API match key), name (display name), icon (emoji), and cat (category label). Add or remove platforms by editing this array — the platform grid, API status panel, and KPI strip all render from it automatically.
id: 's1' (SentinelOne) and the Firewall tile references id: 'forti'. If you rename those platforms, update the matching plats.find(function(p){return p.id==='s1';}) references inside renderC1() and renderC2().
Ten objects. Each needs name, sector, and devices. Replace with your actual client list. The simulation randomly assigns health risk states on each refresh. The detail pane pulls directly from this array when opening client detail cards.
Twelve alert templates drawn from randomly on each refresh to generate the active incident feed. Each object defines title, sub (source description), sev (crit/warn/info), icon, detail (full explanation), device, and platform. Customize these to reflect the actual alert types your environment generates most frequently.
Seven ticket objects used as the baseline ticket queue. In production, replace this with live data from your PSA via the API. Each object needs p (priority: p1–p4), subject, client, sla (ok/warn/breach), age, and assignee. This array is only used when the API does not return a tickets field.
All Console 3 actions currently execute as pure simulation — they animate through steps and display success but make no real API calls. Wiring a live action means replacing the simulated step durations with actual API calls inside the runAction() flow.
The cleanest approach is to add a call property to each step and extend runAction() to execute it when present. This keeps the modal animation intact and only replaces the timing simulation with real async calls.
- 1Start with read-only actions.Pull Device Logs, Run EDR Scan, Test Connectivity, and Generate SOC Report are safe to wire first — they cannot cause disruption and let you validate the API integration pattern before touching destructive actions.
- 2Wire ticket creation next.Create P1 Ticket in ConnectWise is the highest-value action for most MSPs and has no security risk. Wire it to your PSA API and test against a sandbox board before going to production.
- 3Wire destructive actions last.Isolate Endpoint, Kill Process, and Block IP affect live systems. Wire these after the read-only and ticket actions are validated. Add a confirmation prompt step before the destructive API call.
The dashboard is a single HTML file with one CDN dependency (Chart.js). It can be hosted anywhere that serves static files over HTTPS.
/api/...) will not work from SharePoint unless you route them through an Azure Function proxy on the same domain./api/ folder for the backend. Azure Static Web Apps automatically routes /api/* calls to the Functions without CORS headers.file://). All simulation features work. API fetch calls will be blocked by browser security policy — this is expected and the dashboard degrades gracefully to simulation mode.Access-Control-Allow-Origin: * on an endpoint that handles platform credentials.
overflow:hidden is intentional — it prevents the page from scrolling so the three-console layout fills the screen edge to edge at all times.
Run through this checklist after deploying to a new environment or after connecting a live API for the first time.
- ✓Dashboard loads and auto-populates. All three console tabs appear. The rail stats animate from — to real numbers within 2 seconds of page load.
- ✓Console switcher works. Clicking Console 1, 2, and 3 tabs switches the view. The active tab gets an accent underline. The badges (e.g. "2 crit") update on each switch.
- ✓Charts render on Console 1 and 3. If a chart appears blank after switching to a console, it means the canvas was not visible when Chart.js tried to draw it. Check that
renderC3Charts()is being called insideswitchConsole(3). - ✓Detail pane opens from rail stats. Click each stat value in the rail. The pane should slide in from the right showing the correct filtered content. Clicking outside the pane closes it.
- ✓Run Action modal completes. Trigger any action in Console 3. The modal should open, step through all stages, show ✓ Completed, and offer a Close button. If a step hangs, check for a thrown error in the browser console.
- ✓Incident context loads on Console 3. Click an incident in Console 2. Confirm Console 3 becomes active and shows the correct incident in the context panel. Click multiple incidents in the selector list and confirm the context panel updates each time.
- ✓API badge shows ● API when live endpoint is available. If you have an API connected and the badge still shows ◌ SIM, open the browser console (F12) and check for the fetch error. Common causes: wrong path, CORS blocked, or 4xx/5xx response from the server.
- ✓Auto-refresh cycle is running. Watch the rail stats for 30 seconds. The numbers should animate to new values on each cycle. If they freeze, check that the
setIntervalin the init block is not being cleared by an error.
| SYMPTOM | LIKELY CAUSE | FIX |
|---|---|---|
| Rail stats show — and never update | renderAll() threw before completing | Open F12 Console. Find the JS error and fix the affected render function. Common cause: a .find() on PLATS returning undefined. |
| Charts blank on Console 1 or 3 | Canvas not visible when Chart.js initialized | Confirm render7d() and renderC3Charts() are called after the console div becomes visible. Wrap in a setTimeout(..., 80). |
| Badge stays ◌ SIM with a live API | API returning non-200, wrong path, or CORS blocked | Check F12 Network tab for the failing request. Verify the path matches your API host. Add CORS headers if cross-origin. |
| Detail pane doesn't close on outside click | dimov overlay not covering the full viewport | Check that #dimov has position:fixed; inset:0; and that its click handler calls closePane(). |
| Action modal hangs on a step | Unhandled promise rejection in a live API call | Wrap each call() in try/catch and update the step status to an error state before calling return. |
| Incident context doesn't load on Console 3 | DATA is null when loadIncidentContext() is called | The first fetchData() call may not have completed. Add a guard: if(!DATA) return; at the top of loadIncidentContext(). |
| Dashboard scrolls on the monitor | Browser zoom level not at 100% | Press Ctrl+0 to reset zoom. The layout uses height:100vh; overflow:hidden — it only fills the screen at 100% zoom on a 1080p or higher display. |