From 5fe1583655cccde7b4774b1b1dd4018146a15055 Mon Sep 17 00:00:00 2001 From: Alec Rubin <8213066+alecrubin@users.noreply.github.com> Date: Thu, 10 Sep 2026 16:58:12 -0400 Subject: [PATCH] fix(agent): don't warn about unset HUB_URL in SSH-only mode (#2316) --- agent/client.go | 7 ++++++- agent/client_test.go | 22 ++++++++++++++++++++++ agent/connection_manager.go | 10 +++++++++- 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/agent/client.go b/agent/client.go index 9b1351f6..292f6307 100644 --- a/agent/client.go +++ b/agent/client.go @@ -30,6 +30,11 @@ const ( wsDeadline = 120 * time.Second ) +// errNoHubURL is returned when HUB_URL is unset. This is not a failure +// condition: an agent configured with only a public key runs in SSH-only mode, +// where the hub dials the agent and no outbound WebSocket client is expected. +var errNoHubURL = errors.New("HUB_URL environment variable not set") + type caCertFileError struct { err error } @@ -63,7 +68,7 @@ type WebSocketClient struct { func newWebSocketClient(agent *Agent) (client *WebSocketClient, err error) { hubURLStr, exists := utils.GetEnv("HUB_URL") if !exists { - return nil, errors.New("HUB_URL environment variable not set") + return nil, errNoHubURL } client = &WebSocketClient{} diff --git a/agent/client_test.go b/agent/client_test.go index 97cb6654..0478ac7c 100644 --- a/agent/client_test.go +++ b/agent/client_test.go @@ -32,6 +32,28 @@ import ( "golang.org/x/crypto/ssh" ) +// TestNewWebSocketClientNoHubURL verifies that an unset HUB_URL returns the +// errNoHubURL sentinel rather than an opaque error. Callers rely on this to +// distinguish SSH-only mode -- a supported configuration in which the hub dials +// the agent -- from an actual misconfiguration. +func TestNewWebSocketClientNoHubURL(t *testing.T) { + agent := createTestAgent(t) + + // t.Setenv registers restoration of the original value; unset afterwards so + // GetEnv's LookupEnv reports the variable as absent rather than empty. + t.Setenv("BESZEL_AGENT_HUB_URL", "") + os.Unsetenv("BESZEL_AGENT_HUB_URL") + t.Setenv("HUB_URL", "") + os.Unsetenv("HUB_URL") + t.Setenv("BESZEL_AGENT_TOKEN", "test-token") + + client, err := newWebSocketClient(agent) + + require.Error(t, err) + assert.Nil(t, client) + assert.ErrorIs(t, err, errNoHubURL) +} + // TestNewWebSocketClient tests WebSocket client creation func TestNewWebSocketClient(t *testing.T) { agent := createTestAgent(t) diff --git a/agent/connection_manager.go b/agent/connection_manager.go index d9b16d24..28ea98ae 100644 --- a/agent/connection_manager.go +++ b/agent/connection_manager.go @@ -91,7 +91,15 @@ func (c *ConnectionManager) Start(serverOptions ServerOptions) error { if errors.As(err, &caCertErr) { return err } - slog.Warn("Error creating WebSocket client", "err", err) + disableSSH, _ := utils.GetEnv("DISABLE_SSH") + if errors.Is(err, errNoHubURL) && disableSSH != "true" { + // SSH-only mode: the hub dials the agent, so there is nothing to warn + // about. With SSH also disabled there is no connection method at all, + // so that case still warns. + slog.Debug("WebSocket client not configured", "err", err) + } else { + slog.Warn("Error creating WebSocket client", "err", err) + } } c.wsClient = wsClient