climactic/laravel-altcha

ALTCHA proof-of-work spam protection for Laravel โ€” drop-in middleware, validation rule, and challenge endpoint.

Maintainers

Package info

github.com/Climactic/laravel-altcha

Homepage

pkg:composer/climactic/laravel-altcha

Fund package maintenance!

climactic

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

0.1.0 2026-04-17 21:44 UTC

This package is auto-updated.

Last update: 2026-04-17 21:53:51 UTC


README

Laravel Altcha

Laravel Altcha

Drop-in ALTCHA proof-of-work spam protection for Laravel. Privacy-first, self-hosted, no third-party services, no tracking โ€” just a middleware, a validation rule, and a challenge endpoint.

Discord Latest Version on Packagist GitHub Tests Action Status GitHub Code Style Action Status Total Downloads Sponsor on GitHub Support on Ko-fi

๐Ÿ“– Table of Contents

Features

  • ๐Ÿ›ก๏ธ Drop-in middleware โ€” protect any POST route with a single altcha alias
  • โœ… Validation rule โ€” use new Altcha() in FormRequests and inline validators
  • ๐ŸŽฏ Auto-registered challenge endpoint โ€” GET /altcha works out of the box, fully customizable
  • ๐Ÿ” Replay prevention โ€” challenges are single-use, cached by signature
  • ๐Ÿ”€ Pluggable algorithms โ€” PBKDF2, Argon2id, or Scrypt
  • โšก Optional fast verification โ€” skip re-derivation with a secondary HMAC key
  • ๐Ÿงฉ Framework-agnostic frontend โ€” React/TypeScript stubs shipped; any framework supported via the altcha web component
  • ๐ŸŒ i18n-friendly โ€” all user-facing messages routed through __()
  • ๐Ÿงช Tested โ€” 14 feature tests across middleware, rule, and endpoint
  • ๐Ÿ”’ Privacy-first โ€” no cookies, no fingerprinting, no third-party calls, GDPR/HIPAA/CCPA compliant

Built on altcha-org/altcha v2 (PoW v2).

๐ŸŒŸ Sponsors

Your logo here โ€” Become a sponsor and get your logo featured in this README and on our website.

Interested in title sponsorship? Contact us at sponsors@climactic.co for premium placement and recognition.

๐Ÿ“ฆ Installation

Install the package via Composer:

composer require climactic/laravel-altcha

Publish the config:

php artisan vendor:publish --tag="laravel-altcha-config"

Add the required env vars:

ALTCHA_ENABLED=true
ALTCHA_HMAC_SECRET=<long-random-string>

๐Ÿ’ก Generate a strong secret with php -r "echo bin2hex(random_bytes(32));".

โš™๏ธ Configuration

The published config/altcha.php exposes everything you might want to tune:

return [
    'enabled' => (bool) env('ALTCHA_ENABLED', false),
    'hmac_secret' => env('ALTCHA_HMAC_SECRET'),
    'hmac_key_secret' => env('ALTCHA_HMAC_KEY_SECRET'),
    'algorithm' => env('ALTCHA_ALGORITHM', 'pbkdf2'),
    'cost' => (int) env('ALTCHA_COST', 10000),
    'expires' => (int) env('ALTCHA_EXPIRES', 300),
    'memory_cost' => ...,
    'parallelism' => ...,
    'cache_store' => env('ALTCHA_CACHE_STORE'),
    'field' => env('ALTCHA_FIELD', 'altcha'),
    'route' => [
        'enabled' => true,
        'path' => 'altcha',
        'name' => 'altcha.challenge',
        'prefix' => '',
        'domain' => null,
        'middleware' => ['web'],
    ],
];

Environment Variables

