Fix remote-stack log streaming: URL-encode agent token in WS proxy (0.13.2)

The agent-logs WebSocket proxy injected the agent token raw into the upstream
query string (?token=<token>). Tokens containing base64/url-special characters
(+, /, =) were then mangled by the query parser on the agent side (e.g. "+"
decoded to a space), so the agent rejected the stream with close code 4401 even
though the same token works for the HTTP API (where it travels in the
Authorization header). URL-encode the token with urllib.parse.quote so it
round-trips intact.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
menzelj
2026-06-08 11:21:28 +00:00
co-authored by Claude Opus 4.8
parent 5ebd615651
commit 4bc0fb8901
4 changed files with 7 additions and 4 deletions
+1 -1
View File
@@ -40,7 +40,7 @@ from services import backup_service, compose_service
logger = logging.getLogger("stackpilot.agent") logger = logging.getLogger("stackpilot.agent")
AGENT_VERSION = "0.13.1" AGENT_VERSION = "0.13.2"
# --------------------------------------------------------------------------- # # --------------------------------------------------------------------------- #
+1 -1
View File
@@ -55,7 +55,7 @@ async def lifespan(app: FastAPI):
schedule_task.cancel() schedule_task.cancel()
app = FastAPI(title="StackPilot", version="0.13.1", lifespan=lifespan) app = FastAPI(title="StackPilot", version="0.13.2", lifespan=lifespan)
app.add_middleware( app.add_middleware(
CORSMiddleware, CORSMiddleware,
+4 -1
View File
@@ -4,6 +4,7 @@ from __future__ import annotations
import asyncio import asyncio
import json import json
import logging import logging
import urllib.parse
import contextlib import contextlib
@@ -114,7 +115,9 @@ async def ws_agent_logs(
ws_url = ("wss://" + base[8:] if base.startswith("https://") ws_url = ("wss://" + base[8:] if base.startswith("https://")
else "ws://" + base[7:] if base.startswith("http://") else "ws://" + base[7:] if base.startswith("http://")
else "ws://" + base) else "ws://" + base)
ws_url += f"/agent/ws/logs/{stack_id}?token={agent.token}" # URL-encode the token: agent tokens may contain base64 chars (+ / =) that
# would otherwise be mangled in the query string and rejected as 4401.
ws_url += f"/agent/ws/logs/{stack_id}?token={urllib.parse.quote(agent.token, safe='')}"
async def _err(detail: str) -> None: async def _err(detail: str) -> None:
with contextlib.suppress(Exception): with contextlib.suppress(Exception):
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"name": "stackpilot-frontend", "name": "stackpilot-frontend",
"private": true, "private": true,
"version": "0.13.1", "version": "0.13.2",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",