# OPS — Localhost auto-sync, cockpit local et supervision NAS

## Objet

Ce document remplace et prolonge la première version de l’architecture `localhost auto-sync`.  
Il fige désormais **l’architecture opératoire complète** obtenue à l’issue de la session de stabilisation du cockpit local d’exploitation.

Le périmètre ne se limite plus à :

- garder `localhost` aligné sur `origin/main` ;
- séparer correctement le repo canonique de développement et le miroir local.

Il couvre maintenant aussi :

- le **deuxième environnement local de travail** sur `4322` ;
- le **cockpit local d’exploitation** sur `4310/4311` ;
- la **supervision NAS** via `stack-health.json` ;
- l’**instrumentation distante** de staging et prod via `__ops/health.json` ;
- la **lecture causale** des conteneurs critiques ;
- la **gradation des alertes** ;
- la **fraîcheur temporelle** du cache NAS.

---

## 1. Architecture finale retenue

L’architecture locale repose désormais sur **cinq espaces / rôles distincts**.

### A. Repo canonique de développement

Chemin :

```text
/Volumes/FunIA/dev/archicratie-edition/site
```

Usage :

- développement normal ;
- branches ;
- commits ;
- PR ;
- tests manuels de nouvelles fonctionnalités ;
- exécution du serveur de dev local sur `4322`.

### B. Worktree miroir local de `main`

Chemin :

```text
~/ops-local/archicratie/localhost-worktree
```

Branche locale :

```text
localhost-sync
```

Usage :

- refléter automatiquement `origin/main` ;
- servir le **miroir local de vérité** sur `http://127.0.0.1:4321` ;
- ne jamais servir de lieu de développement de fond.

### C. Dossier d’ops local hors repo

Chemin :

```text
~/ops-local/archicratie
```

Usage :

- scripts ;
- logs ;
- fichiers d’état ;
- cache NAS ;
- artefacts du cockpit ;
- LaunchAgents ;
- PID files.

### D. Cockpit local d’exploitation

Chemin :

```text
~/ops-local/archicratie/cockpit
```

Usage :

- UI locale en `http://127.0.0.1:4310` ;
- API locale en `http://127.0.0.1:4311` ;
- lecture unifiée des états Git, LaunchAgents, serveurs Astro, staging, prod et stack NAS ;
- déclenchement d’actions unitaires et composées.

### E. NAS DSM 7.3

Chemins clés :

```text
/volume2/docker/archicratie_ops
/volume2/docker/archicratie_ops/public/stack-health.json
/volume2/docker/archicratie_ops/generate-stack-health.sh
```

Usage :

- produire l’état de santé des conteneurs critiques ;
- exposer un JSON simple rapatrié ensuite sur le Mac ;
- rester la source de vérité côté Docker Synology.

---

## 2. Vue d’ensemble opératoire

### 2.1 Miroir local `4321`

Le miroir local `4321` est la **vérité de `main`**.

Flux :

1. `origin/main` avance.
2. Un script d’auto-sync local fetch le repo canonique.
3. Le worktree `localhost-sync` est réaligné.
4. `astro dev` tourne depuis `~/ops-local/archicratie/localhost-worktree`.
5. `http://127.0.0.1:4321` reflète alors `main`.

### 2.2 Atelier local `4322`

Le port `4322` est le **vrai atelier de développement**.

Flux :

1. on travaille dans `/Volumes/FunIA/dev/archicratie-edition/site` ;
2. le serveur de dev local est lancé manuellement depuis ce repo ;
3. le cockpit peut le démarrer / l’arrêter ;
4. un agent local de sync peut réaligner le repo canonique sur `origin/main` quand la branche est `main` et propre.

### 2.3 Cockpit `4310/4311`

Le cockpit lit en continu :

- l’état Git du repo canonique ;
- l’état Git du miroir `localhost-sync` ;
- les réponses HTTP de `4321` et `4322` ;
- les réponses distantes staging / prod ;
- les LaunchAgents locaux ;
- les logs récents ;
- les événements opératoires ;
- le cache NAS rapatrié via SSH.

### 2.4 Supervision NAS

Le NAS génère périodiquement :

```json
stack-health.json
```

Ce fichier est ensuite rapatrié sur le Mac, mis en cache localement, puis injecté dans le cockpit.

---

## 3. Distinction conceptuelle consolidée

### 3.1 `4321 = miroir`
- automatique ;
- worktree dédié ;
- vérité de `main` ;
- ne pas utiliser pour le développement normal.

### 3.2 `4322 = atelier`
- manuel ;
- repo canonique ;
- branches et travail vivant ;
- peut être aligné automatiquement avec `origin/main` **uniquement** si le repo est propre et sur `main`.

