docs: refonte du README (pastilles + captures d écran à jour)
3 changed files +86 −144
modified
README.md
+86 −144
@@ -1,203 +1,145 @@ | ||
| 1 | −# Trouve-KA — Cherche le Québec. | |
| 1 | +# Trouve·Ka | |
| 2 | 2 | |
| 3 | −**Moteur de recherche web indépendant, Québec-first** — son propre crawler, son propre index, son propre ranking, son API et son application web publique. | |
| 3 | +**Cherche le Québec.** Moteur de recherche web indépendant, Québec-first — son propre crawler, son propre index, son propre ranking, son API et son application web publique. | |
| 4 | 4 | |
| 5 | − | |
| 6 | − | |
| 7 | − | |
| 8 | − | |
| 9 | − | |
| 5 | +[](https://www.trouve-ka.com) | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | 10 |  |
| 11 | + | |
| 11 | 12 |  |
| 12 | − | |
| 13 | − | |
| 14 | − | |
| 15 | − | |
| 16 | −--- | |
| 17 | − | |
| 18 | −## Description | |
| 13 | + | |
| 19 | 14 | |
| 20 | −**Trouve-KA** est un **moteur de recherche québécois indépendant**. Ce n'est **pas un métamoteur** : **aucune dépendance à Google, Bing ou Brave** pour les résultats. Le crawler découvre le web québécois à partir de **seeds à forte autorité** (gouvernement, municipalités, universités, médias), suit les liens sortants, juge la **pertinence québécoise** de chaque page (`page_quebec_score` **et** `domain_quebec_score`) et indexe **immédiatement** : une page fetchée est **cherchable en ~2–4 secondes**, pendant que le frontier continue de grandir. L'enrichissement (autorité, entités, embeddings) arrive après, **en asynchrone**, sans jamais bloquer. | |
| 15 | +**Trouve·Ka** est un **vrai moteur de recherche québécois** — pas un métamoteur : **aucune dépendance à Google, Bing ou Brave** pour les résultats. Le crawler découvre le web québécois à partir de **64 seeds à forte autorité** (gouvernement, municipalités, universités, médias), suit les liens sortants, juge la **pertinence québécoise** de chaque page (`page_quebec_score` et `domain_quebec_score`) et indexe **immédiatement** : une page fetchée est **cherchable en ~2–4 secondes**, pendant que le frontier continue de grandir. L'enrichissement (autorité, entités, embeddings) arrive après, en asynchrone, sans jamais bloquer. | |
| 21 | 16 | |
| 22 | 17 | > **Crawl en continu. Indexe immédiatement. Recherche immédiatement. Améliore en asynchrone.** |
| 23 | 18 | |
| 24 | −En production : **https://www.trouve-ka.com** — un moteur de recherche par [**Groupe KA**](https://www.groupe-ka.com). | |
| 25 | − | |
| 26 | 19 | ``` |
| 27 | 20 | Crawler → Frontier → Fetcher → Parser → Classification Québec |
| 28 | 21 | → Déduplication → Indexer → Index → Ranking → API → Web App |
| 29 | 22 | ``` |
| 30 | 23 | |
| 31 | −Détails : [docs/architecture.md](docs/architecture.md) (diagrammes Mermaid) et [docs/decisions.md](docs/decisions.md) (pourquoi OpenSearch, pourquoi Postgres comme frontier, etc.). | |
| 24 | +En production : **https://www.trouve-ka.com** — détails techniques dans [docs/architecture.md](docs/architecture.md) (diagrammes Mermaid) et [docs/decisions.md](docs/decisions.md). | |
| 32 | 25 | |
| 33 | −**Auteur** : Simon-Pierre Boucher — **Contact** : contact@spboucher.ai | |
| 26 | +## Captures d'écran | |
| 34 | 27 | |
| 35 | −--- | |
| 28 | +<p align="center"> | |
| 29 | + <img src="docs/screenshots/trouve-ka-desktop.png" width="640" alt="Accueil — desktop"> | |
| 30 | + <img src="docs/screenshots/trouve-ka-mobile.png" width="200" alt="Accueil — mobile"> | |
| 31 | +</p> | |
| 36 | 32 | |
| 37 | 33 | ## Fonctionnalités |
| 38 | 34 | |
| 39 | 35 | - **Recherche Québec-first** : **59 000+ pages indexées**, **7 900+ domaines québécois** découverts à partir de 64 seeds, **latence de recherche 20–95 ms** (mesurée en prod). |
| 40 | −- **Indexation incrémentale** : fetch → parse → score → index **inline** (`refresh_interval: 1s` côté OpenSearch) → **cherchable en secondes**. Détection de changement par **hash de contenu + ETag/If-Modified-Since**, recrawl adaptatif (inchangé → intervalle ×2, volatil → ÷2). | |
| 41 | −- **Détection Québec déterministe** : TLD (**.qc.ca**, **.quebec**), gazetteer de toponymes, codes postaux **G/H/J**, indicatifs (**418/514/438/…**), organisations connues (Hydro-Québec, RAMQ, UQAM…), JSON-LD, langue française — **aucun LLM dans le chemin chaud**. | |
| 42 | −- **Ranking bilingue** : `function_score` OpenSearch — **BM25 FR/EN** avec synonymes bilingues (thermopompe ↔ heat pump) + scores Québec + **autorité de domaine** (inlinks pondérés) + fraîcheur + **boost de localité** (« plombier Gatineau » → documents avec preuve géographique). | |
| 43 | −- **Crawler poli et assumé** : UA identifié `TrouveKABot`, page publique [/trouveka-bot](https://www.trouve-ka.com/trouveka-bot), **robots.txt respecté** (Protego, cache 24 h), **verrou de politesse Redis par hôte** (défaut 2 s, `Crawl-delay` honoré), **garde SSRF** (IP privées/loopback/métadonnées cloud bloquées, revalidée à chaque redirection), détection de **pièges de crawl** (session IDs, calendriers infinis, facettes explosives). | |
| 44 | −- **Onglet Images** : galerie des pages avec image représentative (**hotlink + attribution**, jamais de crawl d'images). | |
| 45 | −- **Métriques publiques réelles** : `/status` et `/stats` (tableau de bord + rapport PDF Groupe-KA) — **aucun compteur simulé** (règle du projet). | |
| 46 | −- **Crawl distribué multi-nœuds** : workers satellites sur **M2M32b** et **M2M32c**, coordonnés **sans orchestrateur** via le frontier Postgres (`FOR UPDATE SKIP LOCKED`) et les verrous Redis, communication par le **LAN interne 192.168.2.x**. | |
| 47 | −- **SSO KA ID** + design Groupe KA (éditorial sharp, accent azur) + widget **KA Agent** (bulle de chat IA). | |
| 36 | +- **Indexation incrémentale** : fetch → parse → score → index inline (`refresh_interval: 1s` côté OpenSearch) → cherchable en secondes. Détection de changement par hash de contenu + ETag/If-Modified-Since, recrawl adaptatif (inchangé → intervalle ×2, volatil → ÷2). | |
| 37 | +- **Détection Québec déterministe** : TLD (.qc.ca, .quebec), gazetteer de toponymes, codes postaux G/H/J, indicatifs (418/514/438/…), organisations connues (Hydro-Québec, RAMQ, UQAM…), JSON-LD, langue française — aucun LLM dans le chemin chaud. | |
| 38 | +- **Ranking bilingue** : `function_score` OpenSearch — BM25 FR/EN avec synonymes bilingues (thermopompe ↔ heat pump) + scores Québec + autorité de domaine (inlinks pondérés) + fraîcheur + boost de localité (« plombier Gatineau » → documents avec preuve géographique). | |
| 39 | +- **Crawler poli et assumé** : UA identifié `TrouveKABot`, page publique [/trouveka-bot](https://www.trouve-ka.com/trouveka-bot), robots.txt respecté (Protego, cache 24 h), verrou de politesse Redis par hôte (défaut 2 s, `Crawl-delay` honoré), garde SSRF (IP privées/loopback/métadonnées cloud bloquées, revalidée à chaque redirection), détection de pièges de crawl (session IDs, calendriers infinis, facettes explosives). | |
| 40 | +- **Onglet Images** : galerie des pages avec image représentative (hotlink + attribution, jamais de crawl d'images). | |
| 41 | +- **Métriques publiques réelles** : `/status` et `/stats` (tableau de bord + rapports PDF Groupe KA) — aucun compteur simulé, c'est une règle du projet. | |
| 42 | +- **Crawl distribué multi-nœuds** : workers satellites sur M2M32b et M2M32c, coordonnés sans orchestrateur via le frontier Postgres (`FOR UPDATE SKIP LOCKED`) et les verrous Redis, communication par le LAN interne 192.168.2.x. | |
| 43 | +- **SSO KA ID** + favoris « Mon univers Ka », design Groupe KA et widget **KA Agent** (bulle de chat IA). | |
| 48 | 44 | |
| 49 | −--- | |
| 45 | +## Architecture | |
| 50 | 46 | |
| 51 | −## Architecture (multi-services) | |
| 47 | +En production sur **M2M32**, l'application tourne en processus natifs sous **PM2**, avec l'infrastructure de données en **Docker** : | |
| 52 | 48 | |
| 53 | −En production sur le nœud **M2M32**, l'application tourne en **processus natifs sous PM2**, avec l'infrastructure de données en **Docker** : | |
| 54 | − | |
| 55 | −| Service PM2 | Rôle | | |
| 56 | −|---|---| | |
| 57 | −| **tk-web** | Application web publique **Next.js 15** (**port 3000**) — proxifie `/api/*` vers l'API interne | | |
| 58 | −| **tk-api** | API **FastAPI** (`trouveka.api`) — recherche, statut, soumission, admin | | |
| 59 | −| **tk-crawler-1** / **tk-crawler-2** | Workers de crawl (fetch → parse → score → index inline) | | |
| 60 | −| **tk-enrichment** | Enrichissement asynchrone (autorité, entités, embeddings) via **Redis Streams** | | |
| 61 | −| **tk-scheduler** | Planification des recrawls, entretien du frontier | | |
| 62 | −| tk-tun-search / tk-tun-embed | Tunnels vers l'index de recherche et le service d'embeddings | | |
| 63 | − | |
| 64 | −| Conteneur Docker | Rôle | | |
| 49 | +| Processus | Rôle | | |
| 65 | 50 | |---|---| |
| 66 | −| **PostgreSQL 16** (`postgres:16-alpine`, port 5432) | Frontier, domaines, documents, graphe de liens, analytics | | |
| 67 | −| **Redis 7** (`redis:7-alpine`, port 6379) | Politesse par hôte, pause, **Redis Streams** pour l'enrichissement | | |
| 68 | − | |
| 69 | −L'exposition publique se fait par **ngrok** sous **launchd** (`com.trouveka.ngrok`) : `ngrok http --url=www.trouve-ka.com 3000`. **Un seul port exposé** — pas d'URL absolues côté client, cookies/CORS sans surprise derrière le tunnel. | |
| 51 | +| **tk-web** | Application web publique **Next.js 15** (port **3000**) — proxifie `/api/*` vers l'API interne ; un seul port exposé | | |
| 52 | +| **tk-api** | API **FastAPI** (`trouveka.api`) — recherche, statut, soumission d'URL, endpoints admin protégés (`X-Admin-Token`) | | |
| 53 | +| **tk-crawler-1** / **tk-crawler-2** | Workers de crawl : fetch → parse → score Québec → indexation inline, politesse par hôte, garde SSRF | | |
| 54 | +| **tk-enrichment** | Enrichissement asynchrone (autorité de domaine, entités, embeddings) via **Redis Streams** — ne bloque jamais l'indexation | | |
| 55 | +| **tk-scheduler** | Planification des recrawls adaptatifs et entretien du frontier | | |
| 56 | +| **postgres** (Docker, `postgres:16-alpine`) | Frontier, domaines, documents, graphe de liens, analytics | | |
| 57 | +| **redis** (Docker, `redis:7-alpine`) | Verrous de politesse par hôte, pause du crawl, Redis Streams pour l'enrichissement | | |
| 58 | +| **com.trouveka.ngrok** (launchd) | Tunnel `ngrok http --url=www.trouve-ka.com 3000` — exposition publique du port 3000 | | |
| 70 | 59 | |
| 71 | −--- | |
| 72 | − | |
| 73 | −## Stack | |
| 60 | +La recherche s'appuie sur **OpenSearch 2.17** (BM25 FR/EN, synonymes bilingues, `function_score` Québec-first). Dégradation gracieuse partout : embeddings en panne → le lexical continue ; un worker crash → le frontier continue. | |
| 74 | 61 | |
| 75 | −| Couche | Choix | | |
| 76 | −|---|---| | |
| 77 | −| **Web** | **Next.js 15**, TypeScript, React 19, Tailwind (composants style shadcn/ui) | | |
| 78 | −| **API** | **FastAPI** (Python 3.12) | | |
| 79 | −| **Pipeline** | **Python 3.12 async** — httpx, selectolax, Protego | | |
| 80 | −| **BD relationnelle** | **PostgreSQL 16** (frontier, domaines, documents, graphe de liens, analytics) | | |
| 81 | −| **Coordination** | **Redis 7** (politesse par hôte, pause, Redis Streams) | | |
| 82 | −| **Recherche** | **OpenSearch 2.17** (BM25 FR/EN, synonymes bilingues, `function_score` Québec-first) | | |
| 83 | −| **Déploiement** | **PM2 + Docker (postgres, redis) sur M2M32** + ngrok launchd (**www.trouve-ka.com**) | | |
| 84 | − | |
| 85 | −--- | |
| 86 | − | |
| 87 | −## Structure du monorepo | |
| 62 | +## Structure du repo | |
| 88 | 63 | |
| 89 | 64 | ``` |
| 90 | −apps/web # moteur public + dashboard /admin (Next.js) | |
| 91 | −apps/api # FastAPI (trouveka.api) | |
| 65 | +apps/ # web (Next.js 15, moteur public + dashboard /admin) et api (FastAPI) | |
| 92 | 66 | services/ # crawler, frontier, parser, classifier, indexer, ranking, |
| 93 | 67 | # scheduler, enrichment, embedding |
| 94 | 68 | packages/ # config, database, logging, queue, search-core, shared, types |
| 95 | 69 | infrastructure/ # docker/, migrations/, monitoring/, deployment/ |
| 96 | −scripts/ # bootstrap-seeds/, start-crawler/, health-check/, eval/, check-headers.py | |
| 97 | −tests/ # unitaires : canonicalisation, SSRF, robots, scoring Québec, fetcher, ranking | |
| 98 | −docs/ # architecture.md, decisions.md, screenshot.png | |
| 70 | +scripts/ # bootstrap-seeds, start-crawler, health-check, eval, ops, | |
| 71 | + # ka-sitemaps, update-synonyms, check-headers.py… | |
| 72 | +tests/ # unitaires : canonicalisation, SSRF, robots, scoring Québec, ranking… | |
| 73 | +docs/ # architecture.md, decisions.md, RUNBOOK.md, screenshots/ | |
| 99 | 74 | ``` |
| 100 | 75 | |
| 101 | −Le backend Python est un **seul package namespace `trouveka.*`** mappé sur ce layout (voir `pyproject.toml`). | |
| 76 | +Le backend Python est un seul package namespace `trouveka.*` mappé sur ce layout (voir `pyproject.toml`) ; le frontend vit dans un workspace **pnpm**. | |
| 102 | 77 | |
| 103 | −--- | |
| 104 | − | |
| 105 | −## Démarrage local (dev) | |
| 78 | +## Développement (remote-first) | |
| 106 | 79 | |
| 107 | −```bash | |
| 108 | −git clone … && cd trouve-ka | |
| 109 | −cp .env.example .env | |
| 110 | −docker compose up -d postgres redis opensearch # infra | |
| 111 | −uv venv --python 3.12 .venv && uv pip install -e ".[dev]" --python .venv/bin/python | |
| 112 | −pnpm install | |
| 80 | +La **source de vérité est le repo git sur le nœud M2M32** (`~/trouve-ka`) — on n'édite **jamais** la copie laptop. Toute modification se fait sur le nœud via SSH : édition, build, `pm2 restart`, puis commit/push **sur le nœud**. | |
| 113 | 81 | |
| 114 | −pnpm crawl:seed # migrations + 60+ seeds québécoises | |
| 115 | −bash scripts/start-crawler/start.sh & # le crawl démarre | |
| 116 | −.venv/bin/uvicorn trouveka.api.main:app --port 8080 & | |
| 117 | −pnpm dev # → http://localhost:3000 | |
| 118 | −# → des résultats apparaissent en quelques secondes | |
| 119 | −``` | |
| 82 | +- **Remote `origin` = spbgit** (git perso, **https://git.spboucher.ai**) — bare repo sur M3U96a, accessible via l'alias SSH **`gitsrv`** configuré sur les nœuds. Pas GitHub. | |
| 83 | +- Agent forwarding actif : le `git push` fonctionne pendant une session SSH depuis le laptop. | |
| 120 | 84 | |
| 121 | −Ports occupés sur la machine? Surcharger dans `.env` : `PG_PORT`, `REDIS_PORT`, `SEARCH_PORT`, `API_PORT` (et les URLs correspondantes). | |
| 85 | +```bash | |
| 86 | +ssh M2M32 | |
| 87 | +cd ~/trouve-ka | |
| 122 | 88 | |
| 123 | −### API | |
| 89 | +# Backend Python (venv .venv, Python 3.12) | |
| 90 | +.venv/bin/python -m pytest tests/ # tests unitaires | |
| 91 | +python3 scripts/check-headers.py # header auteur obligatoire (CI) | |
| 124 | 92 | |
| 125 | −- `GET /api/search?q=&page=&limit=&language=&category=&quebec_only=&freshness=` | |
| 126 | −- `GET /api/status` — compteurs publics (pages, domaines, débit, état du crawler) | |
| 127 | −- `POST /api/submit {"url": …}` — soumettre un site québécois (soumission ≠ inclusion) | |
| 128 | −- `GET|POST /api/admin/*` — protégé par header `X-Admin-Token` : overview, flux live, pause/reprise, seeds, recrawl, blocage de domaine, inspection du frontier | |
| 93 | +# Frontend Next.js | |
| 94 | +pnpm install | |
| 95 | +pnpm build # build de apps/web | |
| 129 | 96 | |
| 130 | −### Tests | |
| 97 | +# Appliquer un changement | |
| 98 | +pm2 restart tk-web # ou tk-api, tk-crawler-1, tk-crawler-2, | |
| 99 | + # tk-enrichment, tk-scheduler | |
| 131 | 100 | |
| 132 | −```bash | |
| 133 | −.venv/bin/python -m pytest tests/ # URLs, SSRF, pièges, Québec, frontier, parser, fetcher, ranking | |
| 134 | −python3 scripts/check-headers.py # header auteur obligatoire dans chaque fichier source (CI) | |
| 135 | −bash scripts/health-check/check.sh # santé du stack | |
| 101 | +git add <fichiers> && git commit -m "…" && git push origin main | |
| 136 | 102 | ``` |
| 137 | 103 | |
| 138 | −Évaluation du ranking : `python3 scripts/eval/run-eval.py --api http://localhost:8080` (dataset dans `scripts/eval/ranking-eval.yaml`). | |
| 139 | − | |
| 140 | −--- | |
| 104 | +Dev local complet possible : `docker compose up -d postgres redis opensearch`, `pnpm crawl:seed` (migrations + seeds), puis `pnpm dev` → http://localhost:3000. | |
| 141 | 105 | |
| 142 | −## Déploiement (production) | |
| 106 | +## Déploiement | |
| 143 | 107 | |
| 144 | 108 | - **Nœud** : **M2M32** (`~/trouve-ka`) — Mac Studio, 12 cœurs, 32 Go. |
| 145 | −- **Web** : **tk-web** sur le **port 3000**, exposé sur **https://www.trouve-ka.com**. | |
| 146 | −- **Tunnel** : **ngrok** sous **launchd** — label **`com.trouveka.ngrok`** (`~/Library/LaunchAgents/com.trouveka.ngrok.plist`). | |
| 109 | +- **Web** : `tk-web` sur le port **3000**, exposé sur **https://www.trouve-ka.com**. | |
| 147 | 110 | - **Processus** : **PM2** (`pm2 ls` → tk-web, tk-api, tk-crawler-1/2, tk-enrichment, tk-scheduler) — auto-restart, survie au reboot. |
| 148 | 111 | - **Données** : **PostgreSQL 16** + **Redis 7** en **Docker** (`docker compose up -d postgres redis`). |
| 149 | − | |
| 150 | −Opérations courantes sur le nœud : | |
| 151 | − | |
| 152 | −```bash | |
| 153 | −ssh M2M32 | |
| 154 | −cd ~/trouve-ka | |
| 155 | −pm2 ls # état des services | |
| 156 | −pm2 logs tk-crawler-1 # crawl en direct | |
| 157 | −pm2 restart tk-web # après un build web | |
| 158 | −docker ps # postgres + redis | |
| 159 | −``` | |
| 160 | − | |
| 161 | −### Crawl distribué (M2M32b, M2M32c) | |
| 162 | − | |
| 163 | −Des **workers satellites** tournent en natif sur d'autres nœuds du cluster et se coordonnent **sans orchestrateur** via le frontier Postgres (`FOR UPDATE SKIP LOCKED`) et les verrous de politesse Redis (tous nœuds confondus : **jamais plus d'une requête par site par seconde**). Communication par l'Ethernet LAN interne (**192.168.2.x**). | |
| 164 | − | |
| 165 | −```bash | |
| 166 | −# Sur un nœud satellite (uv + python 3.12, .env pointé sur le nœud central) : | |
| 167 | −cd ~/trouve-ka && pm2 start .venv/bin/python --name trouveka-crawler \ | |
| 168 | − --interpreter none -- -m trouveka.crawler.worker | |
| 169 | −``` | |
| 170 | − | |
| 171 | −⚠️ **macOS Sequoia bloque l'accès « réseau local » des processus launchd** : lancer les workers satellites via **pm2/nohup depuis une session SSH**, pas via LaunchAgent/Daemon. | |
| 172 | − | |
| 173 | −### Observabilité | |
| 174 | − | |
| 175 | −Logs JSON structurés par service, métriques réelles via `/api/status` et `/api/admin/overview`, dashboard **`/admin`** (files, débits, distribution HTTP, latences p50/p95, flux live). Voir [infrastructure/monitoring/README.md](infrastructure/monitoring/README.md). | |
| 176 | − | |
| 177 | −--- | |
| 178 | − | |
| 179 | −## Développement remote-first (IMPORTANT) | |
| 180 | − | |
| 181 | −La **source de vérité est le repo git sur le nœud M2M32** (`~/trouve-ka`), **pas** une copie locale sur le laptop. Toute modification se fait **sur le nœud via SSH** : édition, build, `pm2 restart`, puis commit/push **sur le nœud**. | |
| 182 | − | |
| 183 | −- **Remote `origin` = spbgit** (git perso, **git.spboucher.ai**) — bare repo `~/srv/git/trouve-ka.git` sur M3U96a, accessible via l'**alias SSH `gitsrv`** configuré sur les nœuds. **Pas GitHub.** | |
| 184 | −- **Agent forwarding actif** : le `git push` fonctionne pendant une session SSH depuis le laptop. | |
| 112 | +- **Tunnel** : **ngrok** sous **launchd** — label `com.trouveka.ngrok` (`~/Library/LaunchAgents/com.trouveka.ngrok.plist`). | |
| 185 | 113 | |
| 186 | 114 | ```bash |
| 187 | 115 | ssh M2M32 |
| 188 | 116 | cd ~/trouve-ka |
| 189 | −# … éditer, builder, pm2 restart … | |
| 190 | −git add … && git commit -m "…" && git push origin main | |
| 117 | +pm2 ls # état des services | |
| 118 | +pm2 logs tk-crawler-1 # crawl en direct | |
| 119 | +docker ps # postgres + redis | |
| 191 | 120 | ``` |
| 192 | 121 | |
| 193 | −--- | |
| 122 | +Runbook et observabilité : [docs/RUNBOOK.md](docs/RUNBOOK.md), dashboard `/admin` (files, débits, latences p50/p95, flux live) et [infrastructure/monitoring/](infrastructure/monitoring/). | |
| 194 | 123 | |
| 195 | −## Règles du dépôt | |
| 124 | +## Écosystème Groupe KA | |
| 196 | 125 | |
| 197 | −- **Header auteur obligatoire** dans chaque fichier source — vérifié par `scripts/check-headers.py`. | |
| 198 | −- **Aucune donnée factice** : pas de compteurs simulés, pas de résultats hard-codés; les fixtures vivent dans `tests/` uniquement. | |
| 199 | −- **Provenance préservée** : URL originale, canonique, timestamp de crawl, domaine source. **Trouve-KA renvoie vers les éditeurs originaux.** | |
| 126 | +| Plateforme | Vocation | | |
| 127 | +|---|---| | |
| 128 | +| [groupe-ka.com](https://www.groupe-ka.com) | Portail du Groupe KA | | |
| 129 | +| [lou-ka.com](https://www.lou-ka.com) | Logements à louer | | |
| 130 | +| [immo-ka.com](https://www.immo-ka.com) | Propriétés à vendre | | |
| 131 | +| [vrai-prix.com](https://www.vrai-prix.com) | Estimation immobilière | | |
| 132 | +| [auto-ka.com](https://www.auto-ka.com) | Véhicules | | |
| 133 | +| [fabri-ka.com](https://www.fabri-ka.com) | Produits québécois | | |
| 134 | +| [food-ka.com](https://www.food-ka.com) | Épicerie et alimentation | | |
| 135 | +| [resto-ka.com](https://www.resto-ka.com) | Restaurants | | |
| 136 | +| [sorti-ka.com](https://www.sorti-ka.com) | Sorties et événements | | |
| 137 | +| [job-ka.com](https://www.job-ka.com) | Emplois | | |
| 138 | +| [crea-ka.com](https://www.crea-ka.com) | Créateurs | | |
| 139 | +| [trouve-ka.com](https://www.trouve-ka.com) | Petites annonces et recherche — **ce repo** | | |
| 140 | +| [api-ka.com](https://www.api-ka.com) | API de données | | |
| 200 | 141 | |
| 201 | 142 | --- |
| 202 | 143 | |
| 203 | −Un service **Groupe Ka** — [www.groupe-ka.com](https://www.groupe-ka.com) | |
| 144 | +© Groupe KA — Simon-Pierre Boucher · contact@spboucher.ai | |
| 145 | +Ce repo vit sur **spbgit** (git.spboucher.ai), pas sur GitHub. | |
added
docs/screenshots/trouve-ka-desktop.png
+0 −0
Binary file not shown.
added
docs/screenshots/trouve-ka-mobile.png
+0 −0
Binary file not shown.