From 965e908b7127e2fc15a4a864b7aa8123ffb401f7 Mon Sep 17 00:00:00 2001 From: feruz Date: Tue, 21 Jul 2026 13:25:28 +0000 Subject: [PATCH 1/2] docs: how to attribute a private-api 401 These handlers pipe upstream responses through unchanged, so a 401 from a backend is indistinguishable from one this service produced unless you look at the body length. Records that tell, plus two checks that separate a token problem from a misconfigured gate downstream. --- CLAUDE.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/CLAUDE.md b/CLAUDE.md index c38ec7b1..a120e9c2 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -87,3 +87,29 @@ 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 the quickest way to tell them apart: this service +answers `SendText(401, "Unauthorized")`, a 12-byte body. A longer body almost +always means the 401 was piped from further upstream. + +Two cheap checks before assuming a token problem: + +- **Compare against another private-API route that shares `ValidateCode`.** If + notifications succeed while one endpoint 401s for the same session, the code + validated fine and the failure is downstream of this service. Route-specific + auth on the backend, not the caller's token, is then the thing to look at. +- **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 with siblings 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. From 496fc52276d660c578283a7bab261ecebf36da20 Mon Sep 17 00:00:00 2001 From: feruz Date: Tue, 21 Jul 2026 15:43:17 +0000 Subject: [PATCH 2/2] docs: correct the 401 body-length tell and the validation canary The body length only proves upstream in one direction: an upstream answering Unauthorized forwards through at the same 12 bytes, so a short body is inconclusive rather than evidence the 401 was raised here. notifications was the wrong sibling to suggest comparing against. It falls back to a body-supplied user when validation fails and overrides even a validated username with that field, so it can answer for a request whose code was rejected. Points at notifications/unread and notifications/mark instead, which 401 unconditionally, and records the notifications behaviour as a trap. --- CLAUDE.md | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index a120e9c2..8a7c69c1 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -95,20 +95,28 @@ 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 the quickest way to tell them apart: this service -answers `SendText(401, "Unauthorized")`, a 12-byte body. A longer body almost -always means the 401 was piped from further upstream. +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 another private-API route that shares `ValidateCode`.** If - notifications succeed while one endpoint 401s for the same session, the code - validated fine and the failure is downstream of this service. Route-specific - auth on the backend, not the caller's token, is then the thing to look at. +- **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 with siblings at 100% success is not an authentication bug in the - usual sense. + 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