# 0038. The captcha is invisible

- **Status:** Accepted
- **Date:** 2026-09-24

## Context

Every public form — contact page, the sidebar's three forms, the storage page's callback, registration, password
reminder, the proposal request — showed Yii's image captcha. The redesigned forms have no room for it, and the
sibling project (Molino) had already dropped the visible captcha for a hidden one.

## Decision

The `site/captcha` action and every model's `captcha` validator stay. What changes is the field:
`app\helpers\InvisibleCaptcha::field()` renders it hidden, with the current verify code base64-encoded on a
`data-c` attribute, and `resources/js/components/InvisibleCaptcha.js` copies it into the value on submit, in the
window's capture phase so it runs before any handler that serializes the form. AJAX endpoints return the
regenerated code as `captcha`, because a successful check spends the old one.

## Alternatives

- **Keep the image captcha.** Friction on every form, against the design.
- **reCAPTCHA / Turnstile.** A third-party script, a consent question under the cookie banner, and an external
  dependency for what is a low-value target.
- **A plain honeypot field.** Simpler, but it would have meant replacing the validator on seven forms.

## Consequences

- It stops bots that post without running the page's JavaScript, nothing more: the code is in the page for
  anyone who reads it.
- Without JavaScript no form can be sent; the error only says the code is missing.
- A new AJAX form has to hand the code back with `refreshCaptcha( response.captcha )`, or its second submit fails.
