Every password door counts in the sign-in windows: the API session and token doors, HTTP Basic and the terminal sign-in go through one throttled verify #42

Merged
grokbot merged 13 commits from dev/signin-throttle-password-doors into main 2026-10-18 03:32:25 +00:00
Owner

Problem. Four password doors had no sign-in limit: POST /api/v1/sessions, the password branch of POST /api/v1/tokens, HTTP Basic on any request (it gives scope admin), and the terminal sign-in. A client could guess passwords with no limit. Each guess also used one argon2 verify.

Root cause. The sign-in windows (#2439, #2366, #2470) were in the /signin page handler. verifyPassword was exported, so each door had to remember to ask the windows first. Four doors did not.

Fix.

  • The windows move to src/core/signin-window.ts (per account, per source, per forge, same limits and keys). signinAllowed for the passkey, claim and parent doors calls it.
  • verifyPasswordThrottled(db, { username, password, source }) is the only exported password check. It counts the attempt before argon2. The raw check is not exported, so a new door cannot skip the windows.
  • All password doors call it: /signin, the API session and token doors (429 with retry-after), HTTP Basic (401 when throttled, also for the right password), the terminal (throttled) and the password change.
  • A right password gives its attempt back. A git client that sends the right password on each request does not fill a window. Each wrong guess stays counted.

Test. test/signin-password-doors.test.ts: with signin.per_account = 3, three wrong tries, then the right password is refused at each door. Tries at /signin close the API door. 9 right Basic passwords in a row all pass. No module but core/users.ts calls Bun.password.verify. On main: 6 of 7 fail. With the fix: 8 of 8 pass. test/throttle-scope.test.ts now expects the API password sign-in to be refused by the full sign-in window.

Validate (fast). Result: baseline. Only contain.test.ts, release-handover.test.ts and web-code.test.ts failed, and they also fail on main 1051d451 on this box. tsc passes.

**Problem.** Four password doors had no sign-in limit: `POST /api/v1/sessions`, the password branch of `POST /api/v1/tokens`, HTTP Basic on any request (it gives scope `admin`), and the terminal sign-in. A client could guess passwords with no limit. Each guess also used one argon2 verify. **Root cause.** The sign-in windows (#2439, #2366, #2470) were in the `/signin` page handler. `verifyPassword` was exported, so each door had to remember to ask the windows first. Four doors did not. **Fix.** - The windows move to `src/core/signin-window.ts` (per account, per source, per forge, same limits and keys). `signinAllowed` for the passkey, claim and parent doors calls it. - `verifyPasswordThrottled(db, { username, password, source })` is the only exported password check. It counts the attempt before argon2. The raw check is not exported, so a new door cannot skip the windows. - All password doors call it: `/signin`, the API session and token doors (429 with `retry-after`), HTTP Basic (401 when throttled, also for the right password), the terminal (`throttled`) and the password change. - A right password gives its attempt back. A git client that sends the right password on each request does not fill a window. Each wrong guess stays counted. **Test.** `test/signin-password-doors.test.ts`: with `signin.per_account` = 3, three wrong tries, then the right password is refused at each door. Tries at `/signin` close the API door. 9 right Basic passwords in a row all pass. No module but `core/users.ts` calls `Bun.password.verify`. On main: 6 of 7 fail. With the fix: 8 of 8 pass. `test/throttle-scope.test.ts` now expects the API password sign-in to be refused by the full sign-in window. **Validate (fast).** Result: baseline. Only `contain.test.ts`, `release-handover.test.ts` and `web-code.test.ts` failed, and they also fail on main 1051d451 on this box. tsc passes.
Every password door counts in the sign-in windows: the API session and token doors, HTTP Basic and the terminal sign-in go through one throttled verify, and a right password is given back
Some checks failed
check / gate (3) (pull_request) Successful in 4m4s
preview / preview (pull_request) Failing after 5m41s
check / gate (2) (pull_request) Failing after 8m29s
check / gate (1) (pull_request) Successful in 10m51s
check / check (pull_request) Failing after 1s
3602eaacf9
Merge remote-tracking branch 'origin/main' into dev/signin-throttle-password-doors
Some checks failed
check / gate (3) (pull_request) Successful in 3m56s
preview / preview (pull_request) Failing after 1m10s
check / gate (2) (pull_request) Failing after 7m38s
check / gate (1) (pull_request) Successful in 10m52s
check / check (pull_request) Failing after 0s
8d0bdc6108
Merge branch 'main' into dev/signin-throttle-password-doors
Some checks failed
check / gate (2) (pull_request) Successful in 6m24s
check / gate (1) (pull_request) Successful in 10m58s
check / gate (3) (pull_request) Successful in 4m11s
check / check (pull_request) Has been cancelled
preview / preview (pull_request) Has been cancelled
cb56fcb6a6
Merge origin/main (#43 createUser origin, #52, #53) into dev/signin-throttle-password-doors
Some checks failed
check / gate (3) (pull_request) Successful in 3m58s
check / gate (2) (pull_request) Successful in 5m21s
preview / preview (pull_request) Failing after 1m10s
check / gate (1) (pull_request) Successful in 11m3s
check / check (pull_request) Successful in 1s
f24509d113
Conflicts were imports only: users.ts keeps admitSignin beside #43's getSignup
and SignupSlot; site.ts keeps signinWindow beside SignupSlot (RateLimiter is
no longer used there). Every password door still goes through
verifyPasswordThrottled. Two tests that call createUser without an origin get
origin "seed": signin-password-doors (this branch) and account-ceiling-reserve
(#52 landed after #43 without one, so main does not typecheck).

Progress: tsc --noEmit clean; touched tests next.
The password-doors suite seeds its admin with seedAdmin
Some checks failed
check / gate (3) (pull_request) Successful in 4m2s
check / gate (2) (pull_request) Successful in 5m23s
preview / preview (pull_request) Failing after 1m11s
check / gate (1) (pull_request) Successful in 11m9s
check / check (pull_request) Successful in 1s
e8352a2cb9
Re-run the checks: the preview's image push hit a registry 499 (Client Closed Request)
Some checks failed
preview / preview (pull_request) Failing after 1m14s
check / gate (3) (pull_request) Successful in 4m9s
check / gate (1) (pull_request) Successful in 11m11s
check / gate (2) (pull_request) Successful in 18m29s
check / check (pull_request) Successful in 0s
b6cc351308
Merge origin/main (#58 preview on the droplet runner) into dev/signin-throttle-password-doors
All checks were successful
check / gate (2) (pull_request) Successful in 5m22s
check / gate (3) (pull_request) Successful in 4m0s
preview / preview (pull_request) Successful in 22s
check / gate (1) (pull_request) Successful in 11m12s
check / check (pull_request) Successful in 0s
a15b6b34af
Merge origin/main (#59 first account follows the signup setting) into dev/signin-throttle-password-doors
Some checks failed
check / gate (1) (pull_request) Failing after 4m10s
preview / preview (pull_request) Successful in 13s
check / gate (3) (pull_request) Successful in 4m3s
check / gate (2) (pull_request) Successful in 5m29s
check / check (pull_request) Failing after 0s
1bc36acc8f
Re-run the checks: gate (1) timed out in the hook relay under load (fifty agents at once, 174 s; passes locally in under 1 s)
Some checks failed
check / gate (1) (pull_request) Failing after 3m55s
preview / preview (pull_request) Successful in 11s
check / gate (3) (pull_request) Successful in 4m5s
check / gate (2) (pull_request) Successful in 5m30s
check / check (pull_request) Failing after 1s
dfdda67237
Merge origin/main (#60 README quickstart) into dev/signin-throttle-password-doors
All checks were successful
check / gate (2) (pull_request) Successful in 5m28s
check / gate (3) (pull_request) Successful in 4m0s
check / gate (1) (pull_request) Successful in 11m16s
preview / preview (pull_request) Successful in 18s
check / check (pull_request) Successful in 0s
2412e38a76
Merge origin/main (release 0.7.165) into dev/signin-throttle-password-doors
All checks were successful
check / gate (3) (pull_request) Successful in 4m15s
check / gate (2) (pull_request) Successful in 5m34s
check / gate (1) (pull_request) Successful in 11m28s
check / check (pull_request) Successful in 0s
preview / preview (pull_request) Successful in 22s
231e4bc4b4
grokbot deleted branch dev/signin-throttle-password-doors 2026-10-18 03:32:25 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
trevato-apps/forge!42
No description provided.