Current state: requestPasswordReset sends the reset mail inside the request. An address with an account costs a token mint plus a full SMTP conversation before the answer, an unknown address returns after one lookup, so response timing reveals which addresses hold accounts even though the body is byte identical for both. A slow relay also holds the request goroutine for the whole conversation.
Proposal: hand account mail to a small bounded worker and answer before any relay work starts, giving each delivery its own deadline and retrying within the link lifetime. Timing becomes uniform for every address and a relay outage stops costing request goroutines. The invitation path can stay synchronous, it is authenticated and its caller wants the delivery result.
Trigger: the neutral answer was made identical for every address, timing is the channel that remains.
Current state: requestPasswordReset sends the reset mail inside the request. An address with an account costs a token mint plus a full SMTP conversation before the answer, an unknown address returns after one lookup, so response timing reveals which addresses hold accounts even though the body is byte identical for both. A slow relay also holds the request goroutine for the whole conversation.
Proposal: hand account mail to a small bounded worker and answer before any relay work starts, giving each delivery its own deadline and retrying within the link lifetime. Timing becomes uniform for every address and a relay outage stops costing request goroutines. The invitation path can stay synchronous, it is authenticated and its caller wants the delivery result.
Trigger: the neutral answer was made identical for every address, timing is the channel that remains.