From 0b679ccfb188529bc18d3a0eccb46ea834bbf050 Mon Sep 17 00:00:00 2001 From: Michael Neale Date: Tue, 2 Jun 2026 15:43:31 +1000 Subject: [PATCH] fix(serverless): agent connects to first relay of comma list (DNS crash) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ACP harness is single-relay but serverless workspaces pass a comma-separated relay LIST (wss://a,wss://b,...). connect_with_mode fed the raw comma string to connect_async, which failed DNS lookup ("nodename nor servname provided") and crashed the agent before it ever subscribed — so it never saw mentions and never responded. Split to the first (primary) relay, matching the desktop live-WS convention. Regression-covered by the live agent test now using a comma-separated RELAY_URL. --- crates/sprout-acp/src/relay.rs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/crates/sprout-acp/src/relay.rs b/crates/sprout-acp/src/relay.rs index e824b34ef..a443a3f5a 100644 --- a/crates/sprout-acp/src/relay.rs +++ b/crates/sprout-acp/src/relay.rs @@ -669,6 +669,13 @@ impl HarnessRelay { auth_tag: Option, serverless: bool, ) -> Result { + // Serverless workspaces pass a comma-separated relay LIST + // (`wss://a,wss://b,...`). The harness is single-relay: connect to the + // first (primary) relay — connecting to the raw comma string fails DNS + // ("nodename nor servname provided"). The desktop client uses the same + // first-relay convention for its live subscription. + let relay_url = relay_url.split(',').next().unwrap_or(relay_url).trim(); + // Perform the initial connection and auth handshake. // Finding #8: capture the handshake buffer and pass it to the background // task so buffered messages aren't silently discarded. @@ -3812,8 +3819,11 @@ mod tests { use nostr::{EventBuilder, Keys, Kind, Tag}; let _ = rustls::crypto::ring::default_provider().install_default(); - let relay_url = - std::env::var("RELAY_URL").unwrap_or_else(|_| "wss://relay.damus.io".to_string()); + // Pass a COMMA-SEPARATED relay list (as serverless workspaces do) to + // regress the DNS-crash bug where the harness connected to the raw + // comma string. connect_with_mode must split to the first relay. + let relay_url = std::env::var("RELAY_URL") + .unwrap_or_else(|_| "wss://relay.damus.io,wss://nos.lol".to_string()); // The "agent" identity — connects in serverless mode like a managed agent. let agent = Keys::generate(); @@ -3855,7 +3865,9 @@ mod tests { { use futures_util::SinkExt; use tokio_tungstenite::{connect_async, tungstenite::Message}; - let (ws, _) = connect_async(&relay_url).await.expect("sender connect"); + // Sender publishes to the same first relay the agent connects to. + let sender_relay = relay_url.split(',').next().unwrap_or(&relay_url).trim(); + let (ws, _) = connect_async(sender_relay).await.expect("sender connect"); let (mut write, _read) = ws.split(); let ev = serde_json::json!(["EVENT", wrap]).to_string(); write