feat(nodes): hide hub-only views when active node is remote (#1007)

* feat(nodes): hide hub-only views when active node is remote

Fleet, Schedules, Audit, Logs, and Auto-Update operate on hub-owned state
(node registry, fleet schedules, centralized audit, fleet-wide log
aggregation, fleet-wide update preview). When the active node is remote,
proxying those surfaces would show that remote's own disconnected state
instead of the hub's. Hide them from the nav strip and force-redirect to
Home if one was open during the node switch.

Backend hubOnlyGuard middleware sits between nodeContextMiddleware and the
remote proxy and rejects /api/scheduled-tasks, /api/audit-log, and
/api/notification-routes with 403 + HUB_ONLY_ENDPOINT when nodeId resolves
to a remote, closing the script-bypass path the UI gating cannot reach.

Settings sub-sections were already gated via the hiddenOnRemote registry;
this extends the same model to top-level views.

* docs(nodes): note hub-only visibility on Fleet, Schedules, Audit, Logs, Auto-Update

Each of the five hub-only feature pages now points readers to the
canonical "What top-level views show when a remote node is active"
section in multi-node.mdx, so users landing directly on a feature page
understand why the nav item disappears when they switch to a remote node.
This commit is contained in:
Anso
2026-05-08 22:54:58 -04:00
committed by GitHub
parent 7ad9381ede
commit ccad5c925b
16 changed files with 448 additions and 49 deletions
+25
View File
@@ -22,3 +22,28 @@ export function isProxyExemptPath(path: string): boolean {
}
return false;
}
// Path prefixes that are hub-only: they manage state owned by the local hub
// (centralized audit, fleet schedules, notification routing rules). Routed
// to the local hub when nodeId resolves to local, but rejected with 409 when
// nodeId resolves to a remote node so a script/curl call cannot trick the
// proxy into forwarding hub-only authority across a node boundary.
//
// Frontend nav surfaces are gated separately via `HUB_ONLY_VIEWS` in
// useViewNavigationState.ts; this list is the backend defense-in-depth.
//
// Consumed by:
// - middleware/hubOnlyGuard.ts → 409 when nodeId is remote
export const HUB_ONLY_PREFIXES: readonly string[] = [
'/api/scheduled-tasks/',
'/api/audit-log/',
'/api/notification-routes/',
];
/** Returns true when the path is hub-only and must not be proxied to a remote node. */
export function isHubOnlyPath(path: string): boolean {
for (const prefix of HUB_ONLY_PREFIXES) {
if (path.startsWith(prefix)) return true;
}
return false;
}