Key Env Default Notes
enabled ALTCHA_ENABLED false Master switch. When false, middleware and rule pass-through and the endpoint returns 404.
hmac_secret ALTCHA_HMAC_SECRET โ€” Required when enabled. Used to sign challenges.
hmac_key_secret ALTCHA_HMAC_KEY_SECRET null Optional. Enables the fast verification path (skips re-derivation).
algorithm ALTCHA_ALGORITHM pbkdf2 pbkdf2, argon2id, or scrypt.
cost ALTCHA_COST 10000 PoW iterations / time cost. Higher = harder for bots.
expires ALTCHA_EXPIRES 300 Challenge lifetime in seconds. Also used as the replay cache TTL.
memory_cost ALTCHA_MEMORY_COST null Argon2id / Scrypt only.
parallelism ALTCHA_PARALLELISM null Scrypt only.
cache_store ALTCHA_CACHE_STORE default Cache store used for replay prevention.
field ALTCHA_FIELD altcha Request field name the middleware/rule read from.
route.enabled ALTCHA_ROUTE_ENABLED true Disable to register the endpoint yourself.
route.path ALTCHA_ROUTE_PATH altcha Challenge endpoint path.
route.name ALTCHA_ROUTE_NAME altcha.challenge Route name for route() helpers.
route.prefix ALTCHA_ROUTE_PREFIX '' Route group prefix.
route.domain ALTCHA_ROUTE_DOMAIN null Route group domain.

Algorithms

Algorithm Extension When to use
pbkdf2 built-in Default. Works everywhere. Good baseline.
argon2id ext-sodium Strongest. Memory-hard โ€” resistant to ASIC/GPU acceleration.
scrypt php-scrypt Memory-hard alternative.

๐Ÿš€ Usage

๐Ÿ›ก๏ธ Middleware

Protect any POST route with the altcha middleware alias:

use Illuminate\Support\Facades\Route;

Route::post('/contact', ContactController::class)->middleware('altcha');
Route::post('/register', RegisterController::class)->middleware('altcha');

Works inside a middleware group too:

Route::middleware(['web', 'altcha'])->group(function () {
    Route::post('/enterprise', EnterpriseController::class);
    Route::post('/waitlist', WaitlistController::class);
});

Failed verifications throw a ValidationException with an error on the altcha field โ€” so Inertia/Blade forms display the error like any other validation failure.

โœ… Validation Rule

For FormRequests and inline validators:

use Climactic\Altcha\Rules\Altcha;

public function rules(): array
{
    return [
        'email' => ['required', 'email'],
        'altcha' => [new Altcha()],
    ];
}

The rule is implicit โ€” it runs even when the field is empty, so you don't need required unless you want its error message. When altcha.enabled is false, it passes silently.

๐ŸŽฏ Challenge Endpoint

GET /altcha is registered automatically and returns a signed challenge:

{
    "parameters": {
        "algorithm": "pbkdf2",
        "salt": "...",
        "cost": 10000
    },
    "signature": "..."
}

Customize via config/altcha.php โ€” path, prefix, domain, and middleware group are all tunable. Set route.enabled to false and register the controller yourself if you need full control:

use Climactic\Altcha\Http\Controllers\AltchaChallengeController;

Route::get('/security/challenge', AltchaChallengeController::class)
    ->middleware(['web', 'throttle:60,1']);

๐Ÿงฉ Frontend Integration

Install the ALTCHA web component:

npm install altcha

React (Inertia) stubs

Publish the TypeScript wrappers:

php artisan vendor:publish --tag="altcha-frontend"

This copies:

  • resources/js/components/altcha-widget.tsx โ€” React wrapper around the web component
  • resources/js/types/altcha.d.ts โ€” JSX types for <altcha-widget>

Then use it in any form:

import { AltchaWidget } from '@/components/altcha-widget';

export default function ContactForm() {
    return (
        <form method="post" action="/contact">
            <input name="email" type="email" required />
            <textarea name="message" required />

            <AltchaWidget challengeUrl="/altcha" />

            <button type="submit">Send</button>
        </form>
    );
}

The widget solves the challenge in a web worker and injects the solution into a hidden altcha input at submit time โ€” totally invisible to your users.

Other frameworks

Vue, Svelte, Solid, Lit, Alpine, and plain HTML work out of the box with the altcha npm package โ€” no wrapper needed:

