Commit graph

632 commits

Author SHA1 Message Date
rcourtman
702e56c97a Add unit tests for hasLegacyThresholds function (diagnostics)
Covers all legacy threshold field detection paths including
CPU, Memory, Disk, DiskRead, DiskWrite, NetworkIn, NetworkOut,
and mixed modern/legacy configurations.
2025-11-30 10:05:33 +00:00
rcourtman
73e5ff0914 Add unit tests for dockeragent runtime detection functions
Test coverage for:
- detectRuntime: 11 test cases covering podman/docker detection via
  endpoint path, InitBinary, ServerVersion, DriverStatus, SecurityOptions
- buildRuntimeCandidates: 6 test cases verifying candidate ordering,
  deduplication, and preference-based filtering
- randomDuration: 5 test cases for boundary conditions and randomness
- determineSelfUpdateArch: validates architecture detection output

Coverage increased from 17.5% to 21.4%.
2025-11-30 08:47:46 +00:00
rcourtman
f9a64b3251 Add unit tests for notification security utilities (notifications)
Test 7 security-related functions with 118 test cases:
- formatWebhookDuration: webhook payload duration formatting
- isNumeric: Telegram chat_id validation helper
- extractTelegramChatID: Telegram webhook URL parsing
- isPrivateIP: SSRF protection (private IP detection)
- isNumericIP: HTTPS IP address detection
- isEmptyInterface: custom field validation
- ensurePushoverCustomFieldAliases: Pushover legacy field aliasing
2025-11-30 08:05:04 +00:00
rcourtman
132dae92a9 Add unit tests for notification template functions (notifications)
Test GetEmailProviders and GetWebhookTemplates functions covering:
- Provider/template uniqueness, required fields, valid values
- Known provider settings (Gmail, SendGrid, custom)
- Known webhook services (Discord, Slack, Telegram, PagerDuty)
- Payload template validation for required alert fields
2025-11-30 07:48:53 +00:00
rcourtman
19e1d81779 Add unit tests for sanitizeCIDRList (config) 2025-11-30 07:35:52 +00:00
rcourtman
a32aa60600 Add unit tests for config utility functions (config)
Test coverage for IsPasswordHashed, IsValidDiscoveryEnvironment, and
splitAndTrim functions. 63 test cases covering bcrypt hash validation,
discovery environment validation, and comma-separated string parsing.
2025-11-30 07:27:59 +00:00
rcourtman
90e1b8e546 Add unit tests for RateTracker.CalculateRates (monitoring) 2025-11-30 07:07:18 +00:00
rcourtman
d69e1545f2 Add unit tests for alert utility functions (alerts)
Test coverage for pure utility functions:
- sanitizeAlertKey: string normalization for alert IDs
- abs: absolute value for float64
- isQueueOutlier: queue anomaly detection logic
- scaleThreshold: threshold scaling with ceiling
- calculateMedianInt: integer median calculation

78 test cases added, coverage improved from 45.3% to 46.2%.
2025-11-30 06:34:55 +00:00
rcourtman
15d6cae7dd Add unit tests for adaptive scheduler interval selection (scheduler)
46 test cases covering:
- clampFloat helper (9 cases: range checks, boundary conditions)
- Staleness score impact (5 cases: 0/0.25/0.5/0.75/1 scores)
- Error penalty (5 cases: 0 to 10 errors, min clamping)
- Queue depth stretching (5 cases: 1 to 50 depth, max clamping)
- EMA smoothing (convergence behavior with alpha=0.6)
- Jitter (bounds checking, deterministic tests with seeded RNG)
- Boundary conditions (6 cases: zero/negative intervals, max<min)
- Combined factors (3 cases: all factors interacting)
- State persistence (per-instance EMA state isolation)

First unit test file for scheduler.go (previously only had integration tests).
2025-11-30 06:11:22 +00:00
rcourtman
8eea32678e Add unit tests for Docker swarm utility functions (dockeragent)
Test coverage for serviceMode, buildContainerIndex, lookupContainer,
copyStringMap, and isTaskCompletedState. 52 test cases covering service
mode detection, container index building/lookup, and task state classification.
Coverage improved from 14.7% to 17.5%.
2025-11-30 05:32:52 +00:00
rcourtman
36b1c9cf63 Add unit tests for Alert clone and metadata utility functions (alerts)
Tests for:
- normalizePoweredOffSeverity: Verifies severity normalization to warning/critical
- cloneMetadata: Deep copy functionality for metadata maps
- cloneMetadataValue: Type-specific cloning for various slice and map types
- Alert.Clone: Full alert cloning with pointer field independence

46 test cases covering nil handling, deep copy verification, slice/map
independence, and all supported metadata value types.

Coverage improved from 44.8% to 45.3%.
2025-11-30 05:19:38 +00:00
rcourtman
73e5352812 Add unit tests for extractPodmanMetadata and detectHostRemovedError (dockeragent)
- TestExtractPodmanMetadata: 14 test cases covering pod metadata, infra
  containers, compose metadata, auto-update settings, user namespace
  handling, whitespace trimming, and precedence rules
- TestDetectHostRemovedError: 11 test cases covering JSON parsing,
  case-insensitive matching, error code validation, and edge cases

Coverage improved from 11.7% to 14.7% for internal/dockeragent.
2025-11-30 05:03:31 +00:00
rcourtman
722ea83b5d Add unit tests for RateLimiter (internal/api/ratelimit.go)
16 test cases covering:
- Basic rate limiting with Allow() (limit enforcement, different IPs)
- Sliding window behavior (time-based expiry, partial expiry)
- Edge cases (zero limit, large limit, empty IP)
- Cleanup routine (full cleanup, partial cleanup)
- Concurrent access (thread safety with 100 parallel attempts)
- Middleware integration (allowed/denied paths, X-Forwarded-For)

First test file for internal/api package.
2025-11-30 04:33:59 +00:00
rcourtman
c2233fb3f8 Add unit tests for temperature utility functions
Add focused unit tests for four utility functions in temperature.go:
- extractTempInput: 16 test cases for sensor value extraction
- extractCoreNumber: 18 test cases for core number parsing
- extractHostname: 21 test cases for URL hostname extraction
- normalizeSMARTEntries: 15 test cases for SMART data normalization

70 test cases total covering type conversions, edge cases,
boundary conditions, and error handling paths.
2025-11-30 04:20:28 +00:00
rcourtman
ed3c992968 Add unit tests for InstallShAdapter utility functions
- Test parseProgress: 23 test cases for progress parsing from install.sh output
- Test readLastLines: 11 test cases including edge cases
- Test version pattern validation: 45 test cases for command injection prevention
- Test DockerUpdater and AURUpdater basic functionality
- Fix bug in readLastLines: handle n<=0 to prevent slice bounds panic

Coverage increased from 35.9% to 38.2%
2025-11-30 04:04:27 +00:00
rcourtman
351ef94368 Add unit tests for agentupdate utility functions
Tests for:
- verifyBinaryMagic: ELF/Mach-O/PE magic byte validation
- determineArch: platform architecture detection
- unraidPersistentPath: Unraid persistence path generation
- isUnraid: Unraid environment detection
- New(): updater initialization with various configs
- Config defaults and constants

Coverage: 12.9% -> 13.4%
2025-11-30 03:48:50 +00:00
rcourtman
d2a8184833 Add unit tests for notification queue utility functions
Test coverage for calculateBackoff (exponential backoff calculation),
NotificationQueueStatus constants, and QueuedNotification struct fields.
15 test cases covering backoff timing, cap behavior, and struct defaults.
2025-11-30 03:19:23 +00:00
rcourtman
b763ccc412 Add unit tests for HTTP metrics utility functions
83 test cases covering classifyStatus, isNumeric, looksLikeUUID,
normalizeSegment, and normalizeRoute functions used for Prometheus
metrics aggregation. Tests cover status code classification, numeric
string validation, UUID format detection, URL segment normalization,
and route path normalization with query param stripping.
2025-11-30 02:48:05 +00:00
rcourtman
6267554b74 Add unit tests for version utility functions
Test coverage for pure functions in internal/updates/version.go:
- Version.String() - 6 test cases
- Version.Compare() - 14 test cases (major/minor/patch/prerelease)
- Version.IsNewerThan() - 4 test cases
- Version.IsPrerelease() - 4 test cases
- compareInts() - 7 test cases
- extractRCNumber() - 12 test cases
- envBool() - 17 test cases
- sanitizePrereleaseIdentifier() - 14 test cases

