DEV Community

Ronny Cruz
Ronny Cruz

Posted on

The Signup Form With Nothing to Filter

By Ronny Cruz Alvarez, founder of Open Feed Network (Candor Network). Solo founder, one production platform, and a registration flow that deliberately knows almost nothing about you.

The problem I built for myself

Candor's registration collects no email. No phone number. No CAPTCHA account, no OAuth identity, nothing to verify because there is nothing to verify against. You install the app, choose a username, set a PIN, and the browser generates an ECDSA P-256 keypair on your device. The server receives exactly two things: the username and the public key. Twelve seed words and a recovery file stay with you. That's the entire signup.

I built it that way on purpose. Zero-PII registration means there is no email database to breach, no phone list to subpoena, no identity graph to sell. For a platform whose whole thesis is that your voice shouldn't depend on trusting me, it's the right design.

It is also, from a spam-defense perspective, a nightmare I handed to myself. Every signal the industry leans on for signup screening — disposable email domains, email reputation, verification round-trips — assumes an email exists. Mine doesn't. And the flow is fast by design, which means a script can be fast too. Last Friday an AI code review put it bluntly: nothing stopped a bot from looping the registration endpoint and minting fifty thousand accounts, flooding the feed with identities that cost nothing to create.

The review was right. Nothing did.

The irony was that I sell the fix

The week before, I had launched Sentinel Signup — a registration-screening service for Fediverse instances getting hammered by exactly this kind of wave. Six layers: IP and domain reputation, subnet velocity, timing patterns, content analysis, the works. Built for Mastodon admins drowning in fake approval requests.

My own platform wasn't using it. The cobbler's children, barefoot as usual.

Worse: it couldn't use it. The screening API required an email in every request, because every customer I'd imagined had one. My own registration flow — the most privacy-preserving one I know of — was structurally locked out of my own security product. And the engine went further than requiring email: an absent email scored a +30 "invalid email" penalty. Run my own honest users through my own filter, and every single one would start out looking suspicious. A privacy feature read as a fraud signal.

That's worth sitting with, because it isn't a quirk of my code. It's what happens across this industry when anti-abuse systems assume identity signals that privacy-respecting platforms deliberately don't collect. The default posture of spam defense is that knowing less about a user makes them more suspect. If we want privacy-preserving platforms to exist, the security tooling has to stop punishing them for the privacy.

The fix: absence is not malformation

The change was small and philosophically important. The engine now distinguishes two things it previously conflated:

  • Email provided but malformed — still penalized. Someone sent garbage in an email field; that's a real signal.
  • Email not provided at all — recorded as a neutral fact, weight zero. A zero-PII platform has nothing to send, and honesty about that costs its users nothing.

Every check that fires when an email is present — disposable-domain lists, throwaway-pattern matching — fires exactly as before. The test suite proves both directions: no penalty for absence, no softening for garbage.

With that, my registration could finally be screened by my own product, using only what genuinely exists: username, real client IP (from the CDN's forwarded header — the service itself never sees raw connections), user agent, and time. No email, no synthetic placeholder polluting the analysis. What's real gets analyzed; what doesn't exist isn't faked.

The posture question nobody asks until an outage

Wiring the screen into the registration handler forced a decision most integrations skip: what happens when screening itself fails?

For my Mastodon-admin customers, the answer is fail-to-queue — a screening error routes the applicant to the human approval queue, where a moderator decides. Never silently admitted, never silently rejected.

My registration flow has no queue. There is no moderator between a human and their account, by design. So fail-to-queue is meaningless here, and the two textbook options are both wrong: fail-closed means a two-second screening hiccup locks real humans out of joining the platform; fail-open with silence means bots stream through and nobody knows.

The posture I shipped: block only on a hard block verdict; on a flag or any error, allow — loudly. Every flagged registration and every screening failure lands in the logs with the reason. A burst that slips through during an outage is visible and attributable within minutes. A human locked out by a dependency failure would be invisible forever — they'd just leave. Between those two failure costs, the choice isn't close.

What the gate did to the bot scenario

After deploying, I ran the attack against my own production server. First, one legitimate registration — passed clean, token issued, indistinguishable from before. Then the miniature version of the fifty-thousand-account scenario: rapid-fire registrations from a single IP, fresh keypair each time.

The escalation ladder, straight from the logs:

  • Registrations 1–2: passed.
  • Registration 3: flagged — multiple accounts from one IP, high subnet velocity — allowed, logged.
  • Registration 4: blocked — velocity threshold crossed.
  • Registrations 5 through 15: blocked instantly — "previously blocked IP." The gate remembered. Every subsequent attempt died without even being re-scored.

A bot that wanted fifty thousand accounts got four. And the human who registered first never noticed anything happened at all.

Honest limitations

  • My test was a single IP. A real adversary rotates through residential proxies, and same-IP velocity weakens against that. Subnet velocity catches the lazy version; a well-distributed botnet is a harder problem, and I'm not claiming this closes it. Rate-limiting keypair minting per network is a floor, not a ceiling.
  • The remaining signals are thin by design. Username, IP, user agent, timing — that's the whole feature set when you refuse to collect identity. I consider that trade worth it, but it's a trade, and anyone telling you they can fully replace email reputation with four fields is selling something.
  • Thresholds stay private. I've written before about why rejection messages must never teach attackers what tripped them; the same rule applies here. A blocked registration sees "temporarily unavailable," not which layer fired or at what count. That's deliberate, and it means this article tells you the shape of the defense, not its numbers.
  • This is one platform, days old in production. The evidence is my own logs, dated, from my own server. It is not a fleet-scale study.

Why dogfooding was the point

Sentinel Signup's pitch to Mastodon admins has been "built for ourselves, available to you" — the modules were extracted from the platform's own defenses. As of this week that's true in the fullest sense: the same screening engine, the same API, the same tenant system any customer gets is what stands between my own registration endpoint and the next minting run. Customer zero is me. When it breaks, I'm the first one it breaks on — which is exactly the arrangement that keeps a safety product honest.

If you run a platform with a registration flow — especially one that collects less than the industry assumes you should — Sentinel Signup is free during beta at signup.candortheopenfeednetwork.com, and the zero-PII support described here is live for every tenant. If you think my failure posture is wrong, I especially want to hear that: tips@candortheopenfeednetwork.com. I answer everything myself.

Top comments (0)