We deleted the distorted-text CAPTCHA from our own signup
Our registration form was gated by the least accessible challenge there is, while we sold the opposite. Here is what replaced it.
Bollwark's own signup form used to be protected by a distorted-text CAPTCHA.
It came in with the auth crate we vendored from a template, it worked, and for a while nobody looked at it. Then somebody did, and it was hard to unsee: we were selling an accessible, cookie-free challenge while gating our own front door with the least accessible challenge that exists.
Two failures, not one
The first is the obvious one. Distorted text is the canonical example of a challenge that excludes people — low vision, dyslexia, a screen reader, a bad phone camera on a train. Every one of those is a person who wanted to sign up and could not.
The second is subtler and, for us, worse. If we believed the product was good enough to put in front of someone else's login form, it should have been in front of ours. Shipping something else was an admission we hadn't made out loud.
What replaced it
The signup form now runs the same widget our customers embed. It calls
POST /v1/verify with the site secret — not the admin token, which can
delete any site on the instance and has no business anywhere near an
unauthenticated path.
There is no fallback. We didn't keep the text challenge as a safety net, because a safety net made of distorted text is still distorted text; it just waits for a bad day to exclude someone. If the captcha keys aren't configured, registration isn't gated at all, which is what you want on a local checkout.
What it costs us
Verification fails closed. Ok(false) is a rejection and an error is "no
verdict", which returns a retryable failure rather than a guess.
That means an outage at api.bollwark.eu stops new registrations on
bollwark.eu. Existing logins are untouched, since the captcha only gates a new
account, but it's a real cost and worth naming rather than burying.
Upstream's client failover covers the widget losing the service. It cannot
cover this side: if we cannot reach the service, there is nobody to ask.
That is the honest price of eating your own cooking. We think it is the right one to pay.
The part we did not expect
There is a nice second-order effect. When someone confirms the email code we
sent them, we post that verdict's challenge_id back to POST /v1/feedback
as legit — a person read their inbox, so the decision that let them through
was correct.
It is the one place this application can produce ground truth for its own
scorer. It is best effort: a failure there is a log line, never a held login.
And legit is the only verdict we vouch for — an unconfirmed registration is
unfinished, not spam.