docs(installer): lead the remote-website case with a reverse proxy

A TLS reverse proxy in front of the sidecar is a supported deployment already
running on a real domain, not the fallback the guide framed it as. Recommend it
first, keep [web] bind on loopback in that arrangement, and demote widen-the-
bind-and-firewall to the trusted-LAN alternative - on that path the token and
every event cross the network in the clear.

Adds the four things a proxy must actually do, checked against web.rs: forward
the WebSocket upgrade (/ws is the entire live feed, and losing it leaves REST
working with no events - a confusing half-working state); pass headers through
unmodified (auth is Authorization: Bearer or X-Api-Key, and a stripped
X-UOLink-Version silently skips the 409 mismatch check); no buffering and
long-lived connections (the sidecar pings every 30s, so a 60s+ read timeout is
safe as it stands); and no query-string logging, since ?token= is an accepted
auth form. Nothing needs X-Forwarded-For - the sidecar never uses the client IP
for authorization. Includes an nginx block that satisfies all four, and notes
Caddy and Traefik need no equivalent.

The website gets the proxied https:// / wss:// URLs, not the pair the installer
prints: those are composed from the sidecar's own bind address, which knows
nothing about what fronts it. PLAN records that the installer deliberately does
not try to detect a proxy - nothing visible from the sidecar's side says what is
in front of it, so guessing would print a confidently wrong URL.

Two new troubleshooting rows for the symptoms this causes: REST works but no
events (upgrade not forwarded), and a feed that drops every minute or two (read
timeout under the ping interval, or buffering).

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-08-04 12:12:46 -05:00
parent 83bd4ec2d4
commit d0363cd62d
2 changed files with 80 additions and 15 deletions

View File

@@ -358,11 +358,19 @@ Repo work that must land before an installer can exist.
in §6's handoff has a non-interactive equivalent and an unattended install is expressible.
- **A modified `Bridge.cfg` must survive an update** — see Phase 1, where this changes the sync
rule inherited from `deploy.ps1`.
- **Remote-website deployments needed an answer.** `[web] bind` defaults to `127.0.0.1`, which
only works when the site runs on the shard host. The guide says to widen it, firewall it to
the website's address, and front it with TLS or a VPN off a trusted network — because the
token is always required but travels as a plain bearer token over HTTP. `[shard] bind` stays
on loopback, since that socket carries inbound commands *into* the game.
- **Remote-website deployments needed an answer, and it is a reverse proxy.** `[web] bind`
defaults to `127.0.0.1`, which only works when the site runs on the shard host. The guide's
recommended arrangement — already in production on a real domain — is to **leave the bind on
loopback** and put a TLS reverse proxy in front, giving the website the proxied `https://` /
`wss://` URLs in place of the pair the installer prints from the bind address. Four
requirements make that work and are stated with an nginx block that satisfies them: forward
the WebSocket upgrade (`/ws` is the whole live feed), pass headers through unmodified (auth is
`Authorization: Bearer`, and a stripped `X-UOLink-Version` silently skips the mismatch check),
do not buffer and allow long-lived connections (the sidecar pings every 30 s, so a ≥60 s read
timeout is safe), and do not log query strings (`?token=` is an accepted auth form). Widening
the bind and firewalling the port stays documented as the trusted-LAN alternative, not the
default, because on that path the token crosses the network in the clear. `[shard] bind` is
never proxied and never widened — that socket carries inbound commands *into* the game.
### Phase 1 — installer core
@@ -482,6 +490,13 @@ Every value in that block except the host and the site URL comes from one
`0.0.0.0`, which is not something to hand a website — so the installer composes the URLs from the
host it detects or prompts for, rather than echoing the bind address.
**It does not attempt to detect a reverse proxy**, which is the recommended arrangement for a
website on another host (`INSTALL.md` §5). Nothing visible from the sidecar's side says what fronts
it, so guessing would produce a confidently wrong `https://` URL. The two printed URLs always
describe the sidecar itself, and the guide tells the operator to paste their public `https://` /
`wss://` pair instead when there is a proxy. `--host` accepting a full origin later is a cheap
improvement if this proves annoying in practice.
The installer prompts for the site URL only to build that link; it never contacts the website. A
future "installer registers itself with the website" flow (claim code + authenticated endpoint) is
explicitly **out of scope** — it is real backend work in a security-sensitive area and can be added