hCaptcha provider
hCaptcha provider implementing captcha.Provider against the hCaptcha siteverify API.
Overview
hCaptcha is a privacy-oriented captcha service. No Google dependency and no tracking cookies. The provider supports both the free tier, which returns a binary pass or fail, and the Enterprise tier, which returns a risk score. It normalises both into Piko's standard convention where 0.0 means bot and 1.0 means human. The free tier maps to 1.0 on success and 0.0 on failure. The Enterprise tier inverts hCaptcha's native scale, where 0.0 means safe and 1.0 means threat, so one score threshold applies regardless of tier.
The provider injects the widget script, the explicit-render bootstrap, and the script, frame, and connect CSP domains through RenderRequirements. You do not write any frontend glue or render the widget yourself. Mark the action as captcha-required and Piko handles the round trip. The provider implements the same captcha.Provider interface and Config shape as Turnstile and reCAPTCHA v3, so switching providers is one bootstrap line.
The provider is a plain Go module. It carries no build tags and no CGO dependency, so it runs in interpreted dev mode (dev-i) and compiled mode alike. Token verification ships with OpenTelemetry tracing and hardened HTTP handling. It enforces a 10-second timeout, a content-type check, and a 64 KiB cap on the verification response. All public methods are safe for concurrent use.
Requirements
- An hCaptcha account with a registered site. Site key and secret key from the hCaptcha dashboard.
- Network egress to
js.hcaptcha.com(widget script),hcaptcha.comand*.hcaptcha.com(frames and verification), andapi.hcaptcha.com(server-side verification).
Configuration
import (
"piko.sh/piko/wdk/captcha/captcha_provider_hcaptcha"
)
provider, err := captcha_provider_hcaptcha.NewProvider(captcha_provider_hcaptcha.Config{
SiteKey: "10000000-ffff-ffff-ffff-000000000001", // required; public site key
SecretKey: "0x0000000000000000000000000000000000000000", // required; server-side secret
})
if err != nil {
return err
}
To keep keys out of source, inject them through the bootstrap override instead of hardcoding them in Config:
ssr := piko.New(
piko.WithCaptchaProvider("hcaptcha", provider),
piko.WithDefaultCaptchaProvider("hcaptcha"),
piko.WithCaptcha(piko.CaptchaOptions{
SiteKey: os.Getenv("HCAPTCHA_SITE_KEY"),
SecretKey: os.Getenv("HCAPTCHA_SECRET_KEY"),
}),
)
Bootstrap
ssr := piko.New(
piko.WithCaptchaProvider("hcaptcha", provider),
piko.WithDefaultCaptchaProvider("hcaptcha"),
)
Usage
Mark a server action as captcha-required by implementing the CaptchaProtected interface, whose CaptchaConfig method returns a *piko.CaptchaConfig. Piko verifies the token before invoking the action handler.
func (SubmitAction) CaptchaConfig() *piko.CaptchaConfig {
return &piko.CaptchaConfig{Provider: "hcaptcha"}
}
Score threshold
The score threshold only applies to the Enterprise tier, which returns a risk score. The free tier is pass or fail, so the threshold has no effect there. The service default is 0.5. Set the service-wide threshold through the bootstrap override:
piko.WithCaptcha(piko.CaptchaOptions{ScoreThreshold: 0.7})
To override the threshold for one action, set ScoreThreshold on the returned CaptchaConfig. A value of 0 uses the service default.
func (SubmitAction) CaptchaConfig() *piko.CaptchaConfig {
return &piko.CaptchaConfig{Provider: "hcaptcha", ScoreThreshold: 0.7}
}
See also
Other captcha providers:
- reCAPTCHA v3, Google's invisible score-based captcha.
- Cloudflare Turnstile, Cloudflare's free, frictionless alternative.
Framework docs:
- Captcha API reference, every type and method on the captcha service.
- How to gate actions, wiring
CaptchaConfiginto server actions.
External:
- hCaptcha documentation, authoritative reference.
- Enterprise risk scores, score interpretation if you are on Enterprise.