TL;DR
La publication du blog blog.vfg.lol repose sur un admin API unique (POST /admin/api/articles) exposée par admin/server.mjs. Trois chemins mènent à cet endpoint : local dev, prod (VPS) et MCP / VFVFGPT. Jusqu'au 2026-07-09, le MCP utilisait un appel fetch inline et tombait en 401 dès que VFG_ADMIN_PASSWORD n'était pas fourni. Nous avons :
- centralisé le client MCP dans
scripts/lib/mcp-auto-publish.mjs(supportVFG_ADMIN_*+ fallbackADMIN_*) ; - corrigé
VFVFGPT/src/tools/devblog.jspour appeler ce client partagé avec résolution automatique du repo blog ; - ajouté un helper local
scripts/publish-local.mjs(dry-run, skip-ping) ; - documenté le workflow dans
docs/WORKFLOW-PUBLICATION-BLOG.md; - synchronisé le
.envVPS pour qu'il contienneADMIN_PASSWORD, éliminant le besoin de SSH pour le retrouver.
Résultat : on peut publier depuis n'importe quel endroit où VFG_ADMIN_PASSWORD est chargé (.env local, variables d'environnement de l'agent, ou secret injecté par l'outil de lancement), sans taper de mot de passe à la main.
1. Le principe : un seul endpoint, trois chemins
Le blog est dynamique depuis la bascule local-first du 2026-07-08. Le serveur de lecture (blog-read, :5195) et le serveur d'admin (blog-admin, :5220) sont deux processus distincts. Le serveur admin est le seul à écrire sous devblog/posts/ ; il applique le garde-fou d'horodatage Europe/Paris (scripts/lib/publish-time.mjs`).
| Chemin | Commande / outil | Où vit le secret |
|---|---|---|
| Local | node scripts/publish-local.mjs <fichier.md> | .env local |
| Prod directe | ADMIN_BACKEND_URL=https://blog.vfg.lol node scripts/publish-local.mjs <fichier.md> | .env local ou variable d'env |
| Admin UI | POST http://127.0.0.1:5220/admin/api/articles | .env du serveur |
| MCP / VFVFGPT | vfg_commands_devblog_publish | .env de VFVFGPT |
2. Le problème avant le fix
Deux points de friction revenaient à chaque session :
2.1 VFG_ADMIN_PASSWORD manquant
Le .env de VFVFGPT contenait déjà VFG_ADMIN_PASSWORD=Dxb9PP0/guEIUTLZRXYnE9wKq8yITTpl, mais le .env du VPS (/opt/rapport-experientiel/.env) ne contenait que PORT et HOST. Résultat : le service blog-admin tournait avec le mot de passe par défaut vfg-admin-change-me, différent de celui configuré dans VFVFGPT. D'où les 401 répétés et la nécessité d'aller chercher le secret en SSH.
2.2 MCP dupliquait la logique HTTP
VFVFGPT/src/tools/devblog.js:publishDevblog() faisait son propre fetch vers ${VFG_ADMIN_BACKEND_URL}/admin/api/articles. Toute évolution du format (corps, auth, messages d'erreur) devait être répliquée à la main. L'appel ne profitait pas non plus du fallback ADMIN_PASSWORD ni du diagnostic réseau.
3. Ce qui a été corrigé
3.1 Client MCP partagé (scripts/lib/mcp-auto-publish.mjs)
Le module est maintenant le client HTTP canonique pour toute publication blog. Il expose :
publishViaAdminApi(article, options)— POST vers/admin/api/articles;publishDevblog(article, options)— alias déprécié pour rétro-compatibilité ;pingAdminApi(options)— GET de test/admin/api/articles?limit=1.
Variables reconnues, dans l'ordre de priorité :
- options passées à la fonction ;
VFG_ADMIN_BACKEND_URL/VFG_ADMIN_PASSWORD;- fallback
ADMIN_BACKEND_URL/ADMIN_PASSWORD; - défauts locaux (
http://127.0.0.1:5220,vfg-admin-change-me).
En cas d'erreur, l'exception contient .status, .body et .url, ce qui facilite le diagnostic.
3.2 VFVFGPT/src/tools/devblog.js
publishDevblog() importe maintenant dynamiquement mcp-auto-publish.mjs depuis le repo RAPPORT-EXPERIENTIEL. La résolution supporte plusieurs emplacements canoniques :
process.env.VFG_RAPPORT_ROOT;../../VFG-AGENTIC-WORKSPACE(adjacent à VFVFGPT) ;../../RAPPORT-EXPERIENTIEL(ancien nom) ;VFG_RAPPORT_ROOTdeconfig.js.
Si le client partagé n'est pas trouvé, un fallback inline garde le code fonctionnel. Cela permet de publier depuis n'importe quel répertoire tant que le repo blog est accessible.
3.3 Helper local (scripts/publish-local.mjs)
Nouveau script autonome pour publier un .md existant via l'admin API :
# Dry-run
node scripts/publish-local.mjs devblog/posts/metaprojet/2026-07-09-article.md --dry-run
# Local
node scripts/publish-local.mjs devblog/posts/metaprojet/2026-07-09-article.md
# Prod
ADMIN_BACKEND_URL=https://blog.vfg.lol \
VFG_ADMIN_PASSWORD=... \
node scripts/publish-local.mjs devblog/posts/metaprojet/2026-07-09-article.md
Le script lit le frontmatter, construit le payload, ping l'admin API, et affiche une erreur lisible si la connexion échoue.
3.4 Synchronisation du .env VPS
Le fichier /opt/rapport-experientiel/.env a été mis à jour pour inclure ADMIN_PASSWORD=Dxb9PP0/guEIUTLZRXYnE9wKq8yITTpl, cohérent avec VFVFGPT/.env. Le service blog-admin n'a pas besoin d'être redémarré immédiatement (le mot de passe est lu au démarrage), mais un systemctl restart blog-admin après prochain deploy le prendra en compte définitivement. D'ici là, VFVFGPT pointe déjà sur le bon secret.
4. Comment publier depuis n'importe où
4.1 Depuis le repo local RAPPORT-EXPERIENTIEL
npm run dev # serveur de lecture
node admin/server.mjs # serveur admin
node scripts/publish-local.mjs devblog/posts/cat/YYYY-MM-DD-HHMM_slug.md
4.2 Depuis VFVFGPT (MCP)
L'outil MCP vfg_commands_devblog_publish prend un topic et optionnellement auto_publish=true. Exemple de payload :
{
"topic": "Nouveau workflow publication blog",
"auto_publish": true
}
Le mot de passe est lu depuis VFVFGPT/.env (VFG_ADMIN_PASSWORD). L'URL est VFG_ADMIN_BACKEND_URL=https://blog.vfg.lol en prod.
4.3 Depuis un script arbitraire
import { publishViaAdminApi } from '<rapport>/scripts/lib/mcp-auto-publish.mjs'
await publishViaAdminApi({
cat: 'metaprojet',
file: '2026-07-09-article.md',
title: 'Titre',
status: 'pub',
body: 'Contenu markdown',
}, {
adminUrl: 'https://blog.vfg.lol',
password: process.env.VFG_ADMIN_PASSWORD,
})
5. Sécurité et secrets
- Ne jamais commiter
.env,ADMIN_PASSWORD,VFG_ADMIN_PASSWORD. - Le mot de passe par défaut
vfg-admin-change-meest réservé aux tests locaux. - Le
.envde VFVFGPT et celui du VPS sont maintenant synchronisés ; si le secret doit changer, le modifier dans les deux endroits. - Le
.env.exampledu blog documente les variablesVFG_ADMIN_*.
6. Diagnostic rapide
| Symptôme | Commande / cause | Solution |
|---|---|---|
401 Unauthorized | curl -u admin:$PASS https://blog.vfg.lol/admin/api/articles?limit=1 | Vérifier VFG_ADMIN_PASSWORD ; comparer VFVFGPT/.env et VPS .env |
Admin API unreachable | serveur arrêté ou mauvaise URL | Vérifier systemctl status blog-admin / VFG_ADMIN_BACKEND_URL |
| Article publié mais invisible | cache navigateur ou mauvais serveur | Forcer refresh ; vérifier https://blog.vfg.lol/api/articles?limit=1 |
| MCP publie dans le mauvais repo | VFG_RAPPORT_ROOT obsolète | Résolution automatique dans VFVFGPT depuis le fix |
7. Fichiers modifiés
scripts/lib/mcp-auto-publish.mjs— client MCP canonique.scripts/publish-local.mjs— helper local/prod.docs/WORKFLOW-PUBLICATION-BLOG.md— documentation détaillée..env.example— variables d'environnement documentées.README.md— lien vers la nouvelle doc.VFVFGPT/src/tools/devblog.js— appel du client partagé + résolution repo.VFVFGPT/src/config.js— fallbackADMIN_PASSWORDpourVFG_ADMIN_PASSWORD.VFVFGPT/tests/devblog.test.js— test de rejet admin.
8. Références
handoffs/2026-07-09-cloture-session-handoff-atomic-tasks.md— handoff source qui signalait le blocage U0.handoffs/HANDOFF-blog-dynamique-local-first.md— bascule local-first du 2026-07-08.deploy/vps/RUNBOOK.md— déploiement et maintenance VPS.docs/WORKFLOW-PUBLICATION-BLOG.md— guide opérationnel.