mirror of
https://github.com/tiennm99/noitu.git
synced 2026-10-11 03:13:45 +00:00
docs: document the per-IP cap and record two open decisions
NOITU_MAX_CONNECTIONS_PER_IP is now in both env tables, with the warning that it must stay off behind a proxy unless NOITU_TRUSTED_PROXIES names it, since every player otherwise shares one address. Also records two decisions the review flagged as undocumented rather than broken: a second tab presenting a live resume token takes the seat on purpose, and /debug/vars carries the process's argv and heap stats because expvar always publishes them, which is why it lives on its own address.
This commit is contained in:
1 parent
2fece2fcea
commit
8008bf6318
2 files changed
+30
-5
No files matched your search
@@ -207,6 +207,7 @@ Configuration is environment-only; every variable has a working default.
|
|||||||
| `NOITU_TRUSTED_PROXIES` | *(unset)* | Comma-separated proxy addresses or CIDRs whose `X-Forwarded-For` is believed. Unset keys limiters on the socket peer |
|
| `NOITU_TRUSTED_PROXIES` | *(unset)* | Comma-separated proxy addresses or CIDRs whose `X-Forwarded-For` is believed. Unset keys limiters on the socket peer |
|
||||||
| `NOITU_MAX_ROOMS` | `1000` | Ceiling on live rooms across the process; a creator past it is told `server_full` |
|
| `NOITU_MAX_ROOMS` | `1000` | Ceiling on live rooms across the process; a creator past it is told `server_full` |
|
||||||
| `NOITU_MAX_CONNECTIONS` | `2000` | Ceiling on open WebSockets; the next upgrade gets HTTP 503 |
|
| `NOITU_MAX_CONNECTIONS` | `2000` | Ceiling on open WebSockets; the next upgrade gets HTTP 503 |
|
||||||
|
| `NOITU_MAX_CONNECTIONS_PER_IP` | `0` (off) | Ceiling on open WebSockets from one address; the next upgrade from it gets HTTP 503. Leave this at `0` behind a reverse proxy unless `NOITU_TRUSTED_PROXIES` is set — otherwise every player behind it shares one address, and turning this on caps them all at whichever gets there first |
|
||||||
| `NOITU_DEBUG_ADDR` | *(unset)* | A separate listen address for `GET /debug/vars` (expvar). Unset means the operational counters are not exposed anywhere |
|
| `NOITU_DEBUG_ADDR` | *(unset)* | A separate listen address for `GET /debug/vars` (expvar). Unset means the operational counters are not exposed anywhere |
|
||||||
| `NOITU_DRAIN_TIMEOUT` | `0s` | How long a shutdown waits for live *games* (not lobbies) to finish before ending them anyway. `0s` is today's behaviour: end them immediately |
|
| `NOITU_DRAIN_TIMEOUT` | `0s` | How long a shutdown waits for live *games* (not lobbies) to finish before ending them anyway. `0s` is today's behaviour: end them immediately |
|
||||||
|
|
||||||
|
|||||||
+29
-5
@@ -21,6 +21,7 @@ so the image runs with nothing set.
|
|||||||
| `NOITU_TRUSTED_PROXIES` | *(unset)* | Comma-separated proxy addresses or CIDRs whose `X-Forwarded-For` is believed. Unset keys limiters on the socket peer |
|
| `NOITU_TRUSTED_PROXIES` | *(unset)* | Comma-separated proxy addresses or CIDRs whose `X-Forwarded-For` is believed. Unset keys limiters on the socket peer |
|
||||||
| `NOITU_MAX_ROOMS` | `1000` | Ceiling on live rooms across the process; a creator past it is told `server_full` |
|
| `NOITU_MAX_ROOMS` | `1000` | Ceiling on live rooms across the process; a creator past it is told `server_full` |
|
||||||
| `NOITU_MAX_CONNECTIONS` | `2000` | Ceiling on open WebSockets; the next upgrade gets HTTP 503 |
|
| `NOITU_MAX_CONNECTIONS` | `2000` | Ceiling on open WebSockets; the next upgrade gets HTTP 503 |
|
||||||
|
| `NOITU_MAX_CONNECTIONS_PER_IP` | `0` (off) | Ceiling on open WebSockets from one address; the next upgrade from it gets HTTP 503 |
|
||||||
| `NOITU_DEBUG_ADDR` | *(unset)* | Separate listen address for `GET /debug/vars` (expvar counters). Unset means the counters exist in the process but nothing serves them |
|
| `NOITU_DEBUG_ADDR` | *(unset)* | Separate listen address for `GET /debug/vars` (expvar counters). Unset means the counters exist in the process but nothing serves them |
|
||||||
| `NOITU_DRAIN_TIMEOUT` | `0s` | How long a shutdown waits for live games to finish before ending them anyway; see "Draining on deploy" below |
|
| `NOITU_DRAIN_TIMEOUT` | `0s` | How long a shutdown waits for live games to finish before ending them anyway; see "Draining on deploy" below |
|
||||||
|
|
||||||
@@ -168,14 +169,25 @@ client past it is disconnected rather than throttled. The defaults are
|
|||||||
generous for one binary on a small host; lower them if memory is tight,
|
generous for one binary on a small host; lower them if memory is tight,
|
||||||
because a room is a goroutine and an engine held for up to its idle window.
|
because a room is a goroutine and an engine held for up to its idle window.
|
||||||
|
|
||||||
|
A third ceiling, `NOITU_MAX_CONNECTIONS_PER_IP`, bounds how many of those
|
||||||
|
sockets one address may hold at once, and it is off by default. Turning it on
|
||||||
|
is safe only once the client's own address (above) is the real one: behind a
|
||||||
|
reverse proxy with `NOITU_TRUSTED_PROXIES` unset, every player shares the
|
||||||
|
proxy's own address, and the cap would seat one of them and refuse the rest.
|
||||||
|
|
||||||
## Observability
|
## Observability
|
||||||
|
|
||||||
Set `NOITU_DEBUG_ADDR` to a second listen address — one that is not the one
|
Set `NOITU_DEBUG_ADDR` to a second listen address — one that is not the one
|
||||||
players reach — to expose `GET /debug/vars` there: standard-library
|
players reach, and never a public interface — to expose `GET /debug/vars`
|
||||||
[`expvar`](https://pkg.go.dev/expvar), zero extra dependencies, a JSON object
|
there: standard-library [`expvar`](https://pkg.go.dev/expvar), zero extra
|
||||||
of process counters refreshed on every write. It is never mounted on the
|
dependencies, a JSON object of process counters refreshed on every write. It
|
||||||
public address, unset or not, so leaving `NOITU_DEBUG_ADDR` unset is the same
|
is never mounted on the public address, unset or not, so leaving
|
||||||
as not having it. The counters, all prefixed `noitu_`: connections open and
|
`NOITU_DEBUG_ADDR` unset is the same as not having it. Besides the counters
|
||||||
|
below, expvar always publishes the process's full command line and its
|
||||||
|
runtime memory statistics; that is the standard library's own doing, not
|
||||||
|
something this server adds, and it is the whole reason `/debug/vars` lives on
|
||||||
|
a separate address rather than a route on the public mux one config change
|
||||||
|
could expose. The counters, all prefixed `noitu_`: connections open and
|
||||||
total; rooms live and total, each split `bot`/`pvp`; games started and
|
total; rooms live and total, each split `bot`/`pvp`; games started and
|
||||||
finished the same way; words submitted, accepted, and rejected by reason;
|
finished the same way; words submitted, accepted, and rejected by reason;
|
||||||
eliminations by reason; chat lines; join attempts refused, by whether it was
|
eliminations by reason; chat lines; join attempts refused, by whether it was
|
||||||
@@ -258,6 +270,18 @@ turn clock already bounds how long any one game can take, so a timeout a
|
|||||||
little over `NOITU_TURN_LIMIT` covers the common case of a handful of games
|
little over `NOITU_TURN_LIMIT` covers the common case of a handful of games
|
||||||
mid-turn.
|
mid-turn.
|
||||||
|
|
||||||
|
## Resuming from a second tab
|
||||||
|
|
||||||
|
A resume token is a bearer credential: whoever presents a live one takes the
|
||||||
|
seat, and the connection that held it before is closed. Opening the same
|
||||||
|
game in a second tab, or reloading with the token still in `localStorage`, is
|
||||||
|
therefore a takeover, not a copy — the newest connection to present the token
|
||||||
|
wins the seat, on purpose. There is no liveness check on the connection being
|
||||||
|
replaced beyond that; nothing here treats a second tab as an attack, because
|
||||||
|
the token already proves it came from the same player. A future version that
|
||||||
|
wants two tabs to share a seat, rather than fight over it, would need a
|
||||||
|
different design — this one intentionally does not.
|
||||||
|
|
||||||
## What a restart costs
|
## What a restart costs
|
||||||
|
|
||||||
A restart with `NOITU_DRAIN_TIMEOUT` unset, or a signal harder than `SIGTERM`,
|
A restart with `NOITU_DRAIN_TIMEOUT` unset, or a signal harder than `SIGTERM`,
|
||||||
|
|||||||
Reference in new issue
Block a user