Compétence Turnstile Spin
Transforme la demande « configurer Turnstile » en une intégration fonctionnelle end-to-end : un widget, un Worker siteverify managé déployé, des extraits frontend à chaque point d'insertion choisi, et une validation réelle avant de signaler le succès.
Vous êtes l'agent. Exécutez l'assistant ci-dessous en invoquant les scripts situés dans scripts/ et en vous brancher sur leur sortie JSON. Les scripts contiennent la logique déterministe (appels API, gestion des retries/erreurs) ; votre rôle est l'orchestration, la lecture du codebase, la confirmation et les modifications du frontend.
Les instructions canoniques se trouvent sur developers.cloudflare.com/turnstile/spin. Si la page de documentation et ce fichier divergent, faites confiance à la page de documentation.
Quand charger cette compétence
Chargez quand la demande de l'utilisateur mentionne l'une des éléments suivants :
- « Turnstile », « CAPTCHA », « protection contre les bots »
- « siteverify », « cf-turnstile-response »
- « protéger ce formulaire », « arrêter les inscriptions de bots », « inscriptions spam »
- Un formulaire d'inscription, de connexion ou de contact spécifique combiné avec « Cloudflare » ou « bot »
Ne chargez pas pour des tâches Cloudflare sans rapport (Workers, Pages, R2, etc.) à moins que Turnstile soit aussi mentionné.
Flux de conversation
L'utilisateur a collé la demande. Vous êtes dans un dialogue multi-étapes. Détectez ce que vous pouvez, posez des questions uniquement quand vous le devez, confirmez avant chaque étape irréversible. Chaque moment numéroté correspond à un message de l'agent. Les éléments marqués [attendre l'utilisateur] nécessitent une réponse de l'utilisateur.
-
Bref accusé de réception. Une phrase : « Je vais exécuter la configuration Turnstile de bout en bout. C'est : vérifier l'auth, scanner le codebase, créer le widget, déployer le Worker, câbler le frontend, valider. Procéder ? » [attendre l'utilisateur] Ne présentez PAS encore de plan. L'auth + le scan viennent d'abord.
-
Vérification Wrangler.
npx wrangler --version. S'il manque, demandez une seule fois : « Installer wrangler avecnpm install --save-dev wrangler(projet Node) ounpm install -g wrangler(autre) ? Procéder ? » [attendre l'utilisateur] Si l'installation est complètement bloquée (politique d'entreprise, npm bloqué), basculez sur la conduite des étapes 4-5 viacurlcontreapi.cloudflare.com/client/v4/. -
Sonde Auth + scope (PREMIÈRE action irréversible). Exécutez
scripts/auth-probe.sh. Branchez surstatus:ok: passez à l'étape 4. Le script a déjà sélectionné le compte (token single-account, ou un correspondant à$CLOUDFLARE_ACCOUNT_ID).missing_token,missing_scopeoumissing_workers_scope: demandez à l'utilisateur de créer un token sur https://dash.cloudflare.com/profile/api-tokens → Token personnalisé → permissionsAccount.Turnstile:EditetAccount.Workers Scripts:Edit→ inclure le compte cible dans Ressources de compte. Ne le dirigez PAS verswrangler login. Son scope OAuth n'inclut pasAccount.Turnstile:EditouAccount.Workers Scripts:Edit. Proposez trois façons de transmettre le token, la plus propre d'abord :- Exporter + relancer (token ne rentre jamais dans le chat) :
export CLOUDFLARE_API_TOKEN=<token>puis relancez l'agent depuis ce terminal. - Enregistrer dans un fichier (token dans un fichier avec permissions user-only, pas dans le chat) :
umask 077 && printf '%s' '<token>' > ~/.cf-turnstile-token, puis lire avecTOKEN=$(cat ~/.cf-turnstile-token). - Coller dans le chat (plus rapide, mais le token atterrit dans le journal de conversation ; l'utilisateur devrait le faire tourner après si le journal est jamais partagé).
Si l'utilisateur choisit l'option 3 (coller dans le chat), vous pouvez utiliser l'attente pour exécuter les étapes 5, 6, 7 (Domaine, Scan du codebase, Plan d'insertion). Les options 1 et 2 relanceront votre session, donc ne pré-récupérez pas l'état dans ces cas. Quand l'auth est établie, relancez
auth-probe.sh, puis passez à l'étape 8.
- Exporter + relancer (token ne rentre jamais dans le chat) :
multiple_accounts: le token couvre plus d'un compte et$CLOUDFLARE_ACCOUNT_IDest non défini. Présentez la liste numérotée desaccounts. [attendre l'utilisateur] Ensuite exportezCLOUDFLARE_ACCOUNT_ID=<choisi>et relancezauth-probe.sh.account_mismatch:$CLOUDFLARE_ACCOUNT_IDest défini mais ne figure pas parmi les comptes du token. Montrez la liste desaccountset demandez à l'utilisateur soit deunset CLOUDFLARE_ACCOUNT_ID, soit de le définir à l'un de ces IDs.
-
Sélection du compte. Si
auth-probe.sha retournéokaprès un tourmultiple_accounts, c'est déjà fait. Sinon le script a sélectionné le compte unique silencieusement et vous continuez à l'étape 5. -
Domaine. Incluez toujours
localhostet127.0.0.1. Pour la production, scannezpackage.jsonhomepage,wrangler.toml,README.md,AGENTS.md, git remote. Confirmez : « Je vais m'enregistrer pourlocalhost,127.0.0.1et<domaine>. OK ? » [attendre l'utilisateur] Si aucun domaine de production n'est trouvé, demandez. -
Scan du codebase. Détectez silencieusement le framework + les candidats d'insertion.
-
Plan d'insertion. Montrez la liste des candidats avec des marqueurs
[recommandé]/[ignorer par défaut]; demandez à l'utilisateur de confirmer (nombres, « all », « recommended » ou une liste). [attendre l'utilisateur] Si un CAPTCHA existant a été détecté, présentez un plan de migration à la place (voir « Migration à partir d'un autre CAPTCHA »). -
Création du widget. Exécutez
scripts/widget-create.sh --account-id <id> --name <name> --domains <list> --mode managed. Signalez la sitekey. Le secret reste en env ; ne l'écrivez jamais sur disque. -
Déploiement du Worker. Exécutez
scripts/worker-deploy.sh --name turnstile-siteverify-<project-slug>avecWIDGET_SECRETexporté. Signalez l'URL du Worker surstatus: ok. Surset_secret_failed, le Worker s'est déployé maisTURNSTILE_SECRET_KEYn'est pas défini sur lui ; surfacez l'erreur, puis relancez avececho "$WIDGET_SECRET" | npx wrangler secret put TURNSTILE_SECRET_KEY --name <returned worker_name>avant d'exécuter la validation. -
Éditions frontend. Énoncez le contrat : « Je vais ajouter le widget + gater le gestionnaire submit existant sur
success === true. La logique du gestionnaire existant reste la même. » Demandez « yes » / « show ». [attendre l'utilisateur] Si « show », imprimez les diffs unifiés et demandez à nouveau. Ne proposez PAS de comportement alternatif (livraison email, backends personnalisés). -
Validation. Exécutez
scripts/validate.sh. Signalez chaque vérification au fur et à mesure qu'elle réussit. Si l'une échoue, surfacez l'erreur et arrêtez-vous. [attendre l'utilisateur si quelque chose échoue] -
Persister la compétence. Demandez : « Enregistrer la compétence Spin dans
.claude/skills/turnstile-spin/SKILL.mdpour que je puisse la réutiliser pour les tâches de suivi ? » Par défaut oui. [attendre l'utilisateur] Ensuite exécutezscripts/persist-skill.sh --path <agent-specific-path>. -
Rapport final. Imprimez le résumé structuré : ce qui a été créé, ce qui a été validé, quoi faire ensuite.
Choses que vous NE DEVEZ PAS faire
- N'écrivez pas le secret Turnstile sur disque. Transmettez-le uniquement via stdin à
wrangler secret put(le script worker-deploy.sh gère cela). - Ne sautez pas la validation.
- Ne surécrivez pas les fichiers sans afficher une diff.
- Ne déployez pas un Worker sur un compte différent de celui où le widget a été créé.
- N'appelez pas siteverify depuis le navigateur. Toujours : navigateur → Worker de l'utilisateur → siteverify.
- N'utilisez pas
sudoou n'installez pas de packages globaux sans demander.
Limite de portée stricte : NE DEMANDEZ PAS à l'utilisateur à propos de
Spin valide le token Turnstile via un Worker managé avant que le gestionnaire de formulaire existant de l'utilisateur s'exécute. Tout le reste est hors de portée :
- Livraison d'email / SMS / notification. Laissez le gestionnaire submit existant tranquille (gatez-le simplement sur
success === true). Ne proposez pas Resend, Mailchannels, SMTP, mailto. - Code Worker personnalisé. Déployez le modèle Worker standard fourni sur
templates/worker/. N'écrivez pas un nouveau Worker. N'ajoutez pas de fonctionnalités (rate limiting, routage personnalisé, intégrations tierces). - Base de données / paiement / OAuth / persistance de formulaire. Hors de portée.
- Migration, refactorisation ou stylisation du framework frontend. Editez uniquement ce qui est nécessaire.
- Seuils de score reCAPTCHA v3. Turnstile retourne
success: true/false. - Setups de pré-clearance uniquement. Si
clearance_level !== no_clearance, siteverify est facultatif et Spin ne s'applique pas. Redirigez l'utilisateur et quittez.
Flux de récupération : respecter la configuration de widget existante
Si l'utilisateur vous dit qu'il a déjà un widget Turnstile configuré et qu'il veut câbler siteverify sans faire tourner la sitekey (par ex. « j'ai une sitekey mais siteverify n'a jamais fonctionné », « configurer Spin contre mon widget existant <sitekey> ») :
- Sautez l'étape 8 (création de widget). La sitekey existe déjà ; obtenez-la de l'utilisateur.
- Récupérez les métadonnées du widget via
scripts/fetch-secret.sh --account-id <id> --sitekey <key>. Branchez surstatus:ok: lisezsecret,clearance_leveletdomainsdepuis la réponse. Confirmez quedomainsinclut le nom d'hôte de production de l'utilisateur ; sinon surfacez l'écart avant de continuer.missing_read_scope: dites à l'utilisateur d'ajouterAccount.Turnstile:Readau token, ou basculez sur demander le secret collé. Dans le chemin du collage, vous n'avez pasclearance_leveloudomains; demandez à l'utilisateur de confirmer les deux.
- Vérifiez
clearance_leveldepuis la réponse (ou la réponse de l'utilisateur) :no_clearance: récupération standard (déployer Worker, câbler siteverify).- autre : demandez s'il veut siteverify sur pré-clearance, ou quittez selon la limite de portée.
- Continuez à partir de l'étape 9 (déploiement du Worker). La sitekey ne change pas. La colonne
Deploymentdu Dashboard bascule deManualàSpinlors de la première requête portantdata-action="turnstile-spin-v1". - Ne recréez jamais le widget pour obtenir un secret frais. Cela brise la sitekey existante partout où elle est déployée.
Le contrat d'édition frontend
Lors du câblage d'un formulaire existant au Worker (étape 10), le contrat est : gater, ne pas remplacer. Le gestionnaire submit existant de l'utilisateur continue à faire ce qu'il faisait. Spin ajoute seulement une étape de validation avant.
form.addEventListener("submit", async (e) => {
e.preventDefault();
const token = /* read cf-turnstile-response */;
const result = await fetch(WORKER_URL, { method: 'POST', body: JSON.stringify({ token }) });
const data = await result.json();
if (!data.success) return; // show failure
// existing handler logic runs here, unchanged
});
Si le gestionnaire existant était un stub, Spin le laisse un stub gâté sur success. L'utilisateur peut remplacer le stub plus tard ; ce n'est pas le travail de Spin.
Migration à partir d'un autre CAPTCHA
Lors du scan du codebase à l'étape 6, cherchez aussi un reCAPTCHA ou hCaptcha existant. S'il est trouvé, basculez l'étape 7 sur un plan de migration.
Signaux de détection :
- reCAPTCHA :
https://www.google.com/recaptcha/api.js,class="g-recaptcha",data-sitekey="6L...", POST du backend sur/recaptcha/api/siteverify - hCaptcha :
https://js.hcaptcha.com/1/api.js,class="h-captcha", POST du backend surhttps://hcaptcha.com/siteverify
Substitution :
- Remplacez les balises script par
https://challenges.cloudflare.com/turnstile/v0/api.js(async defer). - Remplacez les divs
class="g-recaptcha"/class="h-captcha"parclass="cf-turnstile", mettez à jourdata-sitekeyvers la nouvelle sitekey Turnstile, ajoutezdata-action="turnstile-spin-v1". - Le champ token change de
g-recaptcha-responseàcf-turnstile-response. - L'URL siteverify du backend pointe vers le Worker déployé par Spin. Supprimez les variables env
RECAPTCHA_SECRET/HCAPTCHA_SECRET.
Cas limites à surfacer à l'utilisateur :
- Seuils de score reCAPTCHA v3. Turnstile n'a pas de score. Dites explicitement à l'utilisateur que le code migré rejettera sur
success === false. - reCAPTCHA Enterprise. Ne migrez pas automatiquement. Pointez sur developers.cloudflare.com/turnstile/migration/recaptcha/.
- Valeurs
action=personnalisées. Préservez toute action personnalisée que l'utilisateur a passée àgrecaptcha.executeen tant quedata-actionsur le widget. Utilisezturnstile-spin-v1seulement quand aucune action personnalisée n'existe.
Cas limites
| Situation | Action |
|---|---|
wrangler non installé |
Chemin d'installation : npm install --save-dev wrangler (projet Node) ou npm install -g wrangler (autre) |
| Plusieurs comptes Cloudflare | scripts/auth-probe.sh retourne tous les comptes ; demandez à l'utilisateur de choisir, exportez CLOUDFLARE_ACCOUNT_ID |
| Projet Cloudflare Pages | Déployez le Worker managé de toute façon, OU suggérez le Plugin Pages |
Non-concordance EXPECTED_HOSTNAME |
Mettez à jour les domaines du widget via PUT, pas PATCH (PATCH retourne 10405 Method not allowed) : curl -X PUT .../widgets/$SITEKEY -d '{"name":"...","mode":"managed","domains":[...]}' |
| Conflit de nom de Worker | worker-deploy.sh relance automatiquement avec un suffixe hash |
| Token expiré en cours de flux | Arrêtez, relancez scripts/auth-probe.sh, demandez les nouvelles données d'identification |
L'étape 11 retourne missing-input-secret |
Le secret ne s'est pas propagé. Refixez : echo "$WIDGET_SECRET" \| npx wrangler secret put TURNSTILE_SECRET_KEY --name <worker_name from worker-deploy.sh output>, attendez 10s, revalidez. Utilisez le champ worker_name retourné par worker-deploy.sh ; ne dépendez pas d'une variable env $WORKER_NAME. |
worker-deploy.sh retourne set_secret_failed |
Le Worker est déployé mais le secret n'est pas défini. Relancez uniquement le secret-put en utilisant le worker_name retourné : echo "$WIDGET_SECRET" \| npx wrangler secret put TURNSTILE_SECRET_KEY --name <worker_name>. Surfacez le champ detail à l'utilisateur — il porte l'erreur wrangler. |
Marqueur de télémétrie
Chaque extrait que vous écrivez doit inclure data-action="turnstile-spin-v1". Télémétrie agrégée au niveau du compte, jamais par utilisateur. Cloudflare l'utilise pour mesurer l'activation. Si l'utilisateur supprime l'attribut, l'intégration fonctionne toujours ; seule la segmentation analytique est perdue.
À ne pas faire
- N'écrivez pas le secret sur disque.
- Ne sautez pas la validation (étape 11).
- Ne proposez pas de fonctionnalités en dehors de l'assistant (code Worker personnalisé, domaines personnalisés, règles WAF avancées) à moins qu'on vous le demande.
- N'appelez pas siteverify depuis le navigateur.
- Ne déployez pas le Worker dans un compte différent du widget.