Coverage: 35.2% -> 35.9%
2025-11-30 02:03:35 +00:00
rcourtman
9f2798cc96 Add unit tests for config encryption functions
Tests encryptWithPassphrase and decryptWithPassphrase:
- Roundtrip encryption/decryption with various data types
- Unique output verification (random salt/nonce)
- Wrong passphrase rejection
- Tampered ciphertext detection
- Minimum size validation
- Base64 roundtrip (matching export/import flow)
2025-11-30 01:48:20 +00:00
rcourtman
3c24a51298 Add unit tests for hostagent utility functions
Test normalisePlatform (darwin->macos normalization, case handling,
whitespace trimming) and isLoopback (case-insensitive flag matching).
Package had no tests previously.
2025-11-30 01:34:55 +00:00
rcourtman
ed3773b324 Add unit tests for email template utility functions
Test coverage for titleCase, formatDuration, pluralize, formatMetricValue,
and formatMetricThreshold functions. 71 test cases covering edge cases,
boundary conditions, and all metric type variants.
2025-11-30 01:03:05 +00:00
rcourtman
c91375230b Add unit tests for discovery config_override utility functions
Tests for parseCIDRs, parseCIDRMap, environmentFromOverride,
shouldPruneContainerNetworks, isLikelyContainerPhase, filterPhasesForEnvironment,
and ApplyConfigToProfile. Coverage increased from 29.0% to 51.0%.
2025-11-30 00:50:07 +00:00
rcourtman
d11d81688a Add unit tests for websocket utility functions
Test coverage for pure utility functions:
- isValidPrivateOrigin: validates private network origins (security)
- normalizeForwardedProto: normalizes ws/wss to http/https for proxies
- sanitizeValue: handles NaN/Inf values in JSON data
- cloneMetadata/cloneMetadataValue: deep copies metadata maps/slices
- cloneAlert/cloneAlertData: deep copies alert structures

Coverage increased from 20.9% to 37.3% (80 test cases).
2025-11-30 00:34:38 +00:00
rcourtman
04c12414d2 Add unit tests for diagnostics utility functions
Test coverage for 9 pure functions in internal/api/diagnostics.go:
- isFallbackMemorySource: 13 cases covering fallback and non-fallback sources
- copyStringSlice: 4 cases including nil handling and copy verification
- normalizeHostForComparison: 16 cases for URL normalization
- normalizeVersionLabel: 11 cases for version prefix handling
- contains: 8 cases for string slice membership
- containsFold: 11 cases for case-insensitive matching
- interfaceToStringSlice: 9 cases for type conversion
- preferredDockerHostName: 5 cases for name preference hierarchy
- formatTimeMaybe: 3 cases for time formatting
2025-11-30 00:18:41 +00:00
rcourtman
4244cf6f20 Consolidate duplicate normalizeVersion functions into shared utility
- Move normalizeVersion to utils.NormalizeVersion for single source of truth
- Update agentupdate and dockeragent packages to use shared function
- Add 14 test cases for version normalization

This prevents bugs like issue #773 where a fix applied to one copy
but not the other caused an update loop.
2025-11-29 22:57:33 +00:00
rcourtman
cf17d327f1 Add unit tests for dockeragent utility functions
Tests for calculateCPUPercent, calculateMemoryUsage, safeFloat,
parseTime, trimLeadingSlash, and summarizeBlockIO. 28 test cases
covering edge cases like zero deltas, cache handling, NaN/Inf,
and case-insensitive op matching.

Coverage improved from 8.0% to 11.7%.
2025-11-29 22:18:10 +00:00
rcourtman
1b3effcb10 Fix redactSecretsFromURL matching params with prefixed names
The function was using substring matching for sensitive param names,
causing parameters like "extra_token" or "myapikey" to be incorrectly
redacted when they matched "token=" or "apikey=" as substrings.

Now checks for proper boundary characters (? or &) before matching,
so only actual parameter names are redacted.

Related to ADA knowledge entry: "Query param redaction uses substring matching"
2025-11-29 22:03:15 +00:00
rcourtman
46f90e1585 Show CPU core count on EnhancedCPUBar progress bar 2025-11-29 21:47:22 +00:00
rcourtman
0dd65c5db2 Add unit tests for TaskQueue Upsert and Remove methods 2025-11-29 21:38:33 +00:00
rcourtman
a5fea152e8 Add unit tests for HandleLookup hostname matching
Tests cover the hostname lookup paths in HandleLookup:
- Exact hostname match (case-insensitive)
- DisplayName match (case-insensitive)
- Short hostname match (before first dot)
- Priority of exact over short match
- Sorted iteration order behavior
- Not-found cases

12 test cases covering all hostname matching code paths.
2025-11-29 20:20:15 +00:00
rcourtman
7704a18e52 ADA: Add unit tests for isDirectLoopbackRequest 2025-11-29 20:07:59 +00:00
rcourtman
9393574f05 ADA: Add unit tests for Ceph helper functions
Test coverage for isCephStorageType, countServiceDaemons,
extractCephCheckSummary, and summarizeCephHealth functions.
These parse Ceph storage types and health status JSON.
2025-11-29 19:49:43 +00:00
rcourtman
5eb793f4bb ADA: Add unit tests for TaskQueue.Snapshot 2025-11-29 19:38:54 +00:00
rcourtman
277ec7e9da ADA: Add unit tests for validateSystemSettings 2025-11-29 19:08:58 +00:00
rcourtman
ce47e8ca9d ADA: Add unit tests for sanitizeInstallerURL and sanitizeSetupAuthToken
Add comprehensive test coverage for security-critical URL and token
sanitization functions in config_handlers.go. These functions protect
the setup script endpoint from injection attacks.

TestSanitizeInstallerURL (23 cases): empty/whitespace handling, valid
http/https URLs, fragment stripping, query preservation, control character
rejection, invalid scheme rejection (ftp/file/javascript/data), and
missing host validation.

TestSanitizeSetupAuthToken (19 cases): empty/whitespace handling, valid
hex tokens of various lengths (32-128 chars), mixed case hex, control
character rejection, non-hex character rejection, and length validation.
2025-11-29 18:35:27 +00:00
rcourtman
fce4168f82 ADA: Add unit tests for validateBcryptHash helper function
Tests cover valid bcrypt hash formats ($2a$, $2b$, $2y$), truncated/too-long
hashes, empty input, and invalid prefixes (md5-style, $2x$, no prefix).
2025-11-29 18:06:19 +00:00
rcourtman
9647313570 ADA: Fix redactSecretsFromURL bug and add unit tests
The Telegram bot token redaction had an off-by-one bug: it searched for
the next "/" starting from the "/bot" position, which found the "/" in
"/bot" itself (offset 0) instead of the next "/" after the token.

Result: tokens were not properly redacted and the URL got corrupted with
duplicated path segments, potentially leaking secrets to logs/API responses.

Fix: search from idx+4 (after "/bot") and handle edge cases where there's
no trailing slash (token at end of URL or before query string).

Added 20 comprehensive test cases covering:
- No secrets (passthrough)
- Telegram bot tokens (various patterns)
- Query parameter secrets (token, apikey, api_key, key, secret, password)
- Multiple parameters and edge cases
2025-11-29 17:36:32 +00:00
rcourtman
f4cfe96943 ADA: Add normalizeCommandStatus helper with unit tests
Extract command status normalization logic from HandleCommandAck into
a dedicated helper function. This improves testability and makes the
status alias handling explicit and documented.

The function accepts client-provided status strings and maps them to
internal status constants:
- acknowledged: "", "ack", "acknowledged"
- completed: "success", "completed", "complete"
- failed: "fail", "failed", "error"

Adds 25 table-driven test cases covering all aliases, case insensitivity,
whitespace handling, and invalid inputs.
2025-11-29 17:06:18 +00:00
rcourtman
5201bd7087 Add unit tests for metrics.go helper functions
Test normalizeLabel, normalizeNodeLabel, splitInstanceKey,
breakerStateToValue, sanitizeInstanceLabels, makeMetricKey,
and makeNodeMetricKey with 36 test cases covering edge cases
like empty strings, whitespace, and invalid inputs.
2025-11-29 16:44:37 +00:00
rcourtman
61bb124692 Extract filesystem filtering logic into pkg/fsfilters
Move the inline filesystem skip logic from pollVMsAndContainersEfficient
into a reusable ShouldSkipFilesystem function. This consolidates filtering
for virtual filesystems (tmpfs, cgroup, etc.), network mounts (nfs, cifs,
fuse), and special mountpoints (/dev, /proc, /snap, etc.) into one tested
location.

Reduces cyclomatic complexity of pollVMsAndContainersEfficient and adds
28 test cases covering virtual fs types, network mounts, special mounts,
Windows paths, and edge cases.
2025-11-29 16:38:08 +00:00
rcourtman
69a7d96e4b Merge branch 'ada/20241129-parseversion-test' 2025-11-29 16:32:33 +00:00
rcourtman
a4ef95ef9e Add unit tests for ParseVersion function
Tests basic version parsing, v prefix stripping, prerelease,
build metadata, and error cases for invalid input.
2025-11-29 16:00:44 +00:00
rcourtman
ac5992b6c6 Add regression test for normalizeVersion
Prevents recurrence of the infinite update loop bug fixed in 1b866598
where version prefix mismatch caused continuous self-updates.
2025-11-29 15:22:05 +00:00
rcourtman
e29fda5e5a Rebuild agent token bindings on API token config reload
When api_tokens.json is modified on disk, the ConfigWatcher reloads
the tokens into memory. However, the Monitor's dockerTokenBindings and
hostTokenBindings maps were not synchronized with the new token set,
causing orphaned bindings when agents reconnect after reinstall.

Add SetAPITokenReloadCallback to ConfigWatcher that triggers Monitor's
new RebuildTokenBindings method after token reload. This method
reconstructs the binding maps from current Docker host and host agent
state, keeping only bindings for tokens that still exist in config.

