nowo-tech / altcha-type-bundle
Symfony FormType for ALTCHA — privacy-friendly, self-hosted proof-of-work CAPTCHA alternative for forms (GDPR-friendly, no cookies).
Package info
github.com/nowo-tech/AltchaTypeBundle
Language:JavaScript
Type:symfony-bundle
pkg:composer/nowo-tech/altcha-type-bundle
Fund package maintenance!
Requires
- php: >=8.2 <8.6
- altcha-org/altcha: ^2.3
- psr/cache: ^1.0 || ^2.0 || ^3.0
- psr/clock: ^1.0
- psr/log: ^1.1 || ^2.0 || ^3.0
- symfony/asset: ^6.0 || ^7.0 || ^8.0
- symfony/clock: ^6.3 || ^7.0 || ^8.0
- symfony/config: ^6.0 || ^7.0 || ^8.0
- symfony/dependency-injection: ^6.0 || ^7.0 || ^8.0
- symfony/form: ^6.0 || ^7.0 || ^8.0
- symfony/framework-bundle: ^6.0 || ^7.0 || ^8.0
- symfony/http-foundation: ^6.0 || ^7.0 || ^8.0
- symfony/http-kernel: ^6.0 || ^7.0 || ^8.0
- symfony/options-resolver: ^6.0 || ^7.0 || ^8.0
- symfony/routing: ^6.0 || ^7.0 || ^8.0
- symfony/service-contracts: ^3.0
- symfony/translation: ^6.0 || ^7.0 || ^8.0
- symfony/twig-bundle: ^6.0 || ^7.0 || ^8.0
- symfony/validator: ^6.0 || ^7.0 || ^8.0
- symfony/yaml: ^6.0 || ^7.0 || ^8.0
- twig/extra-bundle: ^3.12
- twig/string-extra: ^3.12
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.0
- igor-php/igor-php: ^0.10.0
- nowo-tech/phpstan-frankenphp: ^1.0
- phpstan/extension-installer: ^1.4
- phpstan/phpstan: ^2.0
- phpstan/phpstan-symfony: ^2.0
- phpunit/phpunit: ^10.0
- rector/rector: ^2.0
- vincentlanglet/twig-cs-fixer: ^4.0
Suggests
- ext-scrypt: Required for ALTCHA v3 SCRYPT profiles (https://github.com/DomBlack/php-scrypt).
- ext-sodium: Required for ALTCHA v3 ARGON2ID profiles.
- pentatrion/vite-bundle: Compile the Stimulus controller with Pentatrion Vite in the host app (Twig vite_entry_* helpers). The Symfony 8 demo uses this stack with pnpm.
Provides
None
Conflicts
None
Replaces
None
README
⭐ Found this useful? Give it a star on GitHub so more developers can find it.
Symfony FormType for ALTCHA — a privacy-friendly, self-hosted proof-of-work CAPTCHA alternative for forms. No cookies, no fingerprinting, GDPR-friendly. Drop-in AltchaType field with named difficulty profiles, Twig themes, and optional Stimulus. For Symfony 6, 7 and 8 · PHP 8.2+.
This bundle is FrankenPHP worker mode friendly (kernel reused / FRANKENPHP_RESET_KERNEL unset or 0). See docs/FRANKENPHP-WORKER-AUDIT.md.
ALTCHA proof-of-work field — ready to verify |
ALTCHA verified — payload synced to the form |
Table of contents
- Quick search terms
- Features
- Installation
- Requirements
- Configuration
- Usage
- Demo
- Development
- Documentation
- Tests and coverage
- License
- Author
Quick search terms
Looking for Symfony ALTCHA, ALTCHA FormType, privacy-friendly captcha Symfony, proof-of-work captcha, GDPR captcha self-hosted, AltchaType, anti-spam form Symfony? You're in the right place.
Features
- ✅
AltchaType— drop-in Symfony form field that renders the ALTCHA widget and validates the PoW payload - ✅ Challenge endpoint —
GET /_nowo/altcha/challengeissues signed challenges (must bePUBLIC_ACCESS) - ✅ Named profiles —
default,low,high,contact,invisible(REQ-CFG-001) - ✅ Server validation —
AltchaValidconstraint via officialaltcha-org/altchaPHP library (v2, PBKDF2) and the ALTCHA v3 widget - ✅ ALTCHA v3 algorithms — PBKDF2 (default), SHA, memory-hard Argon2id and Scrypt per profile (workers shipped)
- ✅ Single-use payloads — replay protection through a PSR-6 cache pool (enabled by default)
- ✅ Profile-bound challenges — the profile is signed into the challenge; cheaper solutions are rejected
- ✅ Optional Sentinel — remote verification (verdict is final; local fallback only on transport errors, opt-in)
- ✅ Test mode —
enable: falseskips verification (demo / PHPUnit) - ✅ Works with or without Stimulus — built IIFE + MutationObserver, or a Stimulus controller
- ✅ TypeScript + Vite + pnpm — bundle IIFE is built with Vite; Symfony 8 demo uses Pentatrion Vite and pnpm only
- ✅ Compatible with Symfony 6, 7 and 8 and FrankenPHP
Installation
composer require nowo-tech/altcha-type-bundle
1. Register the bundle in config/bundles.php:
<?php return [ // ... Nowo\AltchaTypeBundle\NowoAltchaTypeBundle::class => ['all' => true], ];
2. Import routes (attribute route for the challenge endpoint; the Flex recipe does this):
# config/routes/nowo_altcha_type.yaml nowo_altcha_type: resource: '@NowoAltchaTypeBundle/Controller/' type: attribute
If the app uses a global access_control that locks ^/, allow the challenge path:
access_control: - { path: ^/_nowo/altcha/challenge, roles: PUBLIC_ACCESS } # ...
3. Form theme: The bundle automatically prepends its form theme from the form_theme option (see Configuration).
4. Frontend assets: run php bin/console assets:install. With include_script: true (default) the widget emits its CSS/JS tags from the named asset package nowo_altcha_type. To include them yourself (or with use_stimulus: true, see USAGE):
<link rel="stylesheet" href="{{ asset(nowo_altcha_type_asset_path('altcha-type.css'), nowo_altcha_type_asset_package()) }}"> <script src="{{ asset(nowo_altcha_type_asset_path('altcha-type.js'), nowo_altcha_type_asset_package()) }}" defer></script>
5. (Optional) Translations — domain NowoAltchaTypeBundle with the seven required locales (en, es, it, fr, pt, de, nl).
Full steps: docs/INSTALLATION.md.
Requirements
- PHP >= 8.2
- Symfony 6, 7 or 8 (
^6.0 || ^7.0 || ^8.0), including the mandatory floor 7.4, 8.0, and 8.1 - Stimulus optional — use the built script, or register the controller
- Vite to rebuild assets (or use the pre-built files in
src/Resources/public, which bundle the ALTCHA v3 widget) - When bundling the Stimulus controller yourself: npm
altcha^3.2 (the v3 widget speaks the protocol ofaltcha-org/altcha2.x)
Configuration
nowo_altcha_type: enable: true hmac_signature: '%env(ALTCHA_HMAC_SIGNATURE)%' # generated by the Flex recipe hmac_algorithm: SHA-256 default_profile: default form_theme: 'form_div_layout.html.twig' include_script: true use_stimulus: false debug: false replay_protection: cache_pool: cache.app # use a shared pool with several hosts profiles: default: cost: 5000 counter_min: 5000 counter_max: 10000 timeout: 30.0 expires: '+10 minutes' floating: false hide_logo: false hide_footer: false when@test: nowo_altcha_type: enable: false
See docs/CONFIGURATION.md for profiles, replay protection, Sentinel and timeouts.
Usage
use Nowo\AltchaTypeBundle\Form\Type\AltchaType; use Symfony\Component\Form\Extension\Core\Type\EmailType; use Symfony\Component\Form\Extension\Core\Type\TextareaType; use Symfony\Component\Form\Extension\Core\Type\TextType; $builder ->add('name', TextType::class) ->add('email', EmailType::class) ->add('message', TextareaType::class) ->add('security', AltchaType::class, [ 'profile' => 'contact', ]);
Render with a child loop (REQ-TWIG-003 / REQ-TWIG-005):
{{ form_start(form) }}
{% for child in form %}
{% if not child.rendered %}
{{ form_row(child) }}
{% endif %}
{% endfor %}
{{ form_end(form) }}
Demo
make up-symfony8 # http://localhost:8055 (FrankenPHP worker mode) make -C demo/symfony8 test-e2e # Playwright e2e (REQ-DEMO-013) make -C demo/symfony8 demo-screenshots # refreshes docs/images/demo/*.png
Development
composer install
pnpm install
pnpm run build
composer test
Root make release-check runs the full QA chain (REQ-MAKE-002).
Documentation
- Installation
- Configuration
- Usage
- Contributing
- Code of Conduct
- Changelog
- Upgrading
- Release
- Security
- Engram
- Spec-driven development
- GitHub Spec Kit
Additional documentation
- Use cases — which profile for which form
- Theming — form themes, CSS hooks, template overrides
- Demo with FrankenPHP (includes worker mode)
- FrankenPHP worker audit (
FRANKENPHP_RESET_KERNELunset/false) - GitHub Actions CI requirements
- PSR evaluation (REQ-CS-007)
- GitHub About fields
Tests and coverage
- Tests: PHPUnit (PHP), Vitest (TS/JS), Playwright (demo e2e)
- PHP: 100%
- TS/JS: 100%
- Python: N/A
make test-coverage # PHPUnit + coverage-php.txt (.scripts/php-coverage-percent.sh) make assets-test # Vitest + coverage-ts.txt (.scripts/ts-coverage-percent.sh) make coverage-check # fails below 100% PHP lines
TS coverage excludes src/Resources/assets/src/altcha-type.ts (IIFE bootstrap that only wires initAltchaContainer to the DOM; its logic lives in the covered altcha-type-lib.ts).
License
MIT — see LICENSE.


