Runbooks OrbitRunbook — Checklist de lancement

Runbook — Checklist de lancement


title: Runbook — Checklist de lancement description: Actions propriétaire avant le lancement : migration, DataFast, Stripe, Resend, crons, variables, vérifications.

Statut (2026-10-02) : le code des blocages P0 du blueprint est livré (mesure DataFast derrière le consentement, premier contact, durcissement Stripe, e-mails de facturation) ; il reste ces actions côté propriétaire, qu'aucun agent ne peut faire (tableaux de bord, DNS, secrets). Tant qu'elles ne sont pas faites, rien ne casse : DataFast, Stripe Tax, le cron et les e-mails de facturation sont inertes sans leurs variables. Références : Blueprint (tableau P0), Facturation Stripe, skills launch-attribution, billing-stripe, transactional-email, platform-ops.

Règles : aucune valeur secrète dans un fichier, une PR ou une conversation ; un compte, un site, un domaine propres à Orbit (jamais ceux d'un autre produit) ; mode test de Stripe d'abord ; un changement de variable Vercel n'atteint que les nouveaux déploiements (redéployer).

0. Ordre conseillé

  1. Migration de la base (section 1) : avant de fusionner la PR qui l'utilise si la base est celle de production.
  2. Stripe en mode test : endpoint, Tax, catalogue (section 3).
  3. DataFast (section 2) puis un paiement de test attribué.
  4. Resend (section 4), CRON_SECRET (section 5), variables (section 6).
  5. Vérifications de bout en bout (section 7), puis le passage en live.

1. Migration de la base

Une migration additive (deux colonnes Workspace.acquisition JSONB et Workspace.datafastVisitorId TEXT, nulles, sans valeur par défaut) : 20261002160000_workspace_acquisition.

  • GitHub → Actions → Deploy production database → Run workflow (branche main seulement), puis vérifier que db:drift finit au vert. Phase de la base : packages/db/PHASE vaut pre-users (la passer à production le jour de la première inscription réelle : skill database).

2. DataFast (mesure et attribution du revenu)

La variante avec cookie du script est servie en première partie (/js/script.js), seulement en production, avec l'identifiant du site, et après que le visiteur a accepté la mesure d'audience (bannière de consentement). Sans cookie, le revenu ne peut pas être rattaché à un canal.

  • Créer le site DataFast d'Orbit pour orbit.easyconnector.app (jamais celui d'un autre produit).
  • Vercel → variables, Production uniquement : DATAFAST_WEBSITE_ID = l'identifiant dfid_… du site (public : il figure dans chaque page). Pas en Preview : un déploiement Preview est aussi une build de production (NODE_ENV=production) et chargerait le script.
  • Créer une clé API de site (df_…, la plus étroite : elle suffit à écrire un objectif) et la poser en Production sous DATAFAST_API_TOKEN. Les autres types (dft_ jeton de compte, dfbot_ ingestion des robots) ne servent pas à l'app ; ne jamais les écrire dans un fichier.
  • Créer les objectifs (un nom par étape, exactement ces noms) : signup, checkout_started, subscription_started. Ils sont envoyés par le serveur, avec le visiteur capturé à l'inscription. (workspace_created, first_brain_write, first_generation de la liste du skill ne sont pas encore émis : ne pas les créer tant qu'un émetteur n'existe pas.)
  • Créer les entonnoirs dans DataFast à partir de ces objectifs (page d'accueil → signup → checkout_started → subscription_started). Les chiffres exacts de l'entonnoir viendront de la table de jalons d'Orbit (P1, ligne h du blueprint), pas de DataFast : DataFast ne voit pas un visiteur qui refuse les cookies.
  • Connecter Stripe dans le tableau de bord DataFast (attribution du revenu) avec une clé restreinte en lecture seule du compte Stripe d'Orbit (jamais la clé secrète de l'app). C'est ce qui rattache chaque paiement au canal : Orbit copie datafast_visitor_id dans les métadonnées du Checkout et de l'abonnement, DataFast les lit.
  • En mode test de Stripe, accepter la bannière, passer par Checkout avec la carte 4242 4242 4242 4242 et vérifier que le paiement apparaît attribué dans DataFast avant de passer en live.
  • Poser une annotation DataFast à l'heure du lancement (un pic doit s'expliquer).
  • Optionnel : jeton dfbot_ pour mesurer les robots d'IA (skill seo-aeo-geo) ; plan de crawl à décider.

