Cloudflare Turnstile provider
Cloudflare Turnstile provider implementing captcha.Provider against the Turnstile siteverify API.
Overview
Turnstile is Cloudflare's captcha replacement. Most users pass invisibly through passive signals, with a managed challenge only when those signals are inconclusive. Turnstile does not present image-selection puzzles, and Cloudflare offers it at no cost.
The verification API returns a binary success with no risk score. The provider normalises a pass to the score 1.0 and a fail to the score 0.0 on the verification response. A Turnstile-gated action then reads CaptchaScore the same way it reads a score-based provider. This provider implements the same captcha.Provider port as every other captcha provider. Actions reference a provider only by name through CaptchaConfig. Swapping Turnstile for hCaptcha or reCAPTCHA is a one-line bootstrap change with no edits to action code.
The provider self-describes its frontend wiring. It returns the widget script URLs, the content security policy script, frame, and connect domains, and an init script. The Piko runtime injects the widget and the matching CSP from these values, so you do not hand-write script tags or CSP rules. The widget loads from challenges.cloudflare.com. Verification calls hit challenges.cloudflare.com/turnstile/v0/siteverify with a 10-second timeout and a 64 KiB response cap. Tokens are single-use and valid for 5 minutes, so a resubmitted form needs a fresh token. All public methods are safe for concurrent use.
This provider ships in its own Go module, piko.sh/piko/wdk/captcha/captcha_provider_turnstile, so add it as a dependency in your module. It is pure Go with no build tags and no CGO, so it behaves the same in compiled builds and in interpreted dev-i mode. Pair it with Cloudflare's test keys for local development.
Requirements
- A Cloudflare account with a Turnstile site registered in the Cloudflare dashboard. Site key and secret key from the same dashboard.
- Network egress to
challenges.cloudflare.comfor widget script, frames, and verification.
Configuration
import (
"piko.sh/piko/wdk/captcha/captcha_provider_turnstile"
)
provider, err := captcha_provider_turnstile.NewProvider(captcha_provider_turnstile.Config{
SiteKey: "1x00000000000000000000AA", // required; public site key
SecretKey: "1x0000000000000000000000000000000AA", // required; server-side secret
})
if err != nil {
return err
}
The example pairs Cloudflare's always-pass site key with its always-pass secret key. Swap both for production credentials before deploying.
Bootstrap
ssr := piko.New(
piko.WithCaptchaProvider("turnstile", provider),
piko.WithDefaultCaptchaProvider("turnstile"),
)
WithCaptchaProvider registers a named provider. Call it more than once to register providers side by side, then select one per action. WithDefaultCaptchaProvider names the provider that actions use when their CaptchaConfig does not set one.
Usage
Embed piko.ActionMetadata in the action and return a CaptchaConfig that names the provider. The runtime verifies the token before Call runs and exposes the score on the request.
type SubmitAction struct {
piko.ActionMetadata
}
func (SubmitAction) CaptchaConfig() *piko.CaptchaConfig {
return &piko.CaptchaConfig{Provider: "turnstile"}
}
See also
Other captcha providers:
- hCaptcha, privacy-focused alternative with optional Enterprise scoring.
- reCAPTCHA v3, Google's invisible score-based captcha.
Framework docs:
- Captcha API reference, every type and method on the captcha service.
- How to protect an action with a captcha, wiring captcha verification into server actions.
External:
- Turnstile documentation, authoritative reference.
- Turnstile test keys, always-pass and always-fail keys for local development.