Related to #773
2025-11-29 14:09:30 +00:00
courtmanr@gmail.com
ae3a349124 Refine memory visualization with StackedMemoryBar component
- Add StackedMemoryBar component to visualize Active/Balloon/Swap memory
- Integrate StackedMemoryBar into NodeSummaryTable, GuestRow, HostsOverview, and DockerUnifiedTable
- Add TemperatureGauge component for temperature visualization
- Standardize replication job status indicators with StatusDot
- Fix mock data generator to use realistic balloon memory values (0 by default, small % when active)
- Add StackedContainerBar for Docker container status visualization
- Add ZFSHealthMap component for storage pool health visualization
2025-11-29 13:48:30 +00:00
rcourtman
c3a5aacd21 Fix standalone docker agent version comparison prefix mismatch
The unified agent got the version normalization fix (1b866598), but the
standalone docker agent's checkForUpdates() still used direct string
comparison. When server returns "4.34.0" and agent has "v4.34.0", this
caused an infinite self-update loop.

Apply the same normalizeVersion() function used in the unified agent.

Related to #773
2025-11-29 00:04:43 +00:00
rcourtman
806cdd2d87 Fix Docker CPU calculation on systemUsage counter reset
When systemUsage counter goes backward (common in unprivileged LXC
containers), the previous code used the absolute value as systemDelta.
This created an artificially small denominator, inflating CPU to ~100%.

Now leaves systemDelta as 0 on counter reset, falling through to the
time-based calculation which produces accurate results.

Related to #770
2025-11-28 15:07:49 +00:00
rcourtman
8afc4d0c6c Fix hideLocalLogin toggle persistence and login page UI
- Add hideLocalLogin handler in HandleUpdateSystemSettings() so the
  toggle setting is saved to system.json
- Conditionally hide "or" divider and admin credentials message when
  local login is hidden

Related to #750
2025-11-28 09:05:41 +00:00
rcourtman
1b866598c4 Fix agent self-update infinite loop due to version prefix mismatch
The agent's CurrentVersion includes the "v" prefix (e.g., "v4.33.1") but
the server's /api/agent/version endpoint returns versions without it
(e.g., "4.33.1"). This caused the comparison to always fail, triggering
an infinite self-update loop every 30 seconds.

Normalize both versions by stripping the "v" prefix before comparison.

