console: key the CLI write-door rate limit by address, not token
Corrects the previous fix: s.cli.write is keyed by ADDRESS at its existing call site (handleCLINewAccount), and the promote/rollback handlers keyed it by token hash instead — two incompatible key spaces sharing one limiter map, and a developer with several live tokens got an independent write budget per token rather than one budget for the person. Both handlers now key on addr from apiAccountApp, matching handleCLINewAccount exactly.
1 file changed,
+14
−8
internal/console/apicli.go+14 −8
diff --git a/internal/console/apicli.go b/internal/console/apicli.go| index accb23fa7..e6d4f7a39 100644 |
| --- a/internal/console/apicli.go |
| +++ b/internal/console/apicli.go |
| @@ -186,10 +186,13 @@ func (s *Server) handleAPIPromote(w http.ResponseWriter, r *http.Request) { |
| if !ok { |
| return |
| } |
| - // Keyed on the TOKEN, not the client IP (design §3): a token is the |
| - // natural principal for a write a bearer credential is already |
| - // authenticating, the same choice handleCLINewAccount makes. |
| - if !s.cli.write.allow(hashToken(bearer(r)), time.Now()) { |
| + // Keyed on the ADDRESS, not the token: the principal a write budget |
| + // belongs to is the PERSON, not the terminal they happen to be typing on |
| + // — the same key handleCLINewAccount already uses on this limiter. |
| + // Keying by token instead would give someone with several live tokens an |
| + // independent budget per token, which is the limit doing less than it |
| + // should, not more. |
| + if !s.cli.write.allow(addr, time.Now()) { |
| jsonError(w, http.StatusTooManyRequests, "too many requests — wait a minute and try again") |
| return |
| } |
| @@ -271,10 +274,13 @@ func (s *Server) handleAPIRollback(w http.ResponseWriter, r *http.Request) { |
| if !ok { |
| return |
| } |
| - // Keyed on the TOKEN, not the client IP (design §3): a token is the |
| - // natural principal for a write a bearer credential is already |
| - // authenticating, the same choice handleCLINewAccount makes. |
| - if !s.cli.write.allow(hashToken(bearer(r)), time.Now()) { |
| + // Keyed on the ADDRESS, not the token: the principal a write budget |
| + // belongs to is the PERSON, not the terminal they happen to be typing on |
| + // — the same key handleCLINewAccount already uses on this limiter. |
| + // Keying by token instead would give someone with several live tokens an |
| + // independent budget per token, which is the limit doing less than it |
| + // should, not more. |
| + if !s.cli.write.allow(addr, time.Now()) { |
| jsonError(w, http.StatusTooManyRequests, "too many requests — wait a minute and try again") |
| return |
| } |