Deployment and lifecycle management for Nix
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix: use fresh token on garden websocket reconnect

When a garden's websocket disconnects, Slipstream reconnects using the
original URI from init time, which contains an expired boruta token.
Override handle_disconnect to call connect() with a freshly built config
that reads the current token from storage.

sow-141

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

+40 -19
+40 -19
apps/garden/lib/garden/socket.ex
··· 96 96 97 97 @impl Slipstream 98 98 def init(_args) do 99 - do_connect() 100 - end 101 - 102 - defp do_connect() do 103 - config = Application.get_all_env(__MODULE__) 104 - token_param = resolve_connect_token() 105 - 106 - uri = 107 - config 108 - |> Keyword.get(:uri) 109 - |> Map.put(:query, "token=#{token_param}") 110 - |> URI.to_string() 111 - 112 - config = Keyword.put(config, :uri, uri) 113 - 114 - case connect(config) do 99 + case do_connect() do 115 100 {:ok, socket} -> 116 - Logger.debug(msg: "Connecting") 117 101 {:ok, Map.put(socket, :active_deployments, %{})} 118 102 119 103 {:error, reason} -> ··· 126 110 end 127 111 end 128 112 113 + @impl Slipstream 114 + def handle_disconnect(_reason, socket) do 115 + Logger.warning(msg: "Disconnected from server socket") 116 + 117 + case do_connect(socket) do 118 + {:ok, socket} -> {:ok, socket} 119 + {:error, reason} -> {:stop, reason, socket} 120 + end 121 + end 122 + 123 + defp do_connect(socket \\ nil) do 124 + config = build_connect_config() 125 + 126 + case if(socket, do: connect(socket, config), else: connect(config)) do 127 + {:ok, socket} -> 128 + {:ok, socket} 129 + 130 + {:error, reason} -> 131 + {:error, reason} 132 + end 133 + end 134 + 135 + defp build_connect_config do 136 + config = Application.get_all_env(__MODULE__) 137 + token_param = resolve_connect_token() 138 + uri = Keyword.get(config, :uri) 139 + 140 + Logger.info(msg: "Connecting to server socket", endpoint: uri) 141 + 142 + uri = 143 + uri 144 + |> Map.put(:query, "token=#{token_param}") 145 + |> URI.to_string() 146 + 147 + Keyword.put(config, :uri, uri) 148 + end 149 + 129 150 defp resolve_connect_token do 130 151 storage = Storage.read() 131 152 ··· 183 204 }) 184 205 185 206 storage |> Map.put(:oauth_credentials, updated_creds) |> Storage.write() 186 - Logger.info(msg: "Reauthenticated via JWT assertion") 207 + Logger.debug(msg: "Reauthenticated via JWT assertion") 187 208 "boruta:#{token_response.access_token}" 188 209 189 210 {:error, _} -> ··· 194 215 defp try_reauthenticate(_), do: nil 195 216 196 217 defp registration_token do 197 - Logger.debug(msg: "Using registration token") 218 + Logger.info(msg: "Using registration token") 198 219 Base.encode64(Application.fetch_env!(:garden, :config).access_token) 199 220 end 200 221