Related to #740
2025-11-28 06:07:20 +00:00
rcourtman
6ff5ae4200 fix: multiple agent installation and update issues
- Default enableDocker to false in UI to prevent unintended Docker
  agent activation on host-only installs (Related to #766)
- Deploy agent scripts and binaries during web UI upgrades, not just
  the main binary (Related to #760)
- Apply symlink resolution fix to standalone docker agent self-update
  to prevent cross-device rename failures (Related to #737)
2025-11-27 15:49:03 +00:00
rcourtman
cb8c46cfb6 fix: add hash check to polling fallback and persist sidebar state
- Add content hash check to config watcher polling path to match fsnotify
  behavior, preventing unnecessary restarts when .env is touched but
  content unchanged (Related to #748)
- Change settings sidebar to expanded by default and persist user
  preference using usePersistentSignal (Related to #764)
2025-11-27 15:24:23 +00:00
rcourtman
f7ffb36c41 chore: remove dead code and unused exports
Remove ~900 lines of unused code identified by static analysis:

Go:
- internal/logging: Remove 10 unused functions (InitFromConfig, New,
  FromContext, WithLogger, etc.) that were built but never integrated
- cmd/pulse-sensor-proxy: Remove 7 dead validation functions for a
  removed command execution feature
- internal/metrics: Remove 8 unused notification metric functions and
  10 Prometheus metrics that were never wired up

Frontend:
- Delete ActivationBanner.tsx stub component
- Remove unused exports: stopMetricsSampler, getSamplerStatus,
  formatSpeedCompact, parseMetricKey, getResourceAlerts
2025-11-27 13:17:39 +00:00
rcourtman
4cfd1473ba fix(mock): adjust backup time distribution for more realistic data
Reduce never-backed-up guests from 10% to 5% so most mock guests
show backup status rather than grey shields.
2025-11-27 11:04:20 +00:00
courtmanr@gmail.com
0a12200e58 fix(tests): resolve flaky ID generation and symlink resolution on macOS 2025-11-27 10:50:13 +00:00
rcourtman
e9ac1429c0 refactor: remove unnecessary type conversions
Remove redundant type conversions identified by unconvert linter:
- Remove int() conversions for already-int VMID fields
- Remove int64() conversions for already-int64 arithmetic results
- Remove uint64() conversions for already-uint64 Disk/MaxDisk fields
- Remove int() on syscall.Stdin (already int constant)
2025-11-27 10:33:35 +00:00
rcourtman
67b60adb65 style: fix revive linter warnings
- Mark unused stub parameters with underscore
- Rename 'copy' variable to avoid shadowing builtin
- Remove unnecessary else blocks after return statements
2025-11-27 10:26:26 +00:00
rcourtman
1fb4a2c2c6 chore: remove deprecated build tags and use strings.ReplaceAll
- Remove redundant // +build directives (go:build is sufficient in Go 1.17+)
- Replace strings.Replace(..., -1) with strings.ReplaceAll
2025-11-27 10:16:08 +00:00
rcourtman
31927f71e9 fix: mark unused parameters to satisfy unparam linter
Mark intentionally unused parameters with underscore to:
- Silence unparam warnings for legitimate unused parameters
- Keep function signatures intact for API compatibility
- Remove unused req from serveChecksum helper
2025-11-27 10:12:48 +00:00
rcourtman
252d060a58 refactor: use builtin max() and fix unused parameter
- Replace custom maxInt64 helper with Go 1.21+ builtin max()
- Mark unused cfg parameter in newAdaptiveIntervalSelector
- Remove test for deleted helper function
2025-11-27 10:08:37 +00:00
rcourtman
16603e8b4a fix: remove ineffectual assignments
- Fix loop variable reassignment in config_handlers.go
- Remove redundant boolean assignments in swarm.go
2025-11-27 09:48:29 +00:00
rcourtman
60cd019d33 chore: update to non-deprecated Docker SDK types
- Use container.Summary instead of types.Container
- Use swarmtypes.ServiceListOptions instead of types.ServiceListOptions
- Use swarmtypes.TaskListOptions instead of types.TaskListOptions

These types were deprecated in favor of package-specific types.
2025-11-27 09:36:05 +00:00
rcourtman
d7d1631e1c style: use direct type conversions in model converters
Replace struct literal copies with direct type conversions where
the source and destination types have identical field layouts.
2025-11-27 09:24:39 +00:00
rcourtman
95a258ac32 refactor: replace deprecated strings.Title with custom titleCase
Add simple titleCase functions that handle ASCII strings without
the Unicode punctuation issues of strings.Title.
2025-11-27 09:22:52 +00:00
rcourtman
7dad9c7a17 style: fix additional staticcheck warnings
- Lowercase error messages (ST1005)
- Use context.Background() instead of nil (SA1012)
- Fix rand.Intn(1) which always returns 0 (SA4030)
- Remove unnecessary nil check before len() (S1009)
2025-11-27 09:21:11 +00:00
rcourtman
8a1ee3b05e style: fix staticcheck style warnings
- Merge variable declaration with assignment (S1021)
- Use unconditional strings.TrimPrefix (S1017)
- Remove unnecessary nil checks around range (S1031)
- Remove unnecessary fmt.Sprintf (S1039)
- Use copy() instead of manual loop (S1001)
- Use time.Until instead of t.Sub(time.Now()) (S1024)
- Use buf.String() instead of string(buf.Bytes()) (S1030)
2025-11-27 09:19:33 +00:00
rcourtman
e25e1af8cb chore: fix staticcheck SA warnings
- Fix SA4006 unused value issues in ssh.go, validation.go, generator.go
- Replace deprecated ioutil with io/os in config.go
- Replace deprecated tar.TypeRegA with tar.TypeReg
- Remove deprecated rand.Seed calls (auto-seeded in Go 1.20+)
- Fix always-true nil check in main.go
- Fix impossible nil comparison in tempproxy/client.go
- Add nil check for config in monitor.New()
2025-11-27 09:16:53 +00:00
rcourtman
4fd3bdbc04 chore: fix staticcheck U1000 unused code warnings
- Remove unused ipv6Regex from validation.go
- Suppress unused recordAlertFired/recordAlertResolved hooks (kept for future use)
- Remove unused apiLimiter rate limiter
- Remove unused stopOnce fields from csrf_store.go and session_store.go
- Remove unused lastBroadcast field from hub.go
- Remove unused lastUsedIndex field from cluster_client.go
2025-11-27 09:12:17 +00:00
rcourtman
4f25aa9ca1 chore: cleanup proxmox IsAuthError and remove stray comment
- Make IsAuthError unexported (isAuthError) since it's only used internally
- Remove stray '// test comment' from docker_metadata.go
2025-11-27 08:59:01 +00:00
rcourtman
3b23d16c23 chore: remove unused ClusterHealth types
Remove internal/models/cluster.go containing ClusterHealth and
ClusterNodeStatus types that were never used in the codebase.
2025-11-27 08:45:50 +00:00
rcourtman
93723d95c6 chore: remove unused API response types
Remove ChartData, Dataset, ConfigImportResponse, ConfigExportResponse,
InstallScriptResponse, ErrorResponse, and SuccessResponse types that
were defined but never used in the codebase.
2025-11-27 08:37:22 +00:00
rcourtman
4f6c24802c style: fix whitespace in middleware.go 2025-11-27 08:34:31 +00:00
rcourtman
c8e76a52b7 chore: remove unused store methods
Remove 121 lines of unused store methods:
- CSRFTokenStore: Stop, ExtendCSRFToken
- SessionStore: Stop, ExtendSession, GetSession
- RecoveryTokenStore: Stop, save, GetActiveTokenCount, ValidateRecoveryToken

These methods were part of a standard store pattern but never wired up
to the application lifecycle. The constant-time validation variant is
used instead of the timing-vulnerable ValidateRecoveryToken.
2025-11-27 08:31:50 +00:00
rcourtman
72b2444a6c chore: remove unused DockerMetadataHandler.Reload method 2025-11-27 05:14:56 +00:00
rcourtman
b0ce0d932f chore: remove additional dead code
Remove 241 lines of unreachable code across internal and pkg:
- internal/crypto/crypto.go: unused NewCryptoManager wrapper
- internal/monitoring/scheduler.go: unused fixedIntervalSelector type
- internal/ssh/knownhosts/manager.go: unused hostKeyExists function
- internal/updates/manager.go: unused getLatestRelease wrapper
- internal/updates/updater.go: unused GetAll method
- pkg/discovery/discovery.go: unused scanWorker and runPhase (legacy compat)
- pkg/proxmox/client.go: unused post, getTaskStatus, waitForTaskCompletion, getTaskLog
- pkg/proxmox/cluster_client.go: unused markUnhealthy wrapper
2025-11-27 05:13:26 +00:00
rcourtman
25542ae51d chore: remove more dead code
Remove 330 lines of unreachable code:
- internal/monitoring/temperature_service.go: unused temperature service abstraction
- internal/monitoring/temperature.go: unused NewTemperatureCollector wrapper
- internal/mock/generator.go: unused GenerateAlerts function
- internal/mock/integration.go: unused ToggleMockMode wrapper
- internal/notifications/notifications.go: unused sendEmailWithContent,
  generatePayloadFromTemplate, isPrivateRange172, groupAlerts
- internal/notifications/email_providers.go: unused GetProviderDefaults
2025-11-27 00:10:55 +00:00
rcourtman
28aaecd74d chore: remove dead code and unused files
Remove 604 lines of unreachable code identified by deadcode analysis:
- internal/config/credentials.go: unused credential resolver
- internal/config/registration.go: unused registration config
- internal/monitoring/poller.go: unused channel-based polling (keep types)
- internal/api/middleware.go: unused TimeoutHandler, JSONHandler, NewAPIError, ValidationError
- internal/api/security.go: unused IsLockedOut, SecurityHeaders
- internal/api/auth.go: unused min helper
- internal/config/config.go: unused SaveConfig
- internal/config/client_helpers.go: unused CreatePBSConfigFromFields
- internal/logging/logging.go: unused NewRequestID
2025-11-27 00:05:04 +00:00
rcourtman
506484b072 chore: remove unused API types
Remove 261 lines of unused type definitions from types.go:
- NodeRequest, SettingsRequest (unused, actual impl in config_handlers.go)
- ConfigResponse, NodeConfig, SettingsConfig (unused)
- BackupResponse, BackupInfo, MetricsResponse, MetricData (unused)
- StorageResponse, StorageInfo, StorageTotals (unused)
- DiagnosticsResponse and related types (unused)
- SecurityStatusResponse, ExportRequest, ImportRequest (unused)
- NotificationTestRequest, UpdateCheckResponse (unused)
- WebSocketMessage, LoginRequest, LoginResponse (unused)
- TestConnectionResponse, NodeConnectionResponse (unused)
- DiscoveryResponse, DiscoveredServer (pkg/discovery has own types)
- AutoRegisterResponse (unused)
2025-11-26 23:51:41 +00:00
rcourtman
2fe7bb6141 style: fix gofmt formatting inconsistencies
Run gofmt -w to fix tab/space inconsistencies across 33 files.
2025-11-26 23:44:36 +00:00
rcourtman
b40a33fd2b fix: prevent context leak in temperature collection
- Use defer for tempCancel() to ensure context is always cancelled
- Remove redundant shouldCollect variable that was always true
- Fix indentation after removing the unnecessary conditional block
2025-11-26 23:43:18 +00:00
rcourtman
91c72a274c feat: add backup status indicator to guest table
Shows shield icon next to guest name when backup is stale or missing:
- Yellow shield: backup 24-72 hours old
- Red shield: backup older than 72 hours
- Gray shield: no backup found

Fresh backups (<24h) show no indicator to keep the UI clean.
Templates are excluded from backup status display.
2025-11-26 21:30:41 +00:00
rcourtman
d2a303ed0c fix: resolve symlinks for agent self-update to avoid cross-device rename
When the agent binary is symlinked (e.g., from /opt/pulse/bin to
/usr/local/bin), the self-update would fail with "invalid cross-device
link" because os.Rename() doesn't work across filesystems.

Now resolves symlinks before update and creates the temp file in the
same directory as the real binary.

Related to #737
2025-11-26 20:16:09 +00:00
courtmanr@gmail.com
c021dc6ca5 Enhance table responsiveness across multiple components 2025-11-26 17:57:09 +00:00
rcourtman
7fd49fb54a test: add additional tests for internal/agentupdate
Add 6 tests covering:
- Config struct zero value defaults
- NewUpdater with InsecureSkipVerify option
- NewUpdater with Disabled option
- determineArch format validation
- verifyBinaryMagic on directories
- Package constants validation
2025-11-26 14:37:01 +00:00
rcourtman
36544d7487 test: add unit tests for internal/models converters
Add 22 tests covering:
- zeroIfNegative helper function
- copyStringFloatMap deep copy helper
- Node.ToFrontend with display name fallback
- VM.ToFrontend including tags, backup time, negative values
- Container.ToFrontend
- DockerContainer.ToFrontend with ports, networks, mounts, block IO
- DockerService.ToFrontend with endpoint ports
- DockerSwarmInfo.ToFrontend
- Storage.ToFrontend
- hostSensorSummaryToFrontend nil handling
- RemovedDockerHost.ToFrontend
- DockerServicePort.ToFrontend
- DockerTask.ToFrontend with timestamps
2025-11-26 14:33:57 +00:00
rcourtman
3c499322b9 test: add unit tests for internal/metrics
Add 14 tests covering:
- RecordAlertFired, RecordAlertResolved, RecordAlertAcknowledged
- RecordNotificationSent with success/failure
- RecordNotificationRetry, RecordNotificationDLQ
- RecordGroupedNotification with various counts
- RecordAlertEscalation with various levels
- RecordAlertSuppressed with various reasons
- UpdateQueueDepth, UpdateHistorySize
- RecordHistorySaveError
- Metric vector initialization verification
2025-11-26 14:27:54 +00:00
rcourtman
cf45a62c12 test: add unit tests for internal/tempproxy
Add 24 tests covering:
- ErrorType enum constants
- ProxyError Error() and Unwrap() methods
- calculateBackoff exponential backoff with jitter
- contains case-insensitive substring matching
- classifyError error classification for node, auth, SSH, sensor, rate limit, timeout, transport
- RPCRequest and RPCResponse struct fields
- Client struct fields
2025-11-26 14:26:59 +00:00
rcourtman
65b0644027 test: add unit tests for internal/agentbinaries 2025-11-26 14:17:58 +00:00
rcourtman
35412b63d8 test: add unit tests for system package 2025-11-26 14:11:25 +00:00
rcourtman
61b2bfe624 test: add unit tests for types package 2025-11-26 14:10:21 +00:00
rcourtman
0396053b73 test: add unit tests for hostagent package 2025-11-26 14:09:55 +00:00
rcourtman
159a1cb3a3 test: add unit tests for errors package 2025-11-26 14:09:14 +00:00
rcourtman
ae876b4a10 test: add unit tests for sensors parser 2025-11-26 14:06:56 +00:00
rcourtman
4bdf1664bb test: add unit tests for utils package
- Test ID generation (uniqueness, format)
- Test JSON response writing (various types, headers)
- Test boolean parsing (truthy/falsy values)
- Test environment variable trimming
- Test data directory resolution
- Test large payload handling
2025-11-26 13:56:30 +00:00
rcourtman
d37247d220 test: add unit tests for crypto package
- Test encrypt/decrypt round-trip (various data types)
- Test string encryption (base64 output)
- Test key persistence across manager instances
- Test key file permissions (0600)
- Test decryption of invalid/corrupted data
- Test encryption uniqueness (random nonce)
- Test orphaned data protection
- Test large data encryption (1MB)
2025-11-26 13:52:05 +00:00
rcourtman
16d70123bf test: add unit tests for auth package
- Test API token generation (uniqueness, format)
- Test API token hashing (SHA3-256, deterministic)
- Test constant-time token comparison
- Test token hash detection
- Test password hashing (bcrypt, salted)
- Test password verification
- Test password complexity validation
- Verify bcrypt cost and minimum password length constants
2025-11-26 13:51:13 +00:00
rcourtman
1ccbdfe046 test: add unit tests for agentupdate security features
- Test binary magic verification (ELF, PE, Mach-O)
- Test architecture detection
- Test Unraid persistent path generation
- Test updater configuration defaults
- Test edge cases: empty files, too short, non-existent
2025-11-26 13:50:19 +00:00
rcourtman
09ec0c3f01 security: harden agent installers and auto-update mechanism
Install script (scripts/install.sh):
- Add multi-platform support: Unraid, OpenRC/Alpine, Synology DSM 6/7
- Add input validation for URL, token format, and interval
- Add binary magic verification (ELF/Mach-O/PE)
- Add cleanup trap for temp files
- Wrap script in main() for partial download protection
- Fix shellcheck compliance issues
- Add curl timeouts

Agent auto-update (agentupdate, dockeragent):
- Enforce TLS 1.2 minimum version
- Make SHA256 checksum verification mandatory
- Add 100MB binary size limit
- Add binary magic verification before replacement
- Add Unraid persistent binary update after self-update
- Add 5-minute download timeout

Frontend:
- Update Linux install description to note auto-detection of init systems
2025-11-26 13:14:58 +00:00
rcourtman
7f449d30e0 fix: cache daemon ID at init to prevent Podman token binding conflicts
Podman can return unstable or empty daemon IDs across API calls. When
the agent fetched info.ID on every report cycle, this could cause the
agent identity to change mid-session, triggering "token already in use"
errors on the server.

Cache the daemon ID at initialization and use it consistently for all
reports.

Related to #740
2025-11-26 10:23:22 +00:00
rcourtman
d07ad8fd3e fix: use correct script paths in unified agent handlers
The unified agent handlers were using r.config.AppRoot which pointed
to /app, but scripts are in /opt/pulse/scripts. Updated to match the
pattern used by other script handlers - check /opt/pulse/scripts first,
then fall back to project root for dev environment.

Also added no-cache headers to prevent stale scripts being served.
2025-11-26 10:05:43 +00:00
rcourtman
21c033c144 fix: remove references to deleted install-host-agent.sh script
The unified agent system replaced install-host-agent.sh with install.sh.
This commit updates all references:
- Dockerfile: removed COPY for deleted script
- router.go: serve install.sh at /install-host-agent.sh endpoint (backwards compatible)
- build-release.sh: removed copy of deleted script
- validate-release.sh: removed validation of deleted script
- install.sh: updated script list for bare-metal installs
2025-11-26 09:57:06 +00:00
rcourtman
e467523a61 feat: serve install scripts from GitHub releases instead of main branch
Scripts like install.sh and install-sensor-proxy.sh are now attached
as release assets and downloaded from releases/latest/download/ URLs.
This ensures users always get scripts compatible with their installed
version, even while development continues on main.

Changes:
- build-release.sh: copy install scripts to release directory
- create-release.yml: upload scripts as release assets
- Updated all documentation and code references to use release URLs
- Scripts reference each other via release URLs for consistency
2025-11-26 08:59:59 +00:00
rcourtman
0f0832d30f fix: propagate unified agent version and improve legacy cleanup
Issues found during scenario testing:

1. Version propagation: The hostagent and dockeragent packages were
   reporting their own Version (0.1.0-dev) instead of the unified
   agent's version. Added AgentVersion config field to pass the
   parent's version down.

2. macOS legacy cleanup: The install.sh script was missing cleanup
   for pulse-docker-agent on macOS.

3. Windows legacy cleanup: The install.ps1 script was missing cleanup
   for legacy PulseHostAgent and PulseDockerAgent services.

These fixes ensure:
- Unified agent reports consistent version across host/docker metrics
- Legacy agents are properly removed on all platforms during upgrade
- Users migrating from legacy agents get a clean transition
2025-11-25 23:39:10 +00:00
rcourtman
920f271b41 feat: improve legacy agent detection and migration UX
Add seamless migration path from legacy agents to unified agent:

- Add AgentType field to report payloads (unified vs legacy detection)
- Update server to detect legacy agents by type instead of version
- Add UI banner showing upgrade command when legacy agents are detected
- Add deprecation notice to install-host-agent.ps1
- Create install-docker-agent.sh stub that redirects to unified installer

Legacy agents (pulse-host-agent, pulse-docker-agent) now show a "Legacy"
badge in the UI with a one-click copy command to upgrade to the unified
agent.
2025-11-25 23:26:22 +00:00
rcourtman
ee35d9e5a5 feat: add auto-update support for unified agent
Implement self-update capability for the unified pulse-agent binary:

- Add internal/agentupdate package with cross-platform update logic
- Hourly version checks against /api/agent/version endpoint
- SHA256 checksum verification for downloaded binaries
- Atomic binary replacement with backup/rollback on failure
- Support for Linux, macOS, and Windows (10 platform/arch combinations)

Build and release changes:
- Dockerfile builds unified agent for all platforms
- build-release.sh includes unified agent in release artifacts
- validate-release.sh validates unified agent binaries
- Install scripts (install.sh, install.ps1) use correct URL format

Related to #727, #737
2025-11-25 23:15:03 +00:00
courtmanr@gmail.com
ebcafa1dac feat: adaptive node table layout, guest row fixes, and legacy agent detection
- Implemented adaptive layout for NodeSummaryTable with responsive columns and sticky name column.
- Fixed GuestRow background display issues.
- Added IsLegacy field to Host and DockerHost models to flag legacy agents (version < 1.0.0).
- Updated monitor to populate IsLegacy based on agent version.
2025-11-25 17:19:36 +00:00
courtmanr@gmail.com
26ebd476da WIP: Save all pending changes including frontend updates and unified agent scaffolding 2025-11-25 11:27:07 +00:00
courtmanr@gmail.com
af2caa4b81 Register unified installer routes
Exposes /api/install/install.sh and /api/install/install.ps1 for the unified agent installer.
2025-11-25 11:25:10 +00:00
courtmanr@gmail.com
3599c6592e Improve installer UX with pauses and popups on failure
Fixes #755. Adds interactive pauses and graphical popups (where available) to installer scripts when critical errors occur, ensuring troubleshooting guides are readable. Also clarifies 'build from source' instructions.
2025-11-25 11:17:37 +00:00
courtmanr@gmail.com
809676cb4f Further reduce setup script verbosity: silence token checks and consolidate permission logs 2025-11-25 10:20:17 +00:00
courtmanr@gmail.com
005a878681 Suppress 'User already exists' message in setup script 2025-11-25 10:16:08 +00:00
courtmanr@gmail.com
1f57a8c77a Improve setup script clarity: reduce verbosity and fix confusing messages 2025-11-25 10:13:20 +00:00
courtmanr@gmail.com
6887773b45 Improve setup script output by hiding irrelevant Docker/proxy info 2025-11-25 10:01:41 +00:00
courtmanr@gmail.com
75a9929afc Refactor: Parallelize PVE node polling 2025-11-25 08:38:03 +00:00
courtmanr@gmail.com
3acd29c3f3 Implement UI toggle for Hide Local Login (related to issue #750) 2025-11-25 08:14:19 +00:00
courtmanr@gmail.com
8b7ff2ad48 Relax container SSH check for temperature monitoring (ref #727) 2025-11-25 08:00:08 +00:00
courtmanr@gmail.com
ea8ae63d70 Fix: Prevent unnecessary config reloads by checking file content hash 2025-11-24 22:42:24 +00:00
courtmanr@gmail.com
5031ba481b Fix: Allow double slashes in install script URLs 2025-11-24 17:58:00 +00:00
courtmanr@gmail.com
6b84b9a2bf Add PULSE_AUTH_HIDE_LOCAL_LOGIN option to hide password form
Implements #750 - allows hiding the username/password login form when
using OIDC SSO to avoid user confusion, while maintaining security.

- Added HideLocalLogin config option (env: PULSE_AUTH_HIDE_LOCAL_LOGIN)
- Exposed hideLocalLogin in /api/security/status endpoint
- Updated Login.tsx to conditionally hide local login form
- Added escape hatch via ?show_local=true URL parameter

This approach avoids the security and upgrade issues that led to
DISABLE_AUTH being removed (see #707, #678), while solving the UX
problem of users being confused by multiple login options.
2025-11-24 17:40:43 +00:00
courtmanr@gmail.com
9caba86389 Fix #735: Allow HEAD requests for download endpoints and fix routing
- Allow HEAD requests in addition to GET for all download handlers
  (install scripts, binaries, checksums) to prevent 405 errors
- Add /uninstall-host-agent.sh to special routes in ServeHTTP
- Add test coverage for HEAD request handling
- Resolves 'method not allowed' errors during agent installation
2025-11-24 15:16:14 +00:00
courtmanr@gmail.com
07d8381858 Fix host agent registration verification issues (#746)
- Change default server listen addresses to empty string (listen on all interfaces including IPv6)
- Add short hostname matching fallback in host lookup API to handle FQDN vs short name mismatches
- Implement retry loop (30s) in both Windows and Linux/macOS installers for registration verification
- Fix lint errors: remove unnecessary fmt.Sprintf and nil checks before len()

This resolves the 'Installer could not yet confirm host registration with Pulse' warning
by addressing timing issues, hostname matching, and network connectivity.
2025-11-24 14:28:09 +00:00
courtmanr@gmail.com
08a83c9adb Add host agent checksum route and bump version to 4.32.6
- Add /download/pulse-host-agent.sha256 route to serve checksums
- Fixes 'Checksum not available' warning during host agent installation
- Bump version to 4.32.6

Related to #746
2025-11-24 07:57:17 +00:00
courtmanr@gmail.com
60721cbc22 Add mutex protection for config watcher reloads (re #748)
Introduced sync.RWMutex to protect concurrent access to configuration
fields (AuthUser, AuthPass, APITokens) that are modified by the
ConfigWatcher at runtime.

- Added global config.Mu RWMutex in internal/config/config.go
- Protected config updates in ConfigWatcher.reloadConfig() and reloadAPITokens()
- Protected config reads in CheckAuth and all API token handlers
- Protected Router.SetConfig() during full config reloads

This prevents race conditions when .env file changes trigger config
reloads while authentication handlers are reading the same fields.
2025-11-24 07:45:21 +00:00
courtmanr@gmail.com
68ffa1bc0b Feat: Add support for Raspberry Pi RP1 ADC temperature sensor (Fixes #745)
- Added 'rp1_adc' to the list of recognized CPU temperature chips
2025-11-23 22:33:16 +00:00
courtmanr@gmail.com
af7eabb56d Fix: Correct context cancellation in loop (Fixes #727)
- Replaced defer in loop with explicit cancellation to avoid resource leak
- Properly tagged issue #727
2025-11-23 22:28:28 +00:00
courtmanr@gmail.com
b34e18e839 Fix: Prevent single node auth failure from disabling global SSH temperature collection
- Removed global legacySSHDisabled flag that was triggered by any single node auth failure
- Changed disableLegacySSHOnAuthFailure to only log warnings
- Fixed potential context leak in monitor.go
- Updated tests to reflect removal of global disable logic
2025-11-23 22:24:15 +00:00
courtmanr@gmail.com
35392c1f96 Improve agent update logging and installer warnings (related to #737) 2025-11-23 22:07:37 +00:00
courtmanr@gmail.com
d0a725fde5 fix: Add dev environment fallback paths for Docker agent downloads
- Add fallback to project root scripts/ directory for install-docker-agent.sh
- Add fallback to project root bin/ directory for pulse-docker-agent binary
- Fixes 404 errors when downloading agent installer and binary in dev mode
- Production paths remain unchanged (/opt/pulse/...)
2025-11-23 16:01:40 +00:00
courtmanr@gmail.com
6e404b24eb Fix install-host-agent.sh function order, remove duplicate, and improve dev serving 2025-11-23 12:27:11 +00:00
courtmanr@gmail.com
4bf9c42dbc Fix temperature collection regression for cluster nodes. Related to #727 2025-11-23 12:13:57 +00:00
courtmanr@gmail.com
1945795d36 Fix ZFS storage reporting on TrueNAS SCALE (#718)
- Refactor collector to support mocking
- Fix ZFS detection to support 'fuse.zfs' and case-insensitivity
- Add regression tests for ZFS dataset deduplication
2025-11-22 23:53:39 +00:00
courtmanr@gmail.com
d5fdf2f471 fix: ensure proxmox nodes are displayed even if cluster endpoints are missing
Fixes #727. Previously, if temperature monitoring was enabled and a node wasn't found in ClusterEndpoints, the entire node processing was skipped. This change ensures we only skip temperature collection.
2025-11-22 23:31:30 +00:00
rcourtman
93d8c624b4 Persist PVE fallback host after portless retry 2025-11-22 17:06:15 +00:00
rcourtman
8606bb47cf Handle PVE portless fallback when default port fails 2025-11-22 17:01:16 +00:00
rcourtman
5dbaf7c596 Add OIDC CA bundle support 2025-11-22 09:44:03 +00:00
rcourtman
cd31bdece1 Add log level control to host agent
Related to #742
2025-11-22 07:48:34 +00:00
rcourtman
19a2cac355 Add log level control for docker agent
Related to #742
2025-11-22 07:43:48 +00:00
rcourtman
ee3f1e0cdb Handle standby SMART temps and capture disk identity 2025-11-22 07:35:13 +00:00
rcourtman
6ff54beb38 Handle ignored docker containers during alert reevaluation (related to #561) 2025-11-21 22:19:19 +00:00
rcourtman
4e68675e0b Add recovery notifications and grouping controls 2025-11-21 22:07:00 +00:00
rcourtman
19408fdb2b Related to #738: make pulse proxy mount migration-safe 2025-11-21 21:29:14 +00:00
rcourtman
1f2993f4a7 Guard WebSocket broadcast buffers when clients stall (Related to #734) 2025-11-21 10:14:10 +00:00
rcourtman
5a6c6091bf Honor configured PVE polling interval in scheduler 2025-11-20 22:00:56 +00:00
rcourtman
ac90859e42 Related to #727: normalize persisted Proxmox hosts 2025-11-20 19:58:05 +00:00
rcourtman
d6cbfc23ec Harden public URL detection and setup token handling 2025-11-20 19:27:14 +00:00
rcourtman
d779328878 Add Apprise test support for notifications
Related to #584
2025-11-20 17:54:20 +00:00
rcourtman
044dadcdf1 Related to #727: restore default Proxmox ports 2025-11-20 16:35:08 +00:00
rcourtman
823508dc48 Related to #712: auto-restore host agent binaries for download 2025-11-20 15:45:21 +00:00
courtmanr@gmail.com
838993cf40 Implement sensor proxy installation and configuration updates 2025-11-20 13:23:21 +00:00
courtmanr@gmail.com
6ca543beb6 refactor: simplify sensor proxy installer argument detection by validating CTID and defaulting to standalone mode. 2025-11-20 12:37:08 +00:00
courtmanr@gmail.com
8bfdf3769b Update config persistence, crypto, and dev script 2025-11-20 11:46:20 +00:00
rcourtman
f4e2cbca48 Improve token collision handling and installer subnet support 2025-11-20 09:45:36 +00:00
rcourtman
22f9750044 Respect user-provided node host URLs (Related to #724) 2025-11-20 09:40:38 +00:00
rcourtman
844c895521 WIP: Fix temperature proxy registration persistence (incomplete)
This commit contains multiple fixes for temperature proxy registration,
but the core issue remains unresolved.

## What's Fixed:
1. Added config pointer and reloadFunc to TemperatureProxyHandlers
2. Added SetConfig method to keep handler in sync with router config changes
3. Added config reload after registration to prevent monitor from overwriting
4. Fixed installer port conflict detection and duplicate YAML key issues
5. Added comprehensive debug logging throughout registration flow

## What's Still Broken:
The TemperatureProxyURL, TemperatureProxyToken, and TemperatureProxyControlToken
fields are NOT persisting to nodes.enc after SaveNodesConfig is called.

Debug logs confirm:
- HandleRegister correctly updates nodesConfig.PVEInstances[matchedIndex]
- The correct data is passed to SaveNodesConfig (verified in logs)
- SaveNodesConfig completes without errors
- Config reload executes successfully
- BUT after Pulse restart, the fields are empty when loaded from disk

The bug is in SaveNodesConfig serialization or file writing logic itself.

Related files:
- internal/api/temperature_proxy.go: Registration handler
- internal/config/persistence.go: SaveNodesConfig implementation
- internal/config/config.go: PVEInstance struct definition
2025-11-19 20:12:19 +00:00
rcourtman
c9c4a07508 test(setup): add fmt.Sprintf argument alignment validation test
Added TestPVESetupScriptArgumentAlignment to prevent future fmt.Sprintf
argument mismatch bugs in the PVE quick setup script template.

The test uses sentinel values (SENTINEL_URL, SENTINEL_HOST, deadbeef...)
to verify that critical placeholders receive the correct argument types:

✓ Repair block INSTALLER_URL uses pulseURL (not authToken)
✓ Repair --pulse-server flags use pulseURL (not authToken)
✓ Authorization headers use runtime $AUTH_TOKEN variable (not hardcoded)
✓ Token ID uses tokenName (pulse-*) (not pulseURL or authToken)

This test would have caught the bugs fixed in commits 2bb73d3c7 and
2053bc5e2, where:
- authToken appeared in --pulse-server URLs (argument shift)
- Authorization headers were hardcoded instead of using runtime variable

Recommended by Codex as a safeguard against this class of regression.
2025-11-19 14:53:44 +00:00
rcourtman
708675803f fix(setup): use runtime AUTH_TOKEN variable for Authorization headers
Changed Authorization headers in ssh-config and verify-temperature-ssh API
calls to use the runtime $AUTH_TOKEN variable instead of compile-time
hardcoded authToken.

This fixes a bug where users who override the auth token via:
- PULSE_SETUP_TOKEN environment variable
- Interactive prompt (when auth_token URL param omitted)

...would still send an empty Bearer token in the Authorization headers,
causing API calls to fail with 401 Unauthorized.

Changes:
- Line 4748: -H "Authorization: Bearer %s" → -H "Authorization: Bearer $AUTH_TOKEN"
- Line 4937: -H "Authorization: Bearer %s" → -H "Authorization: Bearer $AUTH_TOKEN"
- Removed 2 authToken arguments from fmt.Sprintf (lines 5059)

Now the script respects runtime token overrides in all code paths.

Identified by Codex during fmt.Sprintf argument alignment review.
2025-11-19 14:53:44 +00:00
rcourtman
82def8c679 fix(setup): correct fmt.Sprintf argument alignment for PVE quick setup
Fixed critical argument mismatch bug where fmt.Sprintf arguments didn't align
with template placeholders. This caused:
- authToken being passed where pulseURL expected (curl errors)
- pulseURL being passed where authToken expected (empty Authorization headers)
- tokenName misalignment (Token ID placeholder broken)

Root cause: Template has 51 %s placeholders (54 total - 3 escaped %%s), but
argument list had wrong count and ordering.

Solution: Rebuilt argument list (lines 5049-5059) with correct mapping:
- 27 pulseURL (all installer URLs, --pulse-server flags, API endpoints)
- 11 tokenName (token creation, checks, final Token ID)
- 3 authToken (AUTH_TOKEN variable + 2 Authorization headers)
- 3 serverHost (error message rerun hints)
- 1 each: serverName, time, pulseIP, storagePerms, SSH keys, minProxyReadyVersion

Verified with go vet (passes). Mapping confirmed by walking each placeholder
in template and matching to correct argument type.

Related to #TBD (user will test)
2025-11-19 14:53:44 +00:00
rcourtman
69133b45fc fix(setup): make manual repair instructions actionable
Issue: When deployment type cannot be determined, error message referenced
$PROXY_INSTALLER but deleted it immediately, making instructions unusable.

Fix: Provide complete curl commands that users can copy-paste directly:
  curl -fsSL $PULSE_URL/api/install/install-sensor-proxy.sh | bash -s -- ...

This ensures users have a working repair path even when auto-detection fails.

Identified by Codex final review.
2025-11-19 13:33:28 +00:00
rcourtman
7e505775ee fix(setup): production-ready sensor-proxy repair logic
Addresses all remaining issues from Codex final review:

Issue 1: SUMMARY_PROXY_INSTALLED unreliable (only set by install.sh)
Fix: Use PROXY_SOCKET_EXISTED_AT_START flag set at script start - works
     for all installation methods (manual, older installers, etc.)

Issue 2: CTID detection fails when container offline/renamed
Fix: Read SUMMARY_CTID from install_summary.json as fallback. Priority:
     1) Live PULSE_CTID detection
     2) SUMMARY_CTID from json file
     3) Standalone node detection

Issue 3: Failed repair disables working proxy (TEMPERATURE_ENABLED=false)
Fix: Keep TEMPERATURE_ENABLED=true in all failure paths. Comments explain:
     proxy was working before, keep it enabled even if repair fails.

This ensures turnkey repair works reliably across all deployment scenarios
without breaking existing working proxies.
2025-11-19 13:24:08 +00:00
rcourtman
6367c7265f fix(setup): comprehensive repair logic for existing sensor-proxy installations
Addresses all issues found in Codex review:

1. Prevent double-install: Check SUMMARY_PROXY_INSTALLED to distinguish
   between fresh installs (skip repair) vs existing installs (run repair)

2. Fix clustered node failures: Explicitly detect deployment type and bail
   out with clear error message if neither --ctid nor --standalone can be
   determined

3. Add health validation: Mirror main install path - verify service active,
   socket exists, and fetch SSH public key after repair

4. Capture installer output: Show full diagnostics on failure (tail -20)

5. Better error messages: Provide specific manual repair commands when
   deployment type cannot be auto-detected

This ensures the turnkey repair experience works reliably without regressing
fresh install UX.
2025-11-19 13:16:15 +00:00
rcourtman
217ed67ee6 fix(setup): properly reinstall sensor-proxy when socket exists (the real fix)
The previous attempt (ed04926) was ineffective - it only set TEMPERATURE_ENABLED=true
which was redundant (already set at line 4051) and didn't trigger the auto-install block
because that block is gated by SKIP_TEMPERATURE_PROMPT != true.

This fix actually downloads and runs install-sensor-proxy.sh when an existing
socket is detected, which:
- Refreshes control plane tokens (fixes 401 errors)
- Updates control plane URL to correct Pulse instance
- Rewrites config atomically (Phase 2 installer is idempotent)
- Maintains turnkey UX - rerunning setup script now actually works

Detected by Codex final review.
2025-11-19 13:08:54 +00:00
rcourtman
ed049263aa fix(setup): always reinstall sensor-proxy to refresh tokens and config
When sensor-proxy socket is detected, the setup script was skipping
temperature monitoring setup with 'already configured' message. This
left stale control plane URLs/tokens, breaking temperature monitoring.

Now follows Codex recommendation: treat existing installations as
upgrade/repair opportunities. The installer is idempotent (Phase 2),
so rerunning it safely refreshes tokens, updates URLs, and ensures
turnkey operation even on hosts with existing installations.

Changes:
- Remove early return when sensor-proxy socket detected
- Set TEMPERATURE_ENABLED=true to proceed with reinstall
- Update message to clarify repair/upgrade behavior
- Maintains turnkey promise: rerun setup and it just works
2025-11-19 12:52:08 +00:00
rcourtman
f4fd4924d6 fix(hostmetrics): dedupe ZFS pools for usable storage
Related to #718
2025-11-18 23:38:11 +00:00
rcourtman
bf63515ff4 feat: avoid redundant PBS snapshot polling (Related to #717) 2025-11-18 23:10:43 +00:00
rcourtman
999e4271a2 feat: surface LXC mountpoints in UI (related to #715) 2025-11-18 22:57:20 +00:00
rcourtman
3f46d35a81 feat: make PVE polling interval configurable (related to #467) 2025-11-18 21:30:04 +00:00
rcourtman
274c45799f Add direct node fallback for storage polling 2025-11-18 19:58:38 +00:00
rcourtman
2e7f693c8b Improve installer temperature proxy and backup polling 2025-11-18 18:42:33 +00:00
rcourtman
5438a25682 Don't disable storages when cluster metadata omits flags 2025-11-18 17:18:23 +00:00
rcourtman
d9c0ddaae2 Allow PBS backup poll to finish after poller returns 2025-11-18 16:55:46 +00:00
rcourtman
54c47fe2f5 Improve temperature proxy detection 2025-11-18 14:25:09 +00:00
rcourtman
b807fe8773 Allow temperature proxy to authorize standalone nodes 2025-11-18 10:30:41 +00:00
rcourtman
7fa39467c0 Fix setup-script tokens and proxy registration timing 2025-11-18 10:22:54 +00:00
rcourtman
527b7ceacc Skip inactive storages during content scans 2025-11-18 09:46:48 +00:00
rcourtman
5a570c89a5 Fix auto-registration token parsing and hostname 2025-11-18 09:10:03 +00:00
rcourtman
f7261f3080 Harden turnkey install and proxy auto-registration 2025-11-18 00:24:50 +00:00
rcourtman
a479040651 Improve temperature proxy workflow 2025-11-17 14:25:46 +00:00
rcourtman
5951a364f7 Auto-deploy proxy for standalone temp monitoring 2025-11-16 09:47:07 +00:00
rcourtman
3ffd79361d Allow socket proxy registration without URL 2025-11-15 22:14:32 +00:00
rcourtman
de5b314842 Improve temperature proxy control-plane flow 2025-11-15 21:49:51 +00:00
rcourtman
ec822575dd Improve update procedure tracking 2025-11-15 16:43:42 +00:00
rcourtman
4fbb118072 Add PULSE_LXC_CTID env override for LXC CTID detection
Modern Proxmox LXC containers (cgroup v2 + systemd) don't expose the CTID
inside the guest namespace. The auto-detection in DetectLXCCTID() works
for older LXC setups and when hostname is numeric, but fails for most
production containers where users set custom hostnames.

Changes:
- Added PULSE_LXC_CTID environment variable override in router.go:490-495
- Graceful fallback: auto-detect first, then check env var, then show placeholder
- UI already handles missing CTID by showing "pct exec <ctid>" placeholder

This provides a robust solution for thousands of users:
- Stock Proxmox LXC: Shows `pct exec <ctid>` placeholder (user substitutes manually)
- Custom hostname containers: Can set PULSE_LXC_CTID=171 in compose/systemd
- Numeric hostname containers: Auto-detected (backwards compatible)

Related: FirstRunSetup.tsx already has graceful fallback (line 336-339)
2025-11-15 13:25:07 +00:00
rcourtman
639635a84b Add Docker container name auto-detection to bootstrap UI
- Added DetectDockerContainerName() to detect container name from hostname
- Extended /api/security/status to expose dockerContainerName field
- Updated FirstRunSetup to show actual container name when detected:
  * Before: 'docker exec <container-name> cat /data/.bootstrap_token'
  * After: 'docker exec pulse cat /data/.bootstrap_token'

This reduces friction for users - no need to look up the container name.
Works when Docker container is named (--name flag), falls back to
placeholder for auto-generated container IDs.
2025-11-15 10:45:00 +00:00
rcourtman
ae0481c982 Improve bootstrap token UX with smart environment detection
- Added DetectLXCCTID() to internal/system/container.go to detect Proxmox container ID
- Extended /api/security/status to expose inContainer and lxcCtid fields
- Updated FirstRunSetup to show most relevant command based on detected environment:
  * LXC with CTID: Shows 'pct exec 171 -- cat /etc/pulse/.bootstrap_token'
  * Docker: Shows 'docker exec <container-name> cat /data/.bootstrap_token'
  * Bare metal: Shows 'cat /etc/pulse/.bootstrap_token'
- Collapsed alternative methods behind 'Show other retrieval methods' button

This addresses user feedback that showing all options was overwhelming.
Now users see the command most likely to work for their setup first,
with alternatives hidden but still accessible.
2025-11-15 10:18:59 +00:00
rcourtman
bcd8d4e0fa Fix critical cleanup implementation issues found by Codex review
**Host Detection**:
- Now detects localhost by hostname and FQDN, not just IP
- Fixes issue where nodes configured as https://hostname:8006 would skip
  localhost cleanup (API tokens, bind mounts, service removal)

**Systemd Sandbox**:
- Added /etc/pve and /etc/systemd/system to ReadWritePaths
- Allows cleanup script to modify Proxmox configs and systemd units

**Uninstaller Improvements**:
- Use UUID for transient unit names (prevents same-second collisions)
- Added --purge flag for complete removal
- Added --wait and --collect flags to capture exit code
- Now fails cleanup if uninstaller exits non-zero

**Path Migration**:
- Fixed all /usr/local references to use /opt/pulse/sensor-proxy
- Updated forced command in SSH authorized_keys
- Updated self-heal script installer path
- Updated Go backend removal helpers (supports both new and legacy paths)

These fixes address Codex findings: hostname detection, sandbox permissions,
transient unit collisions, incomplete purging, and incomplete path migration.

Related to cleanup implementation testing.
2025-11-15 00:33:41 +00:00
rcourtman
c9b4f7e88b Fix incorrect temperature data during cluster initialization
During cluster startup, nodes were temporarily using the primary cluster
endpoint for temperature collection before cluster metadata validation
completed. This caused all nodes to show the same (incorrect) temperature
values for ~4 minutes until validation finished and per-node endpoints
were established.

Example: minipc would show delly's temperature (90°C) instead of its own
(50°C) from startup until cluster validation completed.

Root cause:
- Temperature collection started immediately at startup
- Cluster endpoint validation happened asynchronously
- Code fell back to primary endpoint when ClusterEndpoints was empty
- All nodes used same endpoint, got same temperature data

Fix: Skip temperature collection for cluster nodes until:
1. ClusterEndpoints array is populated (validation complete)
2. Node's specific endpoint is found in the cluster metadata

This ensures correct temperature data from the very first collection,
maintaining data integrity during startup. When persisted config exists,
endpoints are available immediately so no delay occurs. For new clusters,
temperature collection begins once validation completes (~30s).

Preserves Pulse's correctness guarantee: users can trust metrics
immediately after restart without waiting for "warm-up" period.
2025-11-14 23:38:44 +00:00
rcourtman
387a68ef18 Allow dev builds to skip proxy version gate 2025-11-14 21:34:55 +00:00
rcourtman
7cfe998fca Make download tests use temp bin dir 2025-11-14 13:59:50 +00:00
rcourtman
20194d9bb7 Add CI build workflow and tighten proxy diagnostics 2025-11-14 13:32:29 +00:00
rcourtman
349f5627e5 monitoring: add poll watchdog to prevent worker leaks (refs #696) 2025-11-14 11:24:59 +00:00
rcourtman
6e9a62f663 Ensure Windows download finds .exe (related to #684) 2025-11-14 10:59:45 +00:00
rcourtman
70673c1fdc Improve temperature proxy diagnostics and tests 2025-11-13 22:31:53 +00:00
rcourtman
3c707a7368 Fix HTTP mode reliability: add context timeouts to SSH collection
Critical fix for intermittent HTTP endpoint hangs identified by Codex analysis.

## Root Cause
SSH collection via getTemperatureViaSSH() had no timeout, causing HTTP
handlers to block indefinitely when sensors command hung. This held node-level
mutexes and rate limit slots, creating cascading failures where subsequent
requests queued indefinitely.

## Solution
- Thread request context through to SSH execution
- Add exec.CommandContext with 15s timeout (vs 30s HTTP client timeout)
- Create execCommandWithLimitsContext() to wrap SSH commands
- Ensures handlers always release locks and respond within deadline

## Impact
- HTTP temps endpoint now responds in ~70ms consistently
- Temperature data successfully collected and displayed in Pulse
- Eliminates 'context deadline exceeded' errors
- Prevents node gate deadlocks from slow/stuck SSH sessions

Related to Codex session 019a7e99-00fc-7903-afa3-01100baf47c6
2025-11-13 19:09:50 +00:00
rcourtman
e2bd514899 Fix HTTP mode for pulse-sensor-proxy and improve installer safety
## HTTP Server Fixes
- Add source IP middleware to enforce allowed_source_subnets
- Fix missing source subnet validation for external HTTP requests
- HTTP health endpoint now respects subnet restrictions

## Installer Improvements
- Auto-configure allowed_source_subnets with Pulse server IP
- Add cluster node hostnames to allowed_nodes (not just IPs)
- Fix node validation to accept both hostnames and IPs
- Add Pulse server reachability check before installation
- Add port availability check for HTTP mode
- Add automatic rollback on service startup failure
- Add HTTP endpoint health check after installation
- Fix config backup and deduplication (prevent duplicate keys)
- Fix IPv4 validation with loopback rejection
- Improve registration retry logic with detailed errors
- Add automatic LXC bind mount cleanup on uninstall

## Temperature Collection Fixes
- Add local temperature collection for self-monitoring nodes
- Fix node identifier matching (use hostname not SSH host)
- Fix JSON double-encoding in HTTP client response

Related to #XXX (temperature monitoring fixes)
2025-11-13 18:22:36 +00:00
rcourtman
a2b8940873 Add Pulse API endpoints for temperature proxy registration
Implements REST API endpoints to enable automatic registration of
temperature proxies during sensor-proxy installation.

API endpoints:
- POST /api/temperature-proxy/register
  - Accepts: hostname, proxy_url
  - Returns: authentication token
  - Finds matching PVE instance and configures proxy URL/token
  - No authentication required (called during installation)

- DELETE /api/temperature-proxy/unregister?hostname=X
  - Removes proxy configuration from PVE instance
  - Requires admin authentication

Implementation:
- Uses config.ConfigPersistence for loading/saving nodes.enc
- Matches PVE instances by hostname in Host field or ClusterEndpoints
- Generates cryptographically secure random tokens (32 bytes, base64)
- Atomic config updates (load → modify → save)

Next step: Update install-sensor-proxy.sh to call registration API

Related to #571
2025-11-13 16:20:47 +00:00
rcourtman
22f092f941 Add HTTP mode to pulse-sensor-proxy for multi-instance temperature monitoring
This implements HTTP/HTTPS support for pulse-sensor-proxy to enable
temperature monitoring across multiple separate Proxmox instances.

Architecture changes:
- Dual-mode operation: Unix socket (local) + HTTPS (remote)
- Unix socket remains default for security/performance (no breaking change)
- HTTP mode enables temps from external PVE hosts

Backend implementation:
- Add HTTPS server with TLS + Bearer token authentication to sensor-proxy
- Add TemperatureProxyURL and TemperatureProxyToken fields to PVEInstance
- Add HTTP client (internal/tempproxy/http_client.go) for remote proxy calls
- Update temperature collector to prefer HTTP proxy when configured
- Fallback logic: HTTP proxy → Unix socket → direct SSH (if not containerized)

Configuration:
- pulse-sensor-proxy config: http_enabled, http_listen_addr, http_tls_cert/key, http_auth_token
- PVEInstance config: temperature_proxy_url, temperature_proxy_token
- Environment variables: PULSE_SENSOR_PROXY_HTTP_* for all HTTP settings

Security:
- TLS 1.2+ with modern cipher suites
- Constant-time token comparison (timing attack prevention)
- Rate limiting applied to HTTP requests (shared with socket mode)
- Audit logging for all HTTP requests

Next steps:
- Update installer script to support HTTP mode + auto-registration
- Add Pulse API endpoint for proxy registration
- Generate TLS certificates during installation
- Test multi-instance temperature collection

Related to #571 (multi-instance architecture)
2025-11-13 16:13:53 +00:00
rcourtman
56a7579c99 Add Remember Me feature with sliding session expiration (Related to #707)
Implements a "Remember Me" option that allows users to stay logged in
for 30 days instead of the default 24 hours. This addresses the pain
point of frequent re-authentication in LAN-only environments while
maintaining authentication security.

Backend changes:
- Add rememberMe field to login request handling
- Support variable session durations (24h default, 30d with Remember Me)
- Implement sliding session expiration that extends sessions on each
  authenticated request using the original duration
- Store OriginalDuration in session data for proper sliding window
- Update session cookie MaxAge to match session duration

Frontend changes:
- Add "Remember Me for 30 days" checkbox to login form
- Pass rememberMe flag in login request
- Improve UI with clear duration indication

Key features:
- Sessions extend automatically on each request (sliding window)
- Original duration preserved across session extension
- Backward compatible with existing sessions (legacy sessions work)
- Sessions persist across server restarts

This provides a better user experience for LAN deployments without
compromising security by completely disabling authentication.
2025-11-13 10:37:08 +00:00
rcourtman
1166576b21 Ensure agent ID collisions respect token boundaries (Related to #658) 2025-11-12 22:46:56 +00:00
rcourtman
bb55144637 Improve update integration diagnostics 2025-11-12 22:27:05 +00:00
rcourtman
98adb5e08e Preserve storage backups after partial failures (Related to #704) 2025-11-12 21:10:18 +00:00