### 3.3 `4310/4311 = cockpit`
- interface d’exploitation ;
- ne sert pas le site éditorial ;
- supervise, diagnostique, orchestre et documente l’état global.

---

## 4. Scripts locaux stabilisés

Le dossier `~/ops-local/archicratie` contient maintenant un ensemble cohérent de scripts.

### 4.1 Scripts du miroir `4321`

```text
~/ops-local/archicratie/auto-sync-localhost.sh
~/ops-local/archicratie/run-astro-localhost.sh
~/ops-local/archicratie/doctor-localhost.sh
~/ops-local/archicratie/install-localhost-sync.sh
```

Rôles :

- vérifier runtime et dépendances ;
- fetch `origin/main` ;
- réaligner le worktree ;
- exécuter `npm ci` si besoin ;
- redémarrer le serveur miroir ;
- produire un diagnostic global.

### 4.2 Scripts de l’atelier `4322`

```text
~/ops-local/archicratie/auto-sync-dev-main.sh
~/ops-local/archicratie/run-dev-4322.sh
~/ops-local/archicratie/stop-dev-4322.sh
```

Rôles :

- fetch du repo canonique ;
- réalignement automatique **seulement** si branche `main` + repo propre ;
- démarrage manuel du dev sur `127.0.0.1:4322` ;
- arrêt manuel du dev.

Principe important :

- si le repo canonique est sale, le script note `skip_dirty_main` et ne casse rien ;
- si le repo canonique est déjà aligné, le script note `noop_main_aligned`.

### 4.3 Scripts du cockpit

```text
~/ops-local/archicratie/run-cockpit.sh
~/ops-local/archicratie/stop-cockpit.sh
```

Rôle :

- démarrer / arrêter le cockpit avec son UI Vite et son API Node ;
- standardiser l’exploitation pour éviter les commandes manuelles dispersées.

### 4.4 Script de rapatriement NAS

```text
~/ops-local/archicratie/pull-stack-health.sh
```

Rôle :

- ouvrir une session SSH vers le NAS ;
- lire le fichier `/volume2/docker/archicratie_ops/public/stack-health.json` ;
- le recopier localement dans :

```text
~/ops-local/archicratie/cache/stack-health.json
```

- écrire un log d’exploitation.

---

## 5. Fichiers d’état et journaux locaux

### 5.1 Dossier ops

```text
~/ops-local/archicratie
```

Contient notamment :

```text
astro-localhost.pid
dev-4322.pid
dev-main-4322.pid
cockpit.pid
last-sync.env
last-dev-main-sync.env
cockpit-events.jsonl
```

### 5.2 Logs utiles

```text
~/ops-local/archicratie/logs/auto-sync-localhost.log
~/ops-local/archicratie/logs/astro-localhost.log
~/ops-local/archicratie/logs/pull-stack-health.log
```

Et côté LaunchAgents :

```text
~/Library/Logs/archicratie-pull-stack-health.out.log
~/Library/Logs/archicratie-pull-stack-health.err.log
```

### 5.3 Cache NAS local

```text
~/ops-local/archicratie/cache/stack-health.json
```

Ce fichier est devenu un **artefact stratégique** :

- il permet au cockpit de lire la stack NAS sans dépendre d’un appel live direct au NAS ;
- il permet une supervision plus robuste ;
- il rend possible une **alerte de fraîcheur**.

---

## 6. LaunchAgents et automatisation locale

L’automatisation locale repose désormais sur **trois moteurs indépendants**.

### 6.1 Sync miroir

Rôle :

- tourner périodiquement ;
- réaligner le worktree `localhost-sync` ;
- maintenir `4321` fidèle à `origin/main`.

Cadence constatée :

- environ **toutes les 60 secondes**.

### 6.2 Sync dev-main

Rôle :

- surveiller le repo canonique ;
- le fetch régulièrement ;
- le réaligner si, et seulement si :
  - la branche est `main` ;
  - l’arbre de travail est propre.

Cadence constatée :

- environ **toutes les 75 secondes**.

### 6.3 Pull stack-health NAS

LaunchAgent :

```text
me.archicratie.pull-stack-health
```

Rôle :

- rapatrier le cache NAS via SSH ;
- mettre à jour le fichier local ;
- nourrir la supervision causale.

Cadence constatée :

- environ **toutes les 60 secondes**.

Pilotage manuel du LaunchAgent :

### Désactiver temporairement le rapatriement local

```bash
launchctl bootout "gui/$(id -u)" ~/Library/LaunchAgents/me.archicratie.pull-stack-health.plist
```

### Réactiver le rapatriement local

```bash
launchctl bootstrap "gui/$(id -u)" ~/Library/LaunchAgents/me.archicratie.pull-stack-health.plist
```

### Interprétation :

