The MSSP Unified Security Dashboard is a single-file HTML demo interface that presents a consolidated view of security posture, infrastructure health, email threat data, and backup status across a managed client portfolio. It is pre-wired with exact API references for every displayed metric — every number on screen traces back to a real vendor endpoint, HTTP method, authentication pattern, and response field.
The primary audience is MSSP sales and engineering staff conducting client briefings, QBRs, or new prospect demonstrations. The dashboard communicates the breadth of visibility an MSSP provides at a glance, while the clickable API detail modals let technical stakeholders validate that real data can back every metric shown.
It is not a live operations tool in its current state — all displayed values are demo data. The architecture is intentionally designed to allow direct swap to live fetch() calls once backend proxy infrastructure is in place.
The dashboard is organized as a vertical stack of sections within a fixed-height scrollable container (#scroll). A fixed topbar holds global KPIs and controls. Below it, a live ticker scrolls real-time alert events. The main body is divided into four visual sections plus a two-column panel row layout used for side-by-side platform stats.
padding-top:36px.buildTicker() at init. Items are duplicated for seamless looping. 60s animation cycle.flex:1; overflow-y:auto.#radar-tbl). Sources: SentinelOne, Mimecast, Ninja RMM, Auvik, Backup platforms..two-col grid. Left: SentinelOne 2×2 stat panel. Right: Ninja RMM + N-able 2×2 stat panel..two-col grid. Left: Auvik 2×2 stat panel. Right: Datto / Acronis / Axcient 2×2 stat panel..email-grid. Sources: Mimecast, Proofpoint. Metrics: Phishing, Malware, Impersonation, Suspicious Domains.setModal(title, html). Clicking the backdrop closes it.#ktc-compact) is pre-injected with overrides that tighten padding across the scroll area, section headers, KPI cells, and table rows. This allows the entire dashboard to fit within a typical 1080p browser window without scrolling the outer page.
Every metric cell in the dashboard is backed by an entry in the APIS JavaScript object (approximately line 934 in the source). Clicking any stat cell opens a modal showing the exact endpoint, HTTP method, auth header, parameters, field mapping, and a generated example fetch() call. The following tables document all wired endpoints.
Authorization: ApiToken YOUR_TOKEN — generate in Settings → Users → API Token Integrations.s1_threatss1_quars1_laterals1_exploitAuthorization: Bearer YOUR_ACCESS_TOKEN (OAuth2). N-able Auth: Basic base64(user:apiKey).rmm_offrmm_errrmm_rebootrmm_patchAuthorization: Basic base64(username:apiKey) — generate at My Profile → API. Tenant filter required per client for MSP use: include tenantId param.net_downnet_alertsnet_ifacenet_cpuBasic base64(apiKey:secretKey). Acronis/Axcient Auth: OAuth2 Bearer token. Map Datto deviceId to client via /v1/bcdr/device. Acronis uses tenantId in the URL path.bkp_failbkp_unprotbkp_agebkp_storx-mc-date header + Authorization: MC accessKey:base64(HMAC). Proofpoint Auth: Basic base64(principal:secret). Route all requests through a backend proxy to keep credentials server-side.em_phishem_malwem_impem_domfetch() call must be routed through a thin backend proxy (Node.js, Python, Azure Function, etc.) that adds credentials server-side and forwards the response. Never embed API keys or tokens in the client-side HTML file.
Authorization: ApiToken <token>
client_id and client_secret. Access tokens expire — implement refresh logic in your proxy.
Authorization: Bearer <access_token>
username:apiKey, base64 encode, pass as Authorization header. API key generated at My Profile → API. Include tenantId query param for MSP multi-tenant isolation.
Authorization: Basic base64(user:key)
apiKey:secretKey (not username — it's the API key pair). The apiKey acts as username and the secretKey as password in the Basic credential.
Authorization: Basic base64(apiKey:secretKey)
x-mc-date (RFC 1123), x-mc-req-id (UUID), plus a signed Authorization header composed of accessKey:base64(HMAC-SHA1(signingKey, sigData)). Use Mimecast's official SDK or reference implementation for your proxy.
Authorization: Basic base64(principal:secret)
refreshAll() which re-renders the radar table, resets the 30s countdown, and fires an "ok" toast confirming refresh.event.stopPropagation() to prevent double-firing from the row click handler.openPlatModal) with all endpoints for that platform. Secondary buttons fire contextual toasts.All modals share a single HTML structure: #modal (overlay) → .mbox (container) → .mhdr (header with title + close ✕) → #m-body (dynamic innerHTML). The setModal(title, html) helper populates both the title and body then adds the .open class to #modal, which switches its display from none to flex. closeModal() removes the class.
Clicking the dark overlay backdrop also closes the modal (v1.1 fix). Clicking inside the .mbox stops propagation to prevent accidental close.
openThreatModal(i)
openApiModal(key)
fetch() example. Action buttons: TEST CONNECTION, PORTAL, CLOSE.openPlatModal(key)
openCritModal()
ALL_THREATS. Each row is clickable to chain-open the threat detail modal. Buttons: BULK ESCALATE, ASSIGN ON-CALL, CLOSE.openIncidentModal()
In demo mode all displayed metric values are static constants defined directly in the HTML source. The ALL_THREATS array (line ~1114) defines 15 threat events across four severity tiers representing seven clients and five platforms. Stat cell numbers are hardcoded in their id elements.
The auto-refresh counter (#rtimer, 30s countdown) does call refreshAll() when it reaches zero, which re-renders the radar table and fires an "ok" toast — but no actual API fetch occurs. This is intentional: it demonstrates refresh UX to demo audiences without requiring live credentials.
id element and in ALL_THREATS with the result of fetch() calls to your backend proxy. The refreshAll() function is the correct hook — add your fetch logic there. Each API key in the APIS object already maps to exactly the endpoint and field path your proxy should consume.
{sev, src, client, asset, event, time, status}. The o field is a sort-order integer (0=CRIT, 1=HIGH, 2=MED, 3=LOW).g-clients, g-devices, g-alerts, g-crit.s1-thr, s1-quar, s1-lat, s1-exp.rmm-off, rmm-err, rmm-rbt, rmm-pat.net-dn, net-alt, net-if, net-cpu.bkp-fail, bkp-up, bkp-age, bkp-stor.em-phish, em-malw, em-imp, em-dom..pf element has an inline style="width:X%". These are purely visual and do not auto-calculate from the stat number — update manually to match.All visual variables are declared in a single :root block at the top of the <style> section. Changing a token propagates instantly to every component that references it. The dashboard uses no external CSS frameworks.
For demo customization — adjusting numbers to match a specific prospect's environment, for example — all values can be updated by text-editing the HTML source. No compilation step is required.
g-clients, g-devices, g-alerts, g-crit in the topbar section and change their text content. Also update the matching onclick toast message strings on the parent .gkpi elements to keep them consistent.id attributes. Search for the ID (e.g. id="s1-thr") and update the inner number. If updating progress bars, also find the .pf div inside the same .sp-cell and adjust the inline width percentage to match.ALL_THREATS array in the script block. Each entry uses: {o:0-3, sev:'CRITICAL|HIGH|MEDIUM|LOW', src:'PlatformName', client:'Client Name', asset:'Asset', event:'Description', time:'Xm ago', status:'ACTIVE|INVESTIGATING|OPEN|RESOLVED'}. The SRC_CLS object maps source names to chip color classes.buildTicker() function (~line 1416) contains an items array of HTML strings. Edit or add entries following the existing pattern: <span class="tc|th|tm|tg|ti"> for CRIT / HIGH / MED / RESOLVED / INFO coloring respectively.APIS object (~line 934) and PLAT_INFO object (~line 1280) drive all modal content. To update endpoint details, change the relevant key's endpoint, params, auth, field, or note properties. No changes to HTML structure are needed.MSSP SECURE) is in the .brand-name element inside #topbar. The subtitle (UNIFIED SECURITY DASHBOARD · MANAGED CLIENTS) is in .brand-sub. The page <title> tag should also be updated to match.ALL_THREATS (ACME Corp, Beta Inc, Delta LLC, Gamma Corp, Omega Co) to match names relevant to the prospect's industry vertical. This significantly increases demo realism without any structural changes.
#ktc-demo-bar HOME link points to command.html — ensure that file exists in the same directory or update the href.#ktc-demo-bar at the top of the page contains a link to command.html. This is a reference to a broader command center portal. If the dashboard is being used as a standalone file outside that portal, update the href to # or remove the bar entirely by deleting the <div id="ktc-demo-bar"> block and the associated <style id="ktc-home-bar-style"> block. Also remove body { padding-top: 36px !important; } from that style block.
The following issues were identified through code review and corrected in the v1.1 build released 2026-03-22. The original file (v1.0) was delivered with all five issues present.
setTimeout() toast call at the end of the <script> block. The script tag was never closed. The browser discarded the entire JavaScript payload, leaving every interactive element completely non-functional — no modals, no toasts, no KPI clicks, no radar table rendering.Fix: The partial byte sequence was removed. The incomplete
setTimeout call was reconstructed, the script was properly closed with </script>, and the </body></html> closing tags were appended.finance@omega.com in the CEO impersonation Threat Radar event had been processed by Cloudflare's email obfuscation system, replacing it with an encoded anchor tag that required Cloudflare's external decode script to render. The script reference was to /cdn-cgi/scripts/.../email-decode.min.js, which does not exist in a standalone HTML context.Fix: The Cloudflare script tag was removed. The obfuscated anchor element was replaced with the plain text string
finance@omega.com.#modal) had no click handler. Clicking the dark backdrop had no effect, requiring users to click the ✕ button or a CLOSE/CANCEL button — disruptive during live demos.Fix: Added
onclick="closeModal()" to the #modal overlay div. Added onclick="event.stopPropagation()" to the inner .mbox container so clicks within the modal content do not bubble up and trigger an accidental close.Fix: Global device count updated to 1,312, agent count updated to 1,247, and all matching references in toast strings and footer notes updated for internal consistency.
</body> and </html> closing tags were absent. Browsers tolerate this but it is invalid HTML and can cause subtle rendering inconsistencies in certain embedding contexts.Fix: Both tags appended as part of the script closure fix.
refreshAll() function re-renders the radar table from the same ALL_THREATS array but does not call any API. Making this dashboard live requires a backend proxy layer and replacement of static values with fetch() calls inside refreshAll().x-mc-date, x-mc-req-id, Authorization: MC) cannot be safely computed in client-side JavaScript without exposing the signing key. Use Mimecast's official Python or Node SDK in your proxy, or reference their developer documentation for the exact signing algorithm.refreshAll() 30-second cycle will exhaust a static access token over time without a refresh mechanism.tenantId parameter to scope results to a specific client. When aggregating across all clients (as the dashboard implies), your proxy must iterate all tenants and aggregate the results. A single top-level call will not return cross-tenant data.#ktc-demo-bar HOME link references command.html. If this dashboard is deployed without the parent portal, that link will result in a 404. Update the href or remove the bar as described in the Deployment section.openThreatModal(i) resolves the event using visThreats[i], the current filtered/sorted view. If the table is filtered when a modal opens and then the modal chains to openCritModal drilldown, index alignment may shift. This is cosmetic in demo mode but should be addressed (e.g. by using an event ID rather than array index) before production use.