diff --git a/MIGRATION_SCAFFOLDING.md b/MIGRATION_SCAFFOLDING.md new file mode 100644 index 0000000..d8844f1 --- /dev/null +++ b/MIGRATION_SCAFFOLDING.md @@ -0,0 +1,67 @@ +# Migration Scaffolding Tracker + +This document tracks temporary code added to handle migration paths between versions. All code listed here should be removed according to the specified criteria. + +**Purpose**: These features exist solely to assist users in migrating from old patterns to new ones. They serve no functional purpose beyond migration assistance and represent technical debt that should be cleaned up once the migration period is complete. + +--- + +## Active Migration Code + +### Legacy SSH Detection Banner (Added: v4.23.0) + +**Why it exists**: Users who set up temperature monitoring before v4.23.0 used SSH keys directly in the container. The new secure architecture uses `pulse-sensor-proxy` on the host with Unix socket communication. Users with the old setup need to remove and re-add their nodes to upgrade. + +**Files involved**: +- Backend: `/opt/pulse/internal/api/router.go` - `detectLegacySSH()` function +- Backend: `/opt/pulse/internal/api/types.go` - `HealthResponse.LegacySSHDetected` fields +- Frontend: `/opt/pulse/frontend-modern/src/components/LegacySSHBanner.tsx` +- Frontend: `/opt/pulse/frontend-modern/src/App.tsx` - Banner integration + +**Removal criteria** (ANY of these): +- Version reaches v5.0 or later +- Telemetry shows <1% detection rate for 30+ consecutive days +- 6 months after v4.23.0 release (whichever comes first) + +**How to remove**: +1. Delete `LegacySSHBanner.tsx` component +2. Remove import and usage from `App.tsx` +3. Delete `detectLegacySSH()` function from `router.go` +4. Remove `LegacySSHDetected`, `RecommendProxyUpgrade`, `ProxyInstallScriptAvailable` from `types.go` HealthResponse +5. Remove banner logic from `handleHealth()` in `router.go` +6. Delete this entry from this document + +**Manual override**: Set `PULSE_LEGACY_DETECTION=false` environment variable to disable detection without code changes. + +**Telemetry notes**: Currently no telemetry implemented. Consider adding metrics to track: +- How often the banner is shown +- How many users still have legacy SSH setup +- When detection rate drops below threshold + +--- + +## Removed Migration Code + +(None yet - this document was created v4.23.0) + +--- + +## Guidelines for Adding New Migration Code + +When adding new migration scaffolding: + +1. **Mark it clearly** with `⚠️ MIGRATION SCAFFOLDING - TEMPORARY CODE` comments +2. **Add it to this document** with: + - Why it exists + - Files involved + - Removal criteria + - Removal instructions +3. **Set explicit removal criteria**: + - Version number target + - Time-based target (e.g., "6 months after release") + - Data-driven target (e.g., "when <1% users affected") +4. **Add a kill switch**: Environment variable or feature flag to disable without redeployment +5. **Consider telemetry**: If removal depends on data, instrument the code to collect that data +6. **Create a removal task**: Add to issue tracker with assigned owner and date + +**Remember**: Migration code is technical debt. Make it easy to find and remove later. diff --git a/frontend-modern/src/App.tsx b/frontend-modern/src/App.tsx index f00f95c..0232d27 100644 --- a/frontend-modern/src/App.tsx +++ b/frontend-modern/src/App.tsx @@ -34,6 +34,7 @@ import { SettingsAPI } from './api/settings'; import { eventBus } from './stores/events'; import { updateStore } from './stores/updates'; import { UpdateBanner } from './components/UpdateBanner'; +import { LegacySSHBanner } from './components/LegacySSHBanner'; import { DemoBanner } from './components/DemoBanner'; import { createTooltipSystem } from './components/shared/Tooltip'; import type { State } from '@/types/api'; @@ -548,6 +549,7 @@ function App() { +
{ + const dismissed = localStorage.getItem('legacySSHBannerDismissed'); + if (dismissed === 'true') { + setIsDismissed(true); + setIsVisible(false); + } + }); + + // Check health endpoint for legacy SSH detection + createEffect(async () => { + if (isDismissed()) return; + + try { + const response = await fetch('/api/health'); + const health = await response.json(); + + if (health.legacySSHDetected && health.recommendProxyUpgrade) { + setIsVisible(true); + } + } catch (error) { + // Silently fail - health check failures shouldn't break the UI + } + }); + + const handleDismiss = () => { + setIsDismissed(true); + setIsVisible(false); + // Store dismissal in localStorage so it persists + localStorage.setItem('legacySSHBannerDismissed', 'true'); + }; + + return ( + +
+
+
+
+ {/* Warning icon */} + + + + + + +
+ Legacy temperature monitoring detected. + {' '} + Remove and re-add your nodes to upgrade to the secure proxy architecture. +
+
+ + {/* Dismiss button */} + +
+
+
+
+ ); +} diff --git a/internal/api/router.go b/internal/api/router.go index 5f78cb2..e042953 100644 --- a/internal/api/router.go +++ b/internal/api/router.go @@ -1129,7 +1129,21 @@ func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) { } // detectLegacySSH checks if Pulse is using legacy SSH for temperature monitoring +// +// ⚠️ MIGRATION SCAFFOLDING - TEMPORARY CODE +// This detection exists only to handle migration from legacy SSH-in-container +// to the secure pulse-sensor-proxy architecture introduced in v4.23.0. +// +// REMOVAL CRITERIA: Remove after v5.0 or when banner telemetry shows <1% fire rate +// for 30+ days. This code serves no functional purpose beyond migration assistance. +// +// Can be disabled via environment variable: PULSE_LEGACY_DETECTION=false func (r *Router) detectLegacySSH() (legacyDetected, recommendProxy bool) { + // Check if detection is disabled via environment variable + if os.Getenv("PULSE_LEGACY_DETECTION") == "false" { + return false, false + } + // Check if running in a container inContainer := false if _, err := os.Stat("/.dockerenv"); err == nil {