A privacy-friendly, proof-of-work (PoW) captcha system designed to be a user-centric alternative to traditional captchas. Wicketkeeper protects your web forms from simple bots without requiring users to solve frustrating puzzles.
It achieves this by issuing a small, client-side computational challenge that is easy for a modern device to solve but costly for bots to perform at scale. The system is comprised of a Go backend, an embeddable JavaScript client, and a full-stack demo application.
This project was inspired by and adapts concepts and code from the TecharoHQ/anubis project.
- Features
- How It Works
- Project Structure
- Getting Started: Full Demo Setup
- Usage of Individual Components
- Acknowledgements
- License
- Proof-of-Work Engine: Replaces visual puzzles with a computational challenge that is easy for users but hard for bots.
- Stateless & Secure: Uses signed JSON Web Tokens (JWTs) for challenge/response cycles, eliminating server-side session state.
- Replay Attack Prevention: Leverages Redis Bloom filters for high-performance, time-windowed prevention of challenge reuse.
- Embeddable Client Widget: A lightweight, dependency-free JavaScript widget that integrates easily into any web form.
- Configurable: Easily adjust PoW difficulty, CORS origins, and ports via environment variables.
- Containerized: Full Docker and Docker Compose support for easy deployment of the backend server and its Redis dependency.
- Full-Stack Demo: Includes a complete Express.js + TypeScript example to demonstrate a real-world integration.
The Wicketkeeper ecosystem involves four main actors: the User's Browser, the Client Widget, your Application Backend, and the Wicketkeeper Server.
- Challenge Request: The client widget requests a new PoW challenge from the Wicketkeeper Server.
- Challenge Issuance: The server generates a unique challenge, packages it into a signed JWT, and sends it to the client.
- Proof of Work: The client's browser (using Web Workers) finds a solution (nonce) to the cryptographic puzzle.
- Form Integration: The solution is placed into a hidden input field in your web form.
- Server-Side Verification: When the user submits the form, your application's backend sends the solution to the Wicketkeeper Server's /v0/siteverify endpoint.
- Validation: The Wicketkeeper Server validates the JWT signature, the PoW correctness, and checks a Redis Bloom filter to ensure the challenge hasn't been used before. It returns a final success or failure response.
The repository is organized into three main components:
This guide will help you run the full Wicketkeeper ecosystem, including the backend server, the client widget, and the example application.
The easiest way to run the Go server and its Redis dependency is with Docker Compose.
This will build and start the wicketkeeper Go service on port 8080 and a redis-stack container. On the first run, a wicketkeeper.key file will be generated in server/data/.
The client widget needs to be compiled into a single JavaScript file.
This creates client/dist/wicketkeeper.js. Now, copy this file to the example application's public directory:
The example is an Express.js server that serves a simple HTML form and handles submissions.
You should see the output: 🚀 Server listening on http://localhost:8081.
You can now navigate to http://localhost:8081 in your browser to see the Wicketkeeper demo in action!
The server is configured via environment variables. See server/README.md for more details.
| LISTEN_PORT | The port on which the server will listen. | 8080 |
| REDIS_ADDR | The address of the Redis instance. | 127.0.0.1:6379 |
| DIFFICULTY | Number of leading zeros for the PoW hash. Higher is harder. | 4 |
| ALLOWED_ORIGINS | Comma-separated list of origins for CORS (e.g., https://domain.com). | * |
| PRIVATE_KEY_PATH | Path to store the Ed25519 private key. Will be created if it doesn't exist. | ./wicketkeeper.key |
API Endpoints:
- GET /v0/challenge: Issues a new PoW challenge.
- POST /v0/siteverify: Verifies a solved challenge.
The client is a single JS file (dist/wicketkeeper.js) that can be included in any HTML page.
1. Include the Script
2. Add the Widget to a Form
The script automatically initializes any div with the class .wicketkeeper.
The client can be configured with a custom challenge endpoint during the build step. See client/README.md for details.
This project is licensed under the MIT License.
.png)



