Skip to main content

Crate cratefield_adapter_turnstile

Crate cratefield_adapter_turnstile 

Source
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 — Fail closed.

cratefield-adapter-turnstile on crates.io cratefield-adapter-turnstile documentation MIT

§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) POSTs secret, response, remoteip as a form to https://challenges.cloudflare.com/turnstile/v0/siteverify.
  • The verdict’s reason is the first error-codes entry.
  • 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 response hostname from 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 with hostname-mismatch.
  • .expected_action("signup") checks the response action, so a token minted for another widget flow cannot authorize this one; mismatch or absence fails with action-mismatch.
  • Captcha::binding() reports both checks (and the fail-open posture) so Harness::build can tell “port provided” from “verification actually configured” for HumanForm routes (issue #133).

MIT. Built in the open for Cratefield, a Factory Zero venture.

Structs§

Turnstile
Captcha over Turnstile siteverify.