- bootout retire volontairement l’agent du domaine utilisateur courant ;

- bootstrap recharge ce même agent dans le domaine utilisateur courant ;

- en fonctionnement nominal, cet agent doit rester actif ;

- après bootstrap, le fichier ~/ops-local/archicratie/cache/stack-health.json doit recommencer à être rafraîchi ;

- dans le cockpit, launchAgents.pullStackHealth.loaded doit redevenir true.

### Point d’attention :

- bootout sert au test contrôlé, au diagnostic ciblé ou à une maintenance volontaire ;

- bootout ne constitue pas une commande d’exploitation normale ;

- après tout usage de bootout, il faut restaurer l’état nominal avec bootstrap.
---

## 7. SSH Mac ↔ NAS

### 7.1 Situation initiale

La copie du fichier NAS vers le Mac a rencontré plusieurs problèmes :

- nom d’hôte local non résolu ;
- port SSH non standard ;
- `scp` indisponible / mal supporté dans la configuration DSM observée ;
- authentification par mot de passe trop lourde.

### 7.2 Paramètres réels retenus

- IP locale NAS : `192.168.1.20`
- port SSH : `44322`
- utilisateur : `archicratia`

### 7.3 Méthode de copie finalement retenue

Plutôt que `scp`, la méthode robuste retenue a été :

- `ssh ... 'cat /chemin/fichier' > fichier_local`

Cela a permis :

- de s’affranchir du problème `scp subsystem request failed`;
- de garder un protocole simple ;
- d’intégrer facilement le mécanisme dans `pull-stack-health.sh`.

### 7.4 Authentification par clé

Une clé dédiée a été générée côté Mac et installée côté NAS.  
Le point bloquant n’était pas la clé elle-même, mais les permissions du `HOME` du compte Synology.

Correctif déterminant côté NAS :

- retirer les permissions `go-w` sur le `HOME` ;
- conserver :
  - `~/.ssh` en `700`
  - `authorized_keys` en `600`

Après cette correction :

- l’authentification par clé est devenue fonctionnelle ;
- le LaunchAgent de pull a pu fonctionner sans mot de passe.

### 7.5 Alias SSH local

Une entrée `~/.ssh/config` a ensuite permis d’utiliser un alias opératoire du type :

```text
transhands-ops
```

ce qui a simplifié les scripts de pull.

---

## 8. Supervision NAS : génération de `stack-health.json`

### 8.1 Script côté NAS

Fichier :

```text
/volume2/docker/archicratie_ops/generate-stack-health.sh
```

Rôle :

- inspecter plusieurs conteneurs Docker clés ;
- écrire un JSON simple ;
- exposer cet état dans `public/stack-health.json`.

### 8.2 Exécution et droits

Constat clé :

- sans privilèges suffisants, le script peut échouer ou signaler `docker_unavailable` ;
- via le planificateur DSM 7.3, exécuté correctement, le fichier est bien régénéré.

Le comportement retenu :

- exécution via tâche planifiée DSM avec les droits nécessaires ;
- sortie du JSON écrite dans :

```text
/volume2/docker/archicratie_ops/public/stack-health.json
```

### 8.3 Problème rencontré

Un comportement bloquant est apparu :

- fichier existant possédé par `root` ;
- exécution interactive sans privilège ;
- `mv` demandant confirmation ou échouant ;
- `chmod` impossible.

Conclusion :

- la génération doit être pensée comme **opération root planifiée**, pas comme simple commande utilisateur interactive.

### 8.4 Horodatage local

Le champ `generatedAt` a d’abord été produit en UTC (`...Z`), ce qui déplaisait à l’exploitation locale.

Le script a été ajusté pour produire désormais une heure locale explicite, par exemple :

```text
2026-03-18T13:26:32+01:00
```

---

## 9. Périmètre de la stack NAS désormais supervisée

Le JSON NAS couvre désormais au moins les conteneurs suivants :

- `edge-traefik`
- `auth-authelia`
- `auth-lldap`
- `auth-redis`
- `archicratie-web-blue`
- `archicratie-web-green`
- `gitea-old-2026-02-09-105211`
- `gitea-act-runner`

Le cockpit les remappe en :

- `edgeTraefik`
- `authelia`
- `lldap`
- `authRedis`
- `webBlue`
- `webGreen`
- `gitea`
- `giteaActRunner`

---

## 10. Déploiement staging / prod : instrumentation `__ops/health.json`

### 10.1 Problème initial

Le cockpit savait tester :

- le HTTP public staging ;
- le HTTP public prod.

Mais il ne savait pas lire proprement **le build réellement servi** par `web_blue` et `web_green`.

### 10.2 Cause

Les conteneurs Nginx servaient bien le site, mais pas de manifeste d’état applicatif dédié.  
Les tentatives d’accès à :