<altcha-widget challengeurl="/altcha" auto="onsubmit" floating="bottom"></altcha-widget>

See the official framework starters for idiomatic integrations.

๐Ÿ” Replay Prevention

Every valid solution is recorded in the cache using the challenge signature as the key. A second submission of the same payload fails with a replay error:

Security challenge has already been used. Please try again.

The record TTL equals altcha.expires (so entries self-expire when the challenge itself would have). Use the cache_store config to isolate this from your application's default cache.

๐Ÿงฎ Custom Verification

Need to verify outside the middleware/rule (e.g. an API controller, a queued job)? Resolve the Verifier:

use Climactic\Altcha\Support\Verifier;

$result = app(Verifier::class)->verify($request->input('altcha'));

if (! $result->verified) {
    return response()->json([
        'error' => 'captcha',
        'reason' => $result->reason, // missing | malformed | invalid | expired | replay
    ], 422);
}

๐Ÿ“š API Reference

๐Ÿ”ง Classes

Class Purpose
Climactic\Altcha\Http\Middleware\VerifyAltcha Middleware registered under the altcha alias.
Climactic\Altcha\Http\Controllers\AltchaChallengeController Invokable controller backing GET /altcha.
Climactic\Altcha\Rules\Altcha Implicit ValidationRule implementation.
Climactic\Altcha\Support\Verifier Core verification service. Resolvable from the container.
Climactic\Altcha\Support\VerificationResult Immutable result value object (verified, reason, replayKey).
Climactic\Altcha\Support\AlgorithmFactory Produces a DeriveKeyInterface for the configured algorithm.

๐Ÿง  Middleware Behavior

VerifyAltcha intentionally bypasses verification in a few cases so you can apply it broadly without extra logic:

Condition Outcome
altcha.enabled is false Pass-through
Request method is not POST Pass-through
User is authenticated Pass-through
Missing/invalid payload ValidationException on the altcha field
Valid payload Marked used, request continues

๐Ÿงพ Verifier Reasons

VerificationResult::$reason is one of:

Constant String Meaning
Verifier::REASON_MISSING missing No payload was submitted.
Verifier::REASON_MALFORMED malformed Payload wasn't valid base64 / JSON / shape.
Verifier::REASON_INVALID invalid Signature or derived key didn't match.
Verifier::REASON_EXPIRED expired Challenge passed expiresAt.
Verifier::REASON_REPLAY replay Payload was already accepted once.

๐Ÿงช Testing

composer test

Type check + lint:

composer analyse
composer format

๐Ÿ“‹ Changelog

Please see CHANGELOG for more information on what has changed recently.

๐Ÿค Contributing

Please see CONTRIBUTING for details. You can also join our Discord server to discuss ideas and get help: Discord Invite.

๐Ÿ”’ Security Vulnerabilities

Please report security vulnerabilities to security@climactic.co.

๐Ÿ’– Support This Project

Laravel Altcha is free and open source, built and maintained with care. If this package helps keep spam off your forms or saves you a Cloudflare Turnstile bill, please consider supporting its continued development.

Sponsor on GitHub ย  Support on Ko-fi

โญ Star History

Star History Chart

๐Ÿ“ฆ Other Packages

Check out our other Laravel packages:

Package Description
๐Ÿ›ก๏ธ laravel-spam Anti-spam toolkit: disposable-email and blocklist rules, StopForumSpam lookups, middleware, and a check API. Pairs naturally with Laravel Altcha.
๐Ÿ’ณ laravel-credits Ledger-based credit system for virtual currencies, reward points, and credit-based features, with transfers, metadata querying, and historical balances.
๐Ÿงพ laravel-polar Polar.sh billing integration for Laravel โ€” subscriptions, one-off purchases, webhooks, and license keys.

๐Ÿ“„ License

The MIT License (MIT). Please see License File for more information.

โš–๏ธ Disclaimer

This package is not affiliated with Laravel or ALTCHA. It's for Laravel but is not by Laravel. Laravel is a trademark of Taylor Otwell. ALTCHA is a trademark of Daniel Regeci / ALTCHA.