Expand description
cratefield-adapter-turnstile: the Captcha port over Cloudflare
Turnstile siteverify (issue #7). Runs on the runtime’s HttpClient
port; the 5 s timeout is supplied by the runtime’s Clock.
Fail-closed by default: a transport failure (or timeout) verifies as
{ ok: false, reason: "unavailable" }. .fail_open(true) is for
staging only. If the secret is absent, Turnstile::from_env returns
None so the port is not provided at all — and Harness::build then
refuses a production venture with HumanForm routes (issue #133).
A bound adapter checks what it was bound to: a set of expected
hostnames — an apex and its www, say — rejects responses from any
other site (including a response that omits the hostname), and an
expected action rejects a token minted for a different flow.
Captcha::binding reports both, so the harness can tell “port
present” from “verification configured”.
The siteverify form body is fully percent-encoded and the token is
shape-checked before anything is sent: a malformed token verifies as
invalid-input-response locally, with no request at all (issue #436).
§cratefield-adapter-turnstile
Captcha port over Cloudflare Turnstile
siteverify for the Cratefield harness. Runs on the runtime’s
HttpClient port; the 5 s timeout is supplied by the runtime’s Clock.
§Usage
use std::sync::Arc;
use cratefield_adapter_turnstile::Turnstile;
use cratefield_runtime_cloudflare::{FetchClient, WorkersClock};
// Turnstile::from_env returns None when TURNSTILE_SECRET is absent — the
// port is then simply not provided, and Harness::build refuses a
// production venture whose routes declare a HumanForm policy without an
// effectively configured captcha (issue #133). TURNSTILE_HOSTNAME (a
// comma-separated list of hostnames) and TURNSTILE_ACTION, when set,
// bind the matching checks.
let runtime = match Turnstile::from_env(Arc::new(FetchClient), Arc::new(WorkersClock)) {
Some(turnstile) => runtime.captcha(turnstile),
None => runtime,
};Behavior:
verify(token, remote_ip)POSTssecret,response,remoteipas a form tohttps://challenges.cloudflare.com/turnstile/v0/siteverify.- The verdict’s
reasonis the firsterror-codesentry. - Transport failure or timeout is fail-closed:
{ ok: false, reason: "unavailable" }..fail_open(true)flips that for staging only. .expected_hostnames(["example.com", "www.example.com"])accepts a responsehostnamefrom any member of the set, while.expected_hostname("example.com")still binds exactly one. Either way a mismatch — or a response that omits the hostname — fails withhostname-mismatch..expected_action("signup")checks the responseaction, so a token minted for another widget flow cannot authorize this one; mismatch or absence fails withaction-mismatch.Captcha::binding()reports both checks (and the fail-open posture) soHarness::buildcan tell “port provided” from “verification actually configured” forHumanFormroutes (issue #133).
MIT. Built in the open for Cratefield, a Factory Zero venture.
Structs§
- Turnstile
Captchaover Turnstile siteverify.