// ============================================================================ // Imara - core pages: Overview, Traffic map, Servers // ============================================================================ // ---------- OVERVIEW ---------- // What needs attention first, then the fleet at a glance. Every number comes // from live data; incoming protection from each server's own firewall report. function OverviewPage({ nav, openHost, openAlert, openIntent, intents, alerts }) { const D = window.MS_DATA; const T = window.ImaraTerms; const c = D.fleetCounts; // Busiest servers: the Imara server's own last-hour per-server counts // (GET /api/stats/overview top_hosts_1h). Without it, fall back to the // sampled traffic map and say so. const serverTop = Array.isArray(D.TOP_HOSTS_1H); const topHosts = serverTop ? D.TOP_HOSTS_1H.map((h) => ({ s: D.byId[h.agent_id] || { agent_id: h.agent_id, hostname: h.hostname || h.agent_id }, flows: h.flows_1h || 0, known: !!D.byId[h.agent_id] })) : D.SERVERS.map((s) => ({ s, known: true, flows: (D.TOPO_EDGES || []).filter((e) => e.from === s.agent_id || e.to === s.agent_id).reduce((a, e) => a + e.flows, 0) })) .sort((a, b) => b.flows - a.flows).slice(0, 6); const maxFlow = topHosts[0]?.flows || 1; const openA = alerts.filter((a) => a.status === "open"); const trueOpen = D.ALERT_OPEN_COUNTS; const ac = trueOpen ? { critical: trueOpen.critical || 0, warn: trueOpen.warn || 0, info: trueOpen.info || 0 } : { critical: openA.filter((a) => a.severity === "critical").length, warn: openA.filter((a) => a.severity === "warn").length, info: openA.filter((a) => a.severity === "info").length }; // Protection as of now (a report older than 15 minutes reads "unknown"). const pstate = Object.fromEntries(D.SERVERS.map((s) => [s.agent_id, T.protectionNow(s, D).state])); const prot = (st) => D.SERVERS.filter((s) => pstate[s.agent_id] === st); const askedOff = D.SERVERS.filter((s) => pstate[s.agent_id] === "off" && T.protectionNow(s, D).contributors.some((c) => c.asks === true)); const protOn = prot("on"), protOff = prot("off").filter((s) => !askedOff.includes(s)), protUnsure = D.SERVERS.filter((s) => ["unknown", "waiting"].includes(pstate[s.agent_id])); // Waiting for approval: the ones this person may approve, and the ones // they wrote, edited or sent (others must approve those). const me = (window.IMARA_ME && window.IMARA_ME.user) || ""; const mine = (i) => !!me && [i.created_by, i.last_edited_by, i.requested_by].includes(me); const waitingAll = intents.filter((i) => i.approval_state === "requested"); const waiting = waitingAll.filter((i) => !mine(i) || (i.required_approvers || 1) < 2); const waitingOthers = waitingAll.filter((i) => !waiting.includes(i)); // Rules this person wrote or edited that an approver sent back. const sentBack = intents.filter((i) => i.approval_state === "rejected" && mine(i)); const problems = intents.filter((i) => i.status === "error" || (i.push_errors || []).length > 0); // Rules naming a group the Imara server cannot use now: their servers get // no firewall changes at all until it is fixed. const refsOf = (i) => T.unusableGroupRefs(i, { groups: D.GROUPS || [], servers: D.SERVERS }); const stuck = intents.filter((i) => refsOf(i).some((b) => b.stuck)); const nowhere = intents.filter((i) => !stuck.includes(i) && refsOf(i).some((b) => !b.stuck)); const silent = D.SERVERS.filter((s) => s.fresh === "silent"); const pendingIps = D.SERVERS.filter((s) => (s.pending_ips || []).length > 0); const active = intents.filter((i) => T.confirmedTargets(i, D.byId || {}) > 0).length; const names = (list) => { const n = list.map((s) => s.hostname || (s.body !== undefined ? T.ruleTitle(s) : s.name)); return n.length > 3 ? `${n.slice(0, 3).join(", ")} and ${n.length - 3} more` : T.joinList(n); }; // One rule: open it; several: the Rules page with that filter. const openRules = (list, filter) => () => { if (list.length === 1 && openIntent) openIntent(list[0]); else { IntentsPage.openWith = filter || "all"; nav("intents"); } }; // "5 rules wait for your approval." (the names when there are one or two) const line = (list, one, many, rest) => `${T.plural(list.length, one, many)} ${rest}${list.length <= 2 ? `: ${names(list)}` : ""}.`; const attention = [ waiting.length && { icon: "UserCheck", tone: "warn", text: line(waiting, "rule waits", "rules wait", "for your approval"), go: openRules(waiting, "waiting") }, waitingOthers.length && { icon: "Hourglass", tone: "neutral", text: line(waitingOthers, "rule you sent awaits", "rules you sent await", "2 approvals from others"), go: openRules(waitingOthers, "waiting") }, sentBack.length && { icon: "Pencil", tone: "warn", text: line(sentBack, "rule you sent has", "rules you sent have", "changes requested"), go: openRules(sentBack, "drafts") }, problems.length && { icon: "OctagonAlert", tone: "bad", text: line(problems, "rule has", "rules have", "a problem on a server"), go: openRules(problems, "problems") }, stuck.length && { icon: "OctagonAlert", tone: "bad", text: `${names(stuck)} ${stuck.length === 1 ? "names" : "name"} a group with no usable server. ${stuck.length === 1 ? "Its" : "Their"} servers get no firewall changes until fixed.`, go: openRules(stuck) }, nowhere.length && { icon: "ShieldQuestion", tone: "warn", text: `Applies to no server now: ${names(nowhere)}.`, go: openRules(nowhere) }, silent.length && { icon: "WifiOff", tone: "bad", text: silent.length === 1 ? `Agent offline: ${names(silent)}. Its firewall rules stay in place; changes wait until it is back.` : `${silent.length.toLocaleString("en-US")} agents offline: ${names(silent)}. Their firewall rules stay in place; changes wait until they are back.`, go: () => { if (silent.length === 1) openHost(silent[0]); else { ServersPage.openWith = "silent"; nav("servers"); } } }, alerts.some((a) => a.status === "acknowledged") && (() => { const n = alerts.filter((a) => a.status === "acknowledged").length; return { icon: "Eye", tone: "neutral", text: `${n}${D.ALERT_ACK_PARTIAL ? "+" : ""} acknowledged alert${n === 1 && !D.ALERT_ACK_PARTIAL ? " is" : "s are"} not resolved yet.`, go: () => { AlertsPage.openWith = "acknowledged"; nav("alerts"); } }; })(), ac.critical + ac.warn > 0 && { icon: "Bell", tone: ac.critical ? "bad" : "warn", text: `Open alerts: ${(ac.critical + ac.warn).toLocaleString("en-US")}${ac.critical ? ` (${ac.critical} critical)` : ""}.`, go: () => nav("alerts") }, pendingIps.length && { icon: "Network", tone: "warn", text: `New IP addresses pending approval: ${names(pendingIps)}.`, go: () => { ServerDrawer.openTab = "addresses"; openHost(pendingIps[0]); } }, askedOff.length && { icon: "ShieldAlert", tone: "bad", text: `Default deny not in force on ${names(askedOff)}, though a rule asks for it.`, go: () => openHost(askedOff[0]) }, protUnsure.length && { icon: "ShieldQuestion", tone: "warn", text: `Cannot confirm default deny on ${names(protUnsure)}${D.FW_LOADING ? " (firewall reports still loading)" : ""}.`, go: () => openHost(protUnsure[0]) }, protOff.length && { icon: "Shield", tone: "neutral", text: `Default deny off: ${names(protOff)}.`, go: () => (protOff.length === 1 ? openHost(protOff[0]) : nav("groups")) }, ].filter(Boolean); const toneColor = { ok: "var(--ok)", warn: "var(--warn)", bad: "var(--bad)", neutral: "var(--text-muted)" }; return (
Needs attention
{attention.length === 0 && (D.LOAD_FAILED && Object.values(D.LOAD_FAILED).some(Boolean) ?
Nothing needs attention in the data that loaded. Some data failed to load (see the notice at the top).
:
Nothing needs attention right now.
)} {attention.map((x, i) => ( ))}
nav("servers")} foot={c.silent ? [`${c.live} online`, c.stale && `${c.stale} delayed`].filter(Boolean).join(" · ") : c.stale ? `${c.live} online` : ""} trend={!c.total ? undefined : c.silent ? { tone: "bad", text: `${c.silent} offline` } : c.stale ? { tone: "warn", text: `${c.stale} delayed` } : { tone: "ok", text: "all online" }} /> nav("servers")} /> nav("intents")} foot={problems.length && waitingAll.length ? `${active} active · ${waitingAll.length} waiting` : `${active} active`} trend={problems.length ? { tone: "bad", text: `${problems.length} with a problem` } : waitingAll.length ? { tone: "warn", text: `${waitingAll.length} waiting` } : undefined} /> nav("alerts")} foot={`${ac.warn} warning · ${ac.info} info`} trend={ac.critical ? { tone: "bad", text: `${ac.critical} critical` } : undefined} />
Connections · last hour{(D.FLOW_TS || []).length ? {fmt((D.FLOW_TS || []).reduce((a, b) => a + (b.count || 0), 0))} recorded : null}Connections per minute, between your servers and to the outside. Both ends record a connection between two of your servers.
1 hour agonow
{openA.length > 0 && (
Open alerts
{openA.slice().sort((a, b) => ({ critical: 0, warn: 1, info: 2 }[a.severity] ?? 3) - ({ critical: 0, warn: 1, info: 2 }[b.severity] ?? 3) || (String(b.created_at) > String(a.created_at) ? 1 : -1)).slice(0, 5).map((a) => ( ))}
)}
{serverTop ? "Busiest servers · last hour" : "Busiest servers · from the traffic map"}{!serverTop && last-hour counts unavailableCounted from the latest {T.plural(D.FLOWS_COUNT || 0, "connection")}{D.FLOWS_SINCE ? ` (since ${new Date(D.FLOWS_SINCE).toLocaleTimeString()})` : ""}.}
{topHosts.length === 0 &&
No connections recorded.
} {topHosts.map(({ s, flows, known }, i) => ( ))}
Default deny

