Require the site secret key as a bearer credential on POST /verify master
The verify endpoint checked only the public site key and an Origin header. Server-to-server callers send no Origin, so the documented escape hatch was allowed_origins "*", and anyone holding the public site key could hammer /verify with candidate proofs: each attempt cost an Equihash verification and filled the replay store and logs. POST /verify now requires "Authorization: Bearer <secret_key>", the hCaptcha/reCAPTCHA siteverify model. The secret is compared in constant time, and a missing, malformed, or wrong credential gets 401 with a WWW-Authenticate header before any proof verification runs. The Origin check, the CORS preflight route, and the CORS response headers are gone from /verify: the endpoint is server-to-server by construction and behaves identically in the default and Cloudron builds. allowed_origins now guards GET /challenge alone. - hecapte_verify_requests_total vocabulary: origin_not_allowed -> unauthorized; the verification-failure log line drops the origin field - Demo page stops at the solved proof (the browser holds no credential for /verify) and tells the operator the backend verifies it - New router tests pin the credential gate (missing/malformed/wrong bearer, WWW-Authenticate), the absent CORS surface (preflight 405, no CORS response headers), and the metric series; the end-to-end flow test now carries credentials - README updated for the new contract, CHANGELOG 4.0.0 entry added, PRODUCT.md key model refreshed