3. Stripe

  • Compte Stripe propre à Orbit (distinct de tout autre produit), mode test d'abord.
  • Valider les montants des crédits Studio de apps/web/scripts/stripe-catalog.mjs (et de la page Facturation) ; ceux d'Orbit sont ceux du prototype. Lancer le script avec la clé test (--dry-run d'abord), puis archiver à la main les anciens produits orbit_brain_pro et orbit_brain_max.
  • Vercel → projet Orbit → Settings → Environment Variables : ORBIT_STRIPE_SECRET_KEY (sk_test_ en Preview) et, une fois l'endpoint créé, ORBIT_STRIPE_WEBHOOK_SECRET.
  • Endpoint webhook : Développeurs → Webhooks → Ajouter un endpoint https://orbit.easyconnector.app/api/webhooks/stripe, version d'API celle du SDK (2026-09-30), description = le slug de l'app (orbit-easyconnector-app), et ces dix événements (ni plus, ni moins ; la liste est celle de WEBHOOK_EVENTS, imprimée par stripe-catalog.mjs) : checkout.session.completed, customer.subscription.created, customer.subscription.updated, customer.subscription.deleted, invoice.paid, invoice.payment_failed, invoice.payment_action_required, charge.refunded, charge.dispute.created, charge.dispute.closed (en gras : les nouveaux). Copier le whsec_… dans ORBIT_STRIPE_WEBHOOK_SECRET (un secret par endpoint : test et live sont distincts). Si un endpoint existe déjà, ajouter les cinq nouveaux événements (sinon remboursements, litiges, reçus et SCA ne sont jamais vus).
  • Stripe Tax : Paramètres → Tax → activer ; renseigner l'adresse d'origine, les inscriptions fiscales (là où Orbit doit collecter), le code de taxe par défaut des produits (logiciel en service, usage professionnel) ; puis poser ORBIT_STRIPE_TAX=1 (Production, puis Preview si le compte de test a une configuration fiscale). Tant que ce n'est pas fait, laisser vide : Stripe refuse automatic_tax sur un compte sans configuration fiscale et Checkout échouerait.
  • Relancer node apps/web/scripts/stripe-catalog.mjs (clé test, --dry-run d'abord) : il pose tax_behavior: exclusive sur les prix (les montants du catalogue sont hors taxe ; Stripe ajoute la taxe à Checkout). Puis, après votre feu vert, la même chose en live avec --live.
  • Portail client (Paramètres → Portail client, test puis live) : activer le portail, autoriser la mise à jour du moyen de paiement, la résiliation et le changement d'intervalle (mensuel ↔ annuel) du produit Orbit, ainsi que le changement d'offre entre les produits Orbit Studio (Starter, Pro).
  • Migrations de facturation : appliquer 20261002130000_billing et 20261002140000_single_orbit_plan (GitHub → Actions → Deploy production database). Le workspace du fondateur retombe à 0 CU : relancer pnpm db:seed avec FOUNDER_BRAIN_CAPACITY_CU, ou le passer en mode custom depuis l'app admin.
  • Essai de bout en bout en Preview (carte 4242 4242 4242 4242) : activation depuis /brain, extension d'un pas, /billing, quota.
  • Passer en live : relancer le script de catalogue avec la clé live et --live, recréer l'endpoint webhook en live, poser sk_live_… et le whsec_… live en Production, redéployer.
  • Doublons de reçus : Orbit envoie son propre e-mail « paiement reçu » ; si vous gardez aussi les reçus de Stripe (Paramètres → E-mails des clients → Paiements réussis), le client recevra les deux. Décision à prendre : laisser les reçus de Stripe actifs et ignorer ceux d'Orbit, ou désactiver ceux de Stripe.
  • Tests à faire en mode test (cartes de billing-stripe, section 6) : un échec 4000 0000 0000 0341, une carte SCA, un remboursement total (le plan retombe à 0 CU), un litige 4000 0000 0000 0259. Décision à prendre (écrite dans la page Facturation) : un remboursement total de bonne volonté retire aussi la capacité.
  • Restreindre toute clé donnée à un MCP Stripe à la lecture seule (rk_…), dans le shell de l'opérateur, jamais dans Vercel.

4. Resend : le domaine d'envoi

Sans SPF, DKIM et DMARC sur le domaine d'envoi, les codes de connexion tombent en spam sans erreur. L'expéditeur est config.from de studio.mailer dans products/connectors.json (aujourd'hui Orbit <orbit@easyconnector.app>).

  • Resend (le projet branché par l'intégration Vercel Marketplace d'Orbit) → Domains → ajouter orbit.easyconnector.app (le sous-domaine d'Orbit : sa réputation d'envoi ne dépend d'aucun autre produit de la famille).
  • Chez le fournisseur DNS, créer les enregistrements exactement tels que Resend les affiche : SPF (TXT incluant Resend), DKIM (les trois enregistrements fournis), le MX de retour, puis DMARC sur _dmarc.orbit.easyconnector.app : v=DMARC1; p=none; rua=mailto:<une boîte surveillée> pour commencer, p=quarantine une fois les rapports propres. Cliquer Verify dans Resend jusqu'au statut « Verified ».
  • Seulement ensuite, faire changer config.from pour Orbit <orbit@orbit.easyconnector.app> (une ligne de products/connectors.json, une PR) : changer l'expéditeur avant la vérification ferait refuser tous les e-mails (403 du fournisseur, y compris les codes de connexion).
  • Les envois marketing (lettre, annonces) iront un jour sur un sous-domaine séparé (news.orbit.easyconnector.app) avec ses propres enregistrements, jamais sur celui-ci. Il n'y en a pas encore : les e-mails de facturation sont transactionnels (pas de List-Unsubscribe).
  • Vérifier : se connecter, recevoir le code hors du spam ; dans Resend, le message est « Delivered », DKIM et SPF « pass ». Surveiller ensuite : plaintes < 0,3 % (viser moins), rebonds très en dessous de 4 %.
  • Optionnel : webhook Resend (Svix) pour supprimer sur rebond et plainte : pas encore codé (P1, ligne k).

5. CRON_SECRET et les crons

Trois crons (apps/web/vercel.json) : generations-sweep, retention et le nouveau reconcile-stripe (tous les jours à 05:20 UTC : il relit chez Stripe les abonnements et répare un webhook perdu).

  • Générer un secret long et aléatoire (openssl rand -hex 32) et le poser sous CRON_SECRET (Production et Preview). Vercel l'envoie en Authorization: Bearer ; sans lui, les trois crons répondent 401 (le comportement voulu : rien ne passe sans secret).
  • Après le déploiement : Vercel → projet → Settings → Cron Jobs → Run sur reconcile-stripe ; la réponse JSON doit dire halted: null et repaired: 0 sur un compte propre. Un halted non nul se lit dans les journaux (cron.reconcile_stripe.done) ; too_many_missing veut dire une mauvaise clé Stripe (le cron a refusé d'annuler en masse).
  • Rotation : voir le skill platform-ops, section 3 (changer dans Vercel, redéployer).

6. Variables d'environnement Vercel (noms seulement)

VariableProductionPreviewRemarque
DATABASE_URLouioui (branche Neon)base propre à Orbit
NEXTAUTH_SECRET, NEXTAUTH_URLouiouisessions
ORBIT_STRIPE_SECRET_KEYsk_live_ / rk_live_sk_test_jamais un STRIPE_*
ORBIT_STRIPE_WEBHOOK_SECRETwhsec_ de l'endpoint livewhsec_ de l'endpoint de testun par endpoint
ORBIT_STRIPE_TAX1 une fois Tax configurévide, ou 1 si le compte de test est configurédrapeau, pas un secret
DATAFAST_WEBSITE_IDdfid_… (public)absentle script ne charge qu'en production avec lui
DATAFAST_API_TOKENdf_… (secret)absentobjectifs côté serveur ; sans lui, aucun envoi
CRON_SECRETouiouiporte des crons
RESEND_API_KEYinjectée par l'intégration Resendinjectéerien à copier
ORBIT_PUBLIC_URLhttps://orbit.easyconnector.appselon le besoin

apps/web/src/env/schema.ts refuse AUTH_SECRET, STRIPE_*, INTERNAL_ORG_SLUGS, INTERNAL_SPEND_ALERT_USD, HIGGSFIELD(S)_* et MOTION_* : une valeur copiée d'un autre produit n'est pas lue. NEXTAUTH_SECRET et DATABASE_URL portent le même nom ailleurs : seule la relecture peut refuser une valeur copiée, elles doivent être propres à Orbit.

7. Vérifications avant le passage en live

  • Première visite : la bannière apparaît ; aucun cookie datafast_* ni orbit_acq avant le choix ; après « Tout accepter » avec ?utm_source=x&utm_campaign=essai, orbit_acq est posé (une seule fois, jamais écrasé) et le script /js/script.js se charge.
  • Une inscription depuis ce lien : dans l'app admin, la page du workspace montre la section Acquisition (source x, campagne essai) et la vue d'ensemble compte la source.
  • Un paiement de test : DataFast le montre attribué ; les trois objectifs apparaissent.
  • Un remboursement total de test : capacité à 0, Memories conservées ; un litige gagné la restaure.
  • Le cron reconcile-stripe répond (section 5), l'endpoint webhook affiche des livraisons en 200.
  • Un e-mail de chaque type reçu dans les deux langues (changer la langue du membre dans son profil).
  • /api/health surveillé par un moniteur externe (skill platform-ops, section 5).

8. Décisions ouvertes

  1. Reçus : ceux de Stripe, ceux d'Orbit, ou les deux (section 3).
  2. Remboursement total de bonne volonté : retire la capacité (politique actuelle) ou non.
  3. Expéditeur : passer config.from à orbit.easyconnector.app après vérification (section 4).
  4. Politique de crawl des robots d'IA (seo-aeo-geo), toujours ouverte.
  5. Table de jalons pour l'entonnoir exact (blueprint, ligne h).