On: inbound traffic that no rule allows is blocked.

From each server's latest firewall report.

{(() => { // Counts first; then every server on a small fleet, or the // first ones without confirmed protection on a large one. const n = { on: 0, off: 0, waiting: 0, unknown: 0 }; D.SERVERS.forEach((s) => { n[pstate[s.agent_id]] = (n[pstate[s.agent_id]] || 0) + 1; }); return D.SERVERS.length > 12 && (
{["on", "off", "waiting", "unknown"].filter((k) => n[k]).map((k) => {T.protectionWord(k)}: {n[k].toLocaleString("en-US")})}
); })()} {(D.SERVERS.length > 12 ? D.SERVERS.filter((s) => pstate[s.agent_id] !== "on").slice(0, 8) : D.SERVERS).map((s) => { const st = pstate[s.agent_id]; return ( ); })} {D.SERVERS.length === 0 &&
No servers yet.
} {D.SERVERS.length > 12 && (() => { const notOn = D.SERVERS.filter((s) => pstate[s.agent_id] !== "on").length; return notOn > 8 ? : notOn === 0 ?
On for every server.
: null; })()}
); } // ---------- TRAFFIC MAP ---------- // How to read the map (exposure, colours, columns) is in its own help. function TopologyPage({ openHost, onCreateRule }) { return (
); } // ---------- SERVERS ---------- function ServersPage({ openHost }) { const D = window.MS_DATA; const T = window.ImaraTerms; const [q, setQ] = useState(""); // Opened from the Overview with a filter chosen ("silent", or a // protection filter such as "notOn"). const opened = ServersPage.openWith; const [fresh, setFresh] = useState(() => (["live", "stale", "silent"].includes(opened) ? opened : "all")); // Opened from the Overview with a protection filter set. const [prot, setProt] = useState(() => { const v = ["on", "notOn"].includes(opened) ? opened : "any"; ServersPage.openWith = null; return v; }); const [sort, setSort] = useState({ key: "hostname", dir: 1 }); const FILTERS = [["all", "All"], ["live", "Online"], ["stale", "Delayed"], ["silent", "Offline"]]; const PROT_FILTERS = [["any", "Any"], ["on", "Default deny on"], ["notOn", "Off or not confirmed"]]; const minute = Math.floor(T.serverNow() / 60000); const protOf = useMemo(() => (prot === "any" ? null : Object.fromEntries(D.SERVERS.map((s) => [s.agent_id, T.protectionNow(s, D).state]))), [prot, D.SERVERS, D.INTENTS, D.GROUPS, D.FW_VERSION, minute]); const filtered = D.SERVERS.filter((s) => { if (fresh !== "all" && s.fresh !== fresh) return false; if (protOf && (prot === "on" ? protOf[s.agent_id] !== "on" : protOf[s.agent_id] === "on")) return false; if (!q) return true; const hay = (s.hostname + " " + Object.entries(s.labels).map(([k, v]) => T.labelText(k, v)).join(" ")).toLowerCase(); return hay.includes(q.toLowerCase()); }); const sorted = [...filtered].sort((a, b) => { // Unreported metrics (null) sort below every real value. const get = (s) => sort.key === "hostname" ? s.hostname : sort.key === "os" ? s.os_distro : sort.key === "cpu" ? (s.cpu == null ? -1 : s.cpu) : sort.key === "mem" ? (s.mem == null ? -1 : s.mem) : sort.key === "last" ? -s.ageSec : s.hostname; const va = get(a), vb = get(b); return (va < vb ? -1 : va > vb ? 1 : 0) * sort.dir; }); const pg = usePaged(sorted, 25, [q, fresh, prot, sort.key, sort.dir].join("|")); const th = (key, label, align) => ( setSort((s) => ({ key, dir: s.key === key ? -s.dir : 1 }))}> {label}{sort.key === key && 0 ? "ChevronUp" : "ChevronDown"} size={12} />} ); // What each server says about itself: site and role from its name, and // its network (labels set at install are on its page). const chipsOf = (s) => T.serverFacts(s); const factsHead = T.namesFollowPattern() ? "Site · role · network" : "Labels"; return (
setQ(e.target.value)} />
{PROT_FILTERS.map(([f, l]) => )}
{FILTERS.map(([f, l]) => )}
{th("hostname", "Server", "left")}{th("last", "Agent status", "left")} {pg.slice.map((s) => { const pr = T.protectionNow(s, D); const st = pr.state; return ( openHost(s)}> ); })} {pg.total === 0 && }
Default deny

From each server's latest firewall report.

On: inbound traffic that no rule allows is blocked.

{factsHead}
{s.hostname}
{s.ip}
{T.freshnessText(s.fresh)}{agoLong(s.last_seen)} {T.protectionWord(st)}
{chipsOf(s).map(([k, v]) => {T.HOSTNAME_PARTS[k] ? String(v).toUpperCase() : T.SUBNET_LABELS[k] ? v : T.labelText(k, v)})}
); } window.OverviewPage = OverviewPage; window.TopologyPage = TopologyPage; window.ServersPage = ServersPage;