```text
/__ops/health.json
```

retournaient d’abord :

- `404` côté conteneurs ;
- puis `302` via Authelia côté domaine public.

### 10.3 Solution produite

Deux modifications complémentaires ont été apportées.

#### A. Côté build / déploiement

Un script :

```text
scripts/write-ops-health.mjs
```

écrit désormais un manifeste :

```text
public/__ops/health.json
```

avec des champs du type :

- `service`
- `env`
- `upstream`
- `buildSha`
- `builtAt`

Ce manifeste est produit pendant le workflow de déploiement pour :

- staging / `web_blue`
- prod / `web_green`

#### B. Côté edge / Traefik / Authelia

Le routage a été ajusté pour que :

```text
https://staging.../__ops/health.json
https://archicratie.../__ops/health.json
```

restent **lisibles sans redirection Authelia**, tout en laissant le reste du site inchangé.

### 10.4 Correction de l’heure `builtAt`

Comme pour `generatedAt`, le champ `builtAt` a été réaligné sur l’heure locale France via :

```text
TZ=Europe/Paris
```

dans le workflow de déploiement.

---

## 11. Cockpit local d’exploitation

### 11.1 Emplacement

```text
~/ops-local/archicratie/cockpit
```

### 11.2 Ports

- UI : `http://127.0.0.1:4310`
- API : `http://127.0.0.1:4311`

### 11.3 Démarrage

Le cockpit est lancé via :

```bash
~/ops-local/archicratie/run-cockpit.sh
```

et arrêté via :

```bash
~/ops-local/archicratie/stop-cockpit.sh
```
### 11.3 bis Mode opératoire recommandé

Le pilotage normal du cockpit doit se faire via les scripts bash d’exploitation :

```bash
~/ops-local/archicratie/run-cockpit.sh
~/ops-local/archicratie/stop-cockpit.sh
```
et non via un lancement manuel dispersé du type npm run dev ou node server/index.mjs exécuté séparément.

### Justification :

- run-cockpit.sh démarre correctement l’ensemble du cockpit ;

- l’UI et l’API sont lancées ensemble dans le bon ordre logique ;

- l’exploitation reste homogène et rejouable ;

- stop-cockpit.sh fournit un arrêt propre, cohérent avec les PID et l’usage opératoire retenu.

### Autrement dit :

- 4310 = façade visuelle ;

- 4311 = moteur d’état ;

- les scripts bash = méthode normale d’exploitation.

### 11.4 Architecture interne

- UI React / Vite
- API Node locale
- lecture des états système via scripts, logs, fichiers JSON et `launchctl`

### 11.4 bis Source de vérité interne

Le cockpit ne repose pas sur une simple interface décorative.

La source de vérité opérationnelle est l’API locale :

```text
http://127.0.0.1:4311/api/status
```

### L’UI affichée sur :

```text
http://127.0.0.1:4310
```

n’est qu’une projection visuelle de cet état consolidé.

### Conséquence pratique :

- si un doute existe sur l’exactitude de l’interface, il faut interroger directement GET /api/status ;

- c’est cette réponse JSON qui fait foi pour :

- l’état Git ;

    - les LaunchAgents ;

    - les serveurs 4321 et 4322 ;

    - staging / prod ;

    - la stack NAS ;

    - les alertes ;

    - les événements.

Cette distinction est importante : l’UI montre, l’API atteste.

### 11.5 Endpoints utiles

L’API du cockpit expose au moins :

- `GET /api/health`
- `GET /api/status`
- `POST /api/action`
- `POST /api/event`
- `POST /api/events/clear`

### Remarques d’exploitation :

- `GET /api/health` vérifie simplement que l’API locale répond ;
- `GET /api/status` est l’endpoint central de supervision ;
- `POST /api/action` déclenche une action opératoire unitaire ;
- `POST /api/event` permet d’écrire un événement dans l’historique local ;
- `POST /api/events/clear` vide explicitement l’historique des événements.

En cas de diagnostic, `GET /api/status` doit être considéré comme la lecture machine de référence.
---

## 12. Ce que le cockpit sait lire

Le cockpit agrège désormais :

### 12.1 Git
- branche du repo canonique ;
- SHA local ;
- `origin/main` ;
- état de propreté (`clean`, `modified`, `untracked`, etc.).

### 12.2 Miroir 4321
- port HTTP ;
- PID ;
- cwd du process ;
- dernier sync ;
- SHA du miroir.

### 12.3 Atelier 4322
- port HTTP ;
- PID ;
- branche du repo canonique ;
- dernier sync dev-main ;
- statut `noop_main_aligned` ou `skip_dirty_main`.

