internal/api is the admin REST + SSE surface and had no test file at all,
so nothing pinned the auth modes, the X-Forwarded-For trust boundary, the
rate limiter or the fact that a read route must not mutate anything. This
adds a local harness (a Server over fake hooks on an httptest listener,
with a real config file on disk) and three test files.
What it pins:
* Routing. A table over every route in Server.routes: each one answers 200
and reaches the hook it is supposed to, so a new route without a case
here is a route nothing tests. Plus method-not-allowed / 404 / outside
the base path, a base path other than /api, and a read-route sweep that
asserts no mutating hook was reached, the config generation did not move
and the file on disk is byte-identical afterwards. A panicking component
becomes a 500 and the server keeps serving, because the admin API is the
only way to fix a broken instance.
* Auth. All three modes. apikey: the right key always works, and a prefix,
a suffix, a case-folded key, a wrong basic password, an unknown bearer
token, an unsupported scheme and a malformed Authorization header never
do; curl -u any:KEY is accepted because that is what clients send.
basic: bad username and bad password refused, a successful login issues a
bearer token, the token works for the whole API, basic auth also works
per request, an API key is not a credential in this mode, logout revokes
the token, and a token past its session_ttl stops working. none: nothing
is required and the caller runs as a writable loopback principal. trust
_loopback: a local client with no credentials is let through, and a
forwarded non-loopback client is not. Every mutating route refuses a
read-only key with 403 without reaching its hook or touching the file
(and the same request with a read-write key succeeds, so the 403 is about
the key and not the payload), and allow_config_write: false refuses the
settings editor with a valid key while the other mutations still work.
The hashing is pinned on the state, not on timing: the authenticator's
maps are keyed on the SHA-256 of the credential and hold no plaintext key
or password, so a copied server struct cannot leak one.
* Product code is untouched; the suite is `package api` with its own setup.