diff --git a/CLAUDE.md b/CLAUDE.md index c38ec7b1..8a7c69c1 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -87,3 +87,37 @@ Covered by `HiveRpcFailoverTests` and `EngineFailoverTests`; keep new failover b - Behavior changes to endpoints need: the route/handler change, a parity `KNOWN_DIVERGENCES` entry when applicable, and a test. - Static data changes (e.g. announcements) are code changes: edit the handler data, merge to main, CI ships it. - Keep comments explaining *why* a quirk exists (usually "matches the original observable behavior") — they prevent well-meaning regressions. + +## Attributing a `/private-api/*` 401 + +A 401 on a private-API route does not necessarily come from this service. These +handlers resolve the username from the signed code and then **pipe an upstream +response through unchanged**, so a 401 returned by the backend behind the +gateway reaches the client looking identical to one this service produced. + +The response body length is a one-way tell. This service answers +`SendText(401, "Unauthorized")`, a 12-byte body. **A longer body means the 401 +was piped from upstream; a 12-byte body proves nothing**, because an upstream +that also answers `Unauthorized` is forwarded through at exactly that length. + +Two cheap checks before assuming a token problem: + +- **Compare against a route that validates strictly.** Pick a sibling that 401s + unconditionally when `ValidateCode` returns nothing - `notifications/unread` + and `notifications/mark` do. If one of those succeeds while another endpoint + 401s for the same session, the code validated fine and the failure is + downstream of this service. + + **Do not use `notifications` as that canary.** It falls back to a + body-supplied `user` when validation fails, and overrides even a validated + username with that field whenever it is present. It can therefore answer + happily for a request whose code was rejected, which tells you nothing about + validation and actively misleads. +- **Check the failure rate.** A token or session problem affects a subset of + users; a backend gate that is misconfigured fails for everyone. An endpoint at + 100% 401 while its siblings are at 100% success is not an authentication bug + in the usual sense. + +Handlers using `RequireAuthedUsername` differ from those calling `ValidateCode` +directly only in that the former sends the 401 for you - the validation is the +same, so it is never the explanation for one route failing while another passes.