The self-hosted CAPTCHA that runs in one container
Bollwark is an MIT-licensed, self-hostable proof-of-work CAPTCHA written in Rust. One binary, no cookies, no third-party calls, nothing stored on the visitor's device.
Bollwark is a CAPTCHA you can run on your own server: one MIT-licensed Rust binary that serves the widget, issues proof-of-work puzzles and verifies the answers. It sets no cookies, makes no outbound network calls, and needs no database to start.
What you get
- The server. One process exposing
GET /v1/puzzle,POST /v1/verify, the widget assets and a/healthzprobe. - The widget. One script tag. It solves an Argon2id proof-of-work puzzle in a Web Worker and puts an opaque token into your form. It is keyboard-operable and screen-reader friendly, with no image puzzles.
- Risk scoring. Every request is scored when the puzzle is issued and again when the form is submitted: request rate, header anomalies, a honeypot, server-measured time on page and behaviour counters, plus IP reputation and TLS fingerprint if you supply them.
- A decision log and dashboard, optional, backed by SQLite, with IP addresses truncated to /24 or /48 and rows pruned after 72 hours by default.
- Monitor mode. Risk verdicts are logged but not enforced, so you can watch what it would block on real traffic before you switch it on. Validation checks (an invalid proof, the origin allowlist, capacity limits) still refuse requests in monitor mode.
Deploy in five minutes
ADMIN_TOKEN="$(openssl rand -hex 32)"
docker run -d --name bollwark -p 3000:3000 \
-e ADMIN_TOKEN="$ADMIN_TOKEN" \
-e SITE_DB_PATH=/data/sites.db \
-v bollwark-data:/data \
ghcr.io/hauju/bollwark:latestCheck it with curl localhost:3000/healthz, then register a site with your
admin token to get a site key and secret:
curl -s -X POST http://localhost:3000/v1/sites \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"my-site"}'Embed the widget in your form, pointing at your own host:
<div id="captcha" data-sitekey="<SITE_KEY>"></div>
<script src="https://captcha.example.com/v1/widget.js"></script>Your backend forwards the token to POST /v1/verify with the site secret. The
deployment guide covers the reverse proxy,
persistence and upgrades.
One limitation to know up front: run a single replica. Challenges and rate counters live in the process's memory, so two replicas do not share them. Scale vertically for now.
Why self-host
- The data never leaves your server. The production binary links no HTTP client; the server pulls one in only for its tests. There is no telemetry, licence check or update ping. Country lookups, if you enable them, read a country database file (.mmdb) on your own disk.
- No vendor in the request path. No third-party script on your pages, no processing contract to sign, no transfer assessment.
- Minimal device access. Nothing is stored on the visitor's device: no cookies, no local storage. The widget does read a few browser properties and counts interactions, which ePrivacy Article 5(3) (§ 25 TDDDG) also covers. We consider that access strictly necessary for the security check, so no consent banner is needed, but assess it for your own setup. More in Is reCAPTCHA GDPR compliant?.
- You can read every line. The source is on GitHub under the MIT licence.
Or let us run it
If you would rather not operate another service, the hosted version is the same binary run by us in Germany, with the dashboard, retention and backups attached. There is a free tier of 1,000 verifies a month on one site; the paid plans are priced but not on sale yet. See hosted plans.
How it compares with other self-hostable options
| Bollwark | ALTCHA | Cap | mCaptcha | |
|---|---|---|---|---|
| Licence | MIT | MIT (widget) | Apache 2.0 | AGPL-3.0 |
| Default puzzle | Argon2id | PBKDF2 | SHA-256 plus time-lock | SHA-256 |
| Server-side risk scoring | Two passes | In paid Cloud and Sentinel tiers | Not described | Load-based difficulty |
Competitor details come from each project's public pages, linked in the header row. Bollwark itself is at version 0.1: young, and without a long production track record yet.
FAQ
Is it free?
Self-hosting is free, with no usage limits, and always will be. The hosted service has a free tier and paid plans.
Which licence?
MIT, for the server and the widget. The bundled Argon2 library (hash-wasm) is MIT as well.
Does it phone home?
No. The production binary makes no outbound HTTP requests at all, and IP reputation lists are read from a local file you control.
How do proof-of-work CAPTCHAs work?
The browser solves a small hash puzzle in the background, and the server checks the answer with one hash. Read how proof-of-work CAPTCHAs work for the details and the limits.
Related Posts
Looking for an hCaptcha alternative? Here is what changes
hCaptcha's free tier shows most visitors a challenge. Bollwark replaces it with an invisible proof-of-work check, no cookies, and the option to self-host. A side-by-side comparison and a migration guide.
Is Google reCAPTCHA GDPR compliant? Not out of the box
reCAPTCHA sets a cookie and loads its code from Google before it can protect anything, so many EU sites gate it behind consent. What that breaks on your forms, and what to use instead.
Proof-of-work CAPTCHA: how it works and where it breaks
A proof-of-work CAPTCHA makes the browser solve a puzzle instead of the human. How it works, why SHA-256 puzzles favour GPUs, why memory-hard Argon2id narrows the gap, and what proof-of-work cannot do alone.