Skip to content

fix(service-auth): backend→SLM WS 403 — route direct-uvicorn vs nginx path correctly + reject diagnostics (#10459) - #10522

Merged
mrveiss merged 3 commits into
Dev_new_guifrom
issue-10459
Jun 26, 2026
Merged

mrveiss merged 3 commits into
Dev_new_guifrom
issue-10459

Conversation

@mrveiss

@mrveiss mrveiss commented Jun 26, 2026

Copy link
Copy Markdown
Owner

Thinking Path

#10400's 4001 secret-mismatch was fixed; this is the remaining facet — the backend→SLM WS handshake gets HTTP 403 before app auth. Traced the handshake path end-to-end.

What Changed

  • Root cause found: in Docker Compose AUTOBOT_SLM_HOST=autobot-slm (not loopback), so slm_client._is_loopback_target chose the nginx /slm/api/ws/events prefix path — but port 8000 is direct uvicorn, which has no /slm/ route → starlette closes pre-accept() → uvicorn maps that to HTTP 403.
  • autobot-backend/services/slm_client.py — replaced _is_loopback_target with _is_direct_uvicorn_url for WS path selection (loopback host OR plain-HTTP on a non-80/443 port → direct uvicorn /api/ws/events; HTTPS/port-80 → nginx /slm/...). Covers Docker-Compose, co-located bare-metal, and proxied deploys.
  • autobot-slm-backend/api/websocket.py — _log_ws_reject_context() logs path/origin/host/peer/subprotocol on every WS rejection so any residual 403 is diagnosable; documented the accept-before-close requirement.

Verification

Model Used

Claude Opus 4.8 (senior-backend-engineer subagent).

…ong path prefix for direct-uvicorn URLs (#10459)

Root cause: `_ws_connect_and_listen` used `_is_loopback_target` to choose
between `/api/ws/events` (direct uvicorn) and `/slm/api/ws/events` (nginx).
In Docker Compose `AUTOBOT_SLM_HOST=autobot-slm` is not a loopback address,
so the nginx-prefix path `/slm/api/ws/events` was used against direct uvicorn
at `:8000`. Starlette finds no `/slm/` route → sends WebSocketClose before
accept → uvicorn converts that to HTTP 403 (uvicorn ws impl line 292).
This explains the flood of 'server rejected WebSocket connection: HTTP 403'
in the backend log.

Fix: replace `_is_loopback_target` with `_is_direct_uvicorn_url` which
correctly identifies direct-uvicorn connections by loopback hostname OR by
plain HTTP on a non-standard port (not 80/443). nginx always serves on
port 443 (HTTPS) or 80 (HTTP standard), so plain HTTP on `:8000` means
uvicorn regardless of hostname.

Covered cases:
- http://127.0.0.1:8000 → /api/ws/events  (co-located bare-metal, unchanged)
- http://autobot-slm:8000 → /api/ws/events (Docker Compose, was broken)
- http://192.168.x.y:8000 → /api/ws/events (direct non-loopback uvicorn)
- https://192.168.x.y → /slm/api/ws/events (nginx TLS, unchanged)
- http://192.168.x.y:80 → /slm/api/ws/events (nginx HTTP, unchanged)

Additional diagnostics: `_log_ws_reject_context` in SLM websocket.py logs
origin/host/peer/sec-websocket-protocol at the point of rejection so any
future environment-specific 403 can be traced without a live debugger.
Also documents the accept-before-close requirement (uvicorn's HTTP 403
conversion) in the _authenticate_websocket_token docstring.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

✅ SSOT Configuration Compliance: Passing

🎉 No hardcoded values detected that have SSOT config equivalents!

github-actions Bot and others added 2 commits June 26, 2026 20:28
Applied Black, isort, and autoflake to match code-quality checks.
Triggered by workflow auto-fix-formatting.yml.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
@mrveiss
mrveiss merged commit bd9e15f into Dev_new_gui Jun 26, 2026
27 of 28 checks passed
@mrveiss
mrveiss deleted the issue-10459 branch June 26, 2026 21:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant