Search by

omnishield / altcha

GlitchArt

Omnishield ALTCHA: a proof of work the site issues and checks itself, on altcha-org/altcha - a challenge signed with the site's key, the solution the visitor's browser found checked, a solution refused the second time; no third party, no cookie.

1.x-dev 2026-10-09 15:30 UTC

This package is not auto-updated.

Last update: 2026-10-10 14:13:37 UTC


README

ALTCHA for glitchr/omnishield: a captcha the site issues and checks itself. The visitor's browser solves a small proof of work - it derives keys until one starts as the challenge asks - and the site checks the solution: signed with the site's key, unexpired, for this action, and never posted before. Nobody else is involved: no third party, no cookie, nothing to ask the visitor's consent for.

Built on the official altcha-org/altcha (^2.3, MIT); the widget is the altcha web component (MIT).

use Omnishield\Altcha\AltchaGatewayFactory;
use Omnishield\Model\Attempt;

$gateway = (new AltchaGatewayFactory($spentTokens))->create(['hmac_key' => getenv('ALTCHA_HMAC_KEY')]);

$widget = $gateway->widget('contact');
echo $widget->html();                       // <script type="module" ...> <altcha-widget challenge="{...}" name="altcha">

$verdict = $gateway->verify(Attempt::fromPost($_POST, $widget, $ip));
$verdict->passed;                           // the second time: false, reasons ['duplicate']
omnishield:
    gateways:
        forms:
            factory: altcha
            options:
                hmac_key: '%env(ALTCHA_HMAC_KEY)%'     # required: long, random, secret
                # the widget's script: served by the Symfony bridge from this package (public/, MIT), the
                # page reaching nobody; in PHP alone a CDN by default - or copy public/altcha.min.js and name it

ALTCHA cannot tell a solution it saw before: the gateway remembers each one in the ReplayStoreInterface given to its factory until it would have lapsed - the application's, shared by every request (CacheReplayStore on a PSR-6 pool; the Symfony bundle gives cache.app). Without one, this process's memory: right in a test or a worker, not behind PHP-FPM.

Documentation: the options, the widget and its script, the challenge in the page or from a route, the action, what was verified - the whole way in PHP: issued, solved by the library, verified, refused when posted again.

License: MIT since 2026-10-09; earlier versions remain published under LGPL-3.0-or-later.

Formerly omniguard/altcha, renamed on 2026-10-10 with its family (glitchr/omnishield).