### 12.4 Staging et prod
- HTTP public ;
- lecture de `__ops/health.json` ;
- `env` ;
- `upstream` ;
- `buildSha` ;
- `builtAt`.

### 12.5 LaunchAgents
- état chargé / non chargé ;
- exit code ;
- compteur de runs.

### 12.6 Logs récents
- sync miroir ;
- sync dev-main ;
- Astro miroir ;
- Astro dev ;
- pull stack-health.

### 12.7 Stack NAS
- chemin du cache local ;
- `generatedAt` ;
- `dockerAccessMode` ;
- services détaillés.

---

## 13. Centre d’alertes et hiérarchie de gravité

La supervision n’est pas seulement binaire ; elle est maintenant **qualifiée**.

### 13.1 Niveau `info`
Exemple :

- `giteaActRunner` indisponible

Justification :

- cela dégrade la chaîne de pipeline / déploiement ;
- cela ne casse pas automatiquement le service public.

### 13.2 Niveau `warn`
Exemples :

- `authRedis` indisponible
- `gitea` indisponible

Justification :

- impact opératoire réel ;
- système encore potentiellement utilisable ;
- attention nécessaire.

### 13.3 Niveau `critical`
Exemples :

- cache NAS trop ancien
- services cœur réellement cassés
- rupture forte de la lecture de confiance

### 13.4 Exemple d’alerte informative

Quand `gitea-act-runner` a été arrêté, le cockpit a correctement affiché :

- une alerte **Info**
- sans transformer à tort l’état global du service public en catastrophe.

### 13.5 Exemple d’alerte `warn`

Quand `auth-redis` a été arrêté, le cockpit a affiché :

- `Redis d’authentification indisponible`
- niveau **Attention**
- avec la stack causale montrant le conteneur `exited / unhealthy`.

### 13.6 Exemple d’alerte `warn` sur la forge

Quand Gitea a été arrêté, le cockpit a affiché :

- `Forge Gitea indisponible`
- niveau **Attention**
- avec staging / prod encore sains.

---

## 14. Fraîcheur du cache NAS

### 14.1 Problème conceptuel identifié

Même avec un bon `stack-health.json`, le cockpit pouvait mentir par omission si :

- le scheduler DSM était cassé ;
- ou le pull SSH Mac était stoppé ;
- ou le cache local restait figé.

### 14.2 Solution

Une lecture d’âge du cache a été ajoutée dans `buildAlerts(...)`.

Règles de seuil :

- pas d’alerte si le cache est frais ;
- **warn** au-delà d’environ 3 minutes ;
- **critical** au-delà d’environ 6 minutes.

### 14.3 Test réel effectué

Le LaunchAgent `pull-stack-health` a été volontairement stoppé.  
Résultats observés :

- dans une fenêtre intermédiaire : le cockpit a bien produit une alerte **Attention** ;
- au-delà du seuil haut : le cockpit a bien produit **Cache NAS trop ancien** avec niveau **Critique**.

Les commandes utilisées pour le test contrôlé de vieillissement du cache sont les suivantes.

### Interrompre volontairement le rapatriement local

```bash
launchctl bootout "gui/$(id -u)" ~/Library/LaunchAgents/me.archicratie.pull-stack-health.plist
```
### Restaurer le fonctionnement nominal

```bash
launchctl bootstrap "gui/$(id -u)" ~/Library/LaunchAgents/me.archicratie.pull-stack-health.plist
```
### Lecture correcte du test

### Pendant ce test :

- launchAgents.pullStackHealth.loaded peut passer à false ;

- ce n’est pas une panne mystérieuse du cockpit ;

- c’est l’effet recherché, puisque le LaunchAgent a été volontairement déchargé ;

- le fichier ~/ops-local/archicratie/cache/stack-health.json reste alors figé à sa dernière valeur connue.

### Point d’interprétation essentiel

Le cockpit peut encore afficher une stack NAS apparemment OK tout en levant une alerte de fraîcheur warn puis critical.

Ce comportement est correct.

Il signifie :

- que le dernier cache disponible décrivait un état sain ;

- mais que ce cache est désormais trop ancien pour constituer une vérité d’exploitation suffisamment fiable.

Autrement dit, le cockpit ne dit pas :

“la stack est saine avec certitude”

Il dit :

“le dernier état connu de la stack était sain, mais cette information est devenue trop ancienne pour être pleinement digne de confiance”.

### Conclusion :

- la supervision ne dépend plus uniquement de l’existence du cache ;
- elle qualifie aussi sa **fraîcheur temporelle**.

---

## 15. Actions opératoires disponibles dans le cockpit

Le cockpit fournit maintenant :

### 15.1 Actions composées
- réparer le miroir ;
- préparer l’atelier dev.

### 15.1 bis Composition réelle

