Rate limiter is shared by all users behind the proxy and is exhausted by the UI's own polling #10

Open
opened 2026-09-08 20:57:21 -04:00 by jared · 0 comments
Owner

Overview

apiLimiter allows 300 requests / 15 min per client IP, but app.set('trust proxy') is never configured, so every request arriving via Authelia/the reverse proxy is keyed on the proxy's address – one bucket for the whole team.

The frontend alone burns that bucket: refreshData() issues 4 requests every 30 s (120 per 15 min per open tab), plus loadExecutions()/loadWorkers() on every WebSocket broadcast (every command_result, execution_started, heartbeat-driven worker_update …). With Gandalf executing commands every few seconds, two open browser tabs exceed 300 quickly. The UI then shows "⚠ Failed to load …" with no hint that it was throttled, because 429 responses are not handled.

Where

  • server.js rate limiter setup; no trust proxy.
  • public/index.html refreshData(), connectWebSocket() handlers, setInterval(refreshData, 30000).

Suggested fix

  • Set app.set('trust proxy', 1) (or key the limiter on remote-user) and raise the read-only limit.
  • Debounce/coalesce WebSocket-triggered reloads (e.g. one loadExecutions per 2 s) and apply incoming events to local state instead of re-fetching everything.
  • Show a clear "rate limited, retrying in Ns" message on 429.
## Overview `apiLimiter` allows 300 requests / 15 min **per client IP**, but `app.set('trust proxy')` is never configured, so every request arriving via Authelia/the reverse proxy is keyed on the proxy's address – one bucket for the whole team. The frontend alone burns that bucket: `refreshData()` issues 4 requests every 30 s (120 per 15 min per open tab), plus `loadExecutions()`/`loadWorkers()` on every WebSocket broadcast (every `command_result`, `execution_started`, heartbeat-driven `worker_update` …). With Gandalf executing commands every few seconds, two open browser tabs exceed 300 quickly. The UI then shows "⚠ Failed to load …" with no hint that it was throttled, because 429 responses are not handled. ## Where - `server.js` rate limiter setup; no `trust proxy`. - `public/index.html` `refreshData()`, `connectWebSocket()` handlers, `setInterval(refreshData, 30000)`. ## Suggested fix - Set `app.set('trust proxy', 1)` (or key the limiter on `remote-user`) and raise the read-only limit. - Debounce/coalesce WebSocket-triggered reloads (e.g. one `loadExecutions` per 2 s) and apply incoming events to local state instead of re-fetching everything. - Show a clear "rate limited, retrying in Ns" message on 429.
jared added the buguxfrontendbackendpriority/high labels 2026-09-08 20:57:21 -04:00
Sign in to join this conversation.