A ConfirmEdit backend for MediaWiki that uses Altcha proof-of-work challenges. No external service, no cookies, no tracking.
When a CAPTCHA trigger fires (account creation, bad login, URL addition), the server generates a SHA-256 proof-of-work challenge signed with an HMAC key. The browser widget solves it client-side (typically < 2 seconds) and submits the solution. The server re-derives the expected hash and verifies the HMAC. No stored state required.
The Altcha widget JS is never loaded from a CDN by the browser. Instead, Special:AltchaJS fetches it server-side on first use, caches it in $wgUploadDirectory/altcha-cache/, and serves it locally. The cache refreshes automatically after the configured TTL. If the upstream is unreachable, the stale cache is served as a fallback.
- MediaWiki 1.41+
- ConfirmEdit extension
Tested against MediaWiki 1.44 with the refactored ConfirmEdit AuthManager API.
-
Clone or copy this directory into your wiki's
extensions/AltchaCaptcha/. -
Add to
LocalSettings.php:wfLoadExtension( 'ConfirmEdit' ); wfLoadExtension( 'AltchaCaptcha' ); $wgCaptchaClass = 'AltchaCaptcha'; $wgAltchaHmacKey = 'your-secret-key-here'; // openssl rand -hex 32 # Optional complexity tuning (default shown): $wgAltchaComplexityMin = 40000; $wgAltchaComplexityMax = 200000; # Trigger CAPTCHA on account creation and bad login: $wgCaptchaTriggers['createaccount'] = true; $wgCaptchaTriggers['badlogin'] = true; $wgCaptchaTriggers['addurl'] = true;
-
Generate your HMAC key:
openssl rand -hex 32
The widget JS is fetched and cached automatically on first page load — no manual download required.
| Variable | Default | Description |
|---|---|---|
$wgAltchaHmacKey |
'' |
Required. HMAC-SHA256 key for signing challenges. |
$wgAltchaComplexityMin |
40000 |
Minimum PoW iterations (lower = faster solve). |
$wgAltchaComplexityMax |
200000 |
Maximum PoW iterations. |
$wgAltchaJSUrl |
https://cdn.jsdelivr.net/npm/altcha/dist/altcha.min.js |
Upstream URL the server fetches the widget JS from. |
$wgAltchaJSTTL |
86400 |
Seconds before the cached JS is re-fetched from upstream. |
MIT