Plainwork / Catalogue / Signup Shield Kit
Signup and contact forms for static sites that limit what bots can make you send
A Cloudflare Worker with double opt-in, proof of work, honeypot and per-address caps, plus three sample sites, docs and an attack simulator. One purchase, no subscription.
- Worker (one file, 27.2 KB, no dependencies): double opt-in, proof of work tied to the address, honeypot, signed challenges, per-address, per-IP and daily caps, contact form, subscriber CSV export
- Browser script (12.0 KB): solves the proof in a background thread, announces results to screen readers, no cookies, no third-party requests
- 11 sample pages in 3 designs: a newsletter site, a product waitlist and a studio contact page, each with light and dark themes and starter privacy text
- Attack simulator: 14 scripted scenarios run against the real Worker, with the numbers it prints
- 34 automated tests, a local server that prints emails in your terminal, a form checker and a colour-contrast checker
- Plain-text docs: deploy in 15 minutes, how it works, what it does not stop, free-plan limits, accessibility, licence and changelog
This is real output from the kit's own attack simulator (node tools/simulate.mjs), which runs the shipped Worker against scripted abuse. "Emails" are confirmation emails the Worker asked the email provider to send. Difficulty here is 16 bits and the run takes about a minute; the kit's default is 18.
1. Naive bot posts 1,000 random addresses, no proof of work
attempts 1000 emails sent 0 storage writes 0 expected: 0 emails
2. Form-filling bot fills the hidden honeypot field, 1,000 times
attempts 1000 emails sent 0 storage writes 0 expected: 0 emails
3. Bot solves the proof of work, one IP, 200 random addresses
attempts 200 emails sent 5 storage writes 16 expected: at most 5 emails (IP cap)
4. Botnet: 300 IPs each solve the work, 300 random addresses
attempts 300 emails sent 100 storage writes 301 expected: at most 100 emails (daily cap)
5. Subscription bomb: 300 IPs all target ONE victim address
attempts 300 emails sent 1 storage writes 4 expected: 1 email
5b. Same bomb, but all 300 requests are fired in parallel
attempts 300 emails sent 1 storage writes 4 expected: 1 email
6. Replay one solved proof against 200 different addresses
attempts 200 emails sent 1 storage writes 4 expected: 1 email (the one address the proof was solved for)
7. Same as 6 but the bot posts instantly (before the minimum age)
attempts 200 emails sent 0 storage writes 0 expected: 0 emails
8. Real visitors: 40 people from 40 IPs subscribe
attempts 40 emails sent 40 storage writes 121 expected: 40 emails
9. Storage quota used up (storage writes fail), then 50 real visitors
attempts 50 emails sent 0 storage writes 0 expected: 0 emails, fail closed
10. Mailbox variants: 300 IPs target ONE Gmail inbox as [email protected], v.ictim@, [email protected]
attempts 300 emails sent 1 storage writes 4 expected: 1 email
11. Contact form: one solved proof reused for 200 different messages from 200 IPs
attempts 200 emails sent 1 storage writes 5 expected: 1 email to the owner
12. Contact flood that pays the work for every message (200 IPs), then one real signup
attempts 201 emails sent 31 storage writes 124 expected: at most 30 owner emails (contact cap), and the signup still gets its email
13. One IPv6 /64 (50 addresses in the same network) signs up 50 random addresses
attempts 50 emails sent 5 storage writes 16 expected: at most 5 emails (IP cap counts the /64)And the form markup you put on a page (the script adds the hidden field and does the rest):
<form class="shield" data-shield="subscribe" data-endpoint="https://signup-shield.your-name.workers.dev">
<label for="s-email">Email address</label>
<input id="s-email" name="email" type="email" autocomplete="email" required>
<p data-shield-status id="form-status" role="status" aria-live="polite"></p>
<button type="submit">Subscribe</button>
</form>
<script src="assets/shield.js" defer></script>Open the live demo to try five of these attacks against a cut-down copy of the Worker running in the page.
Made for
Who it's for
One mailbox, one confirmation email a day
Pending for 24 hours, never again once confirmed. Plus tags and Gmail dots count as one mailbox: 300 spellings of one inbox from 300 IPs produced 1 email in the simulator. Other providers' aliases are not folded; ATTACKS.txt says which.
Checks that cost you nothing run first
Wrong origin, bad input, a filled honeypot or a missing proof are refused before anything is stored or sent. A flood of lazy bots costs no storage writes and no email.
No third party in the loop
The check runs in your visitor's browser and on your own Worker. Nothing is sent to a captcha company, and there is no puzzle or cookie.
Fits the free plans, and says so
Designed around the Cloudflare free limits (10 ms CPU per request, a SQLite Durable Object for state). The limits file gives the numbers and what happens when you hit them: it fails closed and sends nothing.
Honest about what it cannot stop
Proof of work raises cost; it does not unmask a bot using a real browser. The docs and the simulator show where the daily ceiling takes over, and what that costs your real visitors.
What's inside
Everything you get
How it works
From checkout to done
Buy
Download the zip and run node tools/serve-local.mjs to try it on your computer
Open
Follow DEPLOY.txt: set three secrets and deploy the Worker
Use
Point a template's forms at your Worker address and upload the site
Questions
Good to know
Does it send my newsletter?
No. It collects and confirms addresses and gives you a CSV; you send your issues with whatever mail tool you already use. It also makes signed one-click unsubscribe links for the addresses you hold.
Can bots still get through?
Some can. A bot that runs a real browser or copies the solver pays the proof of work. What the kit limits is the harm: one email per address a day, per-IP and daily ceilings, and nothing sent when storage is unavailable. ATTACKS.txt in the zip lists what it does not stop.
Why not just use Cloudflare Turnstile?
Turnstile is a strong check and you can add it. This kit avoids a third party by default, includes the opt-in, caps and email around the check, and gives you a way to test the result. Pick the trade-off that suits your audience.
What do I need to run it?
A free Cloudflare account, a free Resend account with a verified sending domain, Node 18 or newer on your computer to deploy, and any static host for the pages. Costs on the free plans are zero; the limits file explains the daily allowances.
Is the privacy text legal advice?
No. The templates include starter privacy wording for you to edit; you are responsible for your notice and for following the email and data-protection rules where you and your readers live.
Can I use it for clients?
Yes, on any number of sites you own or build for clients. You may not resell the kit or its templates. The licence file has the wording.
What if it doesn't work for me?
If it doesn't work as described or you can't access it, email us within 14 days and we'll fix it or refund you. See the refund policy for details.
Signup Shield Kit
Static-site newsletter, waitlist and contact forms with double opt-in and subscription-bombing protection. Worker, 3 sites, simulator.
More from the catalogue
Other small tools
Switch Brief
A sourced replacement brief for the dev tool you want to leave: your must-haves as tests, dated prices and licences, in 24 hours.
Causeway
Free daily bridges puzzle: one answer each, solvable by logic, no ads, no account. Supporter Pass $4.
Turnkeep
Fair on-call and duty rota planner with swaps and a fairness table. Free for 4 people, $6/month flat for a team.