Les actions composées ne sont pas des actions magiques indépendantes.  
Elles orchestrent plusieurs actions unitaires dans un ordre déterminé.

Composition retenue :

- **réparer le miroir** :
  1. exécuter `doctor-localhost`
  2. exécuter le sync miroir `4321`

- **préparer l’atelier dev** :
  1. exécuter le sync `dev-main`
  2. démarrer le serveur local `4322`

Règle d’exécution :

- si la première étape échoue, la chaîne s’arrête ;
- la deuxième étape n’est pas lancée si la première n’est pas valide.

Règle de traçabilité :

- les actions composées écrivent un **événement composite** synthétique dans l’historique ;
- les sous-actions internes peuvent être exécutées en mode silencieux pour éviter un doublonnage inutile dans le journal.

### 15.2 Actions unitaires
- lancer `doctor-localhost` ;
- forcer le sync miroir `4321` ;
- forcer le sync dev-main ;
- démarrer `4322` ;
- arrêter `4322`.

### 15.2 bis Périmètre et garde-fous

Les actions unitaires du cockpit pilotent avant tout **l’exploitation locale sur le Mac**.

Elles agissent notamment sur :

- les scripts d’ops sous `~/ops-local/archicratie` ;
- le miroir local `4321` ;
- le serveur de dev local `4322` ;
- les états et journaux locaux.

Elles ne constituent pas un mécanisme de mutation directe de staging ou prod.  
Staging et prod sont principalement :

- lus ;
- sondés ;
- comparés ;
- diagnostiqués.

Points importants :

- `démarrer 4322` et `arrêter 4322` n’affectent que l’atelier local ;
- `sync dev-main` ne doit pas casser le repo canonique : le réalignement automatique n’a lieu que si le repo est sur `main` et propre ;
- certaines actions sont volontairement désactivées dans l’UI selon l’état courant ;
- une confirmation utilisateur peut être demandée avant déclenchement.

### 15.3 Journal d’événements

Les actions déclenchées depuis le cockpit peuvent être journalisées dans :

```text
~/ops-local/archicratie/cockpit-events.jsonl
```
### Ce journal peut contenir :

- des événements unitaires ;

- des événements composites.

### Chaque événement conserve au moins des informations du type :

- horodatage ;

- type d’action ;

- libellé ;

- succès / échec ;

- code de sortie ;

- durée ;

- script concerné ;

- résumé opératoire.

### Ce journal constitue une mémoire locale d’exploitation utile pour :

- relire les actions effectuées ;

- comprendre un enchaînement opératoire ;

- garder une trace minimale des gestes réalisés depuis le cockpit.

### Le cockpit sait aussi vider cet historique via l’API dédiée :

- POST /api/events/clear

### Point important :

- vider l’historique des événements ne supprime pas les logs techniques ;

- cela ne modifie ni les LaunchAgents, ni les PID, ni les fichiers d’état, ni les repos Git ;

- cela efface uniquement la mémoire locale des événements cockpit.

---

## 16. Règles d’exploitation consolidées

### 16.1 Règle d’or
Le repo canonique n’est pas le miroir local.

### 16.2 Règle miroir
`4321` doit raconter la vérité de `main`, pas l’état du travail en cours.

### 16.3 Règle atelier
`4322` est le lieu du travail vivant, du test manuel et des branches.

### 16.4 Règle d’ops
Les scripts d’ops et LaunchAgents vivent sous `HOME`, pas sur `/Volumes/...`.

### 16.5 Règle de vérité
Un état n’est réellement fiable que si l’on connaît :

- le SHA ;
- le cwd ;
- le process réel ;
- la fraîcheur de la donnée ;
- la causalité de la panne.

---

## 17. Commandes de contrôle essentielles

### 17.1 SHA et branches

```bash
git -C ~/ops-local/archicratie/localhost-worktree rev-parse HEAD
git -C /Volumes/FunIA/dev/archicratie-edition/site rev-parse HEAD
git -C /Volumes/FunIA/dev/archicratie-edition/site ls-remote origin refs/heads/main
git -C ~/ops-local/archicratie/localhost-worktree branch --show-current
```

### 17.2 Cockpit

```bash
~/ops-local/archicratie/run-cockpit.sh
~/ops-local/archicratie/stop-cockpit.sh
curl -s http://127.0.0.1:4311/api/status | python3 -m json.tool | sed -n '1,260p'
```

### 17.3 Dev 4322

```bash
~/ops-local/archicratie/run-dev-4322.sh
~/ops-local/archicratie/stop-dev-4322.sh
```

### 17.4 Pull stack-health

```bash
~/ops-local/archicratie/pull-stack-health.sh
launchctl print "gui/$(id -u)/me.archicratie.pull-stack-health" | sed -n '1,120p'
cat ~/ops-local/archicratie/cache/stack-health.json
tail -n 20 ~/ops-local/archicratie/logs/pull-stack-health.log
```

