# Forma·Ka ### Toutes les formations du Québec. Un seul endroit. **[www.forma-ka.com](https://www.forma-ka.com)** ![Python](https://img.shields.io/badge/Python-3.14-141814?style=for-the-badge&logo=python&logoColor=ffd54d) ![FastAPI](https://img.shields.io/badge/FastAPI-API-141814?style=for-the-badge&logo=fastapi&logoColor=ffd54d) ![React](https://img.shields.io/badge/React_18-Vite_+_TS-141814?style=for-the-badge&logo=react&logoColor=ffd54d) ![SQLite](https://img.shields.io/badge/SQLite-stockage-141814?style=for-the-badge&logo=sqlite&logoColor=ffd54d) ![PWA](https://img.shields.io/badge/PWA-installable-141814?style=for-the-badge&logoColor=ffd54d) ![Sources](https://img.shields.io/badge/%C3%A9tablissements-18-1d3f66?style=flat-square) ![Connecteurs](https://img.shields.io/badge/connecteurs_actifs-18-1d3f66?style=flat-square) ![Formations](https://img.shields.io/badge/formations_actives-4%C2%A0930%2B-1d3f66?style=flat-square) ![Gratuites](https://img.shields.io/badge/formations_gratuites-135-1d3f66?style=flat-square) ![En ligne](https://img.shields.io/badge/en_ligne-3%C2%A0556-1d3f66?style=flat-square) ![Couverture](https://img.shields.io/badge/couverture-tout_le_Qu%C3%A9bec-1d3f66?style=flat-square) *Agrégateur indépendant de formations dans la province de Québec — cours en ligne, cours universitaires et collégiaux, séminaires, ateliers, conférences, certifications et bootcamps — chaque formation avec sa fiche détaillée standardisée et un lien direct vers la page originale de l'établissement. Toujours à jour, automatiquement.*
**Auteur : Simon-Pierre Boucher — [contact@spboucher.ai](mailto:contact@spboucher.ai)**
--- ## Sommaire 1. [Visite guidée en 10 captures](#visite-guidée-en-10-captures) 2. [Pourquoi Forma-Ka ?](#pourquoi-forma-ka-) 3. [Fonctionnalités](#fonctionnalités) 4. [Architecture](#architecture) 5. [Le pipeline d'ingestion en détail](#le-pipeline-dingestion-en-détail) 6. [Les backends de fetch (chaîne résiliente)](#les-backends-de-fetch-chaîne-résiliente) 7. [Le schéma `Formation`](#le-schéma-formation) 8. [API & routes](#api--routes) 9. [Données & sources agrégées](#données--sources-agrégées) 10. [Ajouter un connecteur](#ajouter-un-connecteur) 11. [Démarrage rapide](#démarrage-rapide) 12. [Tests](#tests) 13. [Déploiement (production)](#déploiement-production) 14. [Dépôt & développement remote-first](#dépôt--développement-remote-first) 15. [Confidentialité](#confidentialité) 16. [Écosystème Groupe Ka](#écosystème-groupe-ka) 17. [Contact](#contact) --- ## Visite guidée en 10 captures *Captures du site en production ([www.forma-ka.com](https://www.forma-ka.com)), prises le 2026-08-28 en 1440 × 900 — plus une vue mobile 390 px. Les fichiers sont dans [`docs/screenshots/`](docs/screenshots/) (voir aussi [`manifest.txt`](docs/screenshots/manifest.txt) pour la correspondance capture → URL).* ### 1 · Accueil — le moteur de recherche ![Accueil](docs/screenshots/01-accueil.jpg) La page d'accueil (`/`) donne le ton « editorial sharp » : gros titres Space Grotesk, surlignage ambré **#ffd54d**, ombres décalées, ticker en continu des types de formations. On y trouve les compteurs en direct (**4 932 formations actives · 135 gratuites · 3 556 en ligne · 18 établissements**), la barre de recherche (sujet, sigle, compétence…), les filtres **Domaine / Mode / Ville / Plus de filtres** et les pastilles de filtre rapide par type (Cours universitaire, Formation continue, Cours collégial, Atelier, Conférence, Séminaire, Webinaire, Gratuites…). En bas, la bannière de témoins rappelle la philosophie : aucun traceur publicitaire, préférences en localStorage. ### 2 · Statistiques — portrait en direct de l'offre québécoise ![Statistiques](docs/screenshots/02-stats.jpg) La page `/stats` dresse le portrait en direct des formations actives, tous établissements confondus : compteurs globaux (prix moyen affiché **858 $**, durée moyenne **14 h**), répartition **par type de formation** (2 268 cours universitaires, 2 058 formations continues, 118 cours collégiaux, 112 ateliers, 104 cours en ligne, 98 conférences, 80 séminaires, 41 webinaires, 27 programmes, 22 certifications, 4 bootcamps) et journal des **synchronisations récentes** par source (trouvées / ajoutées / modifiées / retirées + état OK). ### 3 · Sources agrégées — le registre des établissements ![Sources](docs/screenshots/03-sources.jpg) La page `/sources` expose le registre `data/sources.json` enrichi en direct : chaque établissement avec son type d'offre, son statut de connecteur (**CONNECTÉ**), le nombre de formations actives et l'horodatage de la dernière synchronisation. C'est la vitrine de transparence de Forma-Ka : on voit d'où viennent les données et quand elles ont été rafraîchies. ### 4 · Confidentialité — « Votre vie privée, simplement. » ![Confidentialité](docs/screenshots/04-confidentialite.jpg) La page `/confidentialite` détaille l'approche minimaliste : aucun compte utilisateur, aucun pixel publicitaire, aucun témoin tiers. Le seul stockage est le localStorage du navigateur (choix de consentement, filtres de recherche), avec un bouton « Modifier mes choix de témoins » accessible en tout temps. ### 5 · Fiche formation — le détail d'abord (Isarta · Data Studio) ![Fiche formation Isarta](docs/screenshots/05-formation-isarta-3Adata-studio.jpg) Une fiche `/formation/:uid` (ici `isarta:data-studio`) montre la philosophie « détail d'abord » : fil d'Ariane par catégorie, **plan de la formation** complet (11 points), bloc **Admission / clientèle visée**, colonne des **thèmes** et des **formations similaires** (avec type, mode, durée, prix et source), puis le contenu original signé par la source. Chaque fiche renvoie vers la page originale de l'établissement. ### 6 · Fiche formation — les recommandations « Formations similaires » ![Fiche formation — plan et formations similaires](docs/screenshots/06-formation-detail.jpg) Le cœur d'une fiche vu de près : à gauche le **plan de la formation** point par point et le bloc **Admission / clientèle visée** ; à droite la colonne de recommandations **Formations similaires**, calculée par l'API (même catégorie, même source, thèmes proches) avec pour chacune son type, son mode, sa durée et son prix — un vrai moteur de découverte entre les 4 900+ fiches. ### 7 · Accueil — la grille de résultats ![Accueil — grille de résultats](docs/screenshots/07-accueil-resultats.jpg) L'accueil, défilé jusqu'à la grille : cartes de formation en 3 colonnes avec badge de type (Formation continue, Atelier…), établissement, titre, sigle, mode (présentiel / en ligne) et durée, date de début (« Débute le 3 septembre 2026 ») et traitement franc du prix — **« Prix non affiché »** assumé (prix optionnel), **« Gratuit »** mis en valeur, sinon le montant. ### 8 · Accueil — cartes illustrées & pagination (411 pages) ![Accueil — cartes illustrées et pagination](docs/screenshots/08-accueil-section-2.jpg) Plus bas sur l'accueil : les cartes reprennent les visuels originaux des établissements (Institut de leadership, Événements Les Affaires…) avec prix affiché (395 $, 545 $, 945 $), mode et ville. En pied de grille, la **pagination** — « Page 1 de 411 — 4 930 formations » à 12 fiches par page — puis le pied de page sombre avec la mission de l'agrégateur. ### 9 · Fiche formation en mobile (390 px) ![Fiche formation en mobile](docs/screenshots/09-formation-mobile.jpg) La même fiche Data Studio sur téléphone : menu hamburger, ticker conservé, fil d'Ariane et plan de la formation qui se replient proprement sur une colonne — le socle mobile du Groupe Ka (zones tactiles, safe-area, PWA installable) appliqué à Forma-Ka. ### 10 · Fiche événement — prix optionnel, dates offertes (CRHA · 5 à 7 estival) ![Fiche formation CRHA](docs/screenshots/10-formation-crha-3A202609035-407Lanaudiere.jpg) Deuxième exemple de fiche (`crha:202609035@7Lanaudiere`) qui illustre la souplesse du schéma : un événement de réseautage RH **sans prix affiché** (le prix est optionnel dans Forma-Ka), avec ses **dates offertes** (3 septembre 2026), ses **infos pratiques** (établissement, sigle, horaire affiché, date de synchronisation) et ses thèmes régionaux (Comité régionale de Lanaudière). ### Galerie mobile & desktop (2026-08-25) *Captures WebP précédentes (mobile 390 × 844 · desktop 1440 × 900), conservées dans [`docs/screenshots/mobile/`](docs/screenshots/mobile/) et [`docs/screenshots/desktop/`](docs/screenshots/desktop/).*
Accueil mobile
Accueil mobile
Fiche mobile
Fiche — détail d'abord
Stats mobile
Stats mobile
Menu mobile
Menu mobile
> 🗄️ Les toutes premières captures (PNG) sont archivées dans > [`docs/archive/`](docs/archive/). --- ## Pourquoi Forma-Ka ? Chercher une formation au Québec, c'est ouvrir des dizaines de sites — universités, cégeps, firmes de formation, organisateurs d'événements — chacun avec sa navigation et son format. **Forma-Ka renverse le problème** : un connecteur dédié par établissement visite chaque site, normalise chaque formation dans un schéma unique et détecte les changements en continu. > Les sites de formation n'offrent pas de webhooks. Forma-Ka en reproduit > l'équivalent : **synchronisation périodique + hachage de contenu** → ajouts, > mises à jour et retraits détectés automatiquement. Une formation qui > disparaît du site source disparaît de Forma-Ka (après une période de grâce > de 2 synchronisations). **Philosophie** : contrairement à un agrégateur de produits, **le prix est optionnel** (un cours universitaire n'affiche pas de prix) — ce qui compte, ce sont les **détails** de chaque formation : description complète, objectifs d'apprentissage, plan de cours, préalables, clientèle visée, durée, crédits/UEC, mode de diffusion, dates offertes. ## Fonctionnalités - **Recherche plein texte** (sujet, sigle, compétence) combinable avec les filtres **domaine** (Informatique, Gestion, RH, Marketing…), **type** (cours universitaire, formation continue, atelier, séminaire, webinaire, certification, bootcamp…), **mode** (en ligne, présentiel, hybride, asynchrone), **ville**, **langue**, **niveau**, **gratuit**, **prix maximal** et **date de début** ; - **Fiches détaillées standardisées** : description, objectifs, plan de cours, préalables, clientèle visée, durée (texte + heures), crédits/UEC, sigle, formateur, dates offertes, prix (optionnel, avec mention « à partir de »), thèmes, formations similaires, lien direct vers la source ; - **Statistiques en direct** (`/stats`) : portrait global + journal des synchronisations par source ; - **Registre des sources** (`/sources`) : statut du connecteur, volume, dernière synchro ; - **Ticker en continu** des types de formations en tête de chaque page ; - **PWA installable** (manifest + thème clair, accent ambré `#ffd54d`), pagination 12 par page, feuillet mobile (« bottom sheet ») pour les filtres ; - **Aucun traceur** : consentement localStorage, page confidentialité dédiée ; - **Mise à jour automatique** toutes les 6 h (watcher PM2) avec détection de dérive des connecteurs. ## Architecture ```mermaid flowchart LR subgraph Sources["18 établissements de formation"] S1["ÉTS Formation · TÉLUQ · ULaval
McGill · HEC · UQAM · Technologia
AFI · Cégep à distance · Les Affaires
… un connecteur par site"] end subgraph FormaKa["Forma-Ka"] C["Connecteurs
1 adaptateur / site"] --> N["Normalisation
schéma Formation unique"] N --> D[("SQLite
hash + diff + grâce")] D --> A["FastAPI
/api/formations · /api/facets"] A --> F["React 18 + Vite
PWA mobile · thème clair ambré"] end W["⏱ Watcher périodique
(PM2, 6 h)"] -.-> C S1 --> C F --> U["🎓 Apprenant"] ``` | Couche | Rôle | Fichiers | |---|---|---| | **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` | | **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` | | **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` | | **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` | | **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` | | **API** | Filtres type / catégorie / mode / ville / langue / niveau / gratuit / prix / source / recherche, facettes, stats, déclenchement de synchro | `formaka/web.py` | | **Frontend** | Design « editorial sharp » : Space Grotesk, ombres décalées, accent ambré `#ffd54d`, ticker en continu, feuillet mobile, pagination 12/page, PWA installable | `frontend/` | ### Structure du dépôt ``` forma-ka/ ├── run.py # point d'entrée : sync | watch | serve ├── requirements.txt # fastapi, uvicorn, requests, beautifulsoup4 ├── formaka/ │ ├── schema.py # dataclass Formation + finalize() │ ├── normalize.py # normalisation commune (texte, prix, dates…) │ ├── ingest.py # pipeline sync/watch (diff + grâce + journal) │ ├── db.py # SQLite : formations, detail_cache, sync_log │ ├── web.py # FastAPI : /api/* + service du frontend bâti │ └── connectors/ # 18 connecteurs + base.py + _resilient.py ├── frontend/ # React 18 + Vite + TypeScript (PWA) │ └── src/pages/ # Home, Formation, Stats, Sources, Privacy ├── data/ │ ├── sources.json # registre des établissements (versionné) │ └── formaka.db # base SQLite (NON versionnée) ├── docs/screenshots/ # visite guidée (JPG) + galeries webp └── tests/ # pytest (normalisation) ``` ## Le pipeline d'ingestion en détail 1. **Découverte** — le registre des connecteurs est **auto-découvrant** : chaque module de `formaka/connectors/` qui définit une classe héritant de `BaseConnector` avec un `source_id` est enrôlé automatiquement. 2. **Collecte** — `fetch()` liste les formations du site (listing HTML, API JSON interne, sitemap…), puis visite les pages de détail au besoin. 3. **Cache des fiches** — les pages de détail sont mises en cache dans la base (`detail_cache`) avec une **clé hebdomadaire** : une page n'est revisitée que si elle est nouvelle, modifiée, ou quand la semaine ISO change. Les synchros intermédiaires sont donc rapides et économes. 4. **Normalisation** — `Formation.finalize()` applique la couche commune (types/modes/langues canoniques, prix depuis le libellé, durée en heures, dates françaises → ISO, extraction de niveau/UEC/crédits) sans jamais écraser une valeur explicite du connecteur. 5. **Diff & grâce** — chaque fiche a un `content_hash` SHA-256 ; l'upsert ne touche que ce qui a changé. Une formation absente du site source est retirée après **2 synchros consécutives** d'absence (période de grâce contre les listings instables). 6. **Journal & dérive** — chaque synchro est journalisée (`sync_log` : trouvées / ajoutées / modifiées / retirées + taux de champs nuls) ; une chute anormale du volume ou une explosion des champs vides signale une dérive du connecteur (site remanié) visible sur `/stats` et `/sources`. ## Les backends de fetch (chaîne résiliente) Historiquement, `BaseConnector.fetch_html()` enchaînait trois backends : 1. **requests direct** — sites rendus serveur (rapide, gratuit) ; 2. **Scrapfly** (`SCRAPFLY_API_KEY`) — contournement anti-bot (`asp`), rendu JavaScript (`render_js`), géolocalisation canadienne ; 3. **Firecrawl** (`FIRECRAWL_API_KEY`) — rendu JS de repli, formats html/markdown. Cette chaîne est désormais renforcée par la **chaîne anti-bot résiliente commune du Groupe KA** (`formaka/connectors/_resilient.py`) : quand un site jusque-là ouvert déploie un anti-bot (Cloudflare, Akamai, Incapsula, PerimeterX) ou renvoie 403/429/503, la requête directe n'échoue plus silencieusement — elle **escalade automatiquement** : 1. **Direct** — session du connecteur (`curl_cffi` avec empreinte de navigateur si disponible, sinon `requests`) ; 2. **Proxy résidentiel** — IP résidentielle canadienne propre ; 3. **Scrapfly (ASP)** — bypass anti-bot géré + rendu JS optionnel ; 4. **Bright Data (Web Unlocker)** — déblocage premium, dernier recours. Le premier backend qui renvoie un 200 non vide gagne ; si tous échouent, la dernière réponse est renvoyée telle quelle pour que le connecteur journalise l'échec normalement (aucun blocage silencieux). Les clés d'API vivent dans `.env` (jamais versionné). ## Le schéma `Formation` Chaque connecteur, peu importe le site source, produit des objets `Formation` (`formaka/schema.py`) : | Groupe | Champs | |---|---| | **Identité** | `source`, `external_id`, `url` → `uid = source:external_id` | | **Classement** | `title`, `training_type`, `category`, `tags`, `level`, `language` | | **Diffusion** | `mode` (en ligne / présentiel / hybride / asynchrone), `city`, `start_date` (ISO), `sessions[]`, `schedule_label`, `duration`, `duration_hours` | | **Contenu** | `description`, `objectives[]`, `program[]` (plan de cours), `prerequisites`, `audience`, `instructor`, `code` (sigle), `images[]` | | **Valeur** | `price` *(optionnel — `None` = non affiché, c'est normal !)*, `price_label` (texte original), `is_free`, `credits` (« 3 crédits », « 1,4 UEC »), `credential` | | **Technique** | `details` (JSON structuré dérivé), `content_hash()` (SHA-256 pour la détection de changements) | ## API & routes ### API REST (FastAPI) | Point d'accès | Description | |---|---| | `GET /api/formations` | Liste filtrable (`training_type`, `category`, `mode`, `city`, `language`, `level`, `source`, `free`, `price_max`, `starts_after`, `q`, `sort`, `limit`, `offset`) | | `GET /api/formations/{uid}` | Fiche complète + historique de prix + formations similaires | | `GET /api/facets` | Valeurs distinctes pour construire les filtres | | `GET /api/sources` | Registre des établissements + état de synchro (`active_formations`, `last_sync`) | | `GET /api/stats` | Portrait global (totaux, moyennes, répartition par type) + journal des synchros | | `POST /api/sync` | Déclenche une synchronisation en arrière-plan | ### Routes du frontend (SPA React Router) | Route | Page | |---|---| | `/` | Accueil — recherche, filtres, compteurs en direct | | `/formation/:uid` | Fiche détaillée (ex. `/formation/isarta:data-studio`) | | `/stats` | Statistiques en direct | | `/sources` | Registre des sources agrégées | | `/confidentialite` | Politique de confidentialité & témoins | | `*` | Page 404 (boussole) | Le backend sert aussi le frontend bâti (`frontend/dist`) : `/assets` en statique + rattrapage SPA sur toutes les autres routes. ## Données & sources agrégées **18 établissements connectés — 4 932 formations actives** (relevé du 2026-08-28) : | Établissement | Offre | Formations | Région | |---|---|---:|---| | Université Laval — Formation à distance | Cours universitaires à distance, hybrides et comodaux | 1 757 | Québec (à distance / hybride) | | Technologia | Formation professionnelle — TI, IA, gestion | 590 | Montréal / Québec | | Université TÉLUQ | Cours universitaires 100 % à distance, tous cycles | 511 | Québec (à distance) | | AFI par Edgenda | Formation professionnelle — TI, leadership | 358 | Québec / Montréal | | ÉTS Formation | Formation continue + UEC — technologie, construction, gestion, RH | 296 | Montréal | | Versalys | Bureautique, TI, langues | 257 | Montréal · Québec · Laval · Brossard | | McGill School of Continuing Studies | Formation continue (anglais) | 197 | Montréal | | Isarta Formations | Marketing, communications, RH | 189 | Montréal (virtuel) | | CRHA — Espace Formation | Formations et événements RH | 180 | Québec (province) — surtout en ligne | | Événements Les Affaires | Conférences et webinaires d'affaires | 139 | Montréal / Québec / en ligne | | Cégep à distance | Cours collégiaux à distance | 118 | Québec (en ligne) | | École des dirigeant(e)s HEC Montréal | Séminaires et certifications pour cadres | 93 | Montréal | | ITHQ — Ateliers et formations | Ateliers vins/cuisine + hôtellerie | 78 | Montréal (+ province) | | Formation continue UQAM | Formation continue universitaire — UEC, séminaires | 49 | Montréal | | École des entrepreneurs du Québec | Formations entrepreneur·e·s (souvent gratuites) | 48 | Montréal + en ligne | | Institut de leadership | Certifications et programmes en leadership | 37 | Montréal (+ cohortes en ligne) | | AlphaNumérique | Littératie numérique — gratuit | 29 | Québec (en ligne) | | Le Wagon Montréal | Bootcamps dev web / data / IA | 4 | Montréal | Répartition par type : **2 268** cours universitaires · **2 058** formations continues · **118** cours collégiaux · **112** ateliers · **104** cours en ligne · **98** conférences · **80** séminaires · **41** webinaires · **27** programmes · **22** certifications · **4** bootcamps. Le registre vit dans [`data/sources.json`](data/sources.json) (versionné) ; la base SQLite (`data/formaka.db`) est locale et reconstruite par `sync`. ## Ajouter un connecteur 1. Créer `formaka/connectors/.py` : une classe héritant de `BaseConnector`, définir `source_id` et implémenter `fetch() -> list[Formation]`. Le registre est **auto-découvrant** — rien d'autre à modifier. 2. Ajouter l'entrée correspondante dans `data/sources.json`. 3. Tester : `.venv/bin/python run.py sync `. ```python class MonEcoleConnector(BaseConnector): source_id = "mon_ecole" def fetch(self) -> list[Formation]: html = self.fetch_html(LIST_URL) # direct -> chaîne résiliente ... return [Formation(source=self.source_id, external_id=..., url=..., title=..., description=..., objectives=[...], ...)] ``` ## Démarrage rapide ```bash git clone https://git.spboucher.ai/forma-ka.git && cd forma-ka # Backend python3 -m venv .venv && .venv/bin/pip install -r requirements.txt # Frontend cd frontend && npm install && npm run build && cd .. # Clés des backends de scraping (sites JavaScript / anti-bot) — jamais versionnées cat > .env < http://localhost:8080 .venv/bin/python run.py watch 360 # boucle de synchro (défaut : toutes les 6 h) ``` ## Tests ```bash .venv/bin/python -m pytest tests/ -q ``` ## Déploiement (production) | Élément | Valeur | |---|---| | **Nœud** | `M3U96a` (Mac Studio, cluster MacLustr) — `~/apps/forma-ka` | | **Port** | `8110` | | **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) | | **Domaine** | [www.forma-ka.com](https://www.forma-ka.com) via ngrok | | **Base** | `data/formaka.db` (SQLite, locale au nœud) | | **Résilience** | PM2 avec redémarrage automatique + `pm2 startup` (launchd) | ```bash # Sur le nœud (lecture seule — l'état de référence) pm2 ls | grep forma-ka curl -s localhost:8110/api/stats | head -c 300 ``` ## Dépôt & développement remote-first La **source de vérité est le dépôt git sur le nœud de déploiement** (`M3U96a:~/apps/forma-ka`), pas une copie locale. Le remote `origin` est le git personnel **spbgit** (git.spboucher.ai) — dépôt nu `~/srv/git/forma-ka.git` hébergé sur M3U96a. Cycle de travail : éditer sur le nœud via SSH → `npm run build` (frontend) → `pm2 restart forma-ka` → `git add/commit/push origin main` **sur le nœud** (agent forwarding actif). Ce qui n'est **jamais versionné** (voir [`.gitignore`](.gitignore)) : la base SQLite et les caches (`data/`, sauf `sources.json` déjà suivi), les secrets (`.env*`), les environnements (`.venv/`, `node_modules/`), les artefacts de build (`frontend/dist/`) et les journaux. ## Confidentialité Forma-Ka ne collecte **rien qui identifie l'utilisateur** : pas de compte, pas de formulaire d'inscription, pas de pixel publicitaire, pas de témoin tiers, aucune donnée vendue. Le seul stockage est le **localStorage** du navigateur (choix de consentement, filtres, préférences d'affichage), modifiable en tout temps via « Gérer mes témoins ». Détails : page [`/confidentialite`](https://www.forma-ka.com/confidentialite). ## Écosystème Groupe Ka Forma-Ka fait partie du **Groupe Ka** ([groupe-ka.com](https://www.groupe-ka.com)), la famille d'agrégateurs indépendants du Québec — notamment [Lou-Ka](https://www.lou-ka.com) (logements), [Immo-Ka](https://www.immo-ka.com) (propriétés), [Vrai-Prix](https://www.vrai-prix.com) (épicerie), [Auto-Ka](https://www.auto-ka.com) (véhicules), [Food-Ka](https://www.food-ka.com), [Resto-Ka](https://www.resto-ka.com), [Sorti-Ka](https://www.sorti-ka.com) (sorties), [Job-Ka](https://www.job-ka.com) (emplois), [Trouve-Ka](https://www.trouve-ka.com) (recherche), [Créa-Ka](https://www.crea-ka.com) (créateurs), [Fabri-Ka](https://www.fabri-ka.com) (produits d'ici) et [Ka·Stats](https://www.ka-stats.com) (statistiques du Québec). Même ADN partout : connecteurs dédiés, schéma standardisé, diff par hachage, fiches détaillées, respect de la vie privée — et le mécanisme de Forma-Ka est directement adapté de celui de Lou-Ka. ## Contact
**Simon-Pierre Boucher** [contact@spboucher.ai](mailto:contact@spboucher.ai) · [www.forma-ka.com](https://www.forma-ka.com) © 2026 Simon-Pierre Boucher — tous droits réservés.