Two bugs prevented form submission entirely:
1. `challengeurl` attribute was renamed to `challenge` in altcha v3 — the
widget silently ignored the old name so it never fetched a challenge.
2. `altchaWidget.value` is not an exposed property on the v3 custom element;
read the solved payload from the hidden `<input name="altcha">` the widget
renders in light DOM instead.
Also clears the err-altcha error message at the start of each submit attempt
so it doesn't linger after the user completes verification and retries.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Server: /api/altcha generates a SHA-256 challenge (v3 API); /api/contact
verifies the widget payload before processing the submission
- Widget: added <altcha-widget> from CDN above the submit button
- contact-form.js: blocks submission if altcha value is missing and
appends it to FormData
- docker-compose.yml: passes ALTCHA_HMAC_KEY env var to main-site container
- package.json: added altcha@3.1.0
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- First and last name are now separate required fields
- Server combines them into a full name for emails
- Sends a push notification to NTFY_URL on new inquiry (fire-and-forget)
- NTFY_URL env var wired through docker-compose
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces the third-party iframe form on both the homepage and contact page
with the self-hosted form: drag-and-drop photo upload, honeypot, rate
limiting, inline validation, auto-reply email. Adds multer/sharp/nodemailer
dependencies and the /api/contact endpoint to server.js.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>