v1.2.0 — Proof of work + placeholder widget
What's new
Proof of work
Before this release, the captcha init endpoint was free: a bot could harvest valid tokens and field names at will, then forge a behavioral log. PoW changes the economics — every token costs CPU time first.
The init endpoint becomes two-phase: it first answers an HMAC-signed challenge that the client solves in a background Web Worker (~100–500 ms on desktop, transparent for humans), then issues the token once the solution is verified. Enabled by default (JS and endpoint ship together in the plugin, no desync risk).
Tunable via gaitcha_config:
add_filter( 'gaitcha_config', function ( $config ) {
$config['pow_difficulty'] = 20; // Harder if you're under attack (default: 18).
// $config['pow'] = false; // Or disable entirely.
return $config;
} );Solved challenges are consumed via WPTokenStore (anti-replay was already on by default): one solution = one token.
Placeholder widget
The captcha now appears immediately at page load as a dimmed, non-interactive placeholder — same dimensions as the final widget (zero layout shift), and nothing scrapable (no field name, no token, no hidden inputs). On the first interaction, the PoW resolves in the background and the placeholder upgrades in place.
Fixed
- Ninja Forms (#2) — form could never be resubmitted after a first attempt without checking the captcha. NF kept a stale field error in its Backbone model; the checkbox lives outside that model so checking it never lifted the error, and NF blocked the resubmission client-side. The adapter now clears the field errors when the widget gets checked. Thanks to @clementpolito for the detailed report.
Under the hood
- REST endpoint forwards the decoded JSON request body to
Gaitcha\AbstractEndpoint::handleInit()(two-phase init wiring) - Core dependency:
willybahuaud/gaitcha ^0.7 - Bundled JS includes the PoW solver (inline SHA-256 in a Blob-based Web Worker, with a chunked main-thread fallback for strict CSP setups)
Installation
Auto-update from the WordPress dashboard, or download the ZIP and upload via Plugins > Add New > Upload.