turnstile-spin

Par cloudflare · skills

Configurer Cloudflare Turnstile de bout en bout dans un projet : analyser la base de code, créer le widget via l'API Cloudflare, déployer le Worker siteverify managé, écrire les snippets frontend, valider, et persister le skill. Charger ceci lorsqu'un utilisateur demande à ajouter Turnstile, configurer un CAPTCHA, protéger un formulaire contre les bots, ou corriger une intégration Turnstile. Correspond à developers.cloudflare.com/turnstile/spin.

npx skills add https://github.com/cloudflare/skills --skill turnstile-spin

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.

  1. 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.

  2. Vérification Wrangler. npx wrangler --version. S'il manque, demandez une seule fois : « Installer wrangler avec npm install --save-dev wrangler (projet Node) ou npm 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 via curl contre api.cloudflare.com/client/v4/.

  3. Sonde Auth + scope (PREMIÈRE action irréversible). Exécutez scripts/auth-probe.sh. Branchez sur status :

    • 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_scope ou missing_workers_scope : demandez à l'utilisateur de créer un token sur https://dash.cloudflare.com/profile/api-tokens → Token personnalisé → permissions Account.Turnstile:Edit et Account.Workers Scripts:Edit → inclure le compte cible dans Ressources de compte. Ne le dirigez PAS vers wrangler login. Son scope OAuth n'inclut pas Account.Turnstile:Edit ou Account.Workers Scripts:Edit. Proposez trois façons de transmettre le token, la plus propre d'abord :
      1. Exporter + relancer (token ne rentre jamais dans le chat) : export CLOUDFLARE_API_TOKEN=<token> puis relancez l'agent depuis ce terminal.
      2. 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 avec TOKEN=$(cat ~/.cf-turnstile-token).
      3. 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.
    • multiple_accounts : le token couvre plus d'un compte et $CLOUDFLARE_ACCOUNT_ID est non défini. Présentez la liste numérotée des accounts. [attendre l'utilisateur] Ensuite exportez CLOUDFLARE_ACCOUNT_ID=<choisi> et relancez auth-probe.sh.
    • account_mismatch : $CLOUDFLARE_ACCOUNT_ID est défini mais ne figure pas parmi les comptes du token. Montrez la liste des accounts et demandez à l'utilisateur soit de unset CLOUDFLARE_ACCOUNT_ID, soit de le définir à l'un de ces IDs.
  4. Sélection du compte. Si auth-probe.sh a retourné ok après un tour multiple_accounts, c'est déjà fait. Sinon le script a sélectionné le compte unique silencieusement et vous continuez à l'étape 5.

  5. Domaine. Incluez toujours localhost et 127.0.0.1. Pour la production, scannez package.json homepage, wrangler.toml, README.md, AGENTS.md, git remote. Confirmez : « Je vais m'enregistrer pour localhost, 127.0.0.1 et <domaine>. OK ? » [attendre l'utilisateur] Si aucun domaine de production n'est trouvé, demandez.

  6. Scan du codebase. Détectez silencieusement le framework + les candidats d'insertion.

  7. 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 »).

  8. 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.

  9. Déploiement du Worker. Exécutez scripts/worker-deploy.sh --name turnstile-siteverify-<project-slug> avec WIDGET_SECRET exporté. Signalez l'URL du Worker sur status: ok. Sur set_secret_failed, le Worker s'est déployé mais TURNSTILE_SECRET_KEY n'est pas défini sur lui ; surfacez l'erreur, puis relancez avec echo "$WIDGET_SECRET" | npx wrangler secret put TURNSTILE_SECRET_KEY --name <returned worker_name> avant d'exécuter la validation.

  10. É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).

  11. 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]

  12. Persister la compétence. Demandez : « Enregistrer la compétence Spin dans .claude/skills/turnstile-spin/SKILL.md pour que je puisse la réutiliser pour les tâches de suivi ? » Par défaut oui. [attendre l'utilisateur] Ensuite exécutez scripts/persist-skill.sh --path <agent-specific-path>.

  13. 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 sudo ou 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> ») :

  1. Sautez l'étape 8 (création de widget). La sitekey existe déjà ; obtenez-la de l'utilisateur.
  2. Récupérez les métadonnées du widget via scripts/fetch-secret.sh --account-id <id> --sitekey <key>. Branchez sur status :
    • ok : lisez secret, clearance_level et domains depuis la réponse. Confirmez que domains inclut le nom d'hôte de production de l'utilisateur ; sinon surfacez l'écart avant de continuer.
    • missing_read_scope : dites à l'utilisateur d'ajouter Account.Turnstile:Read au token, ou basculez sur demander le secret collé. Dans le chemin du collage, vous n'avez pas clearance_level ou domains ; demandez à l'utilisateur de confirmer les deux.
  3. Vérifiez clearance_level depuis 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.
  4. Continuez à partir de l'étape 9 (déploiement du Worker). La sitekey ne change pas. La colonne Deployment du Dashboard bascule de Manual à Spin lors de la première requête portant data-action="turnstile-spin-v1".
  5. 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 sur https://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" par class="cf-turnstile", mettez à jour data-sitekey vers la nouvelle sitekey Turnstile, ajoutez data-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.execute en tant que data-action sur le widget. Utilisez turnstile-spin-v1 seulement 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.

Skills similaires