# Commandes complémentaires de test contrôlé :

```bash
launchctl bootout "gui/$(id -u)" ~/Library/LaunchAgents/me.archicratie.pull-stack-health.plist
launchctl bootstrap "gui/$(id -u)" ~/Library/LaunchAgents/me.archicratie.pull-stack-health.plist
```

### 17.5 Miroir et doctor

```bash
~/ops-local/archicratie/auto-sync-localhost.sh
~/ops-local/archicratie/doctor-localhost.sh
```

---

## 18. Procédures de secours

### 18.1 Si `4321` ment
Vérifier :

- SHA du worktree ;
- process Astro ;
- cwd réel ;
- logs du miroir ;
- doctor.

### 18.1 bis Lecture correcte du symptôme

Quand `4321` “ment”, il faut d’abord déterminer de quel mensonge il s’agit réellement :

- le worktree n’est pas aligné sur `origin/main` ;
- le bon worktree existe, mais le mauvais process sert le port `4321` ;
- le sync a bien eu lieu, mais l’interface observée ne reflète pas encore le bon état.

Ordre de lecture recommandé :

1. vérifier le SHA du worktree miroir ;
2. vérifier le SHA attendu de `origin/main` ;
3. vérifier le process réellement attaché au port `4321` ;
4. vérifier le `cwd` réel de ce process ;
5. relire les logs du sync miroir et du serveur Astro ;
6. terminer par `doctor-localhost` si le doute persiste.

Principe :

- un `4321` trompeur est rarement un problème “front” ;
- c’est presque toujours un problème de **SHA**, de **process**, de **cwd**, ou de **chaîne de sync**.

### 18.2 Si `4322` ne suit plus `main`
Vérifier :

- branche du repo canonique ;
- propreté de l’arbre ;
- logs `auto-sync-dev-main.sh`.

### 18.2 bis Ne pas confondre dérive normale et panne

Le port `4322` n’a pas vocation à refléter automatiquement `main` dans tous les cas.

Deux situations sont normales et ne doivent pas être interprétées comme des pannes :

- le repo canonique est sur une autre branche que `main` ;
- le repo canonique est sur `main` mais l’arbre est localement modifié.

Dans ces cas, le cockpit ou les scripts peuvent produire des états du type :

- `skip_dirty_main`
- ou absence de réalignement automatique

Ce comportement est volontaire.

Principe :

- `4322` est un **atelier de travail vivant** ;
- il ne doit pas être réaligné de force si cela risque d’écraser ou de perturber le travail local.

La bonne question n’est donc pas seulement :

- “est-ce que `4322` suit `main` ?”

mais :

- “avait-il vocation à suivre `main` dans l’état courant du repo ?”

### 18.3 Si le cockpit dit n’importe quoi sur le NAS
Vérifier :

- `stack-health.json` sur le NAS ;
- le planificateur DSM ;
- le LaunchAgent `pull-stack-health` ;
- la fraîcheur du cache local.
- si nécessaire, couper puis relancer explicitement le LaunchAgent avec `launchctl bootout ...` puis `launchctl bootstrap ...`.

### 18.3 bis Distinguer panne NAS, panne de rapatriement et cache périmé

Si la lecture NAS semble incohérente, il faut distinguer trois cas très différents :

#### A. Le NAS produit mal son état
Indices possibles :

- `stack-health.json` absent ou invalide côté NAS ;
- `dockerAccessMode` incohérent ;
- conteneurs signalés `docker_unavailable` ;
- problème de tâche planifiée DSM ou de privilèges côté génération.

#### B. Le NAS produit bien son état, mais le Mac ne le rapatrie plus
Indices possibles :

- `stack-health.json` correct sur le NAS ;
- cache local figé sur le Mac ;
- `launchAgents.pullStackHealth.loaded = false` ;
- logs de `pull-stack-health` arrêtés.

#### C. Le dernier cache local est encore lisible, mais il est trop ancien
Indices possibles :

- services encore affichés comme `OK` ;
- alerte de fraîcheur `warn` ou `critical` ;
- `generatedAt` ancien ;
- aucun rafraîchissement récent du cache.

La bonne méthode n’est donc pas seulement de “regarder la stack”, mais de répondre successivement à trois questions :

1. le NAS génère-t-il correctement le fichier ?
2. le Mac le rapatrie-t-il encore ?
3. la donnée affichée est-elle encore fraîche ?

### 18.4 Si staging / prod n’exposent plus `__ops/health.json`
Vérifier :

- le workflow de déploiement ;
- `scripts/write-ops-health.mjs` ;
- le routage Traefik / Authelia ;
- les endpoints internes `8081` / `8082`.

