# Déploiement Job·Ka — nœud m3u96a ## Cible - **Nœud** : `M3U96a.maclustr.io` (Mac Studio, 32 cœurs, 96 Go) — imposé par le CLAUDE.md du projet (§6). Dérogation assumée à la règle « M3U96a = staging » : demande explicite du projet, comme `vrai-prix` et `sorti-ka` déjà sur ce nœud. - **Répertoire** : `~/apps/job-ka` (convention des projets Ka remote-first) - **Port interne** : `8096` (8095 = lou-ka ; vérifier les conflits avec `pick-node.py`/`lsof` avant de démarrer) - **Exposition** : ngrok, domaine réservé `www.job-ka.com` - **Superviseur** : PM2 (3 processus) ## Processus PM2 ``` job-ka-web .venv/bin/python run.py serve 8096 # API + frontend job-ka-sync .venv/bin/python run.py watch 60 # resync horaire job-ka-ngrok ngrok http --url=www.job-ka.com 8096 # tunnel supervisé ``` ## Procédure ```bash # 1. Pousser le code (jamais les données : data/*.db, dist/, .env restent locaux au nœud) ssh M3U96a 'mkdir -p ~/apps' rsync -a --delete \ --exclude .venv --exclude node_modules --exclude dist \ --exclude 'data/*.db' --exclude .env --exclude .git \ ~/Desktop/job-ka/ M3U96a:~/apps/job-ka/ # 2. Sur le nœud : runtime + build + secrets ssh M3U96a 'cd ~/apps/job-ka \ && python3 -m venv .venv && .venv/bin/pip install -r requirements.txt \ && cd frontend && npm install && npm run build' # .env : créer sur le nœud (SCRAPFLY_API_KEY) — jamais commité ni rsyncé # 3. Amorçage des données ssh M3U96a 'cd ~/apps/job-ka && .venv/bin/python run.py sync && .venv/bin/python run.py geocode' # 4. PM2 ssh M3U96a 'cd ~/apps/job-ka \ && pm2 start ".venv/bin/python run.py serve 8096" --name job-ka-web \ && pm2 start ".venv/bin/python run.py watch 60" --name job-ka-sync \ && pm2 start "ngrok http --url=www.job-ka.com 8096" --name job-ka-ngrok \ && pm2 save' ``` ## Vérifications post-déploiement 1. `curl https://www.job-ka.com/health` → `{"status":"ok", ...}` avec `last_sync_age_s` < 7200 ; 2. `https://www.job-ka.com` répond (frontend) ; 3. `https://www.job-ka.com/api/sources` → `last_sync` récents sur chaque source ; 4. `pm2 ls` : les 3 processus `online` ; 5. Mettre à jour `~/Desktop/cluster-skill/cluster-deployments.json` (registre cross-session : app, node, port, domain, process_manager, dir, start, deployed). ## Règles - Jamais d'interface d'admin ni de BD exposée par le tunnel (seuls `/`, `/api/*`, `/health` existent ; `POST /api/sync` est le seul point mutateur — il ne fait que déclencher une resynchronisation). - Les mises à jour de code passent par rsync (ou git sur le nœud, protocole remote-first des projets Ka) puis `pm2 restart job-ka-web job-ka-sync`. ## Variations par rapport à Lou·Ka - Carte : **MapLibre GL + fond Carto libre** au lieu de Mapbox/ka-maps — aucun jeton requis, aucun paquet lié à builder à côté du dépôt. - `dedup.run()` s'exécute aussi après `run.py sync` (pas seulement dans `watch`) ; `geocode_failed` est remis à 0 quand l'adresse d'une offre change. - Endpoint `/health` dédié (Lou·Ka n'en a pas).