From cb25988c826d572dbbd9a485e3052f342255f008 Mon Sep 17 00:00:00 2001 From: msmps <7691252+msmps@users.noreply.github.com> Date: Sat, 11 Jul 2026 15:36:46 +0100 Subject: [PATCH] fix(daemon): don't touch the env-derived socket dir when binding to explicit paths MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit bind_to() unconditionally called paths::ensure_socket_dir(), which resolves the global socket directory from PILOTTY_SOCKET_DIR, XDG_RUNTIME_DIR, or HOME. The paths tests mutate those env vars under a mutex that only serializes the paths tests against each other — env vars are process-global, so a daemon test calling bind_to in parallel could read a poisoned value and fail with 'Read-only file system' while creating the directory. Seen intermittently in CI on both platforms (test_client_connects_to_running_daemon). bind_to receives explicit paths and already creates the socket's parent directory; preparing the env-derived default directory belongs in bind(), the production entry point. Moving it removes bind_to's dependency on global environment state entirely. Co-Authored-By: Claude Fable 5 --- crates/pilotty-cli/src/daemon/server.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/crates/pilotty-cli/src/daemon/server.rs b/crates/pilotty-cli/src/daemon/server.rs index 45c87fb..8ceb88f 100644 --- a/crates/pilotty-cli/src/daemon/server.rs +++ b/crates/pilotty-cli/src/daemon/server.rs @@ -45,6 +45,10 @@ pub struct DaemonServer { impl DaemonServer { /// Create a new daemon server bound to the default socket path. pub async fn bind() -> Result { + // The default socket directory is derived from the environment and + // needs 0700 permissions; explicit-path binds (bind_to) must not + // touch it, so it is prepared here rather than in bind_to. + paths::ensure_socket_dir().context("Failed to create socket directory")?; let socket_path = paths::get_socket_path(None); let pid_path = paths::get_pid_path(None); Self::bind_to(socket_path, pid_path).await @@ -58,9 +62,6 @@ impl DaemonServer { /// 3. If daemon dead, remove stale socket and retry /// 4. If daemon alive, return error pub async fn bind_to(socket_path: PathBuf, pid_path: PathBuf) -> Result { - // Ensure socket directory exists with secure permissions (0700) - paths::ensure_socket_dir().context("Failed to create socket directory")?; - if let Some(parent) = socket_path.parent() { std::fs::create_dir_all(parent).with_context(|| { format!("Failed to create socket directory for {:?}", socket_path)