### 18.4 bis Méthode de tri diagnostique

Quand `__ops/health.json` n’est plus lisible, il faut d’abord identifier **à quel étage** se situe la panne.

#### Cas 1 — l’endpoint interne est déjà en erreur
Si :

- `http://127.0.0.1:8081/__ops/health.json`
- ou `http://127.0.0.1:8082/__ops/health.json`

retourne `404` ou une autre erreur, alors le problème est plutôt :

- côté build ;
- côté contenu servi par Nginx ;
- ou côté déploiement du manifeste.

#### Cas 2 — l’endpoint interne fonctionne mais le domaine public redirige ou bloque
Si l’interne fonctionne, mais que le domaine public renvoie `302`, `401` ou un comportement auth inattendu, alors le problème est plutôt :

- côté Traefik ;
- côté règles de routage ;
- côté Authelia / exemptions ;
- côté exposition publique de `__ops/health.json`.

#### Cas 3 — l’endpoint répond `200`, mais le contenu est incohérent
Si `__ops/health.json` existe et répond, mais que `env`, `upstream`, `buildSha` ou `builtAt` sont faux ou obsolètes, alors le problème est plutôt :

- dans la génération du manifeste ;
- dans le workflow de déploiement ;
- ou dans le moment précis où le manifeste est écrit dans l’image livrée.

Principe :

- `404` interne → problème de build / image / contenu servi ;
- `302` ou `401` public → problème de routage / auth ;
- `200` mais mauvais contenu → problème de génération ou d’injection du manifeste.

---

## 19. Tests réels désormais qualifiés

Les validations suivantes ont été observées pendant la session :

- staging / prod exposent bien `__ops/health.json` en public ;
- `buildSha` et `builtAt` sont lisibles ;
- `builtAt` est revenu à l’heure locale souhaitée ;
- la stack NAS est rapatriée côté Mac ;
- le cockpit sait afficher les services :
  - `authRedis`
  - `gitea`
  - `giteaActRunner`
- arrêt du runner Gitea → alerte `info`
- arrêt de Redis auth → alerte `warn`
- arrêt de Gitea → alerte `warn`
- vieillissement artificiel du cache NAS → `warn` puis `critical`

---

## 20. État final de référence

Quand tout fonctionne correctement, l’état attendu est :

- `4321` = miroir fidèle de `origin/main`
- `4322` = atelier local maîtrisé
- `4310/4311` = cockpit lisible et opératoire
- staging = build lisible via `__ops/health.json`
- prod = build lisible via `__ops/health.json`
- stack NAS = lisible via cache local frais
- alertes = hiérarchisées et causales
- actions cockpit = pilotage rapide sans confusion

---

## 21. Ce qu’il ne faut pas oublier

Cette architecture n’est plus seulement une astuce de localhost.  
C’est une **micro-ingénierie d’exploitation locale complète**.

Elle repose sur :

- séparation des rôles ;
- automatisation légère mais fiable ;
- lecture causale ;
- instrumentation des sites ;
- cache NAS rapatrié ;
- cockpit local ;
- mémoire documentaire durable.

---

## 22. Résumé ultra court

### Pour voir la vérité de `main`
```text
http://127.0.0.1:4321
```

### Pour travailler vraiment
```text
/Volumes/FunIA/dev/archicratie-edition/site
```

### Pour superviser et piloter
```text
http://127.0.0.1:4310
```

### Pour lire l’état machine consolidé
```text
http://127.0.0.1:4311/api/status
```

### Pour vérifier la stack NAS
```text
~/ops-local/archicratie/cache/stack-health.json
```

### Pour réparer vite
```bash
~/ops-local/archicratie/doctor-localhost.sh
~/ops-local/archicratie/auto-sync-localhost.sh
~/ops-local/archicratie/pull-stack-health.sh
```

---

## 23. Pistes futures à forte valeur

Les prochaines améliorations les plus pertinentes seraient :

- journal automatique des incidents détectés / résolus ;
- diagnostic synthétique de cause probable dans le cockpit ;
- regroupement visuel des services NAS par familles :
  - service public
  - authentification
  - forge / déploiement ;
- raffinement documentaire dans `START-HERE.md` et doc d’exploitation.

---

## Conclusion

L’architecture locale est désormais passée :

- d’un simple `localhost auto-sync`
- à un **dispositif d’exploitation local complet**.

Le résultat essentiel est le suivant :

- on sait **où** se trouve la vérité ;
- on sait **quel** serveur sert quoi ;
- on sait **pourquoi** une alerte remonte ;
- on sait **comment** diagnostiquer et réparer ;
- on sait **rejouer** l’installation ;
- on a désormais une mémoire technique durable de l’ensemble.
