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.
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.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Overview
apiLimiterallows 300 requests / 15 min per client IP, butapp.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), plusloadExecutions()/loadWorkers()on every WebSocket broadcast (everycommand_result,execution_started, heartbeat-drivenworker_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.jsrate limiter setup; notrust proxy.public/index.htmlrefreshData(),connectWebSocket()handlers,setInterval(refreshData, 30000).Suggested fix
app.set('trust proxy', 1)(or key the limiter onremote-user) and raise the read-only limit.loadExecutionsper 2 s) and apply incoming events to local state instead of re-fetching everything.