---
title: "Workflow publication blog VFG : local, prod et MCP sans aller chercher le mot de passe"
subtitle: "Guide complet : comment publier un article depuis n'importe où, pourquoi le mot de passe admin n'est plus un blocage, et ce qui a été corrigé dans VFVFGPT"
date: 2026-07-09
time: "20:23"
author: vfg-commands
status: pub
tags: ["workflows-agentiques", "blog", "mcp", "publication", "vfvfgpt", "prod", "local-first"]
---

## 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 :

1. centralisé le client MCP dans `scripts/lib/mcp-auto-publish.mjs` (support `VFG_ADMIN_*` + fallback `ADMIN_*`) ;
2. corrigé `VFVFGPT/src/tools/devblog.js` pour appeler ce client partagé avec résolution automatique du repo blog ;
3. ajouté un helper local `scripts/publish-local.mjs` (dry-run, skip-ping) ;
4. documenté le workflow dans `docs/WORKFLOW-PUBLICATION-BLOG.md` ;
5. synchronisé le `.env` VPS pour qu'il contienne `ADMIN_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é :

1. options passées à la fonction ;
2. `VFG_ADMIN_BACKEND_URL` / `VFG_ADMIN_PASSWORD` ;
3. fallback `ADMIN_BACKEND_URL` / `ADMIN_PASSWORD` ;
4. 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_ROOT` de `config.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 :

```bash
# 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

```bash
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 :

```json
{
  "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

```js
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-me` est réservé aux tests locaux.
- Le `.env` de VFVFGPT et celui du VPS sont maintenant synchronisés ; si le secret doit changer, le modifier dans les deux endroits.
- Le `.env.example` du blog documente les variables `VFG_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` — fallback `ADMIN_PASSWORD` pour `VFG_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.
