diff --git a/frontend-modern/src/components/LegacySSHBanner.tsx b/frontend-modern/src/components/LegacySSHBanner.tsx
index b83a706..ff66bb0 100644
--- a/frontend-modern/src/components/LegacySSHBanner.tsx
+++ b/frontend-modern/src/components/LegacySSHBanner.tsx
@@ -1,4 +1,5 @@
import { Show, createSignal, createEffect, onMount } from 'solid-js';
+import { useNavigate } from '@solidjs/router';
/**
* ⚠️ MIGRATION SCAFFOLDING - TEMPORARY COMPONENT
@@ -12,6 +13,7 @@ import { Show, createSignal, createEffect, onMount } from 'solid-js';
* Can be disabled by setting PULSE_LEGACY_DETECTION=false on backend.
*/
export function LegacySSHBanner() {
+ const navigate = useNavigate();
const [isVisible, setIsVisible] = createSignal(false);
const [isDismissed, setIsDismissed] = createSignal(false);
@@ -34,6 +36,8 @@ export function LegacySSHBanner() {
if (health.legacySSHDetected && health.recommendProxyUpgrade) {
setIsVisible(true);
+ // Log banner impression for telemetry (removal criteria tracking)
+ console.info('[Migration] Legacy SSH banner shown to user');
}
} catch (error) {
// Silently fail - health check failures shouldn't break the UI
@@ -45,6 +49,8 @@ export function LegacySSHBanner() {
setIsVisible(false);
// Store dismissal in localStorage so it persists
localStorage.setItem('legacySSHBannerDismissed', 'true');
+ // Log dismissal for telemetry
+ console.info('[Migration] Legacy SSH banner dismissed by user');
};
return (
@@ -70,10 +76,18 @@ export function LegacySSHBanner() {
-
-
Legacy temperature monitoring detected.
- {' '}
-
Remove and re-add your nodes to upgrade to the secure proxy architecture.
+
+
+ Legacy temperature monitoring detected.
+ {' '}
+ Remove and re-add your nodes to upgrade to the secure proxy architecture.
+
+
diff --git a/internal/api/router.go b/internal/api/router.go
index e042953..57b6857 100644
--- a/internal/api/router.go
+++ b/internal/api/router.go
@@ -1144,16 +1144,46 @@ func (r *Router) detectLegacySSH() (legacyDetected, recommendProxy bool) {
return false, false
}
- // Check if running in a container
+ // Check if running in a container using multiple detection methods
inContainer := false
+
+ // Method 1: Check for /.dockerenv (Docker)
if _, err := os.Stat("/.dockerenv"); err == nil {
inContainer = true
- } else if data, err := os.ReadFile("/proc/1/cgroup"); err == nil {
- if strings.Contains(string(data), "docker") || strings.Contains(string(data), "lxc") {
+ }
+
+ // Method 2: Check /proc/1/cgroup (Docker/LXC)
+ if !inContainer {
+ if data, err := os.ReadFile("/proc/1/cgroup"); err == nil {
+ cgroupStr := string(data)
+ if strings.Contains(cgroupStr, "docker") ||
+ strings.Contains(cgroupStr, "lxc") ||
+ strings.Contains(cgroupStr, "/docker/") ||
+ strings.Contains(cgroupStr, "/lxc/") {
+ inContainer = true
+ }
+ }
+ }
+
+ // Method 3: Check /run/systemd/container (systemd containers)
+ if !inContainer {
+ if _, err := os.Stat("/run/systemd/container"); err == nil {
inContainer = true
}
}
+ // Method 4: Check /proc/1/environ for container indicators
+ if !inContainer {
+ if data, err := os.ReadFile("/proc/1/environ"); err == nil {
+ environStr := string(data)
+ if strings.Contains(environStr, "container=") ||
+ strings.Contains(environStr, "DOCKER") ||
+ strings.Contains(environStr, "LXC") {
+ inContainer = true
+ }
+ }
+ }
+
// If not in container, no need for proxy
if !inContainer {
return false, false
@@ -1179,13 +1209,25 @@ func (r *Router) detectLegacySSH() (legacyDetected, recommendProxy bool) {
sshKeysConfigured = true
}
- // If SSH keys exist, check if proxy is running
+ // If SSH keys exist, check if proxy is configured vs just temporarily down
if sshKeysConfigured {
// Check if pulse-sensor-proxy is available via unix socket
proxySocket := "/run/pulse-sensor-proxy/sensor.sock"
+ proxyBinary := "/usr/local/bin/pulse-sensor-proxy"
+
+ // If socket doesn't exist, need to distinguish:
+ // - Legacy setup: proxy never installed (binary doesn't exist)
+ // - Migrated setup: proxy installed but temporarily down
if _, err := os.Stat(proxySocket); err != nil {
- // Socket doesn't exist - legacy SSH method detected
- return true, true
+ // Socket missing - check if proxy was ever installed
+ if _, err := os.Stat(proxyBinary); err != nil {
+ // Proxy binary doesn't exist - this is legacy SSH setup
+ // User needs to remove nodes and re-add them
+ return true, true
+ }
+ // Proxy binary exists but socket is missing - likely just restarting/down
+ // Don't show banner - this is a transient issue, not a configuration problem
+ return false, false
}
}
@@ -1202,6 +1244,14 @@ func (r *Router) handleHealth(w http.ResponseWriter, req *http.Request) {
// Detect legacy SSH setup
legacySSH, recommendProxy := r.detectLegacySSH()
+ // Log when legacy SSH is detected for telemetry/metrics
+ // This helps track removal criteria: <1% detection rate for 30+ days
+ if legacySSH && recommendProxy {
+ log.Warn().
+ Str("detection_type", "legacy_ssh_migration").
+ Msg("Legacy SSH configuration detected - user should migrate to proxy architecture")
+ }
+
response := HealthResponse{
Status: "healthy",
Timestamp: time.Now().Unix(),