Summary
Behind the StartOS reverse proxy, Wisp sees every client as a single source IP (the proxy's internal address). With Wisp's default max_connections_per_ip = 10, the entire relay is effectively capped at 10 concurrent WebSocket connections internet-wide. A public relay saturates this instantly and then rejects every new connection with TooManyConnectionsPerIp, never recovering.
This is a packaging/config problem in wisp-startos, not a bug in Wisp. Wisp's defaults (trust_proxy = false, max_connections_per_ip = 10) are reasonable for a relay on its own IP — they're just wrong when the relay is fronted by the StartOS proxy, and that wrapping is defined here.
Root cause
startos/interfaces.ts binds port 7777 as a proxied ws interface — all client traffic arrives via the platform proxy and is collapsed to one source IP.
- Wisp only honors
X-Forwarded-For when trust_proxy = true (Wisp src/server.zig). The package never sets trust_proxy, so it stays undefined → false.
- The per-IP limiter (
src/server.zig, WsConn.init) buckets every connection under the proxy IP and rejects past 10.
The trust_proxy field already exists in the schema (startos/fileModels/wisp.toml.ts:69) but is never written by any action.
Immediate workaround (no rebuild)
StartOS UI → Wisp → Actions → Configure Limits → Max Connections Per IP → set high (>= Max Connections, e.g. 1000). Save + restart. Global Max Connections still provides DoS protection.
Proper fix (in this repo)
Two options:
- Correct — if the StartOS
ws proxy injects X-Forwarded-For: have the package write [security] trust_proxy = true plus trusted_proxies = <proxy address> into wisp.toml. Wisp then reads the real client IP and per-IP limiting works as intended. Wire-up point: startos/init/ (seed defaults) and/or the limits/access action.
- Safe fallback — if the proxy does not forward XFF: raise the package default
max_connections_per_ip to a large value (startos/actions/limits.ts, possibly startos/fileModels/wisp.toml.ts) and rely on global max_connections, since real client IPs aren't recoverable.
TODO
Summary
Behind the StartOS reverse proxy, Wisp sees every client as a single source IP (the proxy's internal address). With Wisp's default
max_connections_per_ip = 10, the entire relay is effectively capped at 10 concurrent WebSocket connections internet-wide. A public relay saturates this instantly and then rejects every new connection withTooManyConnectionsPerIp, never recovering.This is a packaging/config problem in
wisp-startos, not a bug in Wisp. Wisp's defaults (trust_proxy = false,max_connections_per_ip = 10) are reasonable for a relay on its own IP — they're just wrong when the relay is fronted by the StartOS proxy, and that wrapping is defined here.Root cause
startos/interfaces.tsbinds port 7777 as a proxiedwsinterface — all client traffic arrives via the platform proxy and is collapsed to one source IP.X-Forwarded-Forwhentrust_proxy = true(Wispsrc/server.zig). The package never setstrust_proxy, so it staysundefined→false.src/server.zig,WsConn.init) buckets every connection under the proxy IP and rejects past 10.The
trust_proxyfield already exists in the schema (startos/fileModels/wisp.toml.ts:69) but is never written by any action.Immediate workaround (no rebuild)
StartOS UI → Wisp → Actions → Configure Limits → Max Connections Per IP → set high (>= Max Connections, e.g.
1000). Save + restart. Global Max Connections still provides DoS protection.Proper fix (in this repo)
Two options:
wsproxy injectsX-Forwarded-For: have the package write[security] trust_proxy = trueplustrusted_proxies = <proxy address>intowisp.toml. Wisp then reads the real client IP and per-IP limiting works as intended. Wire-up point:startos/init/(seed defaults) and/or the limits/access action.max_connections_per_ipto a large value (startos/actions/limits.ts, possiblystartos/fileModels/wisp.toml.ts) and rely on globalmax_connections, since real client IPs aren't recoverable.TODO
wsbinding appendsX-Forwarded-Forfor a rawwsproxy (start-sdk / StartOS proxy behavior).