From 1d13d96a4ea80df02f991c7f960e9eaa318b462d Mon Sep 17 00:00:00 2001 From: Samuel K Date: Mon, 21 Sep 2026 23:53:52 -0600 Subject: [PATCH 01/13] fix(diagnostics): unify daemon runtime locator paths --- cmd/internal/agent_daemon.go | 7 +- cmd/internal/agent_daemon_diagnostics.go | 6 +- cmd/internal/agentworkspace/logs_daemon.go | 7 +- pkg/daemon/agent/daemon.go | 44 ++++---- pkg/daemon/agent/daemon_test.go | 28 +++-- pkg/machinediagnostics/locator.go | 105 ++++++++++++++++++- pkg/machinediagnostics/runtime_lock.go | 6 +- pkg/machinediagnostics/runtime_paths.go | 62 +++++++++++ pkg/machinediagnostics/runtime_paths_test.go | 46 ++++++++ pkg/machinediagnostics/store_test.go | 86 +++++++++++++++ 10 files changed, 360 insertions(+), 37 deletions(-) create mode 100644 pkg/machinediagnostics/runtime_paths.go create mode 100644 pkg/machinediagnostics/runtime_paths_test.go diff --git a/cmd/internal/agent_daemon.go b/cmd/internal/agent_daemon.go index 63f1badaa..314af52c1 100644 --- a/cmd/internal/agent_daemon.go +++ b/cmd/internal/agent_daemon.go @@ -30,6 +30,7 @@ const ( ) var daemonRuntimeLockPath = machinediagnostics.DefaultRuntimeLockPath +var daemonLocatorPath = machinediagnostics.DefaultLocatorPath type DaemonCmd struct { *flags.GlobalFlags @@ -105,6 +106,10 @@ func (cmd *DaemonCmd) Run(ctx context.Context) error { if fallbackPath := os.Getenv(machinediagnostics.RuntimeLockPathEnv); fallbackPath != "" { runtimeLockPath = fallbackPath } + locatorPath := daemonLocatorPath + if fallbackPath := os.Getenv(machinediagnostics.RuntimeLocatorPathEnv); fallbackPath != "" { + locatorPath = fallbackPath + } runtimeLock, err := machinediagnostics.AcquireRuntimeLock(runtimeLockPath) if err != nil { return err @@ -125,7 +130,7 @@ func (cmd *DaemonCmd) Run(ctx context.Context) error { } else { cmd.recorder = recorder if err := machinediagnostics.WriteLocator( - machinediagnostics.DefaultLocatorPath, + locatorPath, machinediagnostics.Locator{ SessionID: recorder.SessionID(), DiagnosticsDir: machinediagnostics.DiagnosticsDir(location.Root), diff --git a/cmd/internal/agent_daemon_diagnostics.go b/cmd/internal/agent_daemon_diagnostics.go index 99927c5bd..a6d7869dc 100644 --- a/cmd/internal/agent_daemon_diagnostics.go +++ b/cmd/internal/agent_daemon_diagnostics.go @@ -78,10 +78,10 @@ func (cmd *DaemonDiagnosticsCmd) readResponse() (machinediagnostics.ReadResponse Now: time.Now(), } if cmd.StateRoot == "" && cmd.StateLayout == "" { - return machinediagnostics.ReadFromLocator( - machinediagnostics.DefaultLocatorPath, + return machinediagnostics.ReadActive( options, - ), nil + os.UserCacheDir, + ) } if cmd.StateRoot == "" || cmd.StateLayout == "" { return machinediagnostics.ReadResponse{}, fmt.Errorf( diff --git a/cmd/internal/agentworkspace/logs_daemon.go b/cmd/internal/agentworkspace/logs_daemon.go index 41fec4530..da8cfea92 100644 --- a/cmd/internal/agentworkspace/logs_daemon.go +++ b/cmd/internal/agentworkspace/logs_daemon.go @@ -40,11 +40,14 @@ func NewLogsDaemonCmd(flags *flags.GlobalFlags) *cobra.Command { func (cmd *LogsDaemonCmd) Run(ctx context.Context) error { _ = ctx - response := machinediagnostics.ReadFromLocator( - machinediagnostics.DefaultLocatorPath, + response, err := machinediagnostics.ReadActive( machinediagnostics.ReadOptions{ Limit: machinediagnostics.DefaultReadEvents, Now: time.Now(), }, + os.UserCacheDir, ) + if err != nil { + return err + } return json.NewEncoder(os.Stdout).Encode(response) } diff --git a/pkg/daemon/agent/daemon.go b/pkg/daemon/agent/daemon.go index 0fc1a13fa..2c866b3d1 100644 --- a/pkg/daemon/agent/daemon.go +++ b/pkg/daemon/agent/daemon.go @@ -188,11 +188,11 @@ func InstallDaemon( if !isSystemdAvailable() { log.Warnf("systemd not available, falling back to background process") - lockPath, err := fallbackRuntimeLockPath(os.Geteuid(), os.UserCacheDir) + runtimePaths, err := fallbackRuntimePaths(os.Geteuid(), os.UserCacheDir) if err != nil { return err } - return startFallbackDaemon(executable, args, lockPath) + return startFallbackDaemon(executable, args, runtimePaths) } return installSystemdDaemon(opts, executable, args) } @@ -367,7 +367,7 @@ func ensureServiceRunning(needsReload bool, executable string, args []string) er "systemctl", "restart", pkgconfig.BinaryName, ).CombinedOutput(); err != nil { log.Warnf("Error restarting service: %s: %v", string(out), err) - return startFallbackDaemon(executable, args, machinediagnostics.DefaultRuntimeLockPath) + return startFallbackDaemon(executable, args, machinediagnostics.SystemRuntimePaths()) } log.Infof("restarted Devsy daemon with updated config") } else if !isServiceRunning() { @@ -376,7 +376,7 @@ func ensureServiceRunning(needsReload bool, executable string, args []string) er "systemctl", "start", pkgconfig.BinaryName, ).CombinedOutput(); err != nil { log.Warnf("Error starting service: %s: %v", string(out), err) - return startFallbackDaemon(executable, args, machinediagnostics.DefaultRuntimeLockPath) + return startFallbackDaemon(executable, args, machinediagnostics.SystemRuntimePaths()) } log.Infof("installed Devsy daemon into server") } @@ -384,17 +384,16 @@ func ensureServiceRunning(needsReload bool, executable string, args []string) er return nil } -func startFallbackDaemon(executable string, args []string, runtimeLockPath string) error { +func startFallbackDaemon( + executable string, + args []string, + runtimePaths machinediagnostics.RuntimePaths, +) error { daemonArgs := args[1:] // strip executable path err := command.StartBackgroundOnce(pkgconfig.DaemonProcessName, func() (*exec.Cmd, error) { //nolint:gosec // executable is from os.Executable() cmd := exec.Command(executable, daemonArgs...) - if runtimeLockPath != machinediagnostics.DefaultRuntimeLockPath { - cmd.Env = append( - os.Environ(), - machinediagnostics.RuntimeLockPathEnv+"="+runtimeLockPath, - ) - } + cmd.Env = fallbackDaemonEnv(runtimePaths) return cmd, nil }) if err != nil { @@ -404,18 +403,25 @@ func startFallbackDaemon(executable string, args []string, runtimeLockPath strin return nil } -func fallbackRuntimeLockPath( +func fallbackDaemonEnv(runtimePaths machinediagnostics.RuntimePaths) []string { + if runtimePaths.LockPath == machinediagnostics.DefaultRuntimeLockPath && + runtimePaths.LocatorPath == machinediagnostics.DefaultLocatorPath { + return nil + } + return append(os.Environ(), + machinediagnostics.RuntimeLockPathEnv+"="+runtimePaths.LockPath, + machinediagnostics.RuntimeLocatorPathEnv+"="+runtimePaths.LocatorPath, + ) +} + +func fallbackRuntimePaths( uid int, userCacheDir func() (string, error), -) (string, error) { +) (machinediagnostics.RuntimePaths, error) { if uid == 0 { - return machinediagnostics.DefaultRuntimeLockPath, nil - } - cacheDir, err := userCacheDir() - if err != nil { - return "", fmt.Errorf("get user cache directory for daemon lock: %w", err) + return machinediagnostics.SystemRuntimePaths(), nil } - return filepath.Join(cacheDir, "devsy", "agent-daemon.lock"), nil + return machinediagnostics.UserRuntimePaths(userCacheDir) } func RemoveDaemon() error { diff --git a/pkg/daemon/agent/daemon_test.go b/pkg/daemon/agent/daemon_test.go index 402ea3968..df8917467 100644 --- a/pkg/daemon/agent/daemon_test.go +++ b/pkg/daemon/agent/daemon_test.go @@ -6,6 +6,7 @@ import ( "github.com/devsy-org/api/pkg/devsy" "github.com/devsy-org/devsy/pkg/devcontainer/config" + "github.com/devsy-org/devsy/pkg/machinediagnostics" provider2 "github.com/devsy-org/devsy/pkg/provider" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -28,21 +29,34 @@ func TestBuildDaemonArgs(t *testing.T) { }, args) } -func TestFallbackRuntimeLockPath(t *testing.T) { +func TestFallbackRuntimePaths(t *testing.T) { cacheDir := func() (string, error) { return "/home/devsy/.cache", nil } - rootPath, err := fallbackRuntimeLockPath(0, cacheDir) + rootPaths, err := fallbackRuntimePaths(0, cacheDir) require.NoError(t, err) - assert.Equal(t, "/run/devsy/agent-daemon.lock", rootPath) + assert.Equal(t, "/run/devsy/agent-daemon.lock", rootPaths.LockPath) + assert.Equal(t, "/run/devsy/agent-daemon.json", rootPaths.LocatorPath) - userPath, err := fallbackRuntimeLockPath(1000, cacheDir) + userPaths, err := fallbackRuntimePaths(1000, cacheDir) require.NoError(t, err) - assert.Equal(t, "/home/devsy/.cache/devsy/agent-daemon.lock", userPath) + assert.Equal(t, "/home/devsy/.cache/devsy/agent-daemon.lock", userPaths.LockPath) + assert.Equal(t, "/home/devsy/.cache/devsy/agent-daemon.json", userPaths.LocatorPath) - _, err = fallbackRuntimeLockPath(1000, func() (string, error) { + _, err = fallbackRuntimePaths(1000, func() (string, error) { return "", errors.New("unavailable") }) - require.ErrorContains(t, err, "get user cache directory for daemon lock") + require.ErrorContains(t, err, "get user cache directory for daemon runtime") +} + +func TestFallbackDaemonEnvPropagatesBothRuntimePaths(t *testing.T) { + env := fallbackDaemonEnv(machinediagnostics.RuntimePaths{ + LockPath: "/cache/devsy/agent-daemon.lock", + LocatorPath: "/cache/devsy/agent-daemon.json", + }) + assert.Contains(t, env, "DEVSY_DAEMON_RUNTIME_LOCK_PATH=/cache/devsy/agent-daemon.lock") + assert.Contains(t, env, "DEVSY_DAEMON_LOCATOR_PATH=/cache/devsy/agent-daemon.json") + + assert.Nil(t, fallbackDaemonEnv(machinediagnostics.SystemRuntimePaths())) } func TestDaemonUnitStateLocationMatchesExactArgumentValues(t *testing.T) { diff --git a/pkg/machinediagnostics/locator.go b/pkg/machinediagnostics/locator.go index cb79c73a6..14e514203 100644 --- a/pkg/machinediagnostics/locator.go +++ b/pkg/machinediagnostics/locator.go @@ -8,7 +8,7 @@ import ( "time" ) -const DefaultLocatorPath = "/run/devsy/agent-daemon.json" +const DefaultLocatorPath = DefaultRuntimeDir + "/" + RuntimeLocatorFileName type Locator struct { SchemaVersion int `json:"schemaVersion"` @@ -25,7 +25,7 @@ func WriteLocator( if !filepath.IsAbs(path) || !filepath.IsAbs(locator.DiagnosticsDir) { return fmt.Errorf("locator paths must be absolute") } - if err := os.MkdirAll(filepath.Dir(path), 0o750); err != nil { + if err := ensureRuntimeDirForPath(path); err != nil { return err } locator.SchemaVersion = SchemaVersion @@ -106,3 +106,104 @@ func ReadFromLocator(path string, options ReadOptions) ReadResponse { } return Read(locator.DiagnosticsDir, options) } + +// ReadActive reads the highest-priority active daemon locator. A missing or +// inaccessible system locator permits trying the per-user fallback; a corrupt +// system locator is authoritative and is never masked by stale fallback data. +func ReadActive(options ReadOptions, userCacheDir func() (string, error)) (ReadResponse, error) { + paths, err := ActiveLocatorCandidates(userCacheDir) + if err != nil { + return ReadResponse{}, err + } + return readActiveFromCandidates(paths, options), nil +} + +func ActiveLocatorCandidates(userCacheDir func() (string, error)) ([]string, error) { + paths := []string{DefaultLocatorPath} + userPaths, err := UserRuntimePaths(userCacheDir) + if err != nil { + return nil, err + } + if userPaths.LocatorPath != DefaultLocatorPath { + paths = append(paths, userPaths.LocatorPath) + } + return paths, nil +} + +func readActiveFromCandidates(paths []string, options ReadOptions) ReadResponse { + var permissionResponse ReadResponse + for i, path := range paths { + candidate := readLocatorCandidate(path, options) + response, done, updatedPermission := applyLocatorCandidate( + candidate, + i, + permissionResponse, + ) + if done { + return response + } + permissionResponse = updatedPermission + } + if permissionResponse.Availability != "" { + return permissionResponse + } + return ReadFromLocator(DefaultLocatorPath, options) +} + +func applyLocatorCandidate( + candidate locatorCandidate, + index int, + permissionResponse ReadResponse, +) (ReadResponse, bool, ReadResponse) { + switch candidate.state { + case locatorCandidateSuccess: + return candidate.response, true, permissionResponse + case locatorCandidateCorrupt: + if index == 0 || permissionResponse.Availability == "" { + return candidate.response, true, permissionResponse + } + return permissionResponse, true, permissionResponse + case locatorCandidatePermission: + if permissionResponse.Availability == "" { + permissionResponse = candidate.response + } + } + return ReadResponse{}, false, permissionResponse +} + +type locatorCandidateState uint8 + +const ( + locatorCandidateMissing locatorCandidateState = iota + locatorCandidatePermission + locatorCandidateCorrupt + locatorCandidateSuccess +) + +type locatorCandidate struct { + response ReadResponse + state locatorCandidateState +} + +func readLocatorCandidate(path string, options ReadOptions) locatorCandidate { + locator, err := ReadLocator(path) + if err == nil { + return locatorCandidate{ + response: Read(locator.DiagnosticsDir, options), + state: locatorCandidateSuccess, + } + } + if os.IsNotExist(err) { + return locatorCandidate{state: locatorCandidateMissing} + } + if os.IsPermission(err) { + return locatorCandidate{ + response: ReadFromLocator(path, options), + state: locatorCandidatePermission, + } + } + return locatorCandidate{ + response: ReadFromLocator(path, options), + state: locatorCandidateCorrupt, + } +} diff --git a/pkg/machinediagnostics/runtime_lock.go b/pkg/machinediagnostics/runtime_lock.go index 84e5d3462..29b56b784 100644 --- a/pkg/machinediagnostics/runtime_lock.go +++ b/pkg/machinediagnostics/runtime_lock.go @@ -2,15 +2,15 @@ package machinediagnostics import ( "fmt" - "os" "path/filepath" "github.com/gofrs/flock" ) const ( - DefaultRuntimeLockPath = "/run/devsy/agent-daemon.lock" + DefaultRuntimeLockPath = DefaultRuntimeDir + "/" + RuntimeLockFileName RuntimeLockPathEnv = "DEVSY_DAEMON_RUNTIME_LOCK_PATH" + RuntimeLocatorPathEnv = "DEVSY_DAEMON_LOCATOR_PATH" ) // RuntimeLock prevents two singleton machine daemons from supervising the @@ -21,7 +21,7 @@ func AcquireRuntimeLock(path string) (*RuntimeLock, error) { if !filepath.IsAbs(path) { return nil, fmt.Errorf("daemon runtime lock path must be absolute") } - if err := os.MkdirAll(filepath.Dir(path), 0o750); err != nil { + if err := ensureRuntimeDirForPath(path); err != nil { return nil, fmt.Errorf("create daemon runtime directory: %w", err) } lock := flock.New(path) diff --git a/pkg/machinediagnostics/runtime_paths.go b/pkg/machinediagnostics/runtime_paths.go new file mode 100644 index 000000000..57d432612 --- /dev/null +++ b/pkg/machinediagnostics/runtime_paths.go @@ -0,0 +1,62 @@ +package machinediagnostics + +import ( + "fmt" + "os" + "path/filepath" +) + +const ( + DefaultRuntimeDir = "/run/devsy" + RuntimeLockFileName = "agent-daemon.lock" + RuntimeLocatorFileName = "agent-daemon.json" +) + +// RuntimePaths keeps the daemon's singleton lock and discovery metadata in the +// same runtime directory. +type RuntimePaths struct { + LockPath string + LocatorPath string +} + +func SystemRuntimePaths() RuntimePaths { + return runtimePaths(DefaultRuntimeDir) +} + +func UserRuntimePaths(userCacheDir func() (string, error)) (RuntimePaths, error) { + cacheDir, err := userCacheDir() + if err != nil { + return RuntimePaths{}, fmt.Errorf("get user cache directory for daemon runtime: %w", err) + } + return runtimePaths(filepath.Join(cacheDir, "devsy")), nil +} + +func runtimePaths(dir string) RuntimePaths { + return RuntimePaths{ + LockPath: filepath.Join(dir, RuntimeLockFileName), + LocatorPath: filepath.Join(dir, RuntimeLocatorFileName), + } +} + +// EnsureRuntimeDir creates a runtime directory. Shared system runtime +// directories are also repaired when they predate the current permissions. +func EnsureRuntimeDir(path string, shared bool) error { + mode := os.FileMode(0o750) + if shared { + mode = 0o755 + } + if err := os.MkdirAll(path, mode); err != nil { + return err + } + if shared { + if err := os.Chmod(path, mode); err != nil { + return err + } + } + return nil +} + +func ensureRuntimeDirForPath(path string) error { + dir := filepath.Dir(path) + return EnsureRuntimeDir(dir, filepath.Clean(dir) == DefaultRuntimeDir) +} diff --git a/pkg/machinediagnostics/runtime_paths_test.go b/pkg/machinediagnostics/runtime_paths_test.go new file mode 100644 index 000000000..770641572 --- /dev/null +++ b/pkg/machinediagnostics/runtime_paths_test.go @@ -0,0 +1,46 @@ +package machinediagnostics + +import ( + "errors" + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestRuntimePaths(t *testing.T) { + assertPaths := func(t *testing.T, got RuntimePaths, dir string) { + t.Helper() + require.Equal(t, filepath.Join(dir, RuntimeLockFileName), got.LockPath) + require.Equal(t, filepath.Join(dir, RuntimeLocatorFileName), got.LocatorPath) + } + + assertPaths(t, SystemRuntimePaths(), DefaultRuntimeDir) + paths, err := UserRuntimePaths(func() (string, error) { return "/tmp/test-cache", nil }) + require.NoError(t, err) + assertPaths(t, paths, "/tmp/test-cache/devsy") + + _, err = UserRuntimePaths(func() (string, error) { return "", errors.New("unavailable") }) + require.ErrorContains(t, err, "get user cache directory for daemon runtime") +} + +func TestEnsureRuntimeDirRepairsSharedPermissions(t *testing.T) { + dir := filepath.Join(t.TempDir(), "runtime") + require.NoError(t, os.MkdirAll(dir, 0o750)) + require.NoError(t, os.Chmod(dir, 0o750)) + require.NoError(t, EnsureRuntimeDir(dir, true)) + + info, err := os.Stat(dir) + require.NoError(t, err) + require.Equal(t, os.FileMode(0o755), info.Mode().Perm()) +} + +func TestEnsureRuntimeDirKeepsUserRuntimePrivate(t *testing.T) { + dir := filepath.Join(t.TempDir(), "runtime") + require.NoError(t, EnsureRuntimeDir(dir, false)) + + info, err := os.Stat(dir) + require.NoError(t, err) + require.Equal(t, os.FileMode(0o750), info.Mode().Perm()) +} diff --git a/pkg/machinediagnostics/store_test.go b/pkg/machinediagnostics/store_test.go index e0b2f073e..d9ec923e9 100644 --- a/pkg/machinediagnostics/store_test.go +++ b/pkg/machinediagnostics/store_test.go @@ -147,6 +147,92 @@ func TestLocatorRoundTrip(t *testing.T) { assert.Equal(t, SchemaVersion, got.SchemaVersion) assert.Equal(t, want.SessionID, got.SessionID) assert.Equal(t, want.DiagnosticsDir, got.DiagnosticsDir) + want.SessionID = "replacement" + require.NoError(t, WriteLocator(path, want)) + got, err = ReadLocator(path) + require.NoError(t, err) + assert.Equal(t, "replacement", got.SessionID) +} + +func newTestDiagnosticsStore(t *testing.T, state DaemonState) string { + t.Helper() + dir := filepath.Join(t.TempDir(), "diagnostics") + store, err := NewRecorder(Options{ + Dir: dir, + Reader: ReaderIdentity{UID: os.Getuid(), GID: os.Getgid()}, + }) + require.NoError(t, err) + store.Update(Status{State: state, Health: DaemonHealthy}) + return dir +} + +func writeTestLocator(t *testing.T, dir, diagnosticsDir string) string { + t.Helper() + path := filepath.Join(dir, "locator.json") + require.NoError(t, WriteLocator( + path, + Locator{SessionID: dir, DiagnosticsDir: diagnosticsDir}, + )) + return path +} + +func TestReadActiveLocatorCandidates(t *testing.T) { + options := ReadOptions{Limit: 10, Interval: time.Minute, Now: time.Now()} + + t.Run("system wins", func(t *testing.T) { + systemDir := newTestDiagnosticsStore(t, DaemonRunning) + userDir := newTestDiagnosticsStore(t, DaemonStopping) + system := writeTestLocator(t, t.TempDir(), systemDir) + user := writeTestLocator(t, t.TempDir(), userDir) + response := readActiveFromCandidates([]string{system, user}, options) + assert.Equal(t, DaemonRunning, response.Status.State) + }) + + t.Run("user fallback", func(t *testing.T) { + userDir := newTestDiagnosticsStore(t, DaemonRunning) + user := writeTestLocator(t, t.TempDir(), userDir) + response := readActiveFromCandidates( + []string{filepath.Join(t.TempDir(), "missing"), user}, + options, + ) + assert.Equal(t, DaemonRunning, response.Status.State) + }) + + t.Run("corrupt system is authoritative", func(t *testing.T) { + system := filepath.Join(t.TempDir(), "system.json") + require.NoError(t, os.WriteFile(system, []byte("{"), 0o600)) + userDir := newTestDiagnosticsStore(t, DaemonRunning) + user := writeTestLocator(t, t.TempDir(), userDir) + response := readActiveFromCandidates([]string{system, user}, options) + assert.Equal(t, AvailabilityCorrupt, response.Availability) + }) + + t.Run("both missing", func(t *testing.T) { + response := readActiveFromCandidates([]string{ + filepath.Join(t.TempDir(), "system.json"), + filepath.Join(t.TempDir(), "user.json"), + }, options) + assert.Equal(t, AvailabilityNotInitialized, response.Availability) + }) + + if os.Getuid() != 0 { + t.Run("permission system falls back", func(t *testing.T) { + systemDir := t.TempDir() + system := writeTestLocator(t, systemDir, newTestDiagnosticsStore(t, DaemonStopping)) + require.NoError(t, os.Chmod(system, 0o000)) + t.Cleanup(func() { + _ = os.Chmod(system, 0o600) //nolint:gosec // restore test fixture access. + }) + if _, err := ReadLocator(system); !os.IsPermission(err) { + t.Skip("platform does not expose permission-denied file reads") + } + userDir := newTestDiagnosticsStore(t, DaemonRunning) + user := writeTestLocator(t, t.TempDir(), userDir) + response := readActiveFromCandidates([]string{system, user}, options) + require.NotNil(t, response.Status) + assert.Equal(t, DaemonRunning, response.Status.State) + }) + } } func TestRuntimeLockIsExclusive(t *testing.T) { From 4da1e34c557fe1a2b3a08bbafb34b77b5ed07ba2 Mon Sep 17 00:00:00 2001 From: Samuel K Date: Mon, 21 Sep 2026 23:53:56 -0600 Subject: [PATCH 02/13] test(tunnel): require definitive listener shutdown --- pkg/tunnel/local_listener_test.go | 70 +++++++++++++------------------ 1 file changed, 30 insertions(+), 40 deletions(-) diff --git a/pkg/tunnel/local_listener_test.go b/pkg/tunnel/local_listener_test.go index 293fc0c7e..a188307cf 100644 --- a/pkg/tunnel/local_listener_test.go +++ b/pkg/tunnel/local_listener_test.go @@ -69,6 +69,32 @@ func sendAndReceive(t *testing.T, addr string, msg []byte) []byte { return buf } +func waitForListenerClosed(t *testing.T, addr string, timeout time.Duration) { + t.Helper() + deadline := time.NewTimer(timeout) + defer deadline.Stop() + ticker := time.NewTicker(10 * time.Millisecond) + defer ticker.Stop() + var lastErr error + + for { + conn, err := net.DialTimeout("tcp", addr, 50*time.Millisecond) + if err == nil { + _ = conn.Close() + } else if netErr, ok := err.(net.Error); !ok || !netErr.Timeout() { + return + } else { + lastErr = err + } + + select { + case <-deadline.C: + t.Fatalf("listener did not close before deadline; last dial error: %v", lastErr) + case <-ticker.C: + } + } +} + func TestLocalTunnel_ListensOnPort(t *testing.T) { ctx := t.Context() @@ -158,12 +184,7 @@ func TestLocalTunnel_CloseStopsAccepting(t *testing.T) { addr := tun.Addr() _ = tun.Close() - - time.Sleep(50 * time.Millisecond) - _, err = net.DialTimeout("tcp", addr, 500*time.Millisecond) - if err == nil { - t.Error("expected connection to be refused after Close()") - } + waitForListenerClosed(t, addr, 2*time.Second) } func TestLocalTunnel_ContextCancellation(t *testing.T) { @@ -184,22 +205,7 @@ func TestLocalTunnel_ContextCancellation(t *testing.T) { cancel() // The listener is closed asynchronously after cancellation. - deadline := time.After(2 * time.Second) - ticker := time.NewTicker(10 * time.Millisecond) - defer ticker.Stop() - - for { - conn, err := net.DialTimeout("tcp", addr, 50*time.Millisecond) - if err != nil { - return - } - _ = conn.Close() - select { - case <-deadline: - t.Fatal("listener still accepting connections after context cancellation") - case <-ticker.C: - } - } + waitForListenerClosed(t, addr, 2*time.Second) } func TestLocalTunnel_HealthCheckShutdown(t *testing.T) { @@ -217,22 +223,6 @@ func TestLocalTunnel_HealthCheckShutdown(t *testing.T) { } defer func() { _ = tun.Close() }() - // The health check should shut down the tunnel after 3 failures - // 3 * 50ms = 150ms, give generous timeout - deadline := time.After(2 * time.Second) - ticker := time.NewTicker(10 * time.Millisecond) - defer ticker.Stop() - - for { - select { - case <-deadline: - t.Fatal("tunnel did not shut down after health check failures") - case <-ticker.C: - conn, err := net.DialTimeout("tcp", tun.Addr(), 50*time.Millisecond) - if err != nil { - return // tunnel shut down - test passes - } - _ = conn.Close() - } - } + // The health check should shut down the tunnel after 3 failures. + waitForListenerClosed(t, tun.Addr(), 2*time.Second) } From ee68d29d19c9e5c440f88a6bb701fc6380d540b2 Mon Sep 17 00:00:00 2001 From: Samuel K Date: Mon, 21 Sep 2026 23:53:56 -0600 Subject: [PATCH 03/13] fix(secrets): clarify legacy backend remediation --- pkg/secrets/local_store.go | 9 +++++++-- pkg/secrets/store_internal_test.go | 7 +++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/pkg/secrets/local_store.go b/pkg/secrets/local_store.go index 587740575..7af00a507 100644 --- a/pkg/secrets/local_store.go +++ b/pkg/secrets/local_store.go @@ -411,7 +411,11 @@ func (s *localStore) removeFromProbeableBackends(idx *index, key string) error { for _, kind := range []Backend{BackendKeyring, BackendFile} { found, conclusive := s.backends.Probe(kind, idx, key) if !conclusive { - return fmt.Errorf("cannot probe secrets backend %q", kind) + return fmt.Errorf( + "cannot safely delete unowned secret because secrets backend %q could not be probed; "+ + "restore access to that backend or set the secret again", + kind, + ) } if found { present = append(present, kind) @@ -432,7 +436,8 @@ func (s *localStore) removeFromProbeableBackends(idx *index, key string) error { func unownedSecretError(context, name string) error { return fmt.Errorf( "secret %s/%s has no proven owning backend and its value could not be "+ - "located; set it again with `devsy secret set %s` or remove it with "+ + "located; set it again with `devsy secret set %s`; if you intend to "+ + "delete it, first ensure all secret backends are available, then run "+ "`devsy secret delete %s`", context, name, diff --git a/pkg/secrets/store_internal_test.go b/pkg/secrets/store_internal_test.go index 17106bf2d..5cb5dc70d 100644 --- a/pkg/secrets/store_internal_test.go +++ b/pkg/secrets/store_internal_test.go @@ -752,9 +752,12 @@ func TestStore_DeleteUnownedFailsWhenBackendUnprobeable(t *testing.T) { mapBackendRegistry{backends: backends}, ) - require.Error(t, s.Delete(testContext, "LEGACY")) + err := s.Delete(testContext, "LEGACY") + require.Error(t, err) + require.ErrorContains(t, err, `backend "keyring" could not be probed`) + require.ErrorContains(t, err, "restore access") require.Equal(t, "sensitive", backends[BackendFile].values[backendKey(testContext, "LEGACY")]) - _, err := s.Meta(testContext, "LEGACY") + _, err = s.Meta(testContext, "LEGACY") require.NoError(t, err) } From 266f43bdddbcd36459afb5f9ab9da2c92202f520 Mon Sep 17 00:00:00 2001 From: Samuel K Date: Mon, 21 Sep 2026 23:57:43 -0600 Subject: [PATCH 04/13] fix(diagnostics): honor selected runtime environment --- pkg/daemon/agent/daemon.go | 13 +++++++++++-- pkg/daemon/agent/daemon_test.go | 6 +++++- pkg/machinediagnostics/locator.go | 18 ++++++++++++++++-- pkg/machinediagnostics/runtime_paths_test.go | 3 ++- 4 files changed, 34 insertions(+), 6 deletions(-) diff --git a/pkg/daemon/agent/daemon.go b/pkg/daemon/agent/daemon.go index 2c866b3d1..97528f8e2 100644 --- a/pkg/daemon/agent/daemon.go +++ b/pkg/daemon/agent/daemon.go @@ -404,11 +404,20 @@ func startFallbackDaemon( } func fallbackDaemonEnv(runtimePaths machinediagnostics.RuntimePaths) []string { + baseEnv := os.Environ() + filteredEnv := baseEnv[:0] + for _, entry := range baseEnv { + if strings.HasPrefix(entry, machinediagnostics.RuntimeLockPathEnv+"=") || + strings.HasPrefix(entry, machinediagnostics.RuntimeLocatorPathEnv+"=") { + continue + } + filteredEnv = append(filteredEnv, entry) + } if runtimePaths.LockPath == machinediagnostics.DefaultRuntimeLockPath && runtimePaths.LocatorPath == machinediagnostics.DefaultLocatorPath { - return nil + return filteredEnv } - return append(os.Environ(), + return append(filteredEnv, machinediagnostics.RuntimeLockPathEnv+"="+runtimePaths.LockPath, machinediagnostics.RuntimeLocatorPathEnv+"="+runtimePaths.LocatorPath, ) diff --git a/pkg/daemon/agent/daemon_test.go b/pkg/daemon/agent/daemon_test.go index df8917467..cecb60521 100644 --- a/pkg/daemon/agent/daemon_test.go +++ b/pkg/daemon/agent/daemon_test.go @@ -49,6 +49,8 @@ func TestFallbackRuntimePaths(t *testing.T) { } func TestFallbackDaemonEnvPropagatesBothRuntimePaths(t *testing.T) { + t.Setenv(machinediagnostics.RuntimeLockPathEnv, "/inherited/lock") + t.Setenv(machinediagnostics.RuntimeLocatorPathEnv, "/inherited/locator") env := fallbackDaemonEnv(machinediagnostics.RuntimePaths{ LockPath: "/cache/devsy/agent-daemon.lock", LocatorPath: "/cache/devsy/agent-daemon.json", @@ -56,7 +58,9 @@ func TestFallbackDaemonEnvPropagatesBothRuntimePaths(t *testing.T) { assert.Contains(t, env, "DEVSY_DAEMON_RUNTIME_LOCK_PATH=/cache/devsy/agent-daemon.lock") assert.Contains(t, env, "DEVSY_DAEMON_LOCATOR_PATH=/cache/devsy/agent-daemon.json") - assert.Nil(t, fallbackDaemonEnv(machinediagnostics.SystemRuntimePaths())) + systemEnv := fallbackDaemonEnv(machinediagnostics.SystemRuntimePaths()) + assert.NotContains(t, systemEnv, machinediagnostics.RuntimeLockPathEnv+"=/inherited/lock") + assert.NotContains(t, systemEnv, machinediagnostics.RuntimeLocatorPathEnv+"=/inherited/locator") } func TestDaemonUnitStateLocationMatchesExactArgumentValues(t *testing.T) { diff --git a/pkg/machinediagnostics/locator.go b/pkg/machinediagnostics/locator.go index 14e514203..c46ac0277 100644 --- a/pkg/machinediagnostics/locator.go +++ b/pkg/machinediagnostics/locator.go @@ -111,11 +111,25 @@ func ReadFromLocator(path string, options ReadOptions) ReadResponse { // inaccessible system locator permits trying the per-user fallback; a corrupt // system locator is authoritative and is never masked by stale fallback data. func ReadActive(options ReadOptions, userCacheDir func() (string, error)) (ReadResponse, error) { - paths, err := ActiveLocatorCandidates(userCacheDir) + system := readLocatorCandidate(DefaultLocatorPath, options) + if system.state == locatorCandidateSuccess || system.state == locatorCandidateCorrupt { + return system.response, nil + } + userPaths, err := UserRuntimePaths(userCacheDir) if err != nil { return ReadResponse{}, err } - return readActiveFromCandidates(paths, options), nil + user := readLocatorCandidate(userPaths.LocatorPath, options) + if user.state == locatorCandidateSuccess { + return user.response, nil + } + if system.state == locatorCandidatePermission { + return ReadFromLocator(DefaultLocatorPath, options), nil + } + if user.state == locatorCandidateCorrupt { + return user.response, nil + } + return ReadFromLocator(DefaultLocatorPath, options), nil } func ActiveLocatorCandidates(userCacheDir func() (string, error)) ([]string, error) { diff --git a/pkg/machinediagnostics/runtime_paths_test.go b/pkg/machinediagnostics/runtime_paths_test.go index 770641572..efbb30b0c 100644 --- a/pkg/machinediagnostics/runtime_paths_test.go +++ b/pkg/machinediagnostics/runtime_paths_test.go @@ -28,7 +28,8 @@ func TestRuntimePaths(t *testing.T) { func TestEnsureRuntimeDirRepairsSharedPermissions(t *testing.T) { dir := filepath.Join(t.TempDir(), "runtime") require.NoError(t, os.MkdirAll(dir, 0o750)) - require.NoError(t, os.Chmod(dir, 0o750)) + err := os.Chmod(dir, 0o750) //nolint:gosec // regression fixture. + require.NoError(t, err) require.NoError(t, EnsureRuntimeDir(dir, true)) info, err := os.Stat(dir) From a8bd78319cbb4a3db6b6f741cae1e3cd21c392cc Mon Sep 17 00:00:00 2001 From: Samuel K Date: Tue, 22 Sep 2026 23:23:28 -0600 Subject: [PATCH 05/13] fix(diagnostics): ignore stale daemon locators --- cmd/internal/agent_daemon.go | 6 ++-- pkg/machinediagnostics/locator.go | 42 ++++++++++++++++++---------- pkg/machinediagnostics/store_test.go | 12 ++++++++ 3 files changed, 44 insertions(+), 16 deletions(-) diff --git a/cmd/internal/agent_daemon.go b/cmd/internal/agent_daemon.go index 314af52c1..395e6097b 100644 --- a/cmd/internal/agent_daemon.go +++ b/cmd/internal/agent_daemon.go @@ -29,8 +29,10 @@ const ( busyGracePeriod = 20 * time.Minute ) -var daemonRuntimeLockPath = machinediagnostics.DefaultRuntimeLockPath -var daemonLocatorPath = machinediagnostics.DefaultLocatorPath +var ( + daemonRuntimeLockPath = machinediagnostics.DefaultRuntimeLockPath + daemonLocatorPath = machinediagnostics.DefaultLocatorPath +) type DaemonCmd struct { *flags.GlobalFlags diff --git a/pkg/machinediagnostics/locator.go b/pkg/machinediagnostics/locator.go index c46ac0277..b0da3b466 100644 --- a/pkg/machinediagnostics/locator.go +++ b/pkg/machinediagnostics/locator.go @@ -201,23 +201,37 @@ type locatorCandidate struct { func readLocatorCandidate(path string, options ReadOptions) locatorCandidate { locator, err := ReadLocator(path) - if err == nil { - return locatorCandidate{ - response: Read(locator.DiagnosticsDir, options), - state: locatorCandidateSuccess, + if err != nil { + switch { + case os.IsNotExist(err): + return locatorCandidate{state: locatorCandidateMissing} + case os.IsPermission(err): + return locatorCandidate{ + response: ReadFromLocator(path, options), + state: locatorCandidatePermission, + } + default: + return locatorCandidate{ + response: ReadFromLocator(path, options), + state: locatorCandidateCorrupt, + } } } - if os.IsNotExist(err) { - return locatorCandidate{state: locatorCandidateMissing} - } - if os.IsPermission(err) { - return locatorCandidate{ - response: ReadFromLocator(path, options), - state: locatorCandidatePermission, + return classifyLocatorResponse(Read(locator.DiagnosticsDir, options)) +} + +func classifyLocatorResponse(response ReadResponse) locatorCandidate { + if response.Availability == AvailabilityAvailable && response.Status != nil { + if response.Status.State == DaemonStopping { + return locatorCandidate{state: locatorCandidateMissing} } + return locatorCandidate{response: response, state: locatorCandidateSuccess} + } + if response.Availability == AvailabilityNotInitialized { + return locatorCandidate{state: locatorCandidateMissing} } - return locatorCandidate{ - response: ReadFromLocator(path, options), - state: locatorCandidateCorrupt, + if response.Availability == AvailabilityPermissionDenied { + return locatorCandidate{response: response, state: locatorCandidatePermission} } + return locatorCandidate{response: response, state: locatorCandidateCorrupt} } diff --git a/pkg/machinediagnostics/store_test.go b/pkg/machinediagnostics/store_test.go index d9ec923e9..b96521bb6 100644 --- a/pkg/machinediagnostics/store_test.go +++ b/pkg/machinediagnostics/store_test.go @@ -235,6 +235,18 @@ func TestReadActiveLocatorCandidates(t *testing.T) { } } +func TestReadActiveStaleSystemFallsBackToCurrentUser(t *testing.T) { + options := ReadOptions{Limit: 10, Interval: time.Minute, Now: time.Now()} + systemDir := newTestDiagnosticsStore(t, DaemonStopping) + userDir := newTestDiagnosticsStore(t, DaemonRunning) + system := writeTestLocator(t, t.TempDir(), systemDir) + user := writeTestLocator(t, t.TempDir(), userDir) + + response := readActiveFromCandidates([]string{system, user}, options) + require.NotNil(t, response.Status) + assert.Equal(t, DaemonRunning, response.Status.State) +} + func TestRuntimeLockIsExclusive(t *testing.T) { path := filepath.Join(t.TempDir(), "run", "daemon.lock") first, err := AcquireRuntimeLock(path) From 571792e37274fbcc7c7074e29f77d585a8d1bee6 Mon Sep 17 00:00:00 2001 From: Samuel K Date: Tue, 22 Sep 2026 23:34:34 -0600 Subject: [PATCH 06/13] style(daemon): group runtime path variables Signed-off-by: Samuel K From 76f5e576c262e05eb3d52ba359ff4166cd0527c0 Mon Sep 17 00:00:00 2001 From: Samuel K Date: Wed, 23 Sep 2026 08:38:46 -0600 Subject: [PATCH 07/13] fix(diagnostics): prefer fresh locator snapshots --- pkg/machinediagnostics/locator.go | 17 ++++++++-- pkg/machinediagnostics/store_test.go | 51 ++++++++++++++++++++++------ 2 files changed, 56 insertions(+), 12 deletions(-) diff --git a/pkg/machinediagnostics/locator.go b/pkg/machinediagnostics/locator.go index b0da3b466..4987479cd 100644 --- a/pkg/machinediagnostics/locator.go +++ b/pkg/machinediagnostics/locator.go @@ -129,6 +129,9 @@ func ReadActive(options ReadOptions, userCacheDir func() (string, error)) (ReadR if user.state == locatorCandidateCorrupt { return user.response, nil } + if system.state == locatorCandidateStale { + return system.response, nil + } return ReadFromLocator(DefaultLocatorPath, options), nil } @@ -146,6 +149,7 @@ func ActiveLocatorCandidates(userCacheDir func() (string, error)) ([]string, err func readActiveFromCandidates(paths []string, options ReadOptions) ReadResponse { var permissionResponse ReadResponse + var staleResponse ReadResponse for i, path := range paths { candidate := readLocatorCandidate(path, options) response, done, updatedPermission := applyLocatorCandidate( @@ -157,10 +161,16 @@ func readActiveFromCandidates(paths []string, options ReadOptions) ReadResponse return response } permissionResponse = updatedPermission + if candidate.state == locatorCandidateStale && staleResponse.Availability == "" { + staleResponse = candidate.response + } } if permissionResponse.Availability != "" { return permissionResponse } + if staleResponse.Availability != "" { + return staleResponse + } return ReadFromLocator(DefaultLocatorPath, options) } @@ -181,6 +191,8 @@ func applyLocatorCandidate( if permissionResponse.Availability == "" { permissionResponse = candidate.response } + case locatorCandidateStale: + return ReadResponse{}, false, permissionResponse } return ReadResponse{}, false, permissionResponse } @@ -192,6 +204,7 @@ const ( locatorCandidatePermission locatorCandidateCorrupt locatorCandidateSuccess + locatorCandidateStale ) type locatorCandidate struct { @@ -222,8 +235,8 @@ func readLocatorCandidate(path string, options ReadOptions) locatorCandidate { func classifyLocatorResponse(response ReadResponse) locatorCandidate { if response.Availability == AvailabilityAvailable && response.Status != nil { - if response.Status.State == DaemonStopping { - return locatorCandidate{state: locatorCandidateMissing} + if response.Status.State == DaemonStopping || response.Freshness == FreshnessStale { + return locatorCandidate{response: response, state: locatorCandidateStale} } return locatorCandidate{response: response, state: locatorCandidateSuccess} } diff --git a/pkg/machinediagnostics/store_test.go b/pkg/machinediagnostics/store_test.go index b96521bb6..9dc68d80d 100644 --- a/pkg/machinediagnostics/store_test.go +++ b/pkg/machinediagnostics/store_test.go @@ -155,6 +155,10 @@ func TestLocatorRoundTrip(t *testing.T) { } func newTestDiagnosticsStore(t *testing.T, state DaemonState) string { + return newTestDiagnosticsStoreAt(t, state, time.Now().UTC()) +} + +func newTestDiagnosticsStoreAt(t *testing.T, state DaemonState, updatedAt time.Time) string { t.Helper() dir := filepath.Join(t.TempDir(), "diagnostics") store, err := NewRecorder(Options{ @@ -162,7 +166,7 @@ func newTestDiagnosticsStore(t *testing.T, state DaemonState) string { Reader: ReaderIdentity{UID: os.Getuid(), GID: os.Getgid()}, }) require.NoError(t, err) - store.Update(Status{State: state, Health: DaemonHealthy}) + store.Update(Status{State: state, Health: DaemonHealthy, UpdatedAt: updatedAt}) return dir } @@ -177,11 +181,12 @@ func writeTestLocator(t *testing.T, dir, diagnosticsDir string) string { } func TestReadActiveLocatorCandidates(t *testing.T) { - options := ReadOptions{Limit: 10, Interval: time.Minute, Now: time.Now()} + now := time.Date(2026, 9, 23, 12, 0, 0, 0, time.UTC) + options := ReadOptions{Limit: 10, Interval: time.Minute, Now: now} t.Run("system wins", func(t *testing.T) { - systemDir := newTestDiagnosticsStore(t, DaemonRunning) - userDir := newTestDiagnosticsStore(t, DaemonStopping) + systemDir := newTestDiagnosticsStoreAt(t, DaemonRunning, now) + userDir := newTestDiagnosticsStoreAt(t, DaemonRunning, now) system := writeTestLocator(t, t.TempDir(), systemDir) user := writeTestLocator(t, t.TempDir(), userDir) response := readActiveFromCandidates([]string{system, user}, options) @@ -189,7 +194,7 @@ func TestReadActiveLocatorCandidates(t *testing.T) { }) t.Run("user fallback", func(t *testing.T) { - userDir := newTestDiagnosticsStore(t, DaemonRunning) + userDir := newTestDiagnosticsStoreAt(t, DaemonRunning, now) user := writeTestLocator(t, t.TempDir(), userDir) response := readActiveFromCandidates( []string{filepath.Join(t.TempDir(), "missing"), user}, @@ -201,7 +206,7 @@ func TestReadActiveLocatorCandidates(t *testing.T) { t.Run("corrupt system is authoritative", func(t *testing.T) { system := filepath.Join(t.TempDir(), "system.json") require.NoError(t, os.WriteFile(system, []byte("{"), 0o600)) - userDir := newTestDiagnosticsStore(t, DaemonRunning) + userDir := newTestDiagnosticsStoreAt(t, DaemonRunning, now) user := writeTestLocator(t, t.TempDir(), userDir) response := readActiveFromCandidates([]string{system, user}, options) assert.Equal(t, AvailabilityCorrupt, response.Availability) @@ -226,7 +231,7 @@ func TestReadActiveLocatorCandidates(t *testing.T) { if _, err := ReadLocator(system); !os.IsPermission(err) { t.Skip("platform does not expose permission-denied file reads") } - userDir := newTestDiagnosticsStore(t, DaemonRunning) + userDir := newTestDiagnosticsStoreAt(t, DaemonRunning, now) user := writeTestLocator(t, t.TempDir(), userDir) response := readActiveFromCandidates([]string{system, user}, options) require.NotNil(t, response.Status) @@ -235,10 +240,24 @@ func TestReadActiveLocatorCandidates(t *testing.T) { } } +func TestReadActiveStoppingSystemFallsBackToCurrentUser(t *testing.T) { + now := time.Date(2026, 9, 23, 12, 0, 0, 0, time.UTC) + options := ReadOptions{Limit: 10, Interval: time.Minute, Now: now} + systemDir := newTestDiagnosticsStoreAt(t, DaemonStopping, now) + userDir := newTestDiagnosticsStoreAt(t, DaemonRunning, now) + system := writeTestLocator(t, t.TempDir(), systemDir) + user := writeTestLocator(t, t.TempDir(), userDir) + + response := readActiveFromCandidates([]string{system, user}, options) + require.NotNil(t, response.Status) + assert.Equal(t, DaemonRunning, response.Status.State) +} + func TestReadActiveStaleSystemFallsBackToCurrentUser(t *testing.T) { - options := ReadOptions{Limit: 10, Interval: time.Minute, Now: time.Now()} - systemDir := newTestDiagnosticsStore(t, DaemonStopping) - userDir := newTestDiagnosticsStore(t, DaemonRunning) + now := time.Date(2026, 9, 23, 12, 0, 0, 0, time.UTC) + options := ReadOptions{Limit: 10, Interval: time.Minute, Now: now} + systemDir := newTestDiagnosticsStoreAt(t, DaemonRunning, now.Add(-5*time.Minute)) + userDir := newTestDiagnosticsStoreAt(t, DaemonRunning, now) system := writeTestLocator(t, t.TempDir(), systemDir) user := writeTestLocator(t, t.TempDir(), userDir) @@ -247,6 +266,18 @@ func TestReadActiveStaleSystemFallsBackToCurrentUser(t *testing.T) { assert.Equal(t, DaemonRunning, response.Status.State) } +func TestReadActiveStaleSystemIsRetainedWithoutFreshFallback(t *testing.T) { + now := time.Date(2026, 9, 23, 12, 0, 0, 0, time.UTC) + options := ReadOptions{Limit: 10, Interval: time.Minute, Now: now} + systemDir := newTestDiagnosticsStoreAt(t, DaemonRunning, now.Add(-5*time.Minute)) + system := writeTestLocator(t, t.TempDir(), systemDir) + + response := readActiveFromCandidates([]string{system}, options) + require.NotNil(t, response.Status) + assert.Equal(t, DaemonRunning, response.Status.State) + assert.Equal(t, FreshnessStale, response.Freshness) +} + func TestRuntimeLockIsExclusive(t *testing.T) { path := filepath.Join(t.TempDir(), "run", "daemon.lock") first, err := AcquireRuntimeLock(path) From 65b9f34df88010f1b5b0a0946b6058b4fcb4ba9c Mon Sep 17 00:00:00 2001 From: Samuel K Date: Wed, 23 Sep 2026 11:18:41 -0600 Subject: [PATCH 08/13] fix(audit): resolve locator and listener review findings --- pkg/machinediagnostics/locator.go | 26 +++++--------------- pkg/machinediagnostics/runtime_paths_test.go | 6 ++++- pkg/machinediagnostics/store_test.go | 10 ++++++-- pkg/tunnel/local_listener_test.go | 8 ++++-- 4 files changed, 25 insertions(+), 25 deletions(-) diff --git a/pkg/machinediagnostics/locator.go b/pkg/machinediagnostics/locator.go index 4987479cd..1187825f5 100644 --- a/pkg/machinediagnostics/locator.go +++ b/pkg/machinediagnostics/locator.go @@ -111,28 +111,11 @@ func ReadFromLocator(path string, options ReadOptions) ReadResponse { // inaccessible system locator permits trying the per-user fallback; a corrupt // system locator is authoritative and is never masked by stale fallback data. func ReadActive(options ReadOptions, userCacheDir func() (string, error)) (ReadResponse, error) { - system := readLocatorCandidate(DefaultLocatorPath, options) - if system.state == locatorCandidateSuccess || system.state == locatorCandidateCorrupt { - return system.response, nil - } - userPaths, err := UserRuntimePaths(userCacheDir) + paths, err := ActiveLocatorCandidates(userCacheDir) if err != nil { - return ReadResponse{}, err - } - user := readLocatorCandidate(userPaths.LocatorPath, options) - if user.state == locatorCandidateSuccess { - return user.response, nil - } - if system.state == locatorCandidatePermission { - return ReadFromLocator(DefaultLocatorPath, options), nil + return readActiveFromCandidates([]string{DefaultLocatorPath}, options), nil } - if user.state == locatorCandidateCorrupt { - return user.response, nil - } - if system.state == locatorCandidateStale { - return system.response, nil - } - return ReadFromLocator(DefaultLocatorPath, options), nil + return readActiveFromCandidates(paths, options), nil } func ActiveLocatorCandidates(userCacheDir func() (string, error)) ([]string, error) { @@ -171,6 +154,9 @@ func readActiveFromCandidates(paths []string, options ReadOptions) ReadResponse if staleResponse.Availability != "" { return staleResponse } + if len(paths) > 0 { + return ReadFromLocator(paths[0], options) + } return ReadFromLocator(DefaultLocatorPath, options) } diff --git a/pkg/machinediagnostics/runtime_paths_test.go b/pkg/machinediagnostics/runtime_paths_test.go index efbb30b0c..284203418 100644 --- a/pkg/machinediagnostics/runtime_paths_test.go +++ b/pkg/machinediagnostics/runtime_paths_test.go @@ -43,5 +43,9 @@ func TestEnsureRuntimeDirKeepsUserRuntimePrivate(t *testing.T) { info, err := os.Stat(dir) require.NoError(t, err) - require.Equal(t, os.FileMode(0o750), info.Mode().Perm()) + require.Zero( + t, + info.Mode().Perm()&^os.FileMode(0o750), + "user runtime dir must not be world-accessible", + ) } diff --git a/pkg/machinediagnostics/store_test.go b/pkg/machinediagnostics/store_test.go index 9dc68d80d..6ce0b0548 100644 --- a/pkg/machinediagnostics/store_test.go +++ b/pkg/machinediagnostics/store_test.go @@ -190,7 +190,11 @@ func TestReadActiveLocatorCandidates(t *testing.T) { system := writeTestLocator(t, t.TempDir(), systemDir) user := writeTestLocator(t, t.TempDir(), userDir) response := readActiveFromCandidates([]string{system, user}, options) - assert.Equal(t, DaemonRunning, response.Status.State) + systemResponse := Read(systemDir, options) + userResponse := Read(userDir, options) + require.NotNil(t, response.Status) + assert.Equal(t, systemResponse.Status.SessionID, response.Status.SessionID) + assert.NotEqual(t, userResponse.Status.SessionID, response.Status.SessionID) }) t.Run("user fallback", func(t *testing.T) { @@ -263,7 +267,9 @@ func TestReadActiveStaleSystemFallsBackToCurrentUser(t *testing.T) { response := readActiveFromCandidates([]string{system, user}, options) require.NotNil(t, response.Status) - assert.Equal(t, DaemonRunning, response.Status.State) + userResponse := Read(userDir, options) + assert.Equal(t, userResponse.Status.SessionID, response.Status.SessionID) + assert.Equal(t, FreshnessFresh, response.Freshness) } func TestReadActiveStaleSystemIsRetainedWithoutFreshFallback(t *testing.T) { diff --git a/pkg/tunnel/local_listener_test.go b/pkg/tunnel/local_listener_test.go index a188307cf..1c1d7c8cd 100644 --- a/pkg/tunnel/local_listener_test.go +++ b/pkg/tunnel/local_listener_test.go @@ -2,10 +2,12 @@ package tunnel import ( "context" + "errors" "fmt" "io" "net" "sync" + "syscall" "testing" "time" ) @@ -81,10 +83,12 @@ func waitForListenerClosed(t *testing.T, addr string, timeout time.Duration) { conn, err := net.DialTimeout("tcp", addr, 50*time.Millisecond) if err == nil { _ = conn.Close() - } else if netErr, ok := err.(net.Error); !ok || !netErr.Timeout() { + } else if errors.Is(err, syscall.ECONNREFUSED) { return - } else { + } else if netErr, ok := err.(net.Error); ok && netErr.Timeout() { lastErr = err + } else { + t.Fatalf("listener dial failed before closure: %v", err) } select { From f0b70bff0a304a8212a8fbe312875b438547aa8b Mon Sep 17 00:00:00 2001 From: Samuel K Date: Wed, 23 Sep 2026 14:09:46 -0600 Subject: [PATCH 09/13] test(tunnel): accept connection reset as listener closed Signed-off-by: Samuel K --- pkg/tunnel/local_listener_test.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/tunnel/local_listener_test.go b/pkg/tunnel/local_listener_test.go index 1c1d7c8cd..528b2dc0e 100644 --- a/pkg/tunnel/local_listener_test.go +++ b/pkg/tunnel/local_listener_test.go @@ -83,7 +83,9 @@ func waitForListenerClosed(t *testing.T, addr string, timeout time.Duration) { conn, err := net.DialTimeout("tcp", addr, 50*time.Millisecond) if err == nil { _ = conn.Close() - } else if errors.Is(err, syscall.ECONNREFUSED) { + } else if errors.Is(err, syscall.ECONNREFUSED) || errors.Is(err, syscall.ECONNRESET) { + // Refused or reset both mean the dial never reached a live + // listener; macOS reports reset during the shutdown race. return } else if netErr, ok := err.(net.Error); ok && netErr.Timeout() { lastErr = err From 1ac4908011a13ad03774082d23f4c8150bed1311 Mon Sep 17 00:00:00 2001 From: Samuel K Date: Wed, 23 Sep 2026 14:20:16 -0600 Subject: [PATCH 10/13] test(tunnel): extract listener-closed check for lint Signed-off-by: Samuel K --- pkg/tunnel/local_listener_test.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/pkg/tunnel/local_listener_test.go b/pkg/tunnel/local_listener_test.go index 528b2dc0e..dddc3c9fc 100644 --- a/pkg/tunnel/local_listener_test.go +++ b/pkg/tunnel/local_listener_test.go @@ -71,6 +71,12 @@ func sendAndReceive(t *testing.T, addr string, msg []byte) []byte { return buf } +// listenerDialClosed reports whether a dial error means no live listener +// remains: refused, or reset, which macOS returns during the shutdown race. +func listenerDialClosed(err error) bool { + return errors.Is(err, syscall.ECONNREFUSED) || errors.Is(err, syscall.ECONNRESET) +} + func waitForListenerClosed(t *testing.T, addr string, timeout time.Duration) { t.Helper() deadline := time.NewTimer(timeout) @@ -83,9 +89,7 @@ func waitForListenerClosed(t *testing.T, addr string, timeout time.Duration) { conn, err := net.DialTimeout("tcp", addr, 50*time.Millisecond) if err == nil { _ = conn.Close() - } else if errors.Is(err, syscall.ECONNREFUSED) || errors.Is(err, syscall.ECONNRESET) { - // Refused or reset both mean the dial never reached a live - // listener; macOS reports reset during the shutdown race. + } else if listenerDialClosed(err) { return } else if netErr, ok := err.(net.Error); ok && netErr.Timeout() { lastErr = err From a44cffa774cb8d1781eb0cde6d864ef2398c6b87 Mon Sep 17 00:00:00 2001 From: Samuel K Date: Wed, 23 Sep 2026 15:29:33 -0600 Subject: [PATCH 11/13] fix(diagnostics): surface fallback discovery error in ReadActive Signed-off-by: Samuel K --- pkg/machinediagnostics/locator.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pkg/machinediagnostics/locator.go b/pkg/machinediagnostics/locator.go index 1187825f5..888344f67 100644 --- a/pkg/machinediagnostics/locator.go +++ b/pkg/machinediagnostics/locator.go @@ -113,7 +113,14 @@ func ReadFromLocator(path string, options ReadOptions) ReadResponse { func ReadActive(options ReadOptions, userCacheDir func() (string, error)) (ReadResponse, error) { paths, err := ActiveLocatorCandidates(userCacheDir) if err != nil { - return readActiveFromCandidates([]string{DefaultLocatorPath}, options), nil + // The per-user fallback cannot be discovered. The system locator + // still answers when present; otherwise surface the discovery + // failure instead of reporting a bare NotInitialized. + response := readActiveFromCandidates([]string{DefaultLocatorPath}, options) + if response.Availability != AvailabilityNotInitialized { + return response, nil + } + return ReadResponse{}, err } return readActiveFromCandidates(paths, options), nil } From eb73a93229113a09cea6882922b543b0da192314 Mon Sep 17 00:00:00 2001 From: Samuel K Date: Wed, 23 Sep 2026 15:45:51 -0600 Subject: [PATCH 12/13] docs(diagnostics): trim comments to match house style Signed-off-by: Samuel K --- pkg/machinediagnostics/locator.go | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/pkg/machinediagnostics/locator.go b/pkg/machinediagnostics/locator.go index 888344f67..7243ca086 100644 --- a/pkg/machinediagnostics/locator.go +++ b/pkg/machinediagnostics/locator.go @@ -107,15 +107,12 @@ func ReadFromLocator(path string, options ReadOptions) ReadResponse { return Read(locator.DiagnosticsDir, options) } -// ReadActive reads the highest-priority active daemon locator. A missing or -// inaccessible system locator permits trying the per-user fallback; a corrupt -// system locator is authoritative and is never masked by stale fallback data. +// ReadActive reads the highest-priority active daemon locator, falling +// through to the per-user fallback unless the system locator is corrupt. func ReadActive(options ReadOptions, userCacheDir func() (string, error)) (ReadResponse, error) { paths, err := ActiveLocatorCandidates(userCacheDir) if err != nil { - // The per-user fallback cannot be discovered. The system locator - // still answers when present; otherwise surface the discovery - // failure instead of reporting a bare NotInitialized. + // Surface the discovery failure when no system locator exists. response := readActiveFromCandidates([]string{DefaultLocatorPath}, options) if response.Availability != AvailabilityNotInitialized { return response, nil From cbef16146634d2e99e0a7b5e1c3133fc312a80a0 Mon Sep 17 00:00:00 2001 From: Samuel K Date: Wed, 23 Sep 2026 15:46:20 -0600 Subject: [PATCH 13/13] docs(diagnostics): trim comments to match house style Signed-off-by: Samuel K --- pkg/machinediagnostics/runtime_paths.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/machinediagnostics/runtime_paths.go b/pkg/machinediagnostics/runtime_paths.go index 57d432612..a1a661152 100644 --- a/pkg/machinediagnostics/runtime_paths.go +++ b/pkg/machinediagnostics/runtime_paths.go @@ -38,8 +38,8 @@ func runtimePaths(dir string) RuntimePaths { } } -// EnsureRuntimeDir creates a runtime directory. Shared system runtime -// directories are also repaired when they predate the current permissions. +// EnsureRuntimeDir creates a runtime directory, repairing permissions on +// shared system directories that predate them. func EnsureRuntimeDir(path string, shared bool) error { mode := os.FileMode(0o750) if shared {