/docs
Documentation North
Ce produit est un socle SaaS. Les pages techniques (compte, espaces, modules) sont des parcours terminés. Ajoutez uniquement le métier. Un seul catalogue décrit le statut : pas de pourcentage global. Un runtime-pass prouve un scénario, pas toute une fonctionnalité.
Compte, espace, produit, admin et configuration suivent la navigation groupée. Langue et thème se changent depuis le menu utilisateur. La preuve par scénario reste dans FEATURE_MATRIX ; le statut par fonctionnalité est ci-dessous.
Démarrage produit
1. Clone et install figé
Node 22+, pnpm 10.33.3, OpenSSL, Docker Compose. Ce checkout fournit le CLI (`pnpm north:create`). Ne pas copier les .env d’un autre produit.
pnpm install --frozen-lockfile
2. Générer un produit hors checkout
Construire une candidate puis `north create`. Destination neuve, hors worktree. Presets reçus : personal-free, organization-free, hybrid-free, billing-test, enterprise-test, mobile-smoke. Les presets free gardent billing, API, webhooks, SSO, SCIM, MCP OFF. Détail : docs/CREATE.md. Le legacy `pnpm create:product` reste jusqu’à NR-26.
pnpm release:qualified -- --out .data/release-candidate-qualified --allow-dirty pnpm north:create -- --destination ../acme --app-id acme --name Acme --preset personal-free --manifest .data/release-candidate-qualified
3. Secrets
Le générateur écrit un .env.local distinct (mode 0600) : AUTH_SECRET, audience, ports et base nouveaux. Infisical possède les secrets d’environnement. La console n’accepte aucun secret. NORTH_SKIP_INFISICAL=1 est local uniquement.
See docs/INFISICAL.md and docs/ENV_CATALOG.md
4. Services locaux et Portly
Dans le produit généré : inspecter Portly avant tout start. `local-up` refuse 3000/3001/3210/3211/8025/1025/5432 et n’adopte pas un processus étranger. Checkout North : ports développeur 3000/3001/3210/3211 — n’arrêter que les PID enregistrés de ce checkout. Protocole : docs/UPGRADING_STARTER.md §8 et docs/OPERATIONS.md.
node scripts/portly.mjs status node scripts/local-up.mjs node scripts/local-down.mjs portly status --json pnpm run setup --mode autonomous pnpm run doctor bash scripts/stop-stack.sh
5. Premier compte et owner
S’inscrire, vérifier l’e-mail dans Mailpit, terminer l’onboarding. Le premier utilisateur n’est pas administrateur plateforme. Bootstrap admin seulement sur un compte déjà existant.
pnpm --filter @north/auth exec tsx src/bootstrap-admin.ts [email protected] Open http://localhost:3000/configure
6. Publier modules et quotas
Config effective = preset Git + overrides SQL + secrets Infisical. Un save peut rester restart_required jusqu’à ACK Auth, ou migration_required s’il y a des données. NORTH_CONFIG_FILE pointe sur config/<app-id>.json.
pnpm config:apply --patch config/acme.json
7. Première fonctionnalité métier
Dans le produit : table, permission `product.<slug>.*`, page, quota, notification et handler lifecycle — sans éditer vendor/ ni les packages North. Recettes : docs/RECIPES.md.
node scripts/add-feature.mjs --id notes --title-fr Notes --title-en Notes
8. Tests
Les suites d’intégration créent un stack QA isolé. Elles refusent les ports 3000/3001/3210/3211/8025. NORTH_ENV_FILE explicite. Un typecheck n’est pas un build natif.
pnpm test:unit pnpm test:integration pnpm test:e2e pnpm test:mobile-contract
9. Mettre à jour le produit
Lecture seule puis apply local. Compatibilité N−1, reprise `.north/upgrade-apply.json`, rollback par revert des writes — jamais git reset. Registre : vendor/*.tgz tant que `publisher.registry` reste `unspecified`. Voir docs/UPGRADING_STARTER.md et docs/UPGRADE.md. Nettoyage du schéma Convex après une mise à jour North (par exemple l’ancienne table `extracteurJobs`) : [EXTRACTEUR-SPLIT.md](EXTRACTEUR-SPLIT.md#mise-à-niveau-dune-stack-qui-contient-déjà-des-jobs-extracteur).
pnpm north:doctor -- --product ../acme --manifest .data/release-candidate-qualified pnpm north:plan -- --product ../acme --manifest .data/release-candidate-qualified pnpm north:apply -- --product ../acme --manifest .data/release-candidate-qualified
10. Exploitation
deploy-check, backup chiffré, restore isolé. Candidates locales expérimentales (NR-05) et qualifiées (NR-29) : pas de publication ni d’activation payante. Voir docs/RELEASE.md et docs/OPERATIONS.md. Preuve notifications A→B : bash tests/reuse/notifications-upgrade/run.sh. Réception mixte tâches/dossiers + hybrid (NR-27) : bash tests/reuse/acceptance/run.sh. Candidate complète (NR-29) : bash tests/reuse/release-candidate/run.sh.
pnpm deploy:check pnpm backup pnpm restore:verify pnpm release:validate -- .data/release-candidate-qualified
Statut par fonctionnalité
Couches séparées : implémentation, UX, tests locaux, cloud, natif, CI, déploiement.
Déjà fusionné (ne plus marquer ouvert)
- #24–#36 — Invitations, fichiers, webhooks, preuves de confidentialité, OAuth localhost, avatar, notifications, droits, cycle de vie d’organisation (#37, #38, #39, #40, #41, #42, #43, #44, #45, #46, #47, #48, #49)
- #51 — Rejeu de sécurité / pont Auth → Convex (#51)
- #52 — Hooks Git Lefthook (pre-commit / commit-msg / pre-push) (#52)
- #20 — Navigation groupée, locale initiale, thème light/dark/system, destination /docs (#53)
Écarts produit restants
- #21 — Uploads, MFA et grants produit (travail profond). Les parcours locaux TOTP, fichiers et droits existent. Le ticket couvre un approfondissement produit, pas une absence totale.
Compte e-mail / mot de passe
Inscription, vérification Mailpit, restriction des comptes non vérifiés, récupération.
- Implémentation
- disponible
- UX
- disponible
- Tests locaux
- disponible
- Cloud
- n/a
- Natif
- n/a
- CI
- disponible
- Déploiement
- non testé
Magic link et politiques d’auth
Magic link, changement d’e-mail, modes passwordless / vérification optionnelle persistés en SQL.
- Implémentation
- disponible
- UX
- disponible
- Tests locaux
- disponible
- Cloud
- n/a
- Natif
- n/a
- CI
- disponible
- Déploiement
- non testé
OAuth local et fournisseurs cloud
Stub local + canonisation localhost / 127.0.0.1. Google/GitHub/Discord/Apple restent sans clés.
- Implémentation
- disponible
- UX
- disponible
- Tests locaux
- disponible
- Cloud
- optionnel-externe
- Natif
- n/a
- CI
- disponible
- Déploiement
- non testé
TOTP, codes de récupération, passkeys
Parcours Chromium réels. Invites OS passkeys et approfondissement MFA : voir #21.
- Implémentation
- disponible
- UX
- en cours
- Tests locaux
- disponible
- Cloud
- n/a
- Natif
- n/a
- CI
- disponible
- Déploiement
- non testé
Écarts: #21
Sessions, révocation, historique
Deux appareils, révocation distante, déconnexion globale, historique isolé de PostHog.
- Implémentation
- disponible
- UX
- disponible
- Tests locaux
- disponible
- Cloud
- n/a
- Natif
- disponible
- CI
- disponible
- Déploiement
- non testé
Pont JWT Auth → Convex
aud/iss, JWT expiré, refresh concurrent, JWKS, projection manquante, rejeu, crash mid-revoke.
- Implémentation
- disponible
- UX
- disponible
- Tests locaux
- disponible
- Cloud
- n/a
- Natif
- disponible
- CI
- disponible
- Déploiement
- non testé
Tenancy personnel / organisation / hybride
Modes, switcher, matrice de rôles, isolation, dernier owner, départ / transfert / fermeture.
- Implémentation
- disponible
- UX
- disponible
- Tests locaux
- disponible
- Cloud
- n/a
- Natif
- n/a
- CI
- disponible
- Déploiement
- non testé
Invitations d’espace et de plateforme
E-mail, acceptation, refus, resend, jetons plateforme vs membres Convex.
- Implémentation
- disponible
- UX
- disponible
- Tests locaux
- disponible
- Cloud
- n/a
- Natif
- n/a
- CI
- disponible
- Déploiement
- non testé
Fichiers privés et quotas
Tickets one-use, pagination, capabilities liées au membership, module OFF. Uploads avancés : #21.
- Implémentation
- disponible
- UX
- en cours
- Tests locaux
- disponible
- Cloud
- n/a
- Natif
- n/a
- CI
- disponible
- Déploiement
- non testé
Écarts: #21
Avatar de profil
Stockage Convex personnel, magic-bytes, jetons de vue. Parcours navigateur isolé encore non exécuté.
- Implémentation
- disponible
- UX
- non testé
- Tests locaux
- disponible
- Cloud
- n/a
- Natif
- n/a
- CI
- disponible
- Déploiement
- non testé
Export / suppression et preuves d’identité
UI /account/privacy, preuves HMAC, JWT renouvelé ≠ preuve.
- Implémentation
- disponible
- UX
- disponible
- Tests locaux
- disponible
- Cloud
- n/a
- Natif
- n/a
- CI
- disponible
- Déploiement
- non testé
Notifications
Inbox paginée, préférences, producteurs, pas de canal Push fantôme, module OFF.
- Implémentation
- disponible
- UX
- disponible
- Tests locaux
- disponible
- Cloud
- n/a
- Natif
- n/a
- CI
- disponible
- Déploiement
- non testé
Blog, changelog et référencement
Contenu versionné local, brouillons noindex, sitemap/robots, JSON-LD seulement avec des prix réels. OFF par défaut.
- Implémentation
- disponible
- UX
- disponible
- Tests locaux
- disponible
- Cloud
- n/a
- Natif
- n/a
- CI
- disponible
- Déploiement
- non testé
Écarts: #23
E-mail local et Resend
Mailpit, retry SMTP, webhook Svix local. Quota Resend Cloud et domaine non exercés.
- Implémentation
- disponible
- UX
- disponible
- Tests locaux
- disponible
- Cloud
- optionnel-externe
- Natif
- n/a
- CI
- disponible
- Déploiement
- non testé
PostHog consentement / redaction
SDK réel intercepté, refus/retrait, PII masquée. Projet PostHog Cloud EU non contacté.
- Implémentation
- disponible
- UX
- disponible
- Tests locaux
- disponible
- Cloud
- optionnel-externe
- Natif
- n/a
- CI
- disponible
- Déploiement
- non testé
Facturation test et droits
Checkout local, événements signés, catalogue versionné. Stripe Cloud non exercé. OFF sur presets free.
- Implémentation
- disponible
- UX
- disponible
- Tests locaux
- disponible
- Cloud
- optionnel-externe
- Natif
- n/a
- CI
- disponible
- Déploiement
- non testé
Clés API
Portée, expiry, rotate, journal borné. OFF sur presets free.
- Implémentation
- disponible
- UX
- disponible
- Tests locaux
- disponible
- Cloud
- n/a
- Natif
- n/a
- CI
- disponible
- Déploiement
- non testé
Webhooks sortants
SSRF local, test send, historique, récepteur HTTPS QA. Livraison Internet publique non revendiquée.
- Implémentation
- disponible
- UX
- disponible
- Tests locaux
- disponible
- Cloud
- optionnel-externe
- Natif
- n/a
- CI
- disponible
- Déploiement
- non testé
Écarts: #86
SSO / SCIM locaux
Keycloak OIDC/SAML + SCIM officiel. IdP cloud non exercé. Restart Auth requis.
- Implémentation
- disponible
- UX
- disponible
- Tests locaux
- disponible
- Cloud
- optionnel-externe
- Natif
- n/a
- CI
- disponible
- Déploiement
- non testé
MCP authentifié
Outils lecture, refus cross-workspace, écritures non publiées refusées. OFF sur presets free.
- Implémentation
- disponible
- UX
- disponible
- Tests locaux
- disponible
- Cloud
- n/a
- Natif
- n/a
- CI
- disponible
- Déploiement
- non testé
Console propriétaire et config effective
SQL + presets Git + Infisical. États applied / restart_required / migration_required. Premier user ≠ admin.
- Implémentation
- disponible
- UX
- disponible
- Tests locaux
- disponible
- Cloud
- n/a
- Natif
- n/a
- CI
- disponible
- Déploiement
- non testé
Génération de produit
north create (NR-22/23) génère depuis une release immuable (liste positive, north.json). Six presets reçus. create:product legacy reste jusqu’à NR-26. Parcours agent : GETTING_STARTED, RECIPES, UPGRADING_STARTER.
- Implémentation
- disponible
- UX
- n/a
- Tests locaux
- disponible
- Cloud
- n/a
- Natif
- n/a
- CI
- disponible
- Déploiement
- non testé
Backup, restore, doctor, hooks
Snapshot chiffré local, restore isolé, Lefthook. Scan Secret GitHub sur main : limite de dépense, pas une fuite.
- Implémentation
- disponible
- UX
- disponible
- Tests locaux
- disponible
- Cloud
- n/a
- Natif
- n/a
- CI
- optionnel-externe
- Déploiement
- non testé
Réutilisation native
SDKs partagés, iOS simulateur Release. Android et appareils physiques non exécutés.
- Implémentation
- disponible
- UX
- non testé
- Tests locaux
- disponible
- Cloud
- n/a
- Natif
- disponible
- CI
- disponible
- Déploiement
- non testé
a11y, responsive, locales, thèmes
FR/EN, 375/642, dark, clavier, axe 0 sur les parcours de réception. PR #53 a livré la nav groupée, le thème light/dark/system et la locale initiale. Rejeu 3 presets du chrome neuf : voir U01/U02, pas un écart produit ouvert.
- Implémentation
- disponible
- UX
- disponible
- Tests locaux
- disponible
- Cloud
- n/a
- Natif
- n/a
- CI
- disponible
- Déploiement
- non testé
Système de design Untitled UI
Interface sur les composants Untitled UI. Contrat : docs/design-system/UNTITLED-UI.md. @north/untitled-ui est le miroir épinglé ; @north/web-ui porte les compositions.
- Implémentation
- disponible
- UX
- disponible
- Tests locaux
- disponible
- Cloud
- n/a
- Natif
- n/a
- CI
- non testé
- Déploiement
- non testé
Déploiement production
Compose + Caddy documentés, images locales testées. Aucun déploiement DNS/prod ni activation payante.
- Implémentation
- disponible
- UX
- n/a
- Tests locaux
- disponible
- Cloud
- optionnel-externe
- Natif
- n/a
- CI
- optionnel-externe
- Déploiement
- non testé
Recettes métier
Domaine / table / permission
Schéma Convex, garde d’autorisation backend, contrat partagé.
Produit généré : 1) table dans convex/schema.product.ts (refs = strings publiques north.workspace_id / north.user_id). 2) permissions `product.<slug>.*` dans src/lib/extensions.ts — ne pas éditer PERMISSIONS North. 3) mutation dans convex/product.ts : establishPermission / assertModule côté serveur, jamais args.userId client. 4) `node scripts/add-feature.mjs --id notes --title-fr Notes --title-en Notes` écrit ces fichiers et pose coreEdited=false. 5) Ne pas toucher vendor/ ni packages/*.
convex/schema.product.tsconvex/product.tssrc/lib/extensions.tsdocs/adr/contracts/09-product-feature-examples.mdpackages/backend/convex/lib/authz.tspackages/backend/convex/lib/facade.ts
Page web
Route fichier, navigation, états, i18n.
Produit : src/routes/_app/<feature>.tsx (createFileRoute) + entrée PRODUCT_SHELL_NAV dans src/lib/navigation.ts (`group: "product"`, module `product.<slug>.<id>`). États loading / vide / erreur / interdit. Overlay i18n/thème produit. add-feature crée la route mince. Checklist avant de déclarer terminé. Un écran North se monte par ports ; un fork devient code produit.
src/routes/_app/src/lib/navigation.tsdocs/PAGE_CHECKLIST.mdpackages/web/workspace/INTEGRATOR.md
Upload
Fichiers d’espace vs avatar de profil.
Documents d’espace : api.files.generateUploadUrl → POST CONVEX_SITE_URL/files/upload avec le ticket one-use → createDownloadToken pour lire. Quota via resolveWorkspaceEntitlements (plan, puis grant, puis cap déploiement). Avatar : packages/backend/convex/avatars.ts, stockage personnel, indépendant du module files et du workspace actif. Le parcours navigateur avatar isolé n’est pas encore une preuve runtime.
packages/backend/convex/files.tspackages/backend/convex/avatars.tsapps/web/src/routes/_app/files.tsxapps/web/src/routes/_app/account.profile.tsx
Notification
Émettre depuis n’importe quel module métier.
Depuis une mutation produit déjà autorisée : emitNotification / emit via le port (`type` métier libre, catégorie security | account | system). Ne pas éditer `@north/convex-notifications`. Prefs et module OFF sont relus à l’émission. Module notifications OFF = no-op, la ligne métier reste écrite. Preuve A→B sans réécrire les fichiers produit : bash tests/reuse/notifications-upgrade/run.sh.
convex/product.tspackages/convex-notifications/src/index.tspackages/backend/convex/lib/notify.tspackages/backend/convex/lib/coreNotificationPrefs.tstests/reuse/notifications-upgrade/README.md
Auth essentiel vs notifications optionnelles.
Vérification, reset, magic link : mailer Auth (Mailpit local, Resend en production). Invitations d’espace : invitations.deliverEmail. Notifications optionnelles : queueNotificationEmail. Un échec SMTP ne casse pas le produit (E01). Le quota Resend Cloud n’a pas été exercé (E02).
apps/auth/src/packages/backend/convex/lib/notifyQueue.tsdocs/INTEGRATIONS.md
Quota / entitlement
Catalogue versionné, pas des compteurs démo.
Produit : déclarer la clé `product.<slug>.*` dans src/lib/extensions.ts (quotas + plan overlay). reserveQuota / releaseQuota / confirmQuota dans la même mutation que l’insert (convex/product.ts). Ne pas activer billing dans un preset free pour « tester » un compteur. Catalogue North : plan, puis grants, puis cap déploiement via `@north/convex-core`. Preuve concurrence table métier : bash tests/reuse/convex-core-entitlements/run.sh. Stripe Cloud n’est pas cette vérification.
src/lib/extensions.tsconvex/product.tspackages/contracts/src/entitlements.tspackages/convex-core/src/entitlements-engine.tspackages/backend/convex/lib/coreEntitlements.ts
Job d’arrière-plan
Planifier, relire config, respecter module OFF.
Utiliser ctx.scheduler.runAfter depuis une mutation. Exemples : notifications.deliverEmail, files.cleanupAbandoned, privacy chunks, invitations.deliverEmail, feedback.deliver. Le job doit rappeler runtimeModuleOn / préférences avant une livraison optionnelle. Les e-mails d’auth restent dans le service Auth, pas dans Convex.
packages/backend/convex/notifications.tspackages/backend/convex/files.tspackages/backend/convex/privacy.tspackages/backend/convex/invitations.ts
Module optionnel
Preset, console, garde backend, UI.
Déclarer le flag dans @north/contracts MODULE_IDS et packages/config. Preset free = OFF pour billing/api/webhooks/SSO/SCIM/MCP. assertModule aux points d’entrée et runtimeModuleOn dans les producteurs. L’UI utilise ModuleOff sur l’URL directe. Publier via /configure ou pnpm config:apply, puis restart Auth si des plugins froids changent.
packages/config/src/presets.tspackages/backend/convex/lib/authz.tspackages/backend/convex/lib/runtimeConfig.tsapps/web/src/components/PublicChrome.tsx
Blog / changelog / SEO
Publier un article et une nouveauté sans CMS externe.
1) Activer modules.marketing (ou --with-marketing à la génération). 2) Ajouter un fichier `apps/web/content/blog/{slug}.{fr|en}.md` et `changelog/{slug}.{fr|en}.md` avec frontmatter validé. 3) `draft: true` sert la page en noindex et l’exclut du sitemap et des listes. 4) site.url alimente canonical, Open Graph, sitemap.xml et robots.txt. 5) JSON-LD SoftwareApplication seulement si /pricing affiche des prix réels. Les pages légales restent des modèles, pas un avis juridique.
apps/web/content/apps/web/src/lib/seo.tsapps/web/src/routes/blog.index.tsxpackages/config/src/content.tspackages/config/src/seo.ts
Extensions produit typées
Permissions, modules, plans et quotas namespacés sans éditer les sources North.
1) Déclarer un overlay `product.<slug>.*` via composeProductExtensions / applyProductExtensions. 2) Ne pas éditer PERMISSIONS, MODULE_IDS ni PLANS North. 3) Une collision avec un nom North ou le remplacement d’un guard (requireValidSession, requireMembership) est rejeté. 4) Les presets free gardent billing, SSO, SCIM et MCP à OFF. 5) publicCapabilities reste filtré : aucun secret Infisical/Stripe.
packages/contracts/src/extensions.tspackages/config/src/extensions.tspackages/modules/src/extensions.tsdocs/adr/contracts/05-permissions-and-modules.md
Cycle de vie métier
Export / purge produit enregistrés côté serveur, reprise par cursor.
Enregistrer `product.<slug>.<feature>` dans convex/lifecycle.ts (PRODUCT_LIFECYCLE_HANDLERS). exportChunk / purgeChunk paginent par workspaceId ou createdBy. Module OFF n’empêche pas export/purge. Un handler qui lève laisse remainingSteps ; privacyRequests n’est pas completed. Ne pas marcher les tables dans privacy.ts. Ne pas purger SQL depuis une mutation métier.
convex/lifecycle.tsdocs/adr/contracts/04-lifecycle.mddocs/adr/contracts/09-product-feature-examples.md
Support et fork d’écran
Monter un écran North par ports, ou le forker hors garantie.
Support : route mince + overlay nav/i18n/thème ; apply continue de livrer @north/web-account et @north/web-workspace. Fork : copier l’écran dans le dépôt produit — plus de garantie visuelle ; invitations/files/prefs restent les API core. Masquer une entrée n’est pas assertModule. Détail : docs/UPGRADING_STARTER.md §7.
src/lib/navigation.tssrc/lib/theme.tspackages/web/workspace/INTEGRATOR.mddocs/UPGRADING_STARTER.md
Checklist de page / module
- Clavier, nom accessible, axe-core 0 sur la vue (exclure uniquement les overlays documentés).
- 375px et ≥642px sans overflow du shell ; menus refermables (Escape).
- FR/EN via t() ; thème clair/sombre/système ; dates dans le fuseau utilisateur.
- États loading, vide, erreur (role=alert) et succès ; pas de bouton mort.
- Permission Convex réelle ; un viewer ne passe pas par l’UI cachée.
- Module OFF : nav absente, URL directe → Module unavailable, mutation/query/job refusés.
- Preuve de parcours contre le stack isolé (NORTH_ENV_FILE), pas un screenshot ni un fetch simulé.