Python 66.7%
TypeScript 17.1%
CSS 15.7%
HTML 0.5%
1<div align="center">23# Forma·Ka45### Toutes les formations du Québec. Un seul endroit.67**[www.forma-ka.com](https://www.forma-ka.com)**8910111213141516171819202122*Agrégateur indépendant de formations dans la province de Québec — cours en23ligne, cours universitaires et collégiaux, séminaires, ateliers, conférences,24certifications et bootcamps — chaque formation avec sa fiche détaillée25standardisée et un lien direct vers la page originale de l'établissement.26Toujours à jour, automatiquement.*2728<br/>2930**Auteur : Simon-Pierre Boucher — [contact@spboucher.ai](mailto:contact@spboucher.ai)**3132</div>3334---3536## Sommaire37381. [Visite guidée en 10 captures](#visite-guidée-en-10-captures)392. [Pourquoi Forma-Ka ?](#pourquoi-forma-ka-)403. [Fonctionnalités](#fonctionnalités)414. [Architecture](#architecture)425. [Le pipeline d'ingestion en détail](#le-pipeline-dingestion-en-détail)436. [Les backends de fetch (chaîne résiliente)](#les-backends-de-fetch-chaîne-résiliente)447. [Le schéma `Formation`](#le-schéma-formation)458. [API & routes](#api--routes)469. [Données & sources agrégées](#données--sources-agrégées)4710. [Ajouter un connecteur](#ajouter-un-connecteur)4811. [Démarrage rapide](#démarrage-rapide)4912. [Tests](#tests)5013. [Déploiement (production)](#déploiement-production)5114. [Dépôt & développement remote-first](#dépôt--développement-remote-first)5215. [Confidentialité](#confidentialité)5316. [Écosystème Groupe Ka](#écosystème-groupe-ka)5417. [Contact](#contact)5556---5758## Visite guidée en 10 captures5960*Captures du site en production ([www.forma-ka.com](https://www.forma-ka.com)),61prises le 2026-08-28 en 1440 × 900 — plus une vue mobile 390 px. Les fichiers62sont dans63[`docs/screenshots/`](docs/screenshots/) (voir aussi64[`manifest.txt`](docs/screenshots/manifest.txt) pour la correspondance65capture → URL).*6667### 1 · Accueil — le moteur de recherche68697071La page d'accueil (`/`) donne le ton « editorial sharp » : gros titres Space72Grotesk, surlignage ambré **#ffd54d**, ombres décalées, ticker en continu des73types de formations. On y trouve les compteurs en direct (**4 932 formations74actives · 135 gratuites · 3 556 en ligne · 18 établissements**), la barre de75recherche (sujet, sigle, compétence…), les filtres **Domaine / Mode / Ville /76Plus de filtres** et les pastilles de filtre rapide par type (Cours77universitaire, Formation continue, Cours collégial, Atelier, Conférence,78Séminaire, Webinaire, Gratuites…). En bas, la bannière de témoins rappelle la79philosophie : aucun traceur publicitaire, préférences en localStorage.8081### 2 · Statistiques — portrait en direct de l'offre québécoise82838485La page `/stats` dresse le portrait en direct des formations actives, tous86établissements confondus : compteurs globaux (prix moyen affiché **858 $**,87durée moyenne **14 h**), répartition **par type de formation** (2 268 cours88universitaires, 2 058 formations continues, 118 cours collégiaux, 112 ateliers,89104 cours en ligne, 98 conférences, 80 séminaires, 41 webinaires, 2790programmes, 22 certifications, 4 bootcamps) et journal des **synchronisations91récentes** par source (trouvées / ajoutées / modifiées / retirées + état OK).9293### 3 · Sources agrégées — le registre des établissements94959697La page `/sources` expose le registre `data/sources.json` enrichi en direct :98chaque établissement avec son type d'offre, son statut de connecteur99(**CONNECTÉ**), le nombre de formations actives et l'horodatage de la dernière100synchronisation. C'est la vitrine de transparence de Forma-Ka : on voit d'où101viennent les données et quand elles ont été rafraîchies.102103### 4 · Confidentialité — « Votre vie privée, simplement. »104105106107La page `/confidentialite` détaille l'approche minimaliste : aucun compte108utilisateur, aucun pixel publicitaire, aucun témoin tiers. Le seul stockage est109le localStorage du navigateur (choix de consentement, filtres de recherche),110avec un bouton « Modifier mes choix de témoins » accessible en tout temps.111112### 5 · Fiche formation — le détail d'abord (Isarta · Data Studio)113114115116Une fiche `/formation/:uid` (ici `isarta:data-studio`) montre la philosophie117« détail d'abord » : fil d'Ariane par catégorie, **plan de la formation**118complet (11 points), bloc **Admission / clientèle visée**, colonne des119**thèmes** et des **formations similaires** (avec type, mode, durée, prix et120source), puis le contenu original signé par la source. Chaque fiche renvoie121vers la page originale de l'établissement.122123### 6 · Fiche formation — les recommandations « Formations similaires »124125126127Le cœur d'une fiche vu de près : à gauche le **plan de la formation** point128par point et le bloc **Admission / clientèle visée** ; à droite la colonne de129recommandations **Formations similaires**, calculée par l'API (même catégorie,130même source, thèmes proches) avec pour chacune son type, son mode, sa durée et131son prix — un vrai moteur de découverte entre les 4 900+ fiches.132133### 7 · Accueil — la grille de résultats134135136137L'accueil, défilé jusqu'à la grille : cartes de formation en 3 colonnes avec138badge de type (Formation continue, Atelier…), établissement, titre, sigle,139mode (présentiel / en ligne) et durée, date de début (« Débute le 3 septembre1402026 ») et traitement franc du prix — **« Prix non affiché »** assumé (prix141optionnel), **« Gratuit »** mis en valeur, sinon le montant.142143### 8 · Accueil — cartes illustrées & pagination (411 pages)144145146147Plus bas sur l'accueil : les cartes reprennent les visuels originaux des148établissements (Institut de leadership, Événements Les Affaires…) avec prix149affiché (395 $, 545 $, 945 $), mode et ville. En pied de grille, la150**pagination** — « Page 1 de 411 — 4 930 formations » à 12 fiches par page —151puis le pied de page sombre avec la mission de l'agrégateur.152153### 9 · Fiche formation en mobile (390 px)154155156157La même fiche Data Studio sur téléphone : menu hamburger, ticker conservé,158fil d'Ariane et plan de la formation qui se replient proprement sur une159colonne — le socle mobile du Groupe Ka (zones tactiles, safe-area, PWA160installable) appliqué à Forma-Ka.161162### 10 · Fiche événement — prix optionnel, dates offertes (CRHA · 5 à 7 estival)163164165166Deuxième exemple de fiche (`crha:202609035@7Lanaudiere`) qui illustre la167souplesse du schéma : un événement de réseautage RH **sans prix affiché**168(le prix est optionnel dans Forma-Ka), avec ses **dates offertes** (3 septembre1692026), ses **infos pratiques** (établissement, sigle, horaire affiché, date de170synchronisation) et ses thèmes régionaux (Comité régionale de Lanaudière).171172### Galerie mobile & desktop (2026-08-25)173174*Captures WebP précédentes (mobile 390 × 844 · desktop 1440 × 900), conservées175dans [`docs/screenshots/mobile/`](docs/screenshots/mobile/) et176[`docs/screenshots/desktop/`](docs/screenshots/desktop/).*177178<table>179 <tr>180 <td align="center"><img src="docs/screenshots/mobile/home.webp" width="200" alt="Accueil mobile"><br><sub><b>Accueil mobile</b></sub></td>181 <td align="center"><img src="docs/screenshots/mobile/formation.webp" width="200" alt="Fiche mobile"><br><sub><b>Fiche — détail d'abord</b></sub></td>182 <td align="center"><img src="docs/screenshots/mobile/stats.webp" width="200" alt="Stats mobile"><br><sub><b>Stats mobile</b></sub></td>183 <td align="center"><img src="docs/screenshots/mobile/menu-mobile.webp" width="200" alt="Menu mobile"><br><sub><b>Menu mobile</b></sub></td>184 </tr>185</table>186187> 🗄️ Les toutes premières captures (PNG) sont archivées dans188> [`docs/archive/`](docs/archive/).189190---191192## Pourquoi Forma-Ka ?193194Chercher une formation au Québec, c'est ouvrir des dizaines de sites —195universités, cégeps, firmes de formation, organisateurs d'événements — chacun196avec sa navigation et son format. **Forma-Ka renverse le problème** : un197connecteur dédié par établissement visite chaque site, normalise chaque198formation dans un schéma unique et détecte les changements en continu.199200> Les sites de formation n'offrent pas de webhooks. Forma-Ka en reproduit201> l'équivalent : **synchronisation périodique + hachage de contenu** → ajouts,202> mises à jour et retraits détectés automatiquement. Une formation qui203> disparaît du site source disparaît de Forma-Ka (après une période de grâce204> de 2 synchronisations).205206**Philosophie** : contrairement à un agrégateur de produits, **le prix est207optionnel** (un cours universitaire n'affiche pas de prix) — ce qui compte, ce208sont les **détails** de chaque formation : description complète, objectifs209d'apprentissage, plan de cours, préalables, clientèle visée, durée,210crédits/UEC, mode de diffusion, dates offertes.211212## Fonctionnalités213214- **Recherche plein texte** (sujet, sigle, compétence) combinable avec les215 filtres **domaine** (Informatique, Gestion, RH, Marketing…), **type**216 (cours universitaire, formation continue, atelier, séminaire, webinaire,217 certification, bootcamp…), **mode** (en ligne, présentiel, hybride,218 asynchrone), **ville**, **langue**, **niveau**, **gratuit**, **prix maximal**219 et **date de début** ;220- **Fiches détaillées standardisées** : description, objectifs, plan de cours,221 préalables, clientèle visée, durée (texte + heures), crédits/UEC, sigle,222 formateur, dates offertes, prix (optionnel, avec mention « à partir de »),223 thèmes, formations similaires, lien direct vers la source ;224- **Statistiques en direct** (`/stats`) : portrait global + journal des225 synchronisations par source ;226- **Registre des sources** (`/sources`) : statut du connecteur, volume,227 dernière synchro ;228- **Ticker en continu** des types de formations en tête de chaque page ;229- **PWA installable** (manifest + thème clair, accent ambré `#ffd54d`),230 pagination 12 par page, feuillet mobile (« bottom sheet ») pour les filtres ;231- **Aucun traceur** : consentement localStorage, page confidentialité dédiée ;232- **Mise à jour automatique** toutes les 6 h (watcher PM2) avec détection de233 dérive des connecteurs.234235## Architecture236237```mermaid238flowchart LR239 subgraph Sources["18 établissements de formation"]240 S1["ÉTS Formation · TÉLUQ · ULaval<br/>McGill · HEC · UQAM · Technologia<br/>AFI · Cégep à distance · Les Affaires<br/>… un connecteur par site"]241 end242 subgraph FormaKa["Forma-Ka"]243 C["Connecteurs<br/><i>1 adaptateur / site</i>"] --> N["Normalisation<br/><i>schéma Formation unique</i>"]244 N --> D[("SQLite<br/>hash + diff + grâce")]245 D --> A["FastAPI<br/>/api/formations · /api/facets"]246 A --> F["React 18 + Vite<br/>PWA mobile · thème clair ambré"]247 end248 W["⏱ Watcher périodique<br/>(PM2, 6 h)"] -.-> C249 S1 --> C250 F --> U["🎓 Apprenant"]251```252253| Couche | Rôle | Fichiers |254|---|---|---|255| **Connecteurs** | 1 module Python par établissement : HTML rendu serveur, API JSON internes (Shopify `products.json`, WordPress REST, Gatsby `page-data`, Destiny One…), JSON-LD schema.org (`Course`/`Event`), chaîne anti-bot résiliente pour les sites difficiles | `formaka/connectors/*.py` |256| **Chaîne résiliente** | Escalade automatique direct → proxy résidentiel → anti-bot géré → déblocage premium quand un site se ferme (403/429/503, Cloudflare…) | `formaka/connectors/_resilient.py` |257| **Schéma** | `Formation` standardisée : type, catégorie, mode, description, objectifs, plan, préalables, durée, crédits/UEC, sessions, prix *(optionnel)* | `formaka/schema.py` |258| **Normalisation** | Couche commune : nettoyage de texte, types/modes/langues canoniques, prix, durées en heures, dates FR → ISO, extraction de détails (niveau, UEC…) | `formaka/normalize.py` |259| **Moteur de diff** | Upsert par hash de contenu — nouveau / modifié / disparu (période de grâce de 2 synchros), détection de dérive des connecteurs, cache des fiches | `formaka/db.py`, `formaka/ingest.py` |260| **API** | Filtres type / catégorie / mode / ville / langue / niveau / gratuit / prix / source / recherche, facettes, stats, déclenchement de synchro | `formaka/web.py` |261| **Frontend** | Design « editorial sharp » : Space Grotesk, ombres décalées, accent ambré `#ffd54d`, ticker en continu, feuillet mobile, pagination 12/page, PWA installable | `frontend/` |262263### Structure du dépôt264265```266forma-ka/267├── run.py # point d'entrée : sync | watch | serve268├── requirements.txt # fastapi, uvicorn, requests, beautifulsoup4269├── formaka/270│ ├── schema.py # dataclass Formation + finalize()271│ ├── normalize.py # normalisation commune (texte, prix, dates…)272│ ├── ingest.py # pipeline sync/watch (diff + grâce + journal)273│ ├── db.py # SQLite : formations, detail_cache, sync_log274│ ├── web.py # FastAPI : /api/* + service du frontend bâti275│ └── connectors/ # 18 connecteurs + base.py + _resilient.py276├── frontend/ # React 18 + Vite + TypeScript (PWA)277│ └── src/pages/ # Home, Formation, Stats, Sources, Privacy278├── data/279│ ├── sources.json # registre des établissements (versionné)280│ └── formaka.db # base SQLite (NON versionnée)281├── docs/screenshots/ # visite guidée (JPG) + galeries webp282└── tests/ # pytest (normalisation)283```284285## Le pipeline d'ingestion en détail2862871. **Découverte** — le registre des connecteurs est **auto-découvrant** :288 chaque module de `formaka/connectors/` qui définit une classe héritant de289 `BaseConnector` avec un `source_id` est enrôlé automatiquement.2902. **Collecte** — `fetch()` liste les formations du site (listing HTML, API291 JSON interne, sitemap…), puis visite les pages de détail au besoin.2923. **Cache des fiches** — les pages de détail sont mises en cache dans la base293 (`detail_cache`) avec une **clé hebdomadaire** : une page n'est revisitée294 que si elle est nouvelle, modifiée, ou quand la semaine ISO change. Les295 synchros intermédiaires sont donc rapides et économes.2964. **Normalisation** — `Formation.finalize()` applique la couche commune297 (types/modes/langues canoniques, prix depuis le libellé, durée en heures,298 dates françaises → ISO, extraction de niveau/UEC/crédits) sans jamais299 écraser une valeur explicite du connecteur.3005. **Diff & grâce** — chaque fiche a un `content_hash` SHA-256 ; l'upsert ne301 touche que ce qui a changé. Une formation absente du site source est302 retirée après **2 synchros consécutives** d'absence (période de grâce303 contre les listings instables).3046. **Journal & dérive** — chaque synchro est journalisée (`sync_log` :305 trouvées / ajoutées / modifiées / retirées + taux de champs nuls) ; une306 chute anormale du volume ou une explosion des champs vides signale une307 dérive du connecteur (site remanié) visible sur `/stats` et `/sources`.308309## Les backends de fetch (chaîne résiliente)310311Historiquement, `BaseConnector.fetch_html()` enchaînait trois backends :3123131. **requests direct** — sites rendus serveur (rapide, gratuit) ;3142. **Scrapfly** (`SCRAPFLY_API_KEY`) — contournement anti-bot (`asp`), rendu315 JavaScript (`render_js`), géolocalisation canadienne ;3163. **Firecrawl** (`FIRECRAWL_API_KEY`) — rendu JS de repli, formats317 html/markdown.318319Cette chaîne est désormais renforcée par la **chaîne anti-bot résiliente320commune du Groupe KA** (`formaka/connectors/_resilient.py`) : quand un site321jusque-là ouvert déploie un anti-bot (Cloudflare, Akamai, Incapsula,322PerimeterX) ou renvoie 403/429/503, la requête directe n'échoue plus323silencieusement — elle **escalade automatiquement** :3243251. **Direct** — session du connecteur (`curl_cffi` avec empreinte de326 navigateur si disponible, sinon `requests`) ;3272. **Proxy résidentiel** — IP résidentielle canadienne propre ;3283. **Scrapfly (ASP)** — bypass anti-bot géré + rendu JS optionnel ;3294. **Bright Data (Web Unlocker)** — déblocage premium, dernier recours.330331Le premier backend qui renvoie un 200 non vide gagne ; si tous échouent, la332dernière réponse est renvoyée telle quelle pour que le connecteur journalise333l'échec normalement (aucun blocage silencieux). Les clés d'API vivent dans334`.env` (jamais versionné).335336## Le schéma `Formation`337338Chaque connecteur, peu importe le site source, produit des objets `Formation`339(`formaka/schema.py`) :340341| Groupe | Champs |342|---|---|343| **Identité** | `source`, `external_id`, `url` → `uid = source:external_id` |344| **Classement** | `title`, `training_type`, `category`, `tags`, `level`, `language` |345| **Diffusion** | `mode` (en ligne / présentiel / hybride / asynchrone), `city`, `start_date` (ISO), `sessions[]`, `schedule_label`, `duration`, `duration_hours` |346| **Contenu** | `description`, `objectives[]`, `program[]` (plan de cours), `prerequisites`, `audience`, `instructor`, `code` (sigle), `images[]` |347| **Valeur** | `price` *(optionnel — `None` = non affiché, c'est normal !)*, `price_label` (texte original), `is_free`, `credits` (« 3 crédits », « 1,4 UEC »), `credential` |348| **Technique** | `details` (JSON structuré dérivé), `content_hash()` (SHA-256 pour la détection de changements) |349350## API & routes351352### API REST (FastAPI)353354| Point d'accès | Description |355|---|---|356| `GET /api/formations` | Liste filtrable (`training_type`, `category`, `mode`, `city`, `language`, `level`, `source`, `free`, `price_max`, `starts_after`, `q`, `sort`, `limit`, `offset`) |357| `GET /api/formations/{uid}` | Fiche complète + historique de prix + formations similaires |358| `GET /api/facets` | Valeurs distinctes pour construire les filtres |359| `GET /api/sources` | Registre des établissements + état de synchro (`active_formations`, `last_sync`) |360| `GET /api/stats` | Portrait global (totaux, moyennes, répartition par type) + journal des synchros |361| `POST /api/sync` | Déclenche une synchronisation en arrière-plan |362363### Routes du frontend (SPA React Router)364365| Route | Page |366|---|---|367| `/` | Accueil — recherche, filtres, compteurs en direct |368| `/formation/:uid` | Fiche détaillée (ex. `/formation/isarta:data-studio`) |369| `/stats` | Statistiques en direct |370| `/sources` | Registre des sources agrégées |371| `/confidentialite` | Politique de confidentialité & témoins |372| `*` | Page 404 (boussole) |373374Le backend sert aussi le frontend bâti (`frontend/dist`) : `/assets` en375statique + rattrapage SPA sur toutes les autres routes.376377## Données & sources agrégées378379**18 établissements connectés — 4 932 formations actives** (relevé du3802026-08-28) :381382| Établissement | Offre | Formations | Région |383|---|---|---:|---|384| Université Laval — Formation à distance | Cours universitaires à distance, hybrides et comodaux | 1 757 | Québec (à distance / hybride) |385| Technologia | Formation professionnelle — TI, IA, gestion | 590 | Montréal / Québec |386| Université TÉLUQ | Cours universitaires 100 % à distance, tous cycles | 511 | Québec (à distance) |387| AFI par Edgenda | Formation professionnelle — TI, leadership | 358 | Québec / Montréal |388| ÉTS Formation | Formation continue + UEC — technologie, construction, gestion, RH | 296 | Montréal |389| Versalys | Bureautique, TI, langues | 257 | Montréal · Québec · Laval · Brossard |390| McGill School of Continuing Studies | Formation continue (anglais) | 197 | Montréal |391| Isarta Formations | Marketing, communications, RH | 189 | Montréal (virtuel) |392| CRHA — Espace Formation | Formations et événements RH | 180 | Québec (province) — surtout en ligne |393| Événements Les Affaires | Conférences et webinaires d'affaires | 139 | Montréal / Québec / en ligne |394| Cégep à distance | Cours collégiaux à distance | 118 | Québec (en ligne) |395| École des dirigeant(e)s HEC Montréal | Séminaires et certifications pour cadres | 93 | Montréal |396| ITHQ — Ateliers et formations | Ateliers vins/cuisine + hôtellerie | 78 | Montréal (+ province) |397| Formation continue UQAM | Formation continue universitaire — UEC, séminaires | 49 | Montréal |398| École des entrepreneurs du Québec | Formations entrepreneur·e·s (souvent gratuites) | 48 | Montréal + en ligne |399| Institut de leadership | Certifications et programmes en leadership | 37 | Montréal (+ cohortes en ligne) |400| AlphaNumérique | Littératie numérique — gratuit | 29 | Québec (en ligne) |401| Le Wagon Montréal | Bootcamps dev web / data / IA | 4 | Montréal |402403Répartition par type : **2 268** cours universitaires · **2 058** formations404continues · **118** cours collégiaux · **112** ateliers · **104** cours en405ligne · **98** conférences · **80** séminaires · **41** webinaires · **27**406programmes · **22** certifications · **4** bootcamps.407408Le registre vit dans [`data/sources.json`](data/sources.json) (versionné) ;409la base SQLite (`data/formaka.db`) est locale et reconstruite par `sync`.410411## Ajouter un connecteur4124131. Créer `formaka/connectors/<source_id>.py` : une classe héritant de414 `BaseConnector`, définir `source_id` et implémenter415 `fetch() -> list[Formation]`. Le registre est **auto-découvrant** — rien416 d'autre à modifier.4172. Ajouter l'entrée correspondante dans `data/sources.json`.4183. Tester : `.venv/bin/python run.py sync <source_id>`.419420```python421class MonEcoleConnector(BaseConnector):422 source_id = "mon_ecole"423424 def fetch(self) -> list[Formation]:425 html = self.fetch_html(LIST_URL) # direct -> chaîne résiliente426 ...427 return [Formation(source=self.source_id, external_id=..., url=...,428 title=..., description=..., objectives=[...], ...)]429```430431## Démarrage rapide432433```bash434git clone https://git.spboucher.ai/forma-ka.git && cd forma-ka435436# Backend437python3 -m venv .venv && .venv/bin/pip install -r requirements.txt438439# Frontend440cd frontend && npm install && npm run build && cd ..441442# Clés des backends de scraping (sites JavaScript / anti-bot) — jamais versionnées443cat > .env <<EOF444FIRECRAWL_API_KEY=fc-votre-cle445SCRAPFLY_API_KEY=scp-live-votre-cle446EOF447448# Ingestion, puis service449.venv/bin/python run.py sync # toutes les sources (ou : run.py sync ets_formation teluq)450.venv/bin/python run.py serve 8080 # API + frontend -> http://localhost:8080451.venv/bin/python run.py watch 360 # boucle de synchro (défaut : toutes les 6 h)452```453454## Tests455456```bash457.venv/bin/python -m pytest tests/ -q458```459460## Déploiement (production)461462| Élément | Valeur |463|---|---|464| **Nœud** | `M3U96a` (Mac Studio, cluster MacLustr) — `~/apps/forma-ka` |465| **Port** | `8110` |466| **Processus PM2** | `forma-ka` (API + frontend, `run.py serve 8110`) · `forma-ka-sync` (`run.py watch 360` → synchro toutes les 6 h) · `forma-ka-ngrok` (tunnel) |467| **Domaine** | [www.forma-ka.com](https://www.forma-ka.com) via ngrok |468| **Base** | `data/formaka.db` (SQLite, locale au nœud) |469| **Résilience** | PM2 avec redémarrage automatique + `pm2 startup` (launchd) |470471```bash472# Sur le nœud (lecture seule — l'état de référence)473pm2 ls | grep forma-ka474curl -s localhost:8110/api/stats | head -c 300475```476477## Dépôt & développement remote-first478479La **source de vérité est le dépôt git sur le nœud de déploiement**480(`M3U96a:~/apps/forma-ka`), pas une copie locale. Le remote `origin` est le481git personnel **spbgit** (git.spboucher.ai) — dépôt nu482`~/srv/git/forma-ka.git` hébergé sur M3U96a.483484Cycle de travail : éditer sur le nœud via SSH → `npm run build` (frontend) →485`pm2 restart forma-ka` → `git add/commit/push origin main` **sur le nœud**486(agent forwarding actif).487488Ce qui n'est **jamais versionné** (voir [`.gitignore`](.gitignore)) : la base489SQLite et les caches (`data/`, sauf `sources.json` déjà suivi), les secrets490(`.env*`), les environnements (`.venv/`, `node_modules/`), les artefacts de491build (`frontend/dist/`) et les journaux.492493## Confidentialité494495Forma-Ka ne collecte **rien qui identifie l'utilisateur** : pas de compte, pas496de formulaire d'inscription, pas de pixel publicitaire, pas de témoin tiers,497aucune donnée vendue. Le seul stockage est le **localStorage** du navigateur498(choix de consentement, filtres, préférences d'affichage), modifiable en tout499temps via « Gérer mes témoins ». Détails : page500[`/confidentialite`](https://www.forma-ka.com/confidentialite).501502## Écosystème Groupe Ka503504Forma-Ka fait partie du **Groupe Ka** ([groupe-ka.com](https://www.groupe-ka.com)),505la famille d'agrégateurs indépendants du Québec — notamment506[Lou-Ka](https://www.lou-ka.com) (logements),507[Immo-Ka](https://www.immo-ka.com) (propriétés),508[Vrai-Prix](https://www.vrai-prix.com) (épicerie),509[Auto-Ka](https://www.auto-ka.com) (véhicules),510[Food-Ka](https://www.food-ka.com), [Resto-Ka](https://www.resto-ka.com),511[Sorti-Ka](https://www.sorti-ka.com) (sorties),512[Job-Ka](https://www.job-ka.com) (emplois),513[Trouve-Ka](https://www.trouve-ka.com) (recherche),514[Créa-Ka](https://www.crea-ka.com) (créateurs),515[Fabri-Ka](https://www.fabri-ka.com) (produits d'ici) et516[Ka·Stats](https://www.ka-stats.com) (statistiques du Québec).517Même ADN partout : connecteurs dédiés, schéma standardisé, diff par hachage,518fiches détaillées, respect de la vie privée — et le mécanisme de Forma-Ka est519directement adapté de celui de Lou-Ka.520521## Contact522523<div align="center">524525**Simon-Pierre Boucher**526[contact@spboucher.ai](mailto:contact@spboucher.ai) · [www.forma-ka.com](https://www.forma-ka.com)527528© 2026 Simon-Pierre Boucher — tous droits réservés.529530</div>531