Fixed the "host is offline" banner on the My device page incorrectly appearing (#46091)

This commit is contained in:
Victor Lyuboslavsky
2026-05-22 18:37:57 -05:00
committed by GitHub
parent a276ae26fb
commit fc10afe147
3 changed files with 31 additions and 2 deletions
@@ -0,0 +1 @@
* Fixed the "host is offline" banner on the My device page incorrectly appearing during the first few minutes after an enrollment.
@@ -67,6 +67,7 @@ import {
isSoftwareScriptSetup,
isIPhone,
isIPad,
isRecentlyEnrolled,
} from "./helpers";
import PolicyDetailsModal from "../cards/Policies/HostPoliciesTable/PolicyDetailsModal";
@@ -295,10 +296,16 @@ const DeviceUserPage = ({
if (!refetchStartTime) {
// Here and below: iOS/iPadOS refetches use MDM commands which can be slower/less predictable
// than osquery. Don't show an error, just reset and let the user try again.
// Recently enrolled hosts are also exempted: orbit endpoints don't update host_seen_times,
// so a fresh host can read as offline until its first osquery distributed-read.
const isIOSOrIPadOS =
responseHost.platform === "ios" ||
responseHost.platform === "ipados";
if (responseHost.status === "online" || isIOSOrIPadOS) {
if (
responseHost.status === "online" ||
isIOSOrIPadOS ||
isRecentlyEnrolled(responseHost.last_enrolled_at)
) {
setRefetchStartTime(Date.now());
setTimeout(() => {
refetchDupDetails();
@@ -317,7 +324,11 @@ const DeviceUserPage = ({
const isIOSOrIPadOS =
responseHost.platform === "ios" ||
responseHost.platform === "ipados";
if (responseHost.status === "online" || isIOSOrIPadOS) {
if (
responseHost.status === "online" ||
isIOSOrIPadOS ||
isRecentlyEnrolled(responseHost.last_enrolled_at)
) {
setTimeout(() => {
refetchDupDetails();
refetchExtensions();
@@ -47,6 +47,23 @@ export const isSoftwareScriptSetup = (s: ISetupStep) => {
return s.source === "sh_packages" || s.source === "ps1_packages";
};
// Hosts after enrollment during which we suppress the "host is offline" banner.
// Orbit endpoints do not update host_seen_times, so a freshly enrolled host can appear offline
// until its first osquery distributed-read check-in (typically within 5-10 minutes).
const RECENTLY_ENROLLED_THRESHOLD_MS = 10 * 60 * 1000;
export const isRecentlyEnrolled = (
lastEnrolledAt: string | undefined
): boolean => {
if (!lastEnrolledAt) return false;
const enrolledAt = new Date(lastEnrolledAt).getTime();
if (isNaN(enrolledAt)) return false;
// Require a non-negative delta so a future timestamp (e.g. from client/server clock skew) is not
// treated as "recent" and does not hide a real offline state indefinitely.
const delta = Date.now() - enrolledAt;
return delta >= 0 && delta < RECENTLY_ENROLLED_THRESHOLD_MS;
};
// Same solution as defined in /templates/enroll-ota.html (https://github.com/fleetdm/fleet/pull/26592)
export const isIPhone = (navigator: Navigator) =>
/iPhone/i.test(navigator.userAgent);