diff --git a/docs/registration-gates.md b/docs/registration-gates.md index 791a1e4..1b9278a 100644 --- a/docs/registration-gates.md +++ b/docs/registration-gates.md @@ -34,9 +34,12 @@ server verifies the protection, the expiry and the hash, and spends the id once. script all pass the same way, at the cost of CPU time on the client. - Cost: difficulty 18 is about 260,000 hashes on average, well under a second natively and a few seconds in browser JavaScript. It prices a bulk registration run in CPU; it does not stop a determined attacker with CPUs, and it is not a - proof of humanity. -- Replay: the spent-id set is in memory and bounded; a restart forgets it, so a challenge can be replayed for at most its - five-minute life. When the set is full the endpoint refuses rather than grows. + proof of humanity. The difficulty is embedded in the signed challenge, so a challenge verifies at the difficulty it was issued with. +- Replay: the spent-id set is in memory and bounded (10,000, entries dropped at expiry); a restart forgets it, so a challenge + can be replayed for at most its five-minute life. When the set is full the endpoint refuses rather than grows. One address + cannot fill it alone: `GET /auth/bifrost/challenge` and `POST /auth/bifrost/register` share one per-address budget + (`Bifrost:Registration:Limits`, 10 per minute by default, IPv6 by /64, over budget is 429 with Retry-After). An attacker + with many addresses can still fill the set; that is the accepted bound, the same as oidc-issuer.md. Not built: the sign-up form. No registration page exists today; the endpoint is the contract and the only client is a script. The form (solving in a worker, a `