From c17134a2a41d2c175632e97bc6ce12512590ee13 Mon Sep 17 00:00:00 2001 From: ZacharyZcR Date: Tue, 25 Aug 2026 02:14:51 +0800 Subject: [PATCH] fix: authenticate unwatched hosts during the status probe (#1337) With metrics enabled, the status probe left SSH authentication to the metrics poll - which only runs while someone is viewing the host. An unwatched host therefore never left "reachable", while a host with metrics disabled (whose probe always authenticates) showed online. The probe now authenticates whenever no metrics poll will. --- src/backend/hosts/metrics/host-status.test.ts | 8 ++++++++ src/backend/hosts/metrics/host-status.ts | 15 +++++++++++++++ src/backend/hosts/metrics/index.ts | 6 +++++- 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/backend/hosts/metrics/host-status.test.ts b/src/backend/hosts/metrics/host-status.test.ts index f41c4544..55ab03c8 100644 --- a/src/backend/hosts/metrics/host-status.test.ts +++ b/src/backend/hosts/metrics/host-status.test.ts @@ -1,5 +1,6 @@ import { describe, expect, it } from "vitest"; import { + needsStatusPollAuthentication, statusAfterAuthentication, statusAfterReachabilityCheck, } from "./host-status.js"; @@ -21,4 +22,11 @@ describe("host availability status", () => { expect(statusAfterAuthentication(false, "online")).toBe("reachable"); expect(statusAfterAuthentication(false, "offline")).toBe("offline"); }); + + it("authenticates in the status probe unless a metrics poll will do it", () => { + expect(needsStatusPollAuthentication(false, false)).toBe(true); + expect(needsStatusPollAuthentication(false, true)).toBe(true); + expect(needsStatusPollAuthentication(true, false)).toBe(true); + expect(needsStatusPollAuthentication(true, true)).toBe(false); + }); }); diff --git a/src/backend/hosts/metrics/host-status.ts b/src/backend/hosts/metrics/host-status.ts index 3c5b3914..7c1a39e5 100644 --- a/src/backend/hosts/metrics/host-status.ts +++ b/src/backend/hosts/metrics/host-status.ts @@ -15,3 +15,18 @@ export function statusAfterAuthentication( if (authenticated) return "online"; return current === "offline" ? "offline" : "reachable"; } + +/** + * Whether the cheap status probe must also authenticate over SSH. + * + * "online" means authenticated, and normally the metrics poll proves that. + * That poll only runs while someone is viewing the host, so an unwatched host + * with metrics enabled would otherwise never leave "reachable" - while a host + * with metrics disabled, whose probe always authenticates, shows online. + */ +export function needsStatusPollAuthentication( + metricsEnabled: boolean, + hasViewers: boolean, +): boolean { + return !metricsEnabled || !hasViewers; +} diff --git a/src/backend/hosts/metrics/index.ts b/src/backend/hosts/metrics/index.ts index 4229be4c..be431cf1 100644 --- a/src/backend/hosts/metrics/index.ts +++ b/src/backend/hosts/metrics/index.ts @@ -71,6 +71,7 @@ import { } from "./helpers.js"; import { type HostStatus, + needsStatusPollAuthentication, statusAfterAuthentication, statusAfterReachabilityCheck, } from "./host-status.js"; @@ -581,7 +582,10 @@ class PollingManager { if ( isOnline && supportsMetrics(refreshedHost) && - !config?.statsConfig.metricsEnabled + needsStatusPollAuthentication( + !!config?.statsConfig.metricsEnabled, + this.activeViewers.has(refreshedHost.id), + ) ) { try { await withSshConnection(refreshedHost, async () => undefined);