Why
Guessing a credential is bounded by the request rate, not by the hash. That is the reasoning
recorded in
decision 0006:
a fast hash for API keys is correct because they carry 128 bits of token_hex and nobody guesses
them, and argon2id protects the administration password against somebody who already holds
security.json. Neither does anything about an attacker who simply keeps asking.
Today nothing limits that. There is no throttle, no lockout and no backoff anywhere in the service.
The security hardening added the missing half of the picture — a rejected API key and a rejected
administration login each write a warning naming no value, so the attempts are at least visible —
but visibility is not a control.
The administration password is the target that matters. It is human-chosen, it may be short, and the
documented fallback is a single well-known word. An unthrottled POST /api/auth/token is the whole
attack. This becomes considerably more pressing when the service is exposed as a Home Assistant
add-on.
What has to be decided
- Where the counters live. In-process state has the same problem as the refresh tokens: it is
per worker, so a limit of five attempts becomes five per worker. Either the counters wait for the
SQLite persistence work, or they are explicitly in-memory and the limit is documented as
approximate. The rest of that state moves in the persistence phase anyway.
- What is counted, and per what. Per client address is the obvious key, but the service runs
behind ProxyHeadersMiddleware and, as an add-on, behind Ingress — so the address is only as
trustworthy as the proxy in front. Counting per account for the administration login avoids that
question and introduces another: an attacker can then lock the administrator out on purpose.
- Throttle or lock out. A delay that grows with the number of failures keeps the legitimate
administrator working while making a guessing run useless; a hard lockout is simpler and is a
denial-of-service lever pointed at the owner of the instance.
- The answer to a limited request.
429 with Retry-After is the honest one, which means a new
LearningHouseException subclass — the error classes carry their own status code and OpenAPI
description, so a new one documents itself.
Scope
POST /api/auth/token (password login) — the important one.
PUT /api/auth/token (refresh) and API key authentication on every protected endpoint — cheaper
targets, but they should not be the way around the limit.
- Not in scope: anything that would rate-limit training or prediction traffic for legitimate API
keys. A brain being fed a data point per second is normal use.
Acceptance
Why
Guessing a credential is bounded by the request rate, not by the hash. That is the reasoning
recorded in
decision 0006:
a fast hash for API keys is correct because they carry 128 bits of
token_hexand nobody guessesthem, and argon2id protects the administration password against somebody who already holds
security.json. Neither does anything about an attacker who simply keeps asking.Today nothing limits that. There is no throttle, no lockout and no backoff anywhere in the service.
The security hardening added the missing half of the picture — a rejected API key and a rejected
administration login each write a warning naming no value, so the attempts are at least visible —
but visibility is not a control.
The administration password is the target that matters. It is human-chosen, it may be short, and the
documented fallback is a single well-known word. An unthrottled
POST /api/auth/tokenis the wholeattack. This becomes considerably more pressing when the service is exposed as a Home Assistant
add-on.
What has to be decided
per worker, so a limit of five attempts becomes five per worker. Either the counters wait for the
SQLite persistence work, or they are explicitly in-memory and the limit is documented as
approximate. The rest of that state moves in the persistence phase anyway.
behind
ProxyHeadersMiddlewareand, as an add-on, behind Ingress — so the address is only astrustworthy as the proxy in front. Counting per account for the administration login avoids that
question and introduces another: an attacker can then lock the administrator out on purpose.
administrator working while making a guessing run useless; a hard lockout is simpler and is a
denial-of-service lever pointed at the owner of the instance.
429withRetry-Afteris the honest one, which means a newLearningHouseExceptionsubclass — the error classes carry their own status code and OpenAPIdescription, so a new one documents itself.
Scope
POST /api/auth/token(password login) — the important one.PUT /api/auth/token(refresh) and API key authentication on every protected endpoint — cheapertargets, but they should not be the way around the limit.
keys. A brain being fed a data point per second is normal use.
Acceptance
on the security page.
time.
authentication.
against.
restriction is stated where
workersis documented.