The three admin login limiters (5 per address and 50 overall in one
reservation, 5 per known device) keep their numbers and still don't count
refused attempts, but live in the store: memory in this process as before,
or the shared store with several replicas. checkRateLimit and
checkDeviceRateLimit are async now; the login route awaits them and
answers 503 "Sign-in is temporarily unavailable" when the store can't be
reached. The two limiter tests gain await and reset the store's counters,
which vi.resetModules no longer clears.
The admin code tells a store outage by the error's name, since the store
may come from another bundle's copy of the module.
The admin login limit keys on the client IP taken from
X-Forwarded-For. With no reverse proxy in front, that header is
whatever the client sends (Next only fills it from the socket when the
request carries none), so each forged address got a fresh bucket of
five guesses. Route handlers cannot see the socket address, so the
limiter now also stops at 50 attempts per 15 minutes across all
clients. `.env.example` says why a proxy that appends the header is
still needed for trustworthy per-IP limits and audit addresses.