Operations Console
The FortiGate Operations Console (rail brand: FG · CMD) is a self-contained single-file HTML dashboard for MSP security teams managing FortiGate firewall deployments across multiple client tenants. It provides unified visibility into threat events, VPN status, firewall policies, active sessions, HA cluster state, FortiGuard license health, and per-client operational data from a single browser window.
The console runs as a static HTML file — no server, no build process, no dependencies beyond Google Fonts. In demo mode all data is generated by mock functions using randomized but realistic FortiOS data structures. In live mode, a proxy service relays authenticated requests to each client's FortiGate instance or FortiManager.
- NOC Wall Display: Rail stats and event table give shift leads a live security posture summary across all clients.
- Threat Response: Click any event → inspection panel surfaces src/dst IPs, policy context, and one-click Block IP / Create Ticket actions.
- VPN Operations: VPN tab in the center column + ssl.root and ipsec0 interface cards for at-a-glance tunnel health.
- Compliance Review: Policies tab, FortiGuard subscription status, and HA sync state for audit evidence gathering.
- Per-Client Drill-Down: Client selector row filters all panels to a single tenant instantly.
- Individual FortiGate web UIs — no more logging into each client's HTTPS management interface separately.
- FortiManager device views — device inventory, alert feeds, and policy tables are surfaced directly in the console.
- Manual VPN status checks — ipsec and ssl-vpn tunnel counts are visible in the rail and interface cards.
- FortiGuard expiry spreadsheets — the FortiGuard tab shows subscription status, version, and days remaining per service.
- PSA context switching — Create Ticket fires directly to the PSA API (live mode) from the inspection panel without leaving the console.
mockSummary(), mockAlerts(), mockDevices(), mockRoutes(), mockPolicies(), mockQuarantine(), mockUsers(), mockTraffic(), mockFortiGuard(), and mockHA(). No network requests are made in demo mode. See Section 14 for the proxy activation checklist.
Each data source has a different API availability story. The table below reflects the actual FortiOS 7.x REST API as verified against Fortinet documentation — not the theoretical ideal. Features marked "proxy required" are implemented and wired in the code but will return no live data until the proxy service is deployed and DEMO_MODE is set to false.
| Data Source | Status | Endpoint / Notes |
|---|---|---|
| System status & resource usage | PROXY REQUIRED | /api/v2/monitor/system/status /api/v2/monitor/system/resource/usage |
| Alert / event stream | PROXY REQUIRED | /api/v2/log/memory/event?logtype=event&rows=200 — memory log only. FortiAnalyzer REST API required for persistent log queries. |
| Device inventory (multi-device) | FORTIMGR ONLY | POST /json/rpc to FortiManager. Standalone FortiOS has no device-list endpoint. See Section 12. |
| Firewall routing table | PROXY REQUIRED | /api/v2/monitor/router/ipv4 |
| Firewall policies | PROXY REQUIRED | /api/v2/cmdb/firewall/policy — supports ?filter, ?vdom, ?start, ?count |
| Quarantine (banned IP list) | PROXY REQUIRED | /api/v2/monitor/user/banned — FortiOS calls this "banned" not "quarantine". See Section 12. |
| SSL-VPN active users | PROXY REQUIRED | /api/v2/monitor/vpn/ssl |
| IPSec VPN tunnels | PROXY REQUIRED | /api/v2/monitor/vpn/ipsec |
| Traffic / session data | PROXY REQUIRED | /api/v2/monitor/firewall/session + /api/v2/monitor/system/interface. Per-app breakdown requires FortiAnalyzer. See Section 12. |
| FortiGuard license status | PROXY REQUIRED | /api/v2/monitor/license/status |
| HA cluster statistics | PROXY REQUIRED | /api/v2/monitor/system/ha-statistics — note: /api/v2/monitor/ha does NOT exist. See Section 12. |
| FortiAnalyzer connectivity | PROXY REQUIRED | /api/v2/monitor/log/fortianalyzer-connectivity-status |
| Incident objects | NO FORTIOS ENDPOINT | FortiOS has no native incident object endpoint. Incidents are PSA records (ConnectWise / Autotask) or FortiAnalyzer incident views. Console currently uses mock incident data. |
| Action: Block IP | PROXY REQUIRED | PUT /api/v2/monitor/user/banned — adds IP to banned list |
| Action: Create Ticket | PROXY REQUIRED | POST to PSA proxy endpoint — ConnectWise or Autotask |
| Action: Reset VPN | PROXY REQUIRED | POST /api/v2/monitor/vpn/ipsec/tunnel_reset or DELETE on ssl session |
fgFetch() function has the live fetch block pre-written and commented out. Setting DEMO_MODE = false and deploying a proxy is all that is required to transition from simulated to live data. No UI changes are needed.
The console is a single HTML file organized as a vertical stack of fixed-height chrome layers above a three-column body. The layout uses height:100vh with overflow:hidden so the body columns scroll independently without the rail or ticker moving.
loadDashboard() is called immediately. It calls refreshAll() which runs 11 parallel mock data promises via Promise.allSettled().fgFetch(key) call resolves from a corresponding mock function: mockSummary, mockAlerts, mockDevices, mockIncidents, mockRoutes, mockPolicies, mockQuarantine, mockUsers, mockTraffic, mockFortiGuard, mockHA. Each simulates a random 40–200ms latency.FG.data object keyed by data type. fgRenderAll() is called to update every panel.animCount(). KPI tiles are rebuilt from scratch. Left/center/right panels render from the active client–filtered data set. The ticker rebuilds its scrolling event list.loadDashboard() is called again, restarting the cycle. Ctrl+R or the Refresh button resets the timer immediately.The 46px rail is the top-most chrome element. It holds the brand identity on the left, six clickable stat counters in the center, and operational controls on the right. Stats animate from zero on every refresh using animCount().
| Stat | Element ID | Color | Source | Click Action |
|---|---|---|---|---|
| Threats | rb-crit | RED | Combined crit count from incidents + alerts + devices | fgRailClick('crit') — filters event table to CRIT severity |
| High | rb-high | ORANGE | Combined high count | fgRailClick('high') — filters event table to HIGH severity |
| Policies | rb-policies | ACCENT | summary.activePolicies | fgOpenKpi('policies') — switches center tab to Policies |
| VPN Up | rb-vpn | TEAL | summary.vpnUp | fgOpenKpi('vpn') — switches center to Events, filters VPN type |
| Sessions | rb-sessions | GREEN | summary.activeSessions | fgOpenKpi('sessions') — switches center tab to Users |
| Interfaces | rb-intf | BLUE | summary.ifUp | fgOpenKpi('interfaces') — switches center tab to Traffic |
The 20px ticker sits immediately below the rail. It contains all current events (incidents + alerts + devices) in a CSS-animated continuous scroll. Color coding follows severity: red for crit, orange for high, green for low.
- Hover pauses the scroll — CSS
animation-play-state:pausedon hover for closer inspection. - Format per item:
HH:MM · EVENT-ID · TITLE - Ticker content is rebuilt via
buildTicker()on every refresh cycle. - Animation duration is 70 seconds for a full cycle — adjust in
.tscrollCSS for faster or slower display contexts.
The 36px client selector row is the multi-tenant control layer. It sits between the ticker and the refresh progress bar. Every panel below it — the KPI tiles, event table, alert stream, and inspection panel — responds to the active client selection.
cc-all. Calls fgSelectClient(null).FG_CLIENTS. Clicking calls fgSelectClient(clientName), stores the name in FG.activeClient, and triggers fgRenderAll(). The active chip highlights using the client's configured color.fgGetFiltered() which filters FG.data.incidents + FG.data.alerts + FG.data.devices by a.client === FG.activeClient. Routing, policy, quarantine, traffic, and user data are not per-client filtered in demo mode — those panels reflect the single connected FortiGate in live mode.client string field. The selector filters by exact string match. In demo mode the values are: Acme Corp, BlueSky Logistics, Cortex Financial, Delta Medical, Ember Analytics. In live mode these must match values returned by your API data mapping layer.
Seven KPI tiles sit in a full-width grid below the refresh bar. Each tile has a colored 2px accent bar at the top. Clicking any tile populates the right column inspection panel with expanded detail and quick actions.
| Tile | Color | Source Field | Sub-Label | Click → Right Panel |
|---|---|---|---|---|
| CPU Usage | ORANGE | summary.cpuPct | ⚠ Elevated if >70%, else Normal | CPU bar + CPU Diagnostics action if >70% |
| Memory | BLUE | summary.memPct | ⚠ High if >80%, else Normal | Memory usage bar + Performance Report action |
| IPS Blocked | RED | summary.ipsBlocked | Last 24h | Block count + View IPS Events + IPS Report |
| App Control | ACCENT | summary.appCtrlEvents | Events today | Count detail + View Report action |
| Web Blocked | YELLOW | summary.webFilterBlocked | DNS + URL | Count detail + View Report action |
| VPN Tunnels | TEAL | summary.vpnUp / vpnDown | ⚠ N down if any down | Up/Down counts + Reset VPN + View VPN Events |
| HA Status | GREEN | summary.haState | summary.fwVersion | HA State, Sync, Version, Uptime + View HA Detail |
The 272px left column is the monitor/triage panel. It has a fixed header showing the column title and a live event count, followed by four tabs. Only one tab panel is visible at a time.
Two filter rows sit above a scrollable alert stream. The top row filters by event type; the second row filters by severity. Both filters stack — active type and active severity both apply simultaneously.
| Filter Row | Options | State Variable |
|---|---|---|
| Type | All · Threat · VPN · Policy · Intf · Sys | FG.filter — set by fgSetFilter(f) |
| Severity | CRIT · HIGH · MED · LOW · ALL | FG.sev — set by fgSetSev(s) |
The alert stream below shows compact event rows (colored severity dot · title · client name · age). Clicking any row calls fgSelectRow(id) which populates the right column inspection panel and highlights the matching row in the center event table.
Lists all FortiOS REST API endpoints used by the console with live latency and status. In demo mode latencies are randomized. In live mode they reflect actual round-trip times from the proxy.
- Green dot = OK. Orange = WARN (>300ms or intermittent). Red = FAIL (no response).
- Clicking an endpoint row populates the right panel with the endpoint path, latency, status, and a Re-test + Log Service Issue action.
- Endpoints verified against FortiOS 7.x REST API documentation are listed in Section 11.
Shows all FortiGuard subscription services with current signature version, days until expiry, and status badge. Data source: mockFortiGuard() in demo mode, /api/v2/monitor/license/status in live mode.
- Services tracked: AntiVirus, IPS, Web Filter, DNS Filter, App Control, AntiSpam, Outbreak Prevention, FortiSandbox Cloud.
- Status colors: OK = within expiry. EXPIRING = approaching expiry (random threshold in demo). EXPIRED = past expiry date.
- Clicking a service row opens an inspection panel with version detail and a Force Update action.
- Expiring services also surface a Renewal Ticket action button.
Shows the HA cluster configuration and per-member stats. Data source: mockHA() in demo mode, /api/v2/monitor/system/ha-statistics in live mode.
- Cluster fields: Mode (Active-Passive), Group name, Group ID, Heartbeat state, Last failover timestamp.
- Per-member rows show: name, serial, role (Primary / Secondary), CPU %, memory %, session count, sync state, uptime.
- Clicking a member row opens the inspection panel with progress bars for CPU and memory and Force Failover / Force HA Sync actions.
The center column takes up all remaining horizontal space. It is composed of two sub-regions: an interface card strip at the top and a tabbed content area below.
Seven interface/service cards scroll horizontally above the center tabs. Each card has a colored 2px top bar (green = OK, orange = degraded/standby, red flashing = critical), an icon, interface name, status label, and info line.
| Card | Type | Info Line | Click Action |
|---|---|---|---|
| wan1 | WAN | ISP-A · 1Gbps · bandwidth Mbps | View Traffic + SD-WAN Rules |
| wan2 | WAN | ISP-B · 500Mbps · Active or Degraded | View Traffic + SD-WAN Rules |
| ipsec0 | Tunnel | N tunnels active | Reset Tunnel + View Users |
| ssl.root | VPN | N active users | Reset Tunnel + View Users |
| internal | LAN | Core LAN · bandwidth Mbps | View Traffic |
| dmz | DMZ | DMZ Zone · bandwidth Mbps | View Traffic |
| FortiGuard | Service | Synced or Updating · signature version | Inspection detail |
A scrollable table of all current events filtered by the active client, type filter, and severity filter. Column layout:
| Column | Width | Content |
|---|---|---|
| ID | 70px | Event ID — FGT-XXXX for alerts, INC-XXXX for incidents, DEV-XXXX for devices |
| SEV | 46px | Colored severity badge: CRIT / HIGH / MED / LOW |
| TITLE / DEVICE | flex 1 | Primary title bold + device name in monospace below |
| CLIENT | 110px | Tenant name — populated from a.client field |
| STATUS | 78px | open (red) · investigating (orange) · resolved (dim) · online (green) |
| AGE | 52px | Time since event: Xm or Xh Ym |
| ASSIGNED | 82px | Engineer name or Unassigned |
A 2px left border on each row is colored by severity: red = crit, orange = high, yellow = med, blue = low. Row hover highlights orange-tinted. Selected row (after clicking) highlights with a stronger orange background.
Displays the FortiGate IPv4 routing table. Data source: mockRoutes() / /api/v2/monitor/router/ipv4. Columns: Type (BGP/OSPF/Static/Connected) · Destination · Gateway · Interface · Admin Distance · Status badge.
Clicking any row populates the right panel with route detail and Flush Route / Traceroute actions.
Lists all firewall policies. Data source: mockPolicies() / /api/v2/cmdb/firewall/policy. Columns: ID · Name · Src→Dst · Service · Action (ACCEPT / DENY badge) · Hit Count · Status (enabled / disabled badge).
Clicking any row opens a detail panel with IPS, AV, and web filter profile state and Edit Policy / Disable Policy actions.
Lists IPs in the FortiGate banned (quarantine) list. Data source: mockQuarantine() / /api/v2/monitor/user/banned. Columns: IP Address · Reason · Source engine · Added time · Expires · Release button.
Clicking a row opens a detail panel with Release from Quarantine / Extend Ban / Create Ticket actions.
/api/v2/monitor/user/banned. The endpoint /api/v2/monitor/user/quarantine does NOT exist in FortiOS. Any proxy mapping must use user/banned.
Displays WAN1 and WAN2 bandwidth charts (stacked bar, 20 samples), Top Applications by percentage, and Top Source IPs by bytes and session count. Data source: mockTraffic() / /api/v2/monitor/firewall/session + /api/v2/monitor/system/interface.
Clicking a source IP fires a toast: "🔍 Investigating X.X.X.X". In live mode this should trigger a lookup action.
Active VPN user sessions. Data source: mockUsers() / /api/v2/monitor/vpn/ssl. Columns: Username · Auth Type · IP Address · Duration · Rx/Tx bytes · Status badge.
Clicking any row surfaces Disconnect User and Block IP actions.
The 300px right column is the inspection and action panel. It starts in a prompt state and populates when any row, tile, card, or API endpoint is clicked. The column header shows the selected item ID and the content area scrolls independently.
When a row is clicked via fgSelectRow(id), the panel renders:
- Detail grid: Severity, Status, Age, Assigned — 2×2 card grid with large colored values.
- Description: Full detail text from the event's
detailfield — FortiOS log context, IPS rule matched, affected hosts, action taken. - Identifiers list: Event ID, Device, Source, Client, Type, Policy, Src IP, Dst IP, Action, Model, Firmware, CPU%, Mem%, Sessions — only fields that exist for the selected object are rendered.
- Quick Actions: Context-sensitive buttons based on event type. See action table below.
All action buttons call fgAct(type) which triggers the action modal with a multi-step execution sequence. In demo mode steps animate but no real API calls are made. In live mode each step maps to an actual API call via the proxy.
| Action | fgAct() Key | Context | Steps (Live Mode Intent) |
|---|---|---|---|
| Block Source IP | block | Threat events, VPN users | Identify threat context → Look up existing policies → Add IP to block list via FortiOS API → Verify rule active → Log to FortiAnalyzer |
| Edit Firewall Policy | policy | Policy events, policy rows | Fetch current policy config → Analyze hit counts → Apply modification via API → Verify policy compile |
| Disable Policy | policy-disable | Policy rows | Fetch policy state → Set status disabled → Confirm disabled |
| Reset VPN Tunnel | vpn | VPN events, ipsec/ssl cards | Identify affected tunnel → Clear IKE/IPSec SAs → Trigger Phase 1 renegotiation → Confirm tunnel Up → Verify BGP routes restored |
| Create PSA Ticket | ticket | All event types | Collect event metadata → Check for duplicates → Create PSA ticket → Notify assigned engineer |
| Generate Report | report | KPI tiles, system events | Compile event timeline → Gather policy + threat summary → Render via FortiAnalyzer → Send to stakeholders |
| Resolve Event | resolve | All event types | Verify threat neutralized → Log resolution notes → Notify client |
| Reboot Device | reboot | Device events | Backup config → Send reboot command → Wait for device online → Verify reachable |
| Flush Route | route-flush | Routing rows | Identify route entry → Clear from RIB → Confirm re-learned |
| Traceroute | route-trace | Routing rows | Execute traceroute from FortiGate → Resolve hops → Display path results |
| Update SD-WAN Rules | sdwan | WAN interface cards | Fetch SD-WAN rules → Check interface SLA scores → Apply rule update → Verify traffic steering |
| Force HA Failover | ha-failover | HA secondary member | Confirm intent → Send failover command → Wait for secondary to assume primary → Verify new primary active |
| Force HA Sync | ha-sync | HA members | Initiate config sync → Wait for completion → Confirm In-Sync state |
| Force FortiGuard Update | fortiguard-update | FortiGuard tab | Connect to FortiGuard Distribution Network → Download latest signatures → Apply update → Verify version updated |
| Test API Endpoint | api-test | API tab rows | Send test request → Validate response payload → Confirm reachable |
| CPU Diagnostics | diag-cpu | CPU KPI tile (if >70%) | Fetch top process list → Analyze CPU spikes → Log diagnostics to FortiAnalyzer |
| Release from Quarantine | unquarantine | Quarantine rows | Identify quarantine entry → Release IP from banned list → Log release action |
| Disconnect VPN User | disconnect-user | Users tab rows | Locate active session → Terminate session via SSL-VPN API → Confirm session closed |
All actions execute through a full-screen modal overlay that shows each step sequentially. The modal is driven by fgRunModal(title, steps) where each step object has an id, icon, label, and dur (simulated duration in ms).
- Step states:
waiting(dim) →running…(yellow pulsing) →✓ done(green). Steps execute sequentially with 80–200ms gaps between them. - Progress bar: Orange bar fills proportionally as each step completes. Reaches 100% when the final step is done.
- Close button: Hidden during execution. Appears only after all steps complete.
- Keyboard: Escape closes the modal at any time via the global keydown handler.
- Live mode: Each step's
durcan be replaced with a real async API call. The modal's sequential step pattern is designed to accommodate real network latency gracefully.
#dpane) is separate from the action modal. The detail pane opens from event table rows and shows deeper investigation context — IPS rule details, connection logs, related events. It slides in from the right edge. The action modal covers the full screen and is only for executing operational actions.
All endpoints below are verified against FortiOS 7.x REST API documentation. Auth header for all requests: Authorization: Bearer <api_token> or cookie-based: Cookie: APSCOOKIE=...
# System status + firmware version + serial + uptime GET /api/v2/monitor/system/status # CPU, memory, disk utilization GET /api/v2/monitor/system/resource/usage # HA cluster statistics — members, role, sync state GET /api/v2/monitor/system/ha-statistics # Available firmware versions (NOT IPS engine status) GET /api/v2/monitor/system/available-firmware
# Memory log — event stream (no persistent storage) GET /api/v2/log/memory/event?logtype=event&rows=200 # FortiAnalyzer connectivity status GET /api/v2/monitor/log/fortianalyzer-connectivity-status # FortiGuard license & subscription status GET /api/v2/monitor/license/status
# IPv4 routing table — BGP, OSPF, Static, Connected GET /api/v2/monitor/router/ipv4 # IPv6 routing table (if needed) GET /api/v2/monitor/router/ipv6 # Interface list and status GET /api/v2/monitor/system/interface # Active firewall sessions GET /api/v2/monitor/firewall/session
# SSL-VPN active user sessions GET /api/v2/monitor/vpn/ssl # IPSec VPN tunnel status GET /api/v2/monitor/vpn/ipsec
# Firewall policy list — supports ?filter, ?vdom, ?start, ?count GET /api/v2/cmdb/firewall/policy # Banned IP list (called "quarantine" in UI) GET /api/v2/monitor/user/banned # Add IP to banned list (Block IP action) PUT /api/v2/monitor/user/banned Body: { "ip_address": "x.x.x.x", "expiry": 3600 }
# Device list per ADOM — requires FortiManager, not standalone FortiOS POST /json/rpc Body: { "method": "get", "params": [{ "url": "/dvmdb/adom/<adom>/device" }], "session": "<session_token>", "id": 1 } # Per-ADOM policy read via FortiManager proxy GET /api/v2/cmdb/firewall/policy?vdom=<vdom>&adom=<adom>
These limitations are documented verbatim from the source code API audit block. They reflect the actual state of the FortiOS 7.x REST API as of the build date — not oversights in the console design.
/alerts monitor endpoint. Events must be read from /api/v2/log/memory/event (memory log — not persisted across reboots) or forwarded via the FortiAnalyzer REST API. For managed deployments, the FortiAnalyzer REST API should be the primary event source. LIMITATION
FG.data.devices is populated from FortiManager only. LIMITATION
incidents fetch key must route to a PSA or SIEM source, not FortiOS. LIMITATION
/api/v2/monitor/user/banned. The path /api/v2/monitor/user/quarantine does NOT exist and will return a 404. Any proxy configuration must map to user/banned. The UI label "Quarantine" is user-facing terminology only. LIMITATION
/api/v2/monitor/firewall/stats endpoint. Traffic data must be assembled from /api/v2/monitor/firewall/session and /api/v2/monitor/system/interface. Per-application traffic breakdown requires FortiAnalyzer or FortiView data — it is not available directly from FortiOS. LIMITATION
/api/v2/monitor/system/ha-statistics. The path /api/v2/monitor/ha does NOT exist. Any proxy or automation that references the shorter path will fail. LIMITATION
/api/v2/monitor/log/fortianalyzer-connectivity-status. The path /api/v2/monitor/log/fortianalyzer does NOT exist. LIMITATION
/api/v2/monitor/system/available-firmware returns available firmware versions, not IPS engine status. IPS engine version is returned inside the /api/v2/monitor/system/status response. Do not use the firmware endpoint to check IPS signature versions — use /api/v2/monitor/license/status instead. LIMITATION
/api/v2/log/memory/event reads from the FortiGate's volatile memory log buffer. This log is cleared on reboot and has limited capacity. For reliable event history, FortiAnalyzer (or a SIEM receiving syslog from the FortiGate) must be the authoritative log source. The console's Events tab reflects this when wired to live data. LIMITATION
fetch() from the browser to the FortiGate management IP will fail with a CORS error regardless of API token validity. ARCHITECTURAL
All configurable values are located at the top of the <script> block. No config file, no environment variables — edit these constants directly in the HTML source.
| Constant | Default | Purpose |
|---|---|---|
| DEMO_MODE | true | Master toggle. true = all mock data, no network calls. false = live API mode via proxy. |
| FG_BASE_URL | 'https://your-fortigate-hostname' | FQDN or IP of the FortiGate management interface. Used as the base URL for all API calls in live mode. In multi-tenant mode, overridden per-client by FG_CLIENTS. |
| FG_API_TOKEN | '' | FortiOS REST API token. Generate from System > Admin > Administrators > Create API user. Token must have read access to monitor endpoints and write access for action endpoints (block, policy, VPN reset). |
The FG_CLIENTS array defines all managed tenants. One entry per client. Each entry drives the client selector chip UI and (in live mode) provides the credentials for routing API calls to that specific FortiGate instance.
var FG_CLIENTS = [ { id: 'acme', // unique slug — used for chip DOM ID name: 'Acme Corp', // display name — must match a.client in data color: '#4dabff', // chip accent color when selected baseUrl: 'https://fg.acme.corp', // FortiGate FQDN for this client (live mode) apiToken: '' // API token for this client's FortiGate }, // ... one entry per client ];
| Field | Required | Notes |
|---|---|---|
| id | Yes | Unique slug. Used for chip element ID (cc-{id}) and CSS scoping. Lowercase, no spaces. |
| name | Yes | Must exactly match the client string field in alert/incident/device data objects for filtering to work. |
| color | Yes | Any valid CSS color. Used for the chip's active border, text, and background tint. |
| baseUrl | Live only | FortiGate management FQDN including protocol. No trailing slash. In FortiManager deployments, this may be the FortiManager host with ADOM routing handled at the proxy layer. |
| apiToken | Live only | Per-client FortiOS API token. In live mode, fgFetch() reads the active client's token via FG_CLIENTS.find(c => c.name === FG.activeClient). |
Runtime state is stored in var FG = { ... } at the top of the script. Do not edit this — it is initialized programmatically. Key properties for reference:
summary, alerts, devices, incidents, routes, policies, quarantine, users, traffic, fortiguard, ha.null for All Clients view. String matching a FG_CLIENTS[n].name value when a client chip is active. Used by fgGetFiltered().'all' | 'threat' | 'vpn' | 'policy' | 'interface' | 'system'.'all' | 'crit' | 'high' | 'med' | 'low'.'alerts' | 'api' | 'fortiguard' | 'ha'.'events' | 'routing' | 'policies' | 'quarantine' | 'traffic' | 'users'.['J. Torres','K. Patel','M. Chen','S. Adams','R. Nguyen','Unassigned']. Replace with your actual team.Because FortiOS does not return CORS headers, a proxy service is required for all live API calls. The proxy sits between the browser and the FortiGate, injecting the Authorization header and relaying responses. The console's fgFetch() function has the live block pre-written — it just needs the proxy URL and the DEMO_MODE flag changed.
Log & Report, Network, Policy & Objects, User & Device, System and Write access to User & Device (for block IP) and Network (for VPN reset). Copy the generated API token.GET /proxy/system/status → GET {FG_BASE_URL}/api/v2/monitor/system/status with header Authorization: Bearer {FG_API_TOKEN}. The proxy strips the Authorization header from the browser request and injects the real token server-side, keeping the token out of browser DevTools.FG_BASE_URL to your proxy's base URL (not the FortiGate directly). Set FG_API_TOKEN if the proxy uses token pass-through, or leave blank if the proxy handles credential injection internally.baseUrl to that client's FortiGate FQDN and apiToken to that client's API token. The proxy reads these per-request to route to the correct device. If using FortiManager as an upstream aggregator, set all clients to the FortiManager host and differentiate by ADOM at the proxy layer.var DEMO_MODE = true; to var DEMO_MODE = false;. Uncomment the live fetch block inside fgFetch(). The EP_MAP object inside that block maps each data key to the correct proxy endpoint path.span.demo-badge element inside #rail. Remove that element from the HTML to clear the demo indicator from the live deployment.The multi-tenant layer was added to the console as a full feature layer. The client selector row, FG_CLIENTS config, fgSelectClient(), renderClientRow(), and fgGetFiltered() are all implemented. In demo mode client filtering works on the mock data set. In live mode it requires per-tenant API routing at the proxy layer.
Each client has their own FortiGate. The console routes to each device directly. One FG_CLIENTS entry per FortiGate. The proxy uses the selected client's baseUrl and apiToken to target the correct device on each request.
Best for: small MSP with <10 clients, each with standalone FortiGate.
FortiManager is the single upstream. All baseUrl values point to FortiManager. The proxy uses the client's ADOM name (stored in FG_CLIENTS[n].id or a custom field) to scope FortiManager JSON-RPC queries per request. Policy and device data is per-ADOM. Event data routes through FortiAnalyzer.
Best for: MSPs managing 10+ clients through a central FortiManager instance.
- Events tab: Fully filtered — only events where
a.client === FG.activeClientare rendered. - Alert stream (left panel): Fully filtered — mini event rows respect the active client.
- Rail stat counters: Filtered — crit and high counts reflect the selected client's events only.
- KPI tiles: In demo mode, summary data is not per-client (single mock summary object). In live mode,
fgFetch('summary')routes to the selected client's FortiGate, so KPI tiles will be client-specific. - Routing, Policies, Quarantine, Traffic, Users tabs: In live mode these are inherently per-client (the proxy routes to the selected FortiGate). In demo mode they show shared mock data regardless of client selection.
animCount() is called in fgRenderAll() after data arrives and animates from 0 to the target value. Wait 200–400ms for the initial mock delay to resolve.client field in the data objects must exactly match the name value in FG_CLIENTS. Check for extra spaces, different capitalization, or abbreviations. In demo mode the mock values are: Acme Corp, BlueSky Logistics, Cortex Financial, Delta Medical, Ember Analytics.FG_BASE_URL points to the proxy (not the FortiGate). Verify the proxy returns permissive CORS headers: Access-Control-Allow-Origin: *.User & Device. Confirm the proxy routes PUT /api/v2/monitor/user/banned correctly. Check the FortiGate's quarantine (banned) list via GUI under Monitor > Quarantine Monitor./api/v2/cmdb/firewall/policy requires the correct VDOM context. If the FortiGate is in multi-VDOM mode, append ?vdom=root (or the correct VDOM name) to the endpoint. Verify the API user has policy-read access in that VDOM.FG.data.ha.members is undefined. In live mode, confirm the FortiGate is actually in an HA configuration — standalone devices return a minimal or empty response from /api/v2/monitor/system/ha-statistics. The console handles this gracefully by showing the placeholder message.animation: tscrl 70s linear infinite. If the animation appears frozen, check that no parent element has overflow:hidden cutting the animation mid-frame. On iOS Safari, add -webkit-animation prefix to the .tscroll rule. Also verify FG.data.alerts is populated — an empty data set produces an empty ticker with nothing to scroll.272px 1fr 300px) requires at least ~700px of content width after the left column. The rail and ticker remain functional at any width. On displays under 1024px, consider hiding the right column and using the detail pane instead.FG.countTimer. If the page tab becomes inactive (backgrounded), browsers throttle or suspend JavaScript timers. The auto-refresh will resume when the tab is foregrounded. For wall display use, keep the tab active and visible at all times. startAutoRefresh(30000) runs on a separate 30s interval and will also restart on tab refocus.keydown listener calls fgCloseModal() and fgClosePane() on Escape. If another element has captured focus (an input field inside the modal), the keydown may not bubble. Click outside the input first, then press Escape.| What to Change | Where in Source | How |
|---|---|---|
| Refresh interval | startAutoRefresh(30000) at bottom of script | Change 30000 to desired milliseconds. Also update FG.countdown = 30 in fgStartRefreshTimer(). |
| Client names + colors | var FG_CLIENTS = [...] | Edit name and color fields. Names must match client values in mock data or API responses. |
| Mock client names in data | mockAlerts(), mockDevices(), mockIncidents() | Edit the pick([...]) array in the client assignment line in each mock function. |
| Engineer names | FG.engineers = [...] in FG object | Replace with your team members. Last entry should remain 'Unassigned'. |
| FortiGate accent color | :root { --accent: #ff6600 } in CSS | Change hex value. Also update --accent2 for hover states. |
| Interface cards | var ifaces = [...] in fgRenderAll() | Edit names, types, status, and info lines. Each entry needs ic, nm, type, st, sst, n, bw, key. |
| Startup toast message | End of DOMContentLoaded | Modify or remove the fgToast(...) call. |
| Action steps | var ACT_STEPS = { ... } | Each action key maps to an array of step objects. Add, remove, or reorder steps. Each step needs id, icon, label, dur. |