Phase 4 : kit G9 (scripts + compose) + Gitea T4.4 backend Postgres

- compose/gitea : SQLite -> Postgres (source = paquet App Center DSM, gitea_db 13 repos/3 users), image épinglée 1.27.3, push-to-create
- scripts/07a-migrate-gitea.sh : export pg_dump (MVCC, .25 lecture seule) + tar/ssh (sshd DSM refuse rsync/scp) + manifestes sha256
- scripts/03b-secrets.sh : secrets gitea ajoutés
- journal.md : entrées Phase 4
This commit is contained in:
Effi
2026-10-11 07:45:00 +02:00
commit c246427cf5
39 changed files with 6131 additions and 0 deletions
+11
View File
@@ -0,0 +1,11 @@
# Secrets — jamais commités (garde-fou G9)
**/secrets/
*.env
*.cred
*.key
# Fichiers de config remplis (les .example restent versionnés)
g9-migration/configs/restic.env
# Sauvegardes générées par les scripts (ex. 01-hardening.sh)
*.bak.*
+635
View File
@@ -0,0 +1,635 @@
# Dossier — (Re)création de l'infrastructure locale Folzo Home (v2)
**Date** : 02/10/2026
**Auteur** : Jean-Henry Foltzenlogel (Folzo)
**Réseau** : `192.168.1.0/24`
**Statut global** : 🟡 **En cours** — architecture cible validée (02/10/2026) ; le plan d'exécution sera affiné pendant le rebuild.
**Document** : **vivant** — versionné, mis à jour à chaque étape de la (re)création. Chaque section porte son propre statut.
> **Nature du document** : c'est le **cahier des charges + plan de (re)création** de la **v2** de l'infrastructure. Il succède à `archive/DOSSIER-INFRA.md` (v1, 30/09/2026) qui est **supplanté** et conservé en `archive/` comme trace. La différence majeure : la v1 proposait de **décommissionner** l'ancien NAS (G7) ; la v2 **réutilise** ce matériel (G7 = spare) et **redistribue** les rôles autour de 4 machines sobres 24/7.
>
> **Source de vérité des faits matériels** : le **snapshot daté** `ETAT-DE-LART-2026-09-30.md` (scan live 30/09/2026). Ce dossier **ne duplique pas** les specs hardware (CPU, RAM, disques, conteneurs) — il s'y réfère. En cas de conflit sur un fait matériel, c'est le **snapshot** qui fait foi ; pour les **décisions et l'architecture**, c'est **ce dossier** qui fait foi.
---
## Sommaire
- [Résumé exécutif](#résumé-exécutif)
- [R1 — Vision & intention](#r1--vision--intention)
- [R2 — Architecture cible](#r2--architecture-cible)
- [R3 — Configuration des disques](#r3--configuration-des-disques)
- [R4 — Stratégie de backup 3-2-1](#r4--stratégie-de-backup-3-2-1)
- [R5 — Plan de (re)création](#r5--plan-de-recréation)
- [R6 — Décisions ouvertes](#r6--décisions-ouvertes)
- [R7 — Risques & priorités](#r7--risques--priorités)
- [R8 — Montée en compétence](#r8--montée-en-compétence)
- [R9 — Qualité & traçabilité](#r9--qualité--traçabilité)
- [Annexe A — Matrice de traçabilité besoin → décision → action → machine](#annexe-a--matrice-de-traçabilité)
- [Annexe B — Glossaire & abréviations](#annexe-b--glossaire--abréviations)
- [Changelog](#changelog)
---
## Résumé exécutif
**Le problème** : l'informatique de la maison est un système que je **subis** — pas de backup fonctionnel, un routeur à 85°C, un serveur à 94% de disque, un NAS XPEnology dont je redoute chaque mise à jour, et des services GAFAM qui détiennent la vie digitale de la famille.
**La vision** : une infrastructure **100% locale** qui me **dégafamise**, sobre (~4 boîtiers 24/7), redondante (3-2-1 **y compris les données IA**), que je **comprends** et que je **contrôle**.
**La décision clé de la v2** : **redistribuer les rôles autour de 4 machines sobres 24/7** en **réutilisant tout le matériel existant** (pas d'achat) :
| Machine | Rôle | 24/7 ? |
|---------|------|--------|
| **J5040** (ex-effi-server) | **Edge** : firewall + DHCP + DNS + reverse proxy + sites web | ✅ |
| **G9** (HP Pro 400 G9) | **Cœur famille** : photos, fichiers, notes, mots de passe, média | ✅ |
| **PC IA** (ex-folzo-corp, 2× 3060) | **IA 24/7** : assistant vocal, inférence GPU | ✅ |
| **Freebox Delta** | Routeur WAN + domotique (HA) + 2ᵉ copie backup | ✅ |
…et **3 machines on-demand** : **G7** (spare du G9, non branché), **218play** (offsite, éteinte le plus souvent), **BC250 / Laptop** (jeux).
**Le succès** (mesurable, horizon 1 an) : la famille n'utilise plus de service GAFAM pour photos, fichiers, notes, mots de passe, média, agenda ; le système est sauvegardé (3-2-1 **vérifié**, données IA incluses), sobre (~4 boîtiers 24/7), et je sais réparer n'importe quelle brique en 5 minutes parce que c'est **mon** système, documenté.
---
## R1 — Vision & intention
> **Statut** : ✅ Validé (héritée de la v1 — cadrage partagé)
### 1.1 Les 5 piliers
| # | Pilier | Définition (1 phrase) |
|---|--------|------------------------|
| 1 | **Se dégafamiser** | Remplacer chaque service GAFAM de la famille par un besoin satisfait en local, sur du matériel que je possède déjà. (100% = horizon long terme ; le target 1 an est un sous-ensemble défini.) |
| 2 | **Les bons outils** | Choisir, **au cas par cas**, les outils qui améliorent la vie quotidienne — par usage, pas par techno. |
| 3 | **Réutiliser le matériel** | Donner un rôle clair à chaque machine existante, en tenant compte de la sobriété et de la redondance. |
| 4 | **Monter en compétence** | Utiliser l'infra comme terrain d'apprentissage (Docker, réseau, DNS, backup, sécurité) + une source de vérité. |
| 5 | **Être acteur** | Un système fiable, observable, documenté → ne plus subir l'informatique. |
### 1.2 Principes directeurs
| Principe | Signification |
|----------|---------------|
| **100% local** | Les données de la famille ne quittent pas la maison (sauf choix explicite). |
| **Souveraineté** | Indépendance vis-à-vis des GAFAM et des dépendances fragiles (XPEnology). |
| **Réutilisation** | Pas d'achat pour l'achat — donner un rôle au matériel existant d'abord. |
| **Sobriété** | Consommation maîtrisée : ~4 boîtiers 24/7 sobres, le reste on-demand. |
| **Fiabilité** | Le système tourne sans intervention constante (watchdogs, services stables). |
| **Redondance** | Aucune donnée unique : backup 3-2-1 (3 copies, 2 supports, 1 offsite) — **données IA incluses**. |
| **Compréhension** | Chaque brique est documentée et comprise — sinon on ne la garde pas. |
### 1.3 Définition du succès (mesurable, horizon 1 an)
| Critère de succès | Mesure |
|-------------------|--------|
| **Dégafamisation** | Photos, fichiers, notes, mots de passe, média, agenda accessibles **sans** GAFAM. (Mail, messagerie, streaming = horizon > 1 an.) |
| **Fiabilité** | Backup 3-2-1 **vérifié** (restauration testée au moins une fois), **données IA incluses**. |
| **Sobriété** | ~4 boîtiers 24/7 sobres (Freebox + J5040 + G9 + PC IA), conso idle mesurée et stable. |
| **Maîtrise** | Je peux réparer n'importe quelle brique en < 5 min (doc + procédure). |
| **Compétence** | Dossier + docs projet maintenus à jour = portfolio démontrable. |
---
## R2 — Architecture cible
> **Statut** : ✅ **Validée** (02/10/2026) — chaque machine a un **rôle clair**, une force = un rôle, zéro chevauchement.
### 2.1 Rôle de chaque machine
| Machine | IP | **Rôle** | 24/7 ? | Disque |
|---------|-----|----------|--------|--------|
| **Freebox Delta** | .254 | Routeur WAN + **passthrough** (DHCP → J5040) + domotique (HA) + **2ᵉ copie** backup | ✅ | RAID5 3×1To = 2 To |
| **J5040** (ex-effi-server) | **.1** | **Edge** : firewall + DHCP + AdGuard (DNS) + reverse proxy + certificats + sites web | ✅ | 128 Go |
| **G9** (HP Pro 400 G9) | **.25** | **Cœur famille** : photos, fichiers, notes, mots de passe, média (Jellyfin) | ✅ | **NVMe 2 To (990 Pro, mono-disque)** + USB média |
| **PC IA** (ex-folzo-corp) | **.11** | **IA 24/7** : assistant vocal, inférence GPU (2× 3060) — **pas de gaming** | ✅ | NVMe 1,9 To (98 Go modèles) |
| **G7** (ex-xpenology) | **.30** | **Spare du G9** (secours cœur famille) — **non branché** | ⬜ on-demand | 256 Go SSD |
| **218play** | .26 | **Offsite** (3ᵉ copie) — éteinte le plus souvent | ⬜ on-demand | 2× 2 To |
| **BC250** | — | **Jeux** (Steam Machine) | ⬜ on-demand | à préciser |
| **Laptop** | — | Workstation principale + **jeux** (RTX 3050 Ti) | ⬜ on-demand | 2 To + 1 To |
| **Postes enfants** | .15/.16/.17 | Accès stack famille (DNS filtré) | — | — |
| **Média salon** | .51–.59 | Streaming / audio | — | — |
| **NVR Reolink ×3** | .41/.123/.191 | Surveillance (RTSP, autonomes) | ✅ | interne |
| **Alarme** | .21 | Alarme maison | ✅ | — |
> **Note disque J5040** : « 128 Go » = disque physique ; le snapshot (§2.1) mesure **98 Go** de volume LVM utilisable (87 Go utilisés au scan). Les deux sont vrais (physique vs volume). Source des faits matériels : le snapshot.
> **Principe « une force = un rôle »** : la **Freebox** = réseau/domotique, le **J5040** = edge internet, le **G9** = données famille, le **PC IA** = inférence, le **G7** = secours. **Aucun service n'est dupliqué** sur deux machines 24/7.
### 2.2 Le J5040 = l'« edge » complet
Le J5040 (ex-effi-server, 4 ports **2,5 Gb**) est placé **juste après le routeur** et devient l'**entrée internet unique** : tout ce qui est exposé passe par lui.
| Fonction | Outil type | Note |
|----------|-----------|------|
| **Firewall** | nftables | filtre LAN/WAN |
| **DHCP** | dnsmasq / Kea | la Freebox passe en **passthrough** (confirmé) |
| **DNS + filtre** | AdGuard Home | filtre famille (enfants) |
| **Reverse proxy** | Traefik / Nginx | point d'entrée unique |
| **Certificats** | Certbot (Let's Encrypt) | TLS auto-renew |
| **Sites web** | dd.folzocorp, lola.folzocorp… | hébergés sur le J5040 |
```
INTERNET → Freebox (WAN, port-forward) → J5040 (firewall + proxy + web) → LAN
```
> **Conséquence** : le J5040 n'héberge **plus aucune donnée IA** (l'IA part sur le PC IA). Son disque (128 Go) passe de **94% à ~30%** — le risque « disque saturé » disparaît.
### 2.3 Principe 24/7 vs on-demand
| Mode | Machines | Raison |
|------|----------|--------|
| **24/7** (toujours allumées) | **Freebox** + **J5040** + **G9** + **PC IA** | Services critiques famille + edge + cerveau IA vocal. Tous **sobres** (le PC IA est le seul à forte conso, voir R2.4). |
| **On-demand** | **G7** (spare, non branché), **218play** (offsite), **BC250 / Laptop** (jeux) | Ne s'allument que quand c'est nécessaire. |
> **Sobriété** : 4 boîtiers 24/7. Trois sont très sobres (~10 W chacun : Freebox, J5040, G9). Le **PC IA** est l'exception assumée (assistant vocal 24/7) — voir R2.4.
### 2.4 PC IA 24/7 : la conso assumée
L'assistant **vocal 24/7** impose que le PC IA (5800X3D + 2× 3060) soit **allumé en permanence**.
| Paramètre | Action |
|-----------|--------|
| **GPU** | **Power limit** (ex. 70%) pour réduire la conso et l'usure |
| **RAM / VRAM** | **Offload** si inactif > X min (libérer la VRAM quand pas de requête) |
| **LLM** | Chargé en VRAM 24/7 (dispo instantanée) |
| **ComfyUI** | **On-demand** (ne tourne que quand demandé) |
| **Objectif** | Conso idle **< 80 W** (vs ~100 W sans optimisation) |
> **Coût** : ~80-100 W idle → ~150-180 CHF/an (0,20 CHF/kWh). C'est le **prix de la dispo 24/7 de l'assistant vocal**. **Usure des GPU** assumée (conçus pour du gaming, pas du 24/7) — mitigée par le power limit.
>
> **Action** : **mesurer** la conso idle réelle (wattmètre + `nvidia-smi` + `turbostat`) → ajuster les power limits → re-mesurer (phase 0.5).
>
> **Pas de gaming** sur le PC IA : les jeux vont sur le **BC250** ou le **Laptop** (on-demand).
### 2.5 G7 = spare du G9
| Aspect | Détail |
|--------|--------|
| **Rôle** | Si le **G9** tombe → le G7 prend le relais (cœur famille) |
| **État** | **Non branché**, matériel prêt |
| **Préparation** | **Wipe DSM** (résout le problème de légalité XPEnology) + OS neuf (Ubuntu Server) + disque 256 Go |
| **Maintenance** | **Boot test trimestriel** (vérif que ça marche) |
| **Disques** | 256 Go SSD (OS + config ; les données viennent du **backup 3-2-1** en cas de sinistre) |
> Le G7 **n'est pas** un miroir en temps réel du G9. C'est une **machine de secours** : si le G9 est HS, on branche le G7, on **restaure depuis le 3-2-1**, et la famille repart. Le temps de restauration est le prix du spare (vs un RAID actif).
### 2.6 Topologie réseau (schéma)
```
INTERNET (Fibre Free 10 Gb)
│
┌───────▼──────────────────────────────┐
│ FREEBOX DELTA (.254) — routeur WAN │
│ ├─ passthrough (DHCP → J5040) │
│ ├─ Home Assistant — domotique │
│ └─ RAID5 (3×1To) — 2ᵉ copie │
└───┬──────────────────────────────────┘
│ 2× 2,5 Gb
┌───▼──────────────────────────────────┐
│ J5040 (.1) — EDGE (réseau + web) │
│ ├─ Firewall + DHCP + AdGuard │
│ ├─ Reverse proxy + Certs (LE) │
│ └─ Sites : dd/lola.folzocorp… │
└───┬───────────┬───────────┬──────────┬┘
│ │ │ │
┌───▼───┐ ┌────▼────┐ ┌───▼────┐ ┌───▼────────┐
│ G9 │ │ PC IA │ │ G7 │ │ 218play │
│ .25 │ │ .11 │ │ .30 │ │ .26 │
│ Cœur │ │ IA 24/7 │ │ SPARE │ │ Offsite │
│ famille│ │ (GPU) │ │ du G9 │ │ (3ᵉ copie) │
│ NVMe │ │ 2×3060 │ │ i5-7500T│ │ │
│ 2 To │ │ │ │ 15 Go │ │ │
│ +USB │ │ │ │ (non │ │ │
│ 20 To │ │ │ │ branché)│ │ │
└───────┘ └─────────┘ └────────┘ └────────────┘
│
├─ BC250 / Laptop — jeux (on-demand)
├─ Postes enfants (.15/.16/.17)
├─ Média salon (.51–.59)
├─ NVR Reolink ×3 (.41/.123/.191)
└─ 3-2-1 : G9 + PC IA → Freebox (quotidien) → 218play (hebdo)
```
---
## R3 — Configuration des disques
> **Statut** : ✅ **Validée** (02/10/2026), **révisée 10/10/2026** : le G9 est **mono-disque** (Samsung 990 Pro 2 To, seul disque interne, **pas de RAID**, **pas de LUKS** — Option 0, voir changelog).
| Machine | Disques | Config | Contenu |
|---------|---------|--------|---------|
| **G9** | NVMe 2 To (Samsung 990 Pro) — **seul disque interne** | **ext4 `/data` sur LVM (ni RAID, ni LUKS)** | **Données importantes** : photos, fichiers, notes, mots de passe, système — protégées par le **3-2-1** + spare G7 |
| **G9** | USB 15 To + USB 5,5 To | Capacité (pas de miroir) | **Média** (Jellyfin) — films/séries **re-téléchargeables** |
| **J5040** | 128 Go | OS + edge | ~25 Go utilisés (le 94% disparaît) |
| **PC IA** | NVMe 1,9 To | Capacité | **Modèles** (98 Go) + inférence + données IA |
| **G7** | 256 Go SSD | OS neuf | Spare (OS + config) |
| **Freebox** | RAID5 3×1 To = 2 To | RAID5 | **2ᵉ copie** (snapshot G9 + PC IA) |
| **218play** | 2× 2 To | Capacité | **3ᵉ copie** (offsite) |
### 3.1 Le split G9 (NVMe + USB)
| Support | Contenu | Raison |
|---------|---------|--------|
| **NVMe 2 To — `/data` (mono-disque)** | Photos, fichiers, notes, mots de passe, système | Données **importantes** ; protection = **3-2-1** (copies quotidiennes + offsite) + **spare G7** (pas de miroir local, décision 10/10/2026) |
| **USB 1 (15 To) + USB 2 (5,5 To)** | Films, séries (Jellyfin) | Média **re-téléchargeable** → pas besoin de miroir |
> **Contrainte** : les **données importantes** doivent tenir dans **2 To** (photos ~10-500 Go + fichiers + notes + système = OK). Le disque unique n'a **pas de miroir** : la **panne disque** est couverte par le **3-2-1** (restic → Freebox quotidien + 218play hebdo, restauration testée) et le **spare G7** ; la **panne boîtier** (feu, vol) aussi. D'où l'exigence absolue : **backup opérationnel AVANT import de données critiques** (Phase 3).
>
> **Nuance USB** : les disques externes sont moins fiables que du interne, mais le média est **re-téléchargeable** → une panne USB n'est pas catastrophique.
### 3.2 Les modèles IA restent sur le PC IA
Les **98 Go de modèles GGUF** + les modèles Ollama restent sur le **PC IA** (là où sont les GPU + le 1,9 To). Le **G7** (256 Go) ne les stocke **pas** — il n'est que le spare du G9.
---
## R4 — Stratégie de backup 3-2-1
> **Statut** : ✅ **Validée** (02/10/2026) — **données IA incluses** (nouveau vs v1)
**3-2-1** = **3** copies, sur **2** supports différents, dont **1** offsite.
| Copie | Support | Machine | Contenu | Fréquence |
|-------|---------|---------|---------|-----------|
| **1** (originale) | NVMe 2 To (G9) + NVMe 1,9 To (PC IA) | **G9** + **PC IA** | Données famille **et** données IA | continu |
| **2** (intermédiaire) | RAID5 (3×1 To) = 2 To | **Freebox** | Snapshot **G9 + PC IA** | quotidien |
| **3** (offsite) | 2× 2 To | **218play** | Copie des données critiques (photos, mots de passe, docs, données IA) | hebdo / on-demand |
> **État 10/10/2026 (copie 2, Phase 3)** : ✅ **en place** sur le G9 — restic 0.19.1
> + rclone 1.75.2, repo chiffré `Freebox/backup/g9` (Delta, 1.79 To), snapshot initial
> 4.18 GiB, **append-only prouvé** (`forget` refusé), **restauration testée** (sha256
> identique), cron 03:30/04:00/09:15 + alerte Matrix. Détail : `g9-migration/README.md`
> (§Phase 3) et `g9-migration/RESTORE-G9.md`. Le PC IA (copie 2, 2ᵉ machine) reste à
> brancher en Phase 5.
>
> **Phase 5 (planifié, non réalisé)** — renforcer la copie 2 et la rendre
> **indépendante du G9** : la Freebox Delta expose **FTP/SFTP/SMB en local** (port 21
> vérifié, **13366** en distant) mais **ne fait pas tourner restic**. Donc :
> 1. **VM sur la Freebox** faisant tourner **`rest-server --append-only`** (repo
> append-only côté serveur, plus fort que l'append-only imposé par le G9), exposé en
> **HTTPS (TLS, certificat interne)** avec **`htpasswd`** (utilisateurs séparés :
> `g9-backup` en écriture, `g9-restore` en lecture) ; **à valider** : l'**autostart**
> de la VM Freebox (non testé) et le passage du repo de `rclone:` vers `rest:`.
> 2. **`restic copy` G9 → Freebox → 218play** pour la copie 3 (offsite), avec un repo
> **append-only dédié** et des **identifiants `NoAccess` (suppression/renommage
> refusés)** côté SMB.
> 3. **Chroot/rootfs durci** sur le G9 pour isoler le job de backup du reste.
>
> Limitation assumée en attendant : l'append-only actuel est imposé par le **rclone
> local du G9** (`rclone serve restic --stdio --append-only`), pas par la Freebox.
### 4.1 Les données IA dans le 3-2-1
**Règle** : les **données utilisateur** sont sauvegardées ; les **modèles** (re-téléchargeables) ne le sont **pas**.
| Donnée IA | Où | Criticité | Sauvegardée ? |
|-----------|-----|-----------|---------------|
| **Qdrant** (vector DB / RAG) | PC IA | 🔴 **haute** | **Oui** |
| **n8n** (workflows) | G9 | haute | **Oui** |
| **Obsidian** (notes) | PC IA | 🔴 **haute** | **Oui** |
| **Dify / Effi** (config + data) | PC IA | moyenne | **Oui** |
| **Modèles GGUF** (98 Go) | PC IA | basse — **re-téléchargeables** | **Non** |
| **Ollama** (modèles) | PC IA | basse — **re-téléchargeables** | **Non** |
> **Contexte** : actuellement, **aucune donnée IA n'est sauvegardée**. C'est un point soulevé et intégré à la v2. Le périmètre exact (quels volumes/data) sera précisé **quand l'usage IA sera défini** (R6.3) — la **règle** est posée dès maintenant.
### 4.2 Prérequis
Le backup 3-2-1 ne peut être mis en place **qu'après** :
1. la résolution du **disque backup USB HS** (R7, risque #1),
2. l'installation du **G9** (NVMe mono-disque + USB),
3. la migration de l'**IA** sur le PC IA.
---
## R5 — Plan de (re)création
> **Statut** : 🟡 **Phasé, à affiner** — la Phase 0 est posée ; les phases suivantes seront précisées pendant le rebuild. **Structuré pour être utilisé PENDANT le rebuild.**
### 5.0 Règle de lecture
- Chaque phase a : **objectif**, **actions**, **dépendances**, **critères de succès vérifiables**.
- On démarre par les **fondations** (fiabilité/backup), on finit par les **optimisations**.
- **Statut par phase** : ⬜ à faire → 🟡 en cours → ✅ validé.
- **On ne passe à la phase N+1 qu'après validation de la phase N.**
> **Accès au xpenology (pour la migration)** : `ssh -p 222 Effi@192.168.1.25` (mot de passe, groupe `administrators` → `sudo`). Docker **hors PATH** : `/var/packages/ContainerManager/target/usr/bin/docker` (via `sudo`). **Majorité des briques stateful gérées via GUI** (seulement 12 `compose` dans `/volume1/docker/`) → **re-déclarer** les stateful en `compose` (pinning + labels Watchtower + backup) pendant la migration. Détail : snapshot `ETAT-DE-LART-2026-10-06.md` §1.
### 5.1 Phase 0 — Fondations (fiabilité + edge + cœur)
**Objectif** : un système qui ne repose plus sur du sable, avec l'edge et le cœur famille en place.
| # | Action | Dépendance | Critère de succès (vérifiable) |
|---|--------|------------|-------------------------------|
| 0.1 | **Sauvegarder + remplacer** le disque backup USB HS (Seagate 3,6 To, secteurs HS) | — | **3,2 To de données saines copiées** sur 218play + disque remplacé + backup testé (restauration OK) |
| 0.2 | Corriger la **thermique Freebox** (85°C) | — | Température **< 70°C** en charge |
| 0.3 | **Installer le J5040** (edge) + Freebox en **passthrough** | — | Firewall + DHCP + AdGuard + reverse proxy + certs fonctionnels ; Freebox en passthrough ; sites web accessibles |
| 0.3a | **Upgrader la RAM du J5040** (7,6 Go → **16 Go**) | — | `free -h` confirme **16 Go** ; nécessaire pour Matrix+Jitsi+\*arr (voir `DOSSIER-SOFTWARE.md` S2.4) |
| 0.4 | **Installer le G9** (cœur famille) + **/data NVMe** + USB | 0.3 | OS + `/data` (NVMe 2 To) montés + USB média montés ; IP .25 |
| 0.5 | **Migrer l'IA** (J5040 → PC IA) + **power limit** + **mesurer conso** | 0.3 | IA fonctionnelle sur le PC IA ; conso idle **mesurée** et < 80 W ; disque J5040 **< 30%** |
| 0.6 | **Mettre en place le 3-2-1** (G9 + PC IA → Freebox → 218play) | 0.1, 0.4, 0.5 | 3 copies en place + **restauration testée** (données IA incluses) |
| 0.7 | **Préparer le G7** (wipe DSM + OS neuf = spare) | 0.4 | G7 bootable (Ubuntu Server), test de boot OK, prêt à prendre le relais du G9 |
**Statut** : ⬜ à faire
**Dépendances** : aucune (c'est le départ)
> **Note** : 0.5 (migration IA) libère le disque du J5040 (94% → ~30%) → résout le risque « disque saturé ». 0.6 (3-2-1) est le **prérequis** de toute migration ultérieure.
>
> **🔴 Prérequis absolu** : le **backup 3-2-1 fonctionnel** (0.1 + 0.6, **restauration testée**) est la **condition de TOUTE migration** (xpenology → G9) **et** de toute **MAJ stateful**. L'**incident Nextcloud** (06/10/2026 — snapshot `ETAT-DE-LART-2026-10-06.md` §3) prouve que le risque « **MAJ stateful non supervisée** » est **réel** : Watchtower a mis à jour l'app + la DB sans laisser la migration de schéma se terminer → service HS.
>
> **🔴 Action corrective immédiate (sur le NAS actuel, AVANT la migration)** : **exclure les briques stateful de Watchtower** — **urgent** (NAS en production, sans exclusion) et **indépendant** de la migration xpenology → G9. Détail + procédure : `DOSSIER-SOFTWARE.md` S13.0 (Phase A.0) + S11.4.
### 5.2 Phase 1 — Cœur famille (dégafamisation)
**Objectif** : le G9 devient le **cœur famille** (dégafamisation).
| # | Action | Dépendance | Critère de succès |
|---|--------|------------|-------------------|
| 1.1 | Migrer **photos** (Synology → G9) | Phase 0 + **R6.1 tranché** | Photos accessibles, IA OK |
| 1.2 | Migrer **fichiers** (Nextcloud) | Phase 0 | Fichiers accessibles multi-appareils |
| 1.3 | Migrer **mots de passe** (Vaultwarden) | Phase 0 | Coffre accessible famille |
| 1.4 | Installer **média** (Jellyfin + \*arr) sur USB | Phase 0 | Films/séries accessibles |
| 1.5 | Installer **notes** | Phase 0 + **R6.2 tranché** | Notes accessibles |
| 1.6 | Configurer **DNS** (AdGuard) + sous-domaines | Phase 0 | Sous-domaines résolus en local |
| 1.7 | Installer **Gitea** (host canonique du vault — versioning) | Phase 0 | Gitea accessible, vault versionné |
**Statut** : ⬜ à faire
**Dépendances** : Phase 0 + décisions R6.1 / R6.2
### 5.3 Phase 2 — Finitions (dégafamisation complète)
**Objectif** : la famille n'utilise plus de GAFAM.
| # | Action | Dépendance | Critère de succès |
|---|--------|------------|-------------------|
| 2.1 | **Agenda + contacts** (CalDAV/CardDAV) | Phase 1 | Synchro tous téléphones |
| 2.2 | **Mail** (serveur local) | Phase 1 | Mail famille local |
| 2.3 | **Caméras** (vision IA) | Phase 1 | Vision IA locale |
| 2.4 | **Messagerie / vidéo** (alternatives ouvertes) | Phase 1 | Alternatives ouvertes |
| 2.5 | **Jeux** (BC250 / Laptop) | Phase 0 | Jeux accessibles |
| 2.6 | **Décommissionner xpenology** (G7 reprenant le rôle) | 1.1–1.5 validés | xpenology éteint, G7 prêt, G9 seul sur .25 |
**Statut** : ⬜ à faire
**Dépendances** : Phase 1
### 5.4 Vue d'ensemble des phases
```
Phase 0 (Fondations) ──► Phase 1 (Cœur famille G9) ──► Phase 2 (Finitions)
USB HS / Freebox / photos/fichiers/notes/ agenda/mail/
J5040 edge / G9 / média/DNS caméras/jeux /
IA → PC IA / 3-2-1 / décommissionner xpenology
G7 spare
```
---
## R6 — Décisions ouvertes
> **Statut** : 🟡 **Cadrées, non tranchées** — à étudier **au cas par cas**. Ce dossier fournit le **cadre de réflexion + critères d'évaluation**, pas la décision. **On ne tranche jamais à l'arrache.**
### 6.1 Décision ouverte — Photos
**Contexte** : 10 ans de photos sur Synology Photos, **fiable**, mais **enchaîné à XPEnology**. C'est LA décision qui justifie le NAS G9.
**Besoin** : la même **fiabilité** + **IA** + **partage famille**, **sans dépendance XPEnology**.
**Axes de réflexion** :
| Axe | Option A — "gère tout" | Option B — "stockage + accès" |
|-----|------------------------|-------------------------------|
| Exemple | Immich | Nextcloud (photos) |
| IA (reconnaissance, recherche) | intégrée | à ajouter |
| Partage famille | intégrée | à configurer |
| Migration des 10 ans | export + re-upload | rsync / sync |
| Complexité | plus élevée | plus simple |
| Souveraineté | forte | forte |
**Critères d'évaluation (à remplir lors de l'étude)** :
- [ ] Fiabilité sur le long terme (track record)
- [ ] Qualité de l'IA (reconnaissance personnes/lieux, recherche sémantique)
- [ ] Facilité de partage famille (liens, albums)
- [ ] Effort de migration des 10 ans de photos
- [ ] Consommation (CPU/RAM/disk) sur le G9 (16 Go)
- [ ] Maintenance (MAJ, backups)
- [ ] Sortie / export (rester libre)
**Volume à estimer** : ~10 ans de photos → **estimer le volume** (Go / nombre de fichiers) avant de choisir (doit tenir dans le **NVMe 2 To du G9**, espace `/data`).
### 6.2 Décision ouverte — Notes
**Contexte** : essai de **Joplin / Affine / Nextcloud Notes / Obsidian**… toujours pas fixé. Gros point de réflexion lié à la **culture de la donnée**.
**Besoin** : un outil de notes **local, portable, structuré, souverain**, aligné sur ma culture de la donnée (thèse CAS).
**Critères d'évaluation (à remplir lors de l'étude)** :
- [ ] **Local** : données sur le NAS (pas dans le cloud)
- [ ] **Multi-appareils** : synchro laptop / téléphone / tablette
- [ ] **Recherche** : plein texte + (idéalement) sémantique
- [ ] **Structure** : hiérarchie, liens, tags, graph
- [ ] **Format** : ouvert (Markdown) → pas de verrouillage
- [ ] **Export** : rester libre (exporter tout à tout moment)
- [ ] **Collaboration** : partage famille (si besoin)
- [ ] **Intégration** : avec le reste de la stack (RAG, assistant IA)
**Point de réflexion clé** : quelle est la **philosophie** de mes notes — "capture rapide" (type Joplin) vs "second cerveau structuré" (type Obsidian) vs "workspace collaboratif" (type Affine) ?
### 6.3 Décision ouverte — Usage IA (nouveau, v2)
**Contexte** : l'usage de l'IA **n'est pas encore défini**. La **capacité** est dimensionnée (R2 : G7 = CPU, PC IA = GPU), mais **les briques exactes** (outils) seront choisies **quand l'usage sera défini**.
**Ce qui est posé** :
- La **chaîne** : **PC IA** = inférence GPU (LLM, STT, images) ; l'orchestration/RAG tourne aussi sur le PC IA (24/7).
- Les **données IA** (Qdrant, n8n, Obsidian, Dify) sont **sauvegardées** (R4.1).
- Le **périmètre de backup** des données IA sera précisé ici, à la définition de l'usage.
**Critères d'évaluation (à remplir lors de l'étude)** :
- [ ] Quel(s) usage(s) concret(s) ? (assistant vocal, RAG, génération, automatisation…)
- [ ] Quels outils servent cet usage ? (au cas par cas, par usage pas par techno)
- [ ] Quelles données IA sont critiques (→ périmètre backup) ?
- [ ] Charge GPU vs CPU (24/7 vs on-demand)
### 6.4 Règle pour les décisions ouvertes
> Chaque décision ouverte a une **liste de critères d'évaluation prêts à l'emploi** (6.1, 6.2, 6.3). Lors de l'étude ultérieure, on **remplit la grille**, on **note le choix**, et on **met à jour ce dossier** (R5 + R6). **On ne tranche jamais à l'arrache.**
---
## R7 — Risques & priorités
> **Statut** : 🟡 **Cadre fourni, ordre à confirmer** — la matrice est mise à jour pour la v2 ; l'ordre final sera fixé après l'étude des décisions ouvertes (R6).
### 7.1 Cadre d'évaluation des risques
**Formule** : `Risque = Probabilité (1-5) × Impact (1-5)` → score 1-25.
| Score | Niveau | Action |
|-------|--------|--------|
| 15-25 | 🔴 Critique | Traiter **en premier** (fondations) |
| 8-14 | 🟠 Élevé | Traiter tôt |
| 4-7 | 🟡 Moyen | Traiter au fil de l'eau |
| 1-3 | 🟢 Faible | Surveiller |
### 7.2 Risques actuels connus (v2)
| # | Risque | Machine | Prob. | Impact | Score | Niveau | Lien vision |
|---|--------|---------|-------|--------|-------|--------|-------------|
| 1 | **Pas de backup** (USB HS — secteurs HS détectés 01/10 ; **confirmé 06/10** : HyperBackup **données** en échec depuis **01/10/2024** — 108 erreurs, dernière entrée 03/11/2024, destination USB3 3,6 To absente, `status_dest_file_not_exist` ; pas de restic/borg) | xpenology | 4 | 5 | **20** | 🔴 | Pilier 5 — sans backup de **données**, tout repose sur du sable. **Prérequis absolu de toute migration**. Voir snapshot `ETAT-DE-LART-2026-10-06.md` §2 / §4 |
| 2 | **Freebox 85°C** | Freebox | 3 | 4 | **12** | 🟠 | Pilier 5 — usure du routeur = tout le réseau |
| 3 | **Disque J5040 94%** | J5040 | 4 | 3 | **12** | 🟠 | Pilier 5 — **résolu par la migration IA** (phase 0.5) |
| 4 | **XPEnology (légalité + MAJ)** | G7 | 3 | 4 | **12** | 🟠 | Pilier 1, 5 — moteur de la migration ; **résolu par wipe DSM** (phase 0.7) |
| 5 | **J5040 = point de défaillance unique** du réseau | J5040 | 3 | 4 | **12** | 🟠 | Pilier 5 — mitigé par watchdog + fallback DHCP Freebox |
| 6 | **Données IA non sauvegardées** | PC IA | 4 | 4 | **16** | 🔴 | Pilier 5 — **résolu par le 3-2-1** (phase 0.6) |
| 7 | **Usure GPU 24/7** (PC IA) | PC IA | 3 | 3 | **9** | 🟠 | Pilier 5 — mitigé par power limit (phase 0.5) |
| 8 | **G7 spare non testé** | G7 | 3 | 3 | **9** | 🟠 | Pilier 5 — mitigé par test trimestriel (phase 0.7) |
| 9 | **18 modèles legacy (98 Go)** | PC IA | 2 | 2 | **4** | 🟡 | Pilier 3 — nettoyage |
| 10 | **Docs obsolètes (GPU/RAM)** | — | 2 | 2 | **4** | 🟡 | Pilier 4 — culture de la donnée |
### 7.3 Principe « fondations d'abord »
> **On ne construit pas sur du sable.** Avant toute migration ou nouveau service, les **fondations** doivent être solides :
> 1. **Backup 3-2-1** fonctionnel (risque #1, #6)
> 2. **Freebox thermique** corrigée (risque #2)
> 3. **Disque J5040** allégé (risque #3, via migration IA)
>
> Sans ces 3, tout le reste (G9, dégafamisation) repose sur un système fragile.
### 7.4 Ordre de priorité
> **Cadre de décision** : on traite d'abord les 🔴 (score 15+ : #1, #6), puis les 🟠 (8-14), en respectant le principe « fondations d'abord » (7.3). L'ordre final sera confirmé après l'étude des décisions ouvertes (R6).
---
## R8 — Montée en compétence
> **Statut** : ✅ Cadré — l'infra comme terrain d'apprentissage et portfolio.
### 8.1 L'infra comme terrain d'apprentissage
| Compétence | Service / action | Phase |
|------------|------------------|-------|
| Réseau / firewall | nftables + passthrough | 0 |
| DNS / DHCP | AdGuard + dnsmasq/Kea | 0 |
| Reverse proxy + TLS | Traefik/Nginx + Certbot | 0 |
| Stockage | NVMe `/data` + montages USB | 0 |
| Docker | Installation + conteneurs | 0, 1 |
| Backup | 3-2-1 (données IA incluses) | 0 |
| Sécurité | firewall, VPN, mots de passe | 0, 2 |
| IA | LLM, RAG, STT/TTS, power limit | 0 |
| Domotique | Home Assistant | (sur Freebox) |
| Supervision | Beszel + watchdog | 0 |
### 8.2 Principe « une seule source de vérité »
> **Ce dossier + les docs projet** = la source de vérité unique. Plus de dispersion entre `effi/README.md`, mémoires Hermes, docs ad-hoc.
>
> **Règle** : noter la **réalité mesurée** (scan live) plutôt que la documentation héritée. Après chaque évolution majeure → **mettre à jour ce dossier**.
### 8.3 Lien avec le CAS (IA & transformation digitale)
> L'infrastructure est le **portfolio réel** du CAS :
> - **Culture de la donnée** (thèse) → appliquée dans la décision "notes" (R6.2) + la source de vérité (8.2) + le **3-2-1 des données IA** (R4).
> - **IA & transformation digitale** → appliquée dans la chaîne IA (PC IA 24/7).
> - **Méthode** → état de l'art → besoins → architecture → plan (ce dossier).
---
## R9 — Qualité & traçabilité
> **Statut** : ✅ En cours d'application
### 9.1 Cohérence interne (valeurs uniques)
| Point | Valeur unique (pas de contradiction) |
|-------|--------------------------------------|
| IP du G9 | **.25** (partout) |
| IP du J5040 (edge) | **.1** (partout) |
| IP du PC IA | **.11** (partout) |
| IP du G7 (spare) | **.30** (partout) |
| Rôle G9 | **cœur famille** (NVMe mono-disque + USB) |
| Rôle J5040 | **edge** (firewall + DHCP + DNS + proxy + web) |
| Rôle PC IA | **IA 24/7** (pas de gaming) |
| Rôle G7 | **spare du G9** (non branché) |
| GPU PC IA | **2× RTX 3060 12 Go** |
| CPU du G9 | **i3-13100T** |
| CPU du G7 | **i5-7500T** |
| CPU du J5040 | **Pentium Silver J5040** |
### 9.2 Structure & lisibilité
- **Français**, hiérarchie claire (H1/H2/H3), tableaux.
- Sommaire avec ancres.
- Chaque section porte son **statut** (✅ / 🟡 / ⬜).
### 9.3 Document vivant
- **Versionné** : les documents vivants (ce dossier) ont un **nom sans date** ; les dates sont dans le **changelog**. Les **snapshots** (état de l'art) sont **datés et immuables**.
- **Statut par section** : ✅ validé / 🟡 en cours / ⬜ à faire.
- **Traçabilité** : besoin → décision → action → machine (Annexe A).
---
## Annexe A — Matrice de traçabilité
| Besoin | Pilier (R1) | Décision (R6) | Action (R5) | Machine (R2) |
|--------|-------------|---------------|-------------|--------------|
| **Photos** | 1, 2 | **R6.1 (ouvert)** | 1.1 | G9 .25 |
| **Fichiers** | 1, 2 | — | 1.2 | G9 .25 |
| **Notes** | 1, 2, 4 | **R6.2 (ouvert)** | 1.5 | G9 .25 |
| **Mots de passe** | 1 | — | 1.3 | G9 .25 |
| **Films/Séries** | 2 | — | 1.4 | G9 .25 (USB) |
| **Musique** | 2 | — | 1.4 | G9 .25 (USB) |
| **Agenda/Contacts** | 1 | — | 2.1 | G9 .25 |
| **Mail** | 1 | — | 2.2 | G9 .25 |
| **Recherche web** | 1 | — | (edge) | J5040 .1 |
| **Anti-ads / DNS** | 2 | — | 0.3 | J5040 .1 |
| **Domotique** | 2 | — | (sur Freebox) | Freebox .254 |
| **Caméras (vision IA)** | 2 | — | 2.3 | G9 .25 / PC IA |
| **Messagerie/vidéo** | 1 | — | 2.4 | G9 .25 |
| **Jeux** | 2 | — | 2.5 | BC250 / Laptop |
| **IA personnelle** | 2, 5 | **R6.3 (ouvert)** | 0.5 | PC IA .11 |
| **Automatisation (n8n)** | 2, 5 | — | 0.5 | G9 .25 |
| **Versioning (Gitea)** | 4, 5 | — | 1.7 | G9 .25 |
| **Sites web** | 5 | — | 0.3 | J5040 .1 |
| **Fiabilité (global)** | 5 | — | 0.1–0.7 | toutes |
> **Lecture** : chaque besoin a un pilier, une décision (si ouverte), une action, et une machine. **Pas d'orphelin.**
---
## Annexe B — Glossaire & abréviations
| Terme | Définition |
|-------|------------|
| **GAFAM** | Google, Apple, Facebook (Meta), Amazon, Microsoft |
| **Dégafamisation** | Remplacer les services GAFAM par des équivalents locaux |
| **3-2-1** | 3 copies, 2 supports, 1 offsite (stratégie de backup) |
| **Edge** | Point d'entrée internet unique (firewall + proxy + web) |
| **Passthrough** | La Freebox laisse le DHCP/DNS au J5040 (mode bridge) |
| **WoL** | Wake-on-LAN (allumer une machine à distance) |
| **NAS** | Network Attached Storage |
| **NVR** | Network Video Recorder |
| **XPEnology** | DSM (Synology) installé sur hardware non officiel |
| **RAG** | Retrieval-Augmented Generation |
| **LLM** | Large Language Model |
| **STT / TTS** | Speech-To-Text / Text-To-Speech |
| **\*arr** | Stack de gestion média (Sonarr, Radarr, Prowlarr) |
| **CAS** | Certificat d'Aptitude Spéciale (IA & transformation digitale) |
| **J5040** | Serveur edge (ex-effi-server, Pentium Silver J5040) |
| **G9** | HP Pro Mini 400 G9 (i3-13100T) — cœur famille |
| **G7** | Ex-xpenology (i5-7500T) — spare du G9 |
| **PC IA** | Ex-folzo-corp (5800X3D + 2× RTX 3060) — IA 24/7 |
| **Spare** | Machine de secours, non branchée, restaurée depuis le backup en cas de sinistre |
---
## Changelog
| Date | Version | Changement |
|------|---------|------------|
| 10/10/2026 | **v2.3** | **R4 — copie 2 (Freebox) EN PLACE** : backup restic du G9 opérationnel (4.18 GiB, append-only prouvé, restauration testée, cron + alerte Matrix) → **prérequis de migration (G2) satisfait pour le G9** ; le PC IA reste à brancher. **Phase 5 planifiée** (non réalisée) : VM Freebox `rest-server --append-only` (autostart à valider, la Freebox ne fait pas tourner restic), `restic copy` G9 → Freebox → 218play, `NoAccess` SMB, chroot durci. Détail : `g9-migration/README.md` §Phase 3. |
| 10/10/2026 | **v2.3** | **G9 mono-disque (décision utilisateur, Phase 2)** : le G9 ne recevra **qu'un seul disque** (Samsung 990 Pro 2 To) → **RAID1 abandonné**, **LUKS abandonné sur le G9** (données familiales non critiques, protection contre la fuite = ufw LAN-only + edge, protection des copies = restic chiffré). La tolérance panne repose sur le **3-2-1** (Phase 3/5, restauration testée) + **spare G7** (Phase 7). R1/R3/3.1/3.2/R5/R8 + plan 0.4 alignés. Exécution : `g9-migration/scripts/02-data.sh` (non destructif). |
| 06/10/2026 | **v2.2** | **Cohérence inter-dossiers (source unique)** : (1) **n8n = G9** (aligné sur `DOSSIER-SOFTWARE.md` S2.2/S3.1) — R4.1 (tableau données IA) + R8 (tableau besoins) : « PC IA » → « G9 » ; (2) **R5.1** : le bloc « action corrective immédiate (exclure les stateful de Watchtower) » ne **re-déclare plus** la procédure → **pointeur** vers `DOSSIER-SOFTWARE.md` S13.0 (Phase A.0) + S11.4 (source unique). |
| 06/10/2026 | **v2.1** | **Renforcement backup + Watchtower** (après l'**incident Nextcloud** du 06/10, snapshot `ETAT-DE-LART-2026-10-06.md` §3) : **R7 risque #1** confirmé (HyperBackup **données** en échec depuis **01/10/2024** — 108 erreurs, dernière entrée 03/11/2024, destination USB3 absente, pas de restic/borg) ; **R5** : prérequis absolu « backup 3-2-1 fonctionnel AVANT toute migration / MAJ stateful » + **action corrective immédiate** (exclure les stateful de Watchtower sur le NAS actuel) + **accès SSH** au xpenology (`Effi@192.168.1.25:222`, Docker hors PATH, stateful en GUI → re-déclarer en compose). Lien `DOSSIER-SOFTWARE.md` S11.4/S11.5 + S13 A.0. |
| 30/09/2026 | v1.0 | Création du dossier v1 (`DOSSIER-INFRA.md`) — vision, état de l'art, besoins, architecture, décisions ouvertes, risques, plan. |
| 30/09/2026 | v1.1 | (dossier v1) Corrections, clarification scope, risque backup Freebox. |
| 02/10/2026 | **v2.0** | **Nouveau dossier (`DOSSIER-RECREATION.md`)** — architecture cible **redistribuée** : J5040 = edge, G9 = cœur famille (RAID1 + USB), PC IA = IA 24/7, G7 = spare (réutilisé, pas décommissionné), jeux sur BC250/Laptop. **Données IA intégrées au 3-2-1**. Plan Phase 0 (0.1–0.7). Supplante `DOSSIER-INFRA.md`. |
+876
View File
@@ -0,0 +1,876 @@
# Dossier — Stack logicielle de la maison (Partie 5)
**Date** : 06/10/2026
**Auteur** : Jean-Henry Foltzenlogel (Folzo)
**Réseau** : `192.168.1.0/24`
**Statut global** : 🟡 **Conception validée (table rase)** — architecture logicielle ciblée, décisions tranchées, plan de migration posé. **Aucune implémentation** : ce dossier est le **cahier des charges logiciel** qui précède le rebuild.
**Document** : **vivant** — versionné, mis à jour à chaque évolution. Chaque section porte son statut (✅ / 🟡 / ⬜).
> **Nature du document** : c'est le **dossier Partie 5 — couche logicielle** de l'infrastructure. Il fait suite à la **table rase** demandée : on repart de zéro, **sans biais** sur les outils existants, pour choisir la stack qui tient **aujourd'hui ET dans le futur**, qui passe en **production** (plus de R&D), qui est **pérenne**, **100% open source** (licence notée par service), **consolidée** (pas 50 apps — un tout cohérent type Nextcloud), et **résiliente par l'usage** (chaque brique a un utilisateur nommé + un appareil + un usage quotidien).
>
> **Position dans le projet** : `DOSSIER-RECREATION.md` (v2) décrit la **machine** (rôles, disques, 3-2-1, phases). Ce dossier décrit le **logiciel** qui tourne dessus : quelle brique, où, pourquoi, et comment on passe du chaos R&D actuel à un système de production. En cas de conflit sur les **faits matériels**, c'est le **snapshot** `ETAT-DE-LART-2026-09-30.md` qui fait foi ; pour l'**architecture machine**, c'est `DOSSIER-RECREATION.md` ; pour le **logiciel** (ce document), c'est **lui** qui fait foi.
>
> **Règle d'or de la table rase** : chaque brique est **justifiée** (maturité, maintenance, ressource sur la machine cible, licence OSS, **chemin de sortie/export**). Si une brique ne sert **personne au quotidien**, elle ne rentre pas. La **résilience = l'usage** : un service que personne n'utilise est un coût, pas un asset.
---
## Sommaire
- [S1 — Objectif & principes de la table rase](#s1--objectif--principes-de-la-table-rase)
- [S2 — Architecture cible par machine](#s2--architecture-cible-par-machine)
- [S3 — Stack consolidée, brique par brique](#s3--stack-consolidée-brique-par-brique)
- [S4 — Bloc IA (le cœur du futur)](#s4--bloc-ia-le-cœur-du-futur)
- [S5 — Photos (PhotoPrism)](#s5--photos-photoprism)
- [S6 — Fichiers & productivité (Nextcloud)](#s6--fichiers--productivité-nextcloud)
- [S7 — Média (Jellyfin + \*arr + podcasts)](#s7--média-jellyfin--arr--podcasts)
- [S8 — Communication (Matrix + Jitsi + assistant vocal)](#s8--communication-matrix--jitsi--assistant-vocal)
- [S9 — Sécurité & accès distant](#s9--sécurité--accès-distant)
- [S10 — Backup 3-2-1 (restic/borg + LUKS)](#s10--backup-3-2-1-resticborg--luks)
- [S11 — Ops (Compose + Gitea + Watchtower + Beszel)](#s11--ops-compose--gitea--watchtower--beszel)
- [S12 — Enfants & MDM](#s12--enfants--mdm)
- [S13 — Plan de migration R&D → production](#s13--plan-de-migration-rd--production)
- [S14 — Grille des décisions + recommandations](#s14--grille-des-décisions--recommandations)
- [S15 — Critères d'acceptance (AC1–AC10)](#s15--critères-dacceptance-ac1ac10)
- [Changelog](#changelog)
---
## S1 — Objectif & principes de la table rase
> **Statut** : ✅ Validé (05/10/2026)
### 1.1 Pourquoi une table rase
L'infra actuelle est née **empiriquement** : 34 conteneurs sur le J5040, ~30 sur le xpenology, 9 sur le PC IA, des briques IA dupliquées (2× STT, 2× OCR, 2× OpenWebUI, Dify + RAGFlow + Qdrant + mcpo + pipelines…), des conteneurs morts, et des services qui ne servent **plus personne**. C'est du **R&D** : on a tout essayé, rien n'a été **tranché** ni **fiabilisé**.
La table rase, c'est **arrêter de collectionner** et **choisir**. Le critère n'est plus « est-ce que ça marche ? » mais **« est-ce que ça sert quelqu'un, tous les jours, et est-ce que je peux le réparer en 5 min ? »**.
### 1.2 Les 6 principes (le filtre de chaque brique)
| # | Principe | Signification concrète |
|---|----------|------------------------|
| 1 | **Table rase** | On ne part pas de « ce qui est installé ». On part du **besoin**, et on choisit la brique qui le sert. L'existant n'est qu'un **candidat**, pas une contrainte. |
| 2 | **Pérenne / fiable** | On choisit des briques **matures**, **maintenues**, **stables** — pas des toys de démo. Le système doit tourner **sans intervention**. |
| 3 | **100% open source** | Chaque brique est OSS, **licence notée** (S3). Pas de zone grise (XPEnology éliminé), pas de SaaS GAFAM. |
| 4 | **Consolidé** | **Un besoin = une brique = une machine.** On vise **≤ ~20 briques** au total, pas 50 apps. Un tout cohérent (Nextcloud comme socle famille), pas un cimetière de démos. |
| 5 | **Résilience = usage** | Chaque brique a **un utilisateur nommé + un appareil + un usage quotidien** (S8.2 / S15 AC8). Pas de brique orpheline. |
| 6 | **Reversibilité** | Chaque brique a un **chemin de sortie/export** documenté (S3). On ne se verrouille pas : on peut partir à tout moment. |
### 1.3 Ce que la table rase change vs l'existant
| Aspect | Avant (R&D) | Après (production) |
|--------|-------------|--------------------|
| **Nombre de briques** | ~70 conteneurs (34+30+9) | **≤ ~20 briques** cohérentes |
| **Duplication** | 2× STT, 2× OCR, 2× OpenWebUI, 2× n8n, Dify+RAGFlow | **1 seul** par fonction |
| **Critère de garde** | « ça marche » | « ça sert quelqu'un, tous les jours » |
| **Licence** | inconnue / mixte | **notée par brique** (OSS) |
| **Sortie** | aucune | **chemin d'export** documenté |
| **Ops** | ad hoc | **Compose versionné + Watchtower + Beszel** |
---
## S2 — Architecture cible par machine
> **Statut** : ✅ **Validée (Option A)** (05/10/2026) — hérite de `DOSSIER-RECREATION.md` §R2, **affinée** par la redistribution logicielle.
### 2.1 Le choix clé : le PC IA garde toute la RAM
Le **Qwen 3.8max** (le LLM principal) consomme **~20 Go VRAM + ~50 Go RAM**. Le PC IA a **62 Go RAM** : c'est la **seule** machine qui peut le porter. **Conséquence** : le PC IA est **saturé par l'IA** → on **n'y met rien d'autre de lourd**. Cela déplace **Matrix + Jitsi** (comms, qui demandent de la RAM) sur le **J5040 (edge)**, et **Home Assistant** reste sur la **Freebox** (VM légère, déjà en place).
### 2.2 Rôle logiciel de chaque machine (Option A)
| Machine | IP | RAM | **Rôle logiciel** | Briques logicielles |
|---------|-----|-----|-------------------|---------------------|
| **Freebox Delta** | .254 | — | **WAN + Maison + Backup** | **Home Assistant** (enceintes Google ×4-5) · **Frigate** (Reolink, détection) · 2ᵉ copie backup (RAID5 3×1To) |
| **J5040** | .1 | **16 Go (upgradé)** | **Edge + Comms + Downloads** | **Firewall** · **DHCP** · **AdGuardHome** · **SearXNG** (recherche web) · **Caddy** (TLS auto) · **WireGuard** · **Web Station** · **Beszel** · **Matrix (Synapse) + Jitsi** · **Prowlarr/Sonarr/Radarr + qBittorrent** |
| **G9** | .25 | 16 Go (fixe) | **Cœur famille** | **Nextcloud** (+Collabora Office, Deck, Notes, Groupware CalDAV/CardDAV, Mail IMAP) · **PhotoPrism** · **Jellyfin** (vidéo+musique) · **Vaultwarden** · **Gitea** · **n8n** · **Valheim** · SMB |
| **PC IA** | .11 | **62 Go** | **IA (max RAM)** | **Ollama** (Qwen 3.8max) · **Qdrant** · **hermes** · **OpenWebUI** (famille, 1 seul) |
| **G7** | .30 | 15 Go | **Spare / débordement** | (non branché — secours G9, ou débordement Matrix/Jitsi en pic) |
| **218play** | .26 | — | **3ᵉ copie offsite** | (backup) |
### 2.3 Pourquoi cette répartition
| Décision | Raison |
|----------|--------|
| **Matrix + Jitsi sur J5040 (16 Go)** | Le PC IA est saturé par le LLM (62 Go). Matrix (Synapse) + Jitsi (appels) demandent de la RAM → ils vont sur l'edge, qui a de la marge après la libération de l'IA. |
| **Home Assistant sur Freebox** | VM déjà en place, légère, et c'est le **routeur** (proximité domotique). Frigate (vision caméras) y tourne aussi — léger, pas de GPU requis pour la détection de base. |
| **\*arr + qBittorrent sur J5040** | Les téléchargements sont de l'**edge** (proximité internet). Libère le G9 (cœur famille) et son disque RAID1 (données importantes, pas du média brut). |
| **Nextcloud + PhotoPrism + Jellyfin sur G9** | Le **cœur famille** = là où sont les **données importantes** (RAID1). Les apps qui les servent sont à côté des données (pas de traversée réseau). |
| **n8n sur G9** | L'automatisation touche le **cœur famille** (photos, fichiers, agenda). Un seul n8n (le 2ᵉ est supprimé). |
| **Tout le bloc IA sur PC IA** | Ollama + Qdrant + hermes + OpenWebUI = là où sont les **GPU + 62 Go**. Un seul endroit, un seul point de maintenance. |
### 2.4 Budget ressource par machine (AC4)
| Machine | RAM | Charge estimée | Marge | Verdict |
|---------|-----|----------------|-------|---------|
| **J5040** | 16 Go | edge (~2 Go) + SearXNG (~200 Mo) + Matrix+Jitsi (~3-4 Go) + \*arr+qBittorrent (~1-2 Go) + Beszel/Caddy/AdGuard (~1 Go) ≈ **7-9 Go** | ~7 Go | ✅ tenable (G7 = débordement en pic) |
| **G9** | 16 Go | Nextcloud+Collabora (~3-4 Go) + PhotoPrism (~2-3 Go) + Jellyfin (~1-2 Go) + n8n (~1 Go) + Gitea/Vaultwarden (~1 Go) ≈ **8-11 Go** | ~5-8 Go | ✅ tenable (surveiller Collabora en pic) |
| **PC IA** | 62 Go | Ollama Qwen 3.8max (~50 Go) + Qdrant (~1-2 Go) + hermes (~1 Go) + OpenWebUI (~1 Go) ≈ **53 Go** | ~9 Go | ✅ tenable (le LLM domine, c'est assumé) |
| **Freebox** | — | HA + Frigate (légers, VM) | — | ✅ léger (pas de GPU requis) |
> **Point de vigilance unique** : le **J5040** avec Matrix+Jitsi **en pic** (appels vidéo + téléchargements). Le **G7 (15 Go)** est le **débordement** documenté : si le J5040 sature, on y déplace Matrix/Jitsi. C'est le seul vrai risque RAM de l'architecture.
---
## S3 — Stack consolidée, brique par brique
> **Statut** : ✅ **Validée** (05/10/2026) — **~20 briques** sur 4 machines. Chaque brique est justifiée (maturité, maintenance, ressource, **licence OSS**, **chemin de sortie**). C'est la réponse aux **AC2, AC3, AC10**.
### 3.1 La stack complète (une brique = un besoin = une machine)
| # | Brique | Besoin servi | Machine | Licence OSS | Maturité / maintenance | Chemin de sortie / export |
|---|--------|--------------|---------|-------------|------------------------|---------------------------|
| 1 | **Firewall (nftables)** | Protection LAN/WAN | J5040 | GPL-2.0 | Système (noyau Linux), très stable | Config = fichiers, exportable |
| 2 | **DHCP (dnsmasq)** | Attributions IP | J5040 | GPL-2.0 | Système, très stable | Config exportable |
| 3 | **AdGuard Home** | DNS + filtre pubs + **parents** | J5040 | MIT | Très mature, maintenu | Config + listes exportables |
| 4 | **Caddy** | Reverse proxy + **TLS auto** (Let's Encrypt) | J5040 | Apache-2.0 | Très mature (Caddy 2), maintenu | `Caddyfile` = config, exportable |
| 5 | **WireGuard** | Accès distant chiffré | J5040 | GPL-2.0 | Système (noyau), très stable | Clés exportables |
| 6 | **Web Station** | Sites web (dd/lola.folzocorp…) | J5040 | — (contenu) | Statique | Fichiers, exportables |
| 7 | **Beszel** | Monitoring léger + alertes | J5040 | MIT | Mature, maintenu (henrygd) | Base SQLite, exportable |
| 8 | **Matrix (Synapse)** | **Messagerie** (remplace WhatsApp) | J5040 | Apache-2.0 | Très mature (Element/Matrix), maintenu | Export des salons (JSON), comptes |
| 9 | **Jitsi** | **Visioconférence** | J5040 | Apache-2.0 | Mature (Jitsi Meet), maintenu | N/A (appels éphémères) |
| 10 | **\*arr** (Prowlarr + Sonarr + Radarr) — **1 stack** | Gestion **séries/films** (indexers + queue) | J5040 | GPL-3.0 | Très mature (la stack \*arr), maintenu | Bases SQLite exportables, config |
| 11 | **qBittorrent** | **Téléchargement** (torrent/HTTP) | J5040 | GPL-2.0 | Très mature, maintenu | File de tâches exportable |
| 12 | **Nextcloud** | **Fichiers** + socle famille (Drive, partages) | G9 | AGPL-3.0 | Très mature, maintenu | Export complet (fichiers = standard) |
| 13 | ↳ **Collabora Office** (app NC #12) | **Bureautique** (remplace Office/Docs) | G9 | MPL-2.0 (code) | Mature (LibreOffice base), maintenu | Fichiers **ODF** (standard ouvert) |
| 14 | ↳ **Nextcloud Groupware** (app NC #12) | **Agenda + contacts** (CalDAV/CardDAV) | G9 | AGPL-3.0 | Mature, maintenu | **ICS / VCF** (standards ouverts) |
| 15 | ↳ **Nextcloud Deck + Notes** (apps NC #12) | **Tâches + notes rapides** famille | G9 | AGPL-3.0 | Mature, maintenu | Export Markdown / JSON |
| 16 | **PhotoPrism** | **Photos** (timeline, visages, albums, partage, recherche sémantique) | G9 | **AGPL-3.0** (core) | Mature, maintenu | **Lecture in-place** des originaux (pas de verrouillage) |
| 17 | **Jellyfin** | **Vidéo + musique** (streaming famille) | G9 | AGPL-3.0 | Très mature, maintenu | N/A (lecture des fichiers sources) |
| 18 | **Vaultwarden** | **Mots de passe** (coffre famille) | G9 | AGPL-3.0 | Très mature, maintenu | **Export chiffré** (standard Bitwarden) |
| 19 | **Gitea** | **Git** (vault second cerveau + versioning) | G9 | MIT | Très mature, maintenu | **Repos git** (exportables) |
| 20 | **n8n** | **Automatisation** (workflows) | G9 | ⚠️ **Sustainable Use License (fair-code, PAS OSI)** | Très mature, maintenu | Workflows exportables (JSON) |
| 21 | **Valheim** | **Jeu** (monde famille) | G9 | — (jeu) | Stable | Sauvegardes de monde |
| 22 | **Ollama** | **Backend LLM** (Qwen 3.8max) | PC IA | MIT | Très mature, maintenu | Modèles = fichiers (re-téléchargeables) |
| 23 | **Qdrant** | **Vector DB** (RAG / second cerveau) | PC IA | Apache-2.0 | Très mature, maintenu | Collections exportables (JSON) |
| 24 | **hermes** | **Harnais agent** (Effi — mémoire, MCP, knowledge) | PC IA | **MIT** | Communautaire, maintenu (nesquena — `ghcr.io/nesquena/hermes-webui`) | Mémoire = fichiers, exportable |
| 25 | **OpenWebUI** | **Front LLM simple famille** (1 seul) | PC IA | BSD-3 | Très mature, maintenu | N/A (interface) |
| 26 | **Home Assistant** | **Domotique** (enceintes Google, maison) | Freebox | Apache-2.0 | Très mature, maintenu | Config YAML exportable |
| 27 | **Frigate** | **Vision caméras** (Reolink, détection) | Freebox | AGPL-3.0 | Mature, maintenu | Enregistrements = fichiers vidéo |
| 28 | **SearXNG** | **Recherche web** (meta-moteur privé, dégooglisation) | J5040 | AGPL-3.0 | Très mature, maintenu | Config + listes de blocage exportables |
> **Comptage** : **28 lignes** = **25 briques fonctionnelles** (les 3 apps Nextcloud ↳ 13-15 sont des **apps du socle NC #12**, pas des briques séparées ; la stack \*arr #10 = **1 brique**). On reste dans la cible de consolidation **≤ ~20 briques** (S1.2, vs ~70 conteneurs / 50 apps) — c'est le **maximum** justifié, chaque brique ayant un **utilisateur nommé** (S8.2).
>
> **⚠️ Note honnêteté licence (AC10)** : **n8n** n'est **PAS** open source au sens **OSI** — il est sous **Sustainable Use License** (fair-code, source disponible, usage libre, mais restrictions d'usage). C'est le **seul** écart au « 100% OSI ». **Alternative OSI** si on veut du 100% pur : **Activepieces** (MIT) ou **n8n → remplacement progressif**. Décision : on **garde n8n** (maturité + écosystème + usage interne non-commercial = licence confortable) mais c'est **noté** pour transparence.
### 3.2 La liste de suppression explicite (le ballast R&D)
> **AC2** : tout ce qui n'est **pas** dans le tableau 3.1 est **supprimé** du rebuild. C'est la table rase concrète.
| Brique supprimée | Pourquoi |
|------------------|----------|
| **Dify** (14 conteneurs) | Plateforme agents **R&D** — remplacée par **hermes + Ollama + Qdrant** (plus sobre, plus pérenne). |
| **RAGFlow** (crash loop) | RAG **dupliqué** — remplacé par **Qdrant** direct (plus simple, stable). |
| **STT dupliqué** (effi-stt + whisper) | **2×** la même chose → **1 seul** (faster-whisper, sur PC IA, on-demand). |
| **OCR dupliqué** (effi-ocr + kreuzberg) | **2×** → **1 seul** (kreuzberg/PaddleOCR FR, sur PC IA, on-demand). |
| **OpenWebUI multiples** (2×) | **1 seul** (sur PC IA, front famille). |
| **llm-proxy (LiteLLM)** | Remplacé par **Ollama** direct (point d'entrée unique). |
| **mcpo + pipelines** (OpenWebUI) | Remplacés par **hermes** (harnais agent avec MCP natif). |
| **ComfyUI** (port mort) | **On-demand** uniquement, pas 24/7 — réactivé seulement si usage image. |
| **Pterodactyl** (panel + DB + cache) | Remplacé par **Valheim direct** (3 conteneurs → 1). |
| **CouchDB + Obsidian (conteneur)** | Obsidian = **app locale** (pas un conteneur serveur) ; le vault = **Gitea**. |
| **Leantime** | Gestion de projet **R&D** — remplacé par **Nextcloud Deck** (tâques famille). |
| **Affine** | Workspace **R&D** — remplacé par **Obsidian + Gitea** (second cerveau). |
| **Joplin** | Notes **dupliquées** — remplacées par **Obsidian + Gitea** (vault unique). |
| **OnlyOffice** | Bureautique **dupliquée** — remplacée par **Collabora Office** (app Nextcloud). |
| **InfluxDB** | Métriques **dupliquées** — remplacées par **Beszel** (léger, historique intégré). |
| **Flaresolverr** | Anti-Cloudflare **R&D** — supprimé (pas d'usage quotidien). |
| **mini-qr** | Utilitaire **marginal** — supprimé. |
| **Conteneurs morts** (wg-easy, xteve, wol-proxy, youtubedl, mcpo…) | **Morts** — supprimés. |
| **18 modèles Ollama legacy** (98 Go) | **Tri** — garder les modèles utiles (Qwen 3.8max + embeddings), supprimer le reste. |
| **FreshRSS** (lecture RSS) | **Pas d'utilisateur nommé à usage quotidien** au moment de la table rase (résilience = usage). Les podcasts RSS sont déjà couverts **côté client** par **AntennaPod** (zéro serveur). **Chemin de ré-add** : Compose sur G9 en < 5 min (~100 Mo, AGPL-3.0) si le besoin émerge. |
> **Bilan** : ~70 conteneurs → **~20 briques**. C'est la **consolidation** (principe 4).
### 3.3 Justification par brique (maturité + ressource + sortie)
> **AC3** : les briques **clés** sont justifiées en détail. Les autres suivent le même raisonnement (tableau 3.1).
| Brique | Maturité | Ressource (machine cible) | Pourquoi CELLE-ci (vs alternatives) |
|--------|----------|---------------------------|--------------------------------------|
| **Caddy** | Caddy 2, très stable | ~50 Mo (J5040) | **TLS auto** (Let's Encrypt) sans script. Plus simple que Traefik/Nginx pour un usage familial. |
| **Matrix (Synapse)** | Standard de fait, très maintenu | ~2-3 Go (J5040) | **Remplace WhatsApp** (chiffré, fédéré, OSS). Alternative : Element = le client. |
| **Jitsi** | Mature (Jitsi Meet) | ~1-2 Go (J5040) | **Visio** OSS, s'intègre à Matrix (Element). |
| **PhotoPrism** | Mature, AGPL | ~2-3 Go (G9) | **Lecture in-place** (ne déplace pas les originaux) + IA (visages, recherche sémantique) + partage. Plus sobre qu'Immich (qui désorganise). |
| **Jellyfin** | Très mature, AGPL | ~1-2 Go (G9) | **Vidéo + musique** OSS, transcodage, multi-appareils. Standard de fait. |
| **Nextcloud** | Très mature, AGPL | ~2-3 Go (G9) | **Socle famille** : Drive + Office + Groupware + Deck + Notes en **un seul** (consolidation). |
| **Vaultwarden** | Très mature, AGPL | ~100 Mo (G9) | **Mots de passe** OSS (alternative Bitwarden légère). Standard d'export. |
| **Gitea** | Très mature, MIT | ~200 Mo (G9) | **Git** léger (vault second cerveau + versioning). Plus sobre que GitLab. |
| **n8n** | Très mature (fair-code) | ~1 Go (G9) | **Automatisation** (workflows). Écosystème riche. ⚠️ non-OSI (noté). |
| **Ollama** | Très mature, MIT | ~50 Go (PC IA) | **Backend LLM** (Qwen 3.8max). Point d'entrée unique, simple. |
| **Qdrant** | Très mature, Apache-2.0 | ~1-2 Go (PC IA) | **Vector DB** (RAG / second cerveau). Plus performant que sqlite-vec à l'échelle. |
| **hermes** | Communautaire, MIT | ~1 Go (PC IA) | **Harnais agent** (Effi) : mémoire, MCP, knowledge. Remplace Dify/mcpo/pipelines. |
| **OpenWebUI** | Très mature, BSD-3 | ~1 Go (PC IA) | **Front LLM simple** pour la famille (1 seul). |
| **Home Assistant** | Très mature, Apache-2.0 | VM légère (Freebox) | **Domotique** standard de fait (enceintes Google, maison). |
| **Frigate** | Mature, AGPL | léger (Freebox) | **Vision caméras** (Reolink) + détection. Plus simple qu'un NVR dédié. |
| **Beszel** | Mature, MIT | ~50 Mo (J5040) | **Monitoring** léger + historique + alertes. Plus sobre que InfluxDB+Grafana. |
| **AdGuard Home** | Très mature, MIT | ~100 Mo (J5040) | **DNS + filtre pubs + parents**. Standard de fait. |
| **WireGuard** | Système (noyau), GPL-2.0 | négligeable (J5040) | **VPN** moderne, simple, performant. |
| **restic/borg** (S10) | Très mature, Apache/GPL | léger | **Backup** chiffré, dédupliqué, versionné, append-only. |
### 3.4 Usages nommés — briques edge & infra (AC8)
> Chaque brique du S3.1 a un **utilisateur nommé + appareil + usage quotidien** (résilience = usage). Les briques famille sont détaillées en S5.3 / S6.4 / S7.3 / S8.2 / S12.3 ; les briques edge & infra ici.
| Brique | Utilisateur nommé | Appareil | Usage quotidien |
|--------|-------------------|----------|-----------------|
| **SearXNG** | Moi + famille | Laptop + téléphone | **Recherche web** (privé, sans tracking) — remplace la recherche Google pour l'usage perso |
| **AdGuard Home** | Famille (dont enfants) | Tous les appareils | DNS + filtre pubs + profils parents |
| **Caddy / WireGuard / firewall** | Moi | Laptop + téléphone | Accès distant sécurisé (VPN) + TLS auto |
| **Beszel** | Moi | Laptop + téléphone | Monitoring + alertes (→ Matrix) |
---
## S4 — Bloc IA (le cœur du futur)
> **Statut** : ✅ **Validé** (05/10/2026) — le bloc IA est **consolidé** sur le **PC IA** (62 Go). C'est la **différence** vs l'existant (qui était dispersé + dupliqué).
### 4.1 L'architecture du bloc IA (1 seul endroit)
```
┌─────────────────────────────────────────────────────────┐
│ PC IA (.11) — 62 Go RAM, 2× RTX 3060 (24 Go VRAM) │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌──────────────┐ │
│ │ Ollama │ │ Qdrant │ │ hermes │ │
│ │ (LLM) │ │ (vector DB)│ │ (agent) │ │
│ │ Qwen 3.8max│──►│ (RAG / │◄──│ (Effi) │ │
│ │ ~50 Go RAM │ │ 2ᵉ │ │ mémoire + │ │
│ │ ~20 Go VRAM│ │ cerveau) │ │ MCP + │ │
│ └──────┬──────┘ └─────────────┘ │ knowledge │ │
│ │ └──────┬───────┘ │
│ ▼ ▼ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ OpenWebUI (front famille, 1 seul) │ │
│ │ → la famille pose des questions au LLM │ │
│ └─────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
```
### 4.2 Les briques du bloc IA (et pourquoi)
| Brique | Rôle | Pourquoi CELLE-ci |
|--------|------|-------------------|
| **Ollama** | **Backend LLM** (Qwen 3.8max) | Point d'entrée **unique**, simple, MIT. Remplace llama.cpp direct + llm-proxy. |
| **Qwen 3.8max** | **Le LLM principal** | ~20 Go VRAM + ~50 Go RAM → **seul** le PC IA (62 Go) le porte. C'est le **cerveau**. |
| **Qdrant** | **Vector DB** (RAG / 2ᵉ cerveau) | Stocke les **embeddings** (bge-m3) du second cerveau. Apache-2.0, performant. |
| **hermes** | **Harnais agent** (Effi) | **Mémoire** + **MCP** + **knowledge** : c'est l'**Effi** (mon Jarvis). Remplace Dify + mcpo + pipelines. MIT. |
| **OpenWebUI** | **Front famille** (1 seul) | La famille pose des questions au LLM **simplement**. BSD-3. |
### 4.3 Le second cerveau (2 couches) — lien avec `PROJET-SECOND-CERVEAU.md`
> Le bloc IA **porte** le second cerveau (projet séparé). **Modèle 2 couches (source unique)** : `PROJET-SECOND-CERVEAU.md` S4.1 — **couche 1 = la connaissance** (markdown, vérité), **couche 2 = l'index** (cache jetable, reconstruisible).
**Mapping sur les briques** (spécificité du dossier logiciel) :
| Couche | Brique | Machine |
|--------|--------|---------|
| **1 — Connaissance** | **Gitea** (canon) + **G9** (working) | G9 |
| **2 — Index** | **Qdrant** (embeddings) | PC IA |
> L'**hermes** est le **harnais** qui **récupère d'elle-même** le contexte (exigence #2) via **MCP**.
### 4.4 Ce qui est **supprimé** du bloc IA (vs l'existant)
| Supprimé | Remplacé par |
|----------|--------------|
| **Dify** (14 conteneurs) | **hermes + Ollama + Qdrant** |
| **RAGFlow** (crash loop) | **Qdrant** direct |
| **llm-proxy (LiteLLM)** | **Ollama** direct |
| **mcpo + pipelines** | **hermes** (MCP natif) |
| **2× OpenWebUI** | **1 seul** (PC IA) |
| **2× STT** | **1 seul** (faster-whisper, on-demand) |
| **2× OCR** | **1 seul** (kreuzberg/PaddleOCR FR, on-demand) |
| **ComfyUI 24/7** | **On-demand** uniquement |
> **Bilan** : le bloc IA passe de **~30 conteneurs dispersés** à **5 briques** sur **1 machine**. C'est la **consolidation** du futur.
---
## S5 — Photos (PhotoPrism)
> **Statut** : ✅ **Tranché** (05/10/2026) — **PhotoPrism** (lecture **in-place**). Répond à la décision ouverte **R6.1** de `DOSSIER-RECREATION.md`.
### 5.1 Le choix : PhotoPrism (et pourquoi pas Immich)
| Critère | **PhotoPrism** ✅ | Immich ❌ |
|---------|-------------------|----------|
| **Lecture in-place** | ✅ **Lit l'arborescence d'originaux** (triés par année), **ne déplace pas** les fichiers | ❌ **Importe** dans sa propre base (désorganise) |
| **IA (visages, recherche sémantique)** | ✅ intégrée | ✅ intégrée |
| **Partage famille** | ✅ (liens, albums) | ✅ |
| **Auto-upload Android** (téléphones enfants) | ✅ (app + API) | ✅ |
| **Sobriété (G9, 16 Go)** | ✅ ~2-3 Go | 🟡 plus gourmand |
| **Licence** | **AGPL-3.0** (core) | AGPL-3.0 |
| **Sortie** | ✅ originaux **intacts** (on peut partir sans perte) | 🟡 export depuis la base |
> **Décision** : **PhotoPrism** car il **lit les originaux en place** (pas de verrouillage, pas de désorganisation). C'est aligné avec la **culture de la donnée** (pas de déplacement des sources brutes — `PROJET-SECOND-CERVEAU.md` S1.4).
### 5.2 La migration des 10 ans de photos Synology
| Étape | Action |
|-------|--------|
| 1 | **Copier** l'arborescence des originaux Synology → G9 `/data/famille/photos/AAA/` (par année, convention Annexe B du guide). |
| 2 | **Pointeur** PhotoPrism sur `/data/famille/photos/` (lecture in-place). |
| 3 | **Indexation** (visages, lieux, recherche sémantique) — se fait en arrière-plan. |
| 4 | **Reprise des albums** : les albums Synology **ne sont pas importés directement** (les fichiers sont intacts → on **re-crée** les albums ou un **script de mapping**). |
> **Point d'attention** : les **albums Synology** sont une **vue**, pas les fichiers. Les **fichiers** sont intacts → on **re-crée** les albums dans PhotoPrism (ou un script de mapping des noms d'albums). **Aucune perte** (les originaux sont là).
### 5.3 L'usage famille (résilience = usage)
| Utilisateur | Appareil | Usage quotidien |
|-------------|----------|-----------------|
| **Moi** | Laptop + téléphone | Recherche, albums, partage |
| **Femme** | Téléphone | Photos famille, albums |
| **Enfants** | Téléphone Android | **Auto-upload** des photos (règle famille #1 : « les photos vont sur le NAS ») |
---
## S6 — Fichiers & productivité (Nextcloud)
> **Statut** : ✅ **Tranché** (05/10/2026) — **Nextcloud** comme **socle famille** (consolidation). Répond aux décisions **R6.2** (notes) + **2.1/2.2** (agenda/mail).
### 6.1 Nextcloud = le socle (un seul, pas 5 apps)
> **Principe de consolidation** : Nextcloud **regroupe** les briques de productivité famille en **un seul** service (pas Leantime + Affine + Joplin + OnlyOffice + … séparés).
| App Nextcloud | Besoin | Remplace |
|---------------|--------|----------|
| **Nextcloud Drive** | **Fichiers** famille (partages, sync) | Nextcloud existant + Google Drive |
| **Collabora Office** | **Bureautique** (docs, tableurs, présentations) | OnlyOffice + Office GAFAM |
| **Groupware** (CalDAV/CardDAV) | **Agenda + contacts** | Google Agenda/Contacts |
| **Deck** | **Tâches** famille (kanban) | Leantime |
| **Notes** | **Notes rapides** famille | (pas le second cerveau — voir S6.2) |
| **Mail** (IMAP) | **Lecture** mail (voir S6.3) | — |
### 6.2 Les notes : le second cerveau (pas Nextcloud Notes)
> **Décision R6.2 tranchée** : les **notes de connaissance** (second cerveau) = **Obsidian + Gitea** (markdown versionné), **PAS** Nextcloud Notes.
| Type de note | Outil | Pourquoi |
|--------------|-------|----------|
| **Second cerveau** (connaissance, versionné, IA) | **Obsidian** (viewer) + **Gitea** (store) + **Qdrant** (index) | Markdown = format ouvert, versionné git, lisible par l'humain **et** l'IA. Nextcloud Notes = **mauvais fit** comme store (pas de versioning, pas de structure liée — `PROJET-SECOND-CERVEAU.md` S4.3). |
| **Notes rapides famille** (listes, mémos) | **Nextcloud Notes** | Simple, partagé, pas besoin de versioning. |
> **Séparation claire** : le **second cerveau** (moi, connaissance) = Obsidian+Gitea+Qdrant. Les **notes rapides famille** = Nextcloud Notes. **Pas de confusion**.
### 6.3 Le mail : pas de serveur SMTP local (décision pragmatique)
> **Décision** : **pas de serveur mail local** (la **livraison** SMTP est trop difficile à fiabiliser — SPF/DKIM/DMARC, réputation, spam). On garde **Gmail/Hotmail** pour la **livraison**, et **Nextcloud Mail** pour la **lecture** via **IMAP**.
| Aspect | Choix |
|--------|-------|
| **Lecture** | **Nextcloud Mail** (IMAP vers Gmail/Hotmail) |
| **Accès web** | Gmail/Hotmail (externes) pour l'envoi |
| **Étape par étape** | On **lit** en local d'abord ; la **dégafamisation complète** du mail (serveur local) est un **horizon > 1 an** (trop risqué en production). |
> **Honnêteté** : le mail est le **point le plus dur** à dégafamiser (livraison). On ne le force pas : on **lit** en local, on **garde** la livraison externe. C'est **pérenne** (pas de risque spam) et **réversible**.
### 6.4 L'usage famille (résilience = usage)
| Utilisateur | Appareil | Usage quotidien |
|-------------|----------|-----------------|
| **Moi** | Laptop + téléphone | Fichiers, Office, agenda, second cerveau |
| **Femme** | Téléphone | Fichiers, agenda, contacts, mail |
| **Enfants** | PC + téléphone | Fichiers (dossier famille), photos |
---
## S7 — Média (Jellyfin + \*arr + podcasts)
> **Statut** : ✅ **Tranché** (05/10/2026) — **Jellyfin** (vidéo+musique) + **\*arr** (gestion) + **AntennaPod** (podcasts, zéro serveur).
### 7.1 La stack média
| Brique | Rôle | Machine | Licence |
|--------|------|---------|---------|
| **Jellyfin** | **Streaming vidéo + musique** (transcodage, multi-appareils) | G9 | AGPL-3.0 |
| **Prowlarr** | **Indexers** (source de métadonnées) | J5040 | GPL-3.0 |
| **Sonarr** | **Séries** (queue + métadonnées) | J5040 | GPL-3.0 |
| **Radarr** | **Films** (queue + métadonnées) | J5040 | GPL-3.0 |
| **qBittorrent** | **Téléchargement** (torrent/HTTP) | J5040 | GPL-2.0 |
| **AntennaPod** | **Podcasts** (app Android, **zéro serveur**) | Téléphone | GPL-3.0 |
### 7.2 Pourquoi cette répartition
| Décision | Raison |
|----------|--------|
| **\*arr + qBittorrent sur J5040** | Les **téléchargements** sont de l'**edge** (proximité internet). Le G9 (cœur famille) n'est pas saturé par les torrents. |
| **Jellyfin sur G9** | Le **streaming** lit les **fichiers** (USB média sur G9). L'app est à côté des données. |
| **Podcasts = AntennaPod + RSS** | **Zéro serveur** : AntennaPod lit les **flux RSS** directement. Pas de brique serveur pour les podcasts (consolidation). |
### 7.3 L'usage famille (résilience = usage)
| Utilisateur | Appareil | Usage quotidien |
|-------------|----------|-----------------|
| **Famille** | TV (Shield/Mi Box) + Denon | **Jellyfin** (films, séries, musique) |
| **Moi** | Téléphone | **AntennaPod** (podcasts) |
| **Enfants** | TV + PC | **Jellyfin** (contenus filtrés) |
---
## S8 — Communication (Matrix + Jitsi + assistant vocal)
> **Statut** : ✅ **Tranché** (05/10/2026) — **Matrix + Jitsi + Element** (remplace WhatsApp) + **assistant vocal** via Home Assistant.
### 8.1 La stack communication
| Brique | Rôle | Machine | Licence |
|--------|------|---------|---------|
| **Matrix (Synapse)** | **Messagerie** (remplace WhatsApp) | J5040 | Apache-2.0 |
| **Jitsi** | **Visioconférence** | J5040 | Apache-2.0 |
| **Element** | **Client** (Android) | Téléphone | GPL-3.0 |
| **Home Assistant** | **Assistant vocal** (enceintes Google) | Freebox | Apache-2.0 |
### 8.2 L'usage famille (résilience = usage)
| Utilisateur | Appareil | Usage quotidien |
|-------------|----------|-----------------|
| **Famille** | Téléphone (Element) | **Messagerie** (remplace WhatsApp) |
| **Famille** | Téléphone + TV | **Visio** (Jitsi, via Element) |
| **Moi** | Enceintes Google ×4-5 | **Assistant vocal** (Home Assistant) → peut déclencher n8n, contrôler Frigate |
### 8.3 Le bot hermes ↔ Matrix
> **hermes** (Effi, sur PC IA) est **connecté à Matrix** : la famille peut **parler à Effi** dans un salon Matrix. C'est l'**assistant de famille** (projet second cerveau, phase 3).
### 8.4 Les alertes n8n → Matrix
> **n8n** (G9) envoie les **alertes** (Beszel, backup, services) dans un **salon Matrix** dédié. C'est le **canal d'alerte** unique (pas d'e-mail, pas de SMS).
---
## S9 — Sécurité & accès distant
> **Statut** : ✅ **Tranché** (05/10/2026), **révisé 10/10/2026** (G9 mono-disque : LUKS abandonné sur le G9, voir changelog) — **Caddy + TLS auto**, **WireGuard**, **firewall strict**, **chiffrement des copies (restic)**, **2FA**, **sous-domaines** folzocorp.fr. Répond à l'**AC6**.
### 9.1 Le modèle de sécurité (couche par couche)
| Couche | Brique | Détail |
|--------|--------|--------|
| **Périmètre** | **Firewall (nftables)** sur J5040 | Filtre LAN/WAN **strict** : par défaut **dénier**, autoriser seulement les ports exposés. |
| **Entrée internet** | **Caddy** (reverse proxy) | **Point d'entrée unique** + **TLS auto** (Let's Encrypt, auto-renew). Remplace le **script de 6 mois** (pérenne). |
| **Accès distant** | **WireGuard** | **VPN** chiffré pour accéder à la maison depuis l'extérieur. **Pas d'exposition directe** des services. |
| **Chiffrement disque** | **restic (copies) / LUKS disques amovibles** | **Révisé 10/10/2026** : G9 **mono-disque sans LUKS** (données familiales non critiques) ; la protection repose sur les **backups restic déjà chiffrés** + disques de backup amovibles chiffrés. La protection **internet** = firewall/Caddy/WireGuard, pas LUKS. |
| **Identité** | **2FA** | Tous les services exposés (Nextcloud, Vaultwarden, Gitea, Matrix) en **2FA**. |
| **DNS / filtre** | **AdGuard Home** | DNS interne + **filtre pubs** + **profils enfants**. |
| **Mots de passe** | **Vaultwarden** | **Coffre unique** + **2FA** + **emergency access** famille. |
### 9.2 L'accès distant (folzocorp.fr)
| Élément | Détail |
|---------|--------|
| **Domaine** | **folzocorp.fr** (principal) + **sous-domaines** par service |
| **Sous-domaines** | `cloud.folzocorp.fr` (Nextcloud) · `photos.folzocorp.fr` (PhotoPrism) · `media.folzocorp.fr` (Jellyfin) · `matrix.folzocorp.fr` (Matrix) · `git.folzocorp.fr` (Gitea) · `vault.folzocorp.fr` (Vaultwarden) · `ai.folzocorp.fr` (OpenWebUI) |
| **TLS** | **Caddy** génère + renouvelle **automatiquement** les certificats Let's Encrypt (pas de script). |
| **Exposition** | **Seulement** les services **nécessaires** à l'extérieur passent par Caddy. Le **reste** est **LAN-only** (accès via **WireGuard**). |
> **Principe** : **le moins d'exposition possible**. Par défaut **LAN-only** ; l'extérieur passe par **WireGuard** (VPN) ou **Caddy** (services explicitement exposés, TLS + 2FA).
### 9.3 Le firewall strict (J5040)
| Règle | Détail |
|-------|--------|
| **Par défaut** | **Dénier** tout (WAN → LAN). |
| **Autorisé WAN** | Seulement les ports **Caddy** (443) + **WireGuard** (UDP 51820). |
| **LAN** | Libre (les services sont LAN-first). |
| **Enfants** | **AdGuard Home** (DNS filtré) + **profils** (restriction par appareil). |
---
## S10 — Backup 3-2-1 (restic/borg + LUKS)
> **Statut** : ✅ **Tranché** (05/10/2026), **révisé 10/10/2026** (G9 : le disque source n'est pas LUKS — le chiffrement est assuré par **restic lui-même**) — **restic** (ou borg) **chiffré + append-only** + **procédure de restore testée**. Répond à l'**AC5**.
### 10.1 Pourquoi restic/borg (pas rsync)
| Critère | **restic / borg** ✅ | rsync ❌ |
|---------|----------------------|----------|
| **Déduplication** | ✅ (économe en espace) | ❌ |
| **Chiffrement** | ✅ (intégrée) | ❌ (à ajouter) |
| **Versioning** | ✅ (historique) | ❌ |
| **Append-only / anti-ransomware** | ✅ (snapshots non-modifiables) | ❌ |
| **Incidentel** | ✅ (seul les changements) | 🟡 (delta, mais pas versionné) |
> **Décision** : **restic** (ou **borg**) — **chiffré**, **dédupliqué**, **versionné**, **append-only** (résiste au ransomware). C'est la **pérennité** au sens fort.
### 10.2 Le schéma 3-2-1 (source unique : `DOSSIER-RECREATION.md` §R4)
> **Schéma des 3 copies** (originale G9 + PC IA / intermédiaire Freebox / offsite 218play) : **source unique** = `DOSSIER-RECREATION.md` §R4 (tableau complet : supports, machines, contenu, fréquence).
**Outil** : **restic** (ou borg) — **chiffré + append-only** (S10.1). Copie 2 (Freebox) = **quotidien**, copie 3 (218play) = **hebdo**.
> **État 10/10/2026 (copie 2, G9)** : ✅ **restic 0.19.1 + rclone 1.75.2**, repo
> `rclone:freeboxftp:Freebox/backup/g9` (FTP local de la Freebox, port **21**), snapshot
> initial **4.18 GiB**, **append-only prouvé**, **restore testée**, jobs cron
> 03:30 (backup, conteneurs stateful stoppés) / 04:00 (prune 7-4-12-5) / 09:15 (health
> + alerte Matrix). **Mont CIFS abandonné** (restic échoue sur `chmod`, FUSE perd des
> objets) → **backend rclone**, donc **aucune dépendance de montage** (résiste au
> reboot, pas besoin de `smbd` au boot).
>
> **Phase 5 (planifié, non réalisé)** : la Freebox **ne peut pas exécuter restic**
> (FTP/SFTP/SMB/WebDAV exposés, pas de conteneur pour rest-server, **autostart VM non
> validé**) → ① **VM Freebox** avec **`rest-server --append-only`** (repo append-only
> **côté serveur**, plus fort que l'actuel imposé par le rclone local du G9), servi en
> **HTTPS/TLS** + **`htpasswd`** (`g9-backup` en écriture, `g9-restore` en lecture), repo
> cible `rest:https://…` ;
> ② **`restic copy`** G9 → Freebox → **218play** (copie 3, repo append-only dédié,
> identifiants **`NoAccess`** côté SMB) ; ③ **chroot/rootfs durci** sur le G9 pour
> isoler le job de backup.
### 10.3 Les données IA dans le 3-2-1
| Donnée IA | Où | Sauvegardée ? |
|-----------|-----|---------------|
| **Qdrant** (vector DB / 2ᵉ cerveau) | PC IA | ✅ **Oui** (critique) |
| **n8n** (workflows) | G9 | ✅ **Oui** |
| **Vault second cerveau** (md) | Gitea (G9) | ✅ **Oui** (critique) |
| **hermes** (mémoire) | PC IA | ✅ **Oui** |
| **Modèles GGUF** (98 Go) | PC IA | ❌ **Non** (re-téléchargeables) |
| **Ollama** (modèles) | PC IA | ❌ **Non** (re-téléchargeables) |
### 10.4 La procédure de restore **testée** (AC5)
> **Un backup non testé = pas de backup.** La procédure est **documentée** et **testée au moins 1×/trimestre**.
| Étape | Action |
|-------|--------|
| 1 | **Restaurer** un snapshot restic sur une **machine de test** (G7). |
| 2 | **Vérifier** l'intégrité (photos, fichiers, mots de passe, données IA). |
| 3 | **Valider** que la famille **peut repartir** (services accessibles). |
| 4 | **Documenter** le résultat (date, durée, anomalies). |
> **Continuité** (si « il m'arrive quelque chose ») : la **famille sait restaurer** (testé 1×), les **mots de passe** ont un **emergency access**, la **doc** est lisible par un tiers.
---
## S11 — Ops (Compose + Gitea + Watchtower + Beszel)
> **Statut** : ✅ **Tranché** (05/10/2026) — **Docker Compose versionné dans Gitea** + **Watchtower** (MAJ **sélective** : stateless auto / stateful manuel) + **Beszel** (monitoring) + **n8n** (alertes). Pas de Portainer, pas de PaaS. *(Affiné 06/10/2026 après l'incident Nextcloud — voir §11.4.)*
### 11.1 L'approche ops (sobre, versionnée, observable)
| Brique | Rôle | Machine | Licence |
|--------|------|---------|---------|
| **Docker Compose** | **Déclaration** des services (fichiers `docker-compose.yml`) | toutes | Apache-2.0 |
| **Gitea** | **Versioning** des `docker-compose.yml` + config (source de vérité) | G9 | MIT |
| **Watchtower** | **Mises à jour** automatiques **sélectives** (stateless auto / stateful exclu + manuel) | J5040 + G9 + PC IA | Apache-2.0 |
| **Beszel** | **Monitoring** léger + historique + **alertes** | J5040 (hub) + agents | MIT |
| **n8n** | **Alertes** + **automatisation** (workflows) | G9 | ⚠️ fair-code |
### 11.2 Pourquoi pas Portainer / PaaS
| Option | Verdict | Raison |
|--------|---------|--------|
| **Portainer** | ❌ | **Redondant** avec Compose + Gitea. Une brique de plus à maintenir. |
| **PaaS (ex. Coolify)** | ❌ | **Trop lourd** pour 4 machines. Compose + Gitea suffit. |
| **Compose + Gitea + Watchtower** | ✅ | **Sobre**, **versionné**, **reproductible**. C'est la **production**. |
### 11.3 Le flux ops (reproductible)
```
docker-compose.yml (source de vérité, dans Gitea)
│
▼
git pull → docker compose up -d (déploiement)
│
▼
Watchtower (MAJ images **stateless** uniquement — stateful exclu)
│
▼
Beszel (monitoring) → alertes → n8n → Matrix
```
> **Principe** : **tout est dans git** (les `docker-compose.yml` + config). Une machine tombe → on **re-crée** depuis Gitea en **< 5 min** (c'est la **maîtrise**, pilier 5).
### 11.4 Watchtower **sélectif** (règle stateless / stateful)
> **Contexte** : l'**incident Nextcloud** (06/10/2026 — voir `ETAT-DE-LART-2026-10-06.md` §3) a montré qu'un Watchtower **non sélectif** a mis à jour **l'application ET sa base MariaDB** la nuit, sans laisser la **migration de schéma** se terminer → Nextcloud en **maintenance mode** (503). **Watchtower ne « teste » rien** : il remplace l'image et relance le conteneur, point. La sécurité vient de la **sélectivité**, pas d'un test.
**La règle** :
| Type de conteneur | Traitement Watchtower | Pourquoi |
|-------------------|-----------------------|----------|
| **Stateless** (pas d'état persistant : Caddy, AdGuard, SearXNG, Beszel, n8n*…) | **Auto** (Watchtower les met à jour) | Pas d'état à migrer → le swap d'image est sûr. |
| **Stateful** (base de données, cache persistant, état : Nextcloud, MariaDB/Postgres, Redis, Vaultwarden, Gitea, InfluxDB, OnlyOffice/Collabora, Leantime, Affine, Joplin, Qdrant…) | **Exclu** (label `com.centurylinklabs.watchtower.enable=false`) + **MAJ manuelle** (§11.5) | Une MAJ d'image **sans migration de schéma** = **corruption / service HS**. La migration doit être **supervisée**, avec **backup en place**. |
> **\* n8n** : son **état** (workflows) est dans **Postgres** → le conteneur n8n est **stateless** (image), mais sa **DB** est **stateful** (exclue). On traite la **DB** comme stateful.
**Les briques à exclure de Watchtower (stateful, sur le NAS actuel + cible G9) :**
| Brique | Image | Pourquoi stateful |
|--------|-------|-------------------|
| **Nextcloud** | `nextcloud:latest` | App + migration de schéma (l'incident). |
| **MariaDB** (Nextcloud DB) | `mariadb:10.6` | Base de données — migration de schéma. |
| **Redis** (Nextcloud cache) | `redis:latest` | Cache persistant (RDB/AOF). |
| **Vaultwarden** | `vaultwarden/server:latest` | Coffre (Postgres interne). |
| **Gitea** | `gitea/gitea:latest` | Repos + DB (Postgres/SQLite). |
| **n8n** + **Postgres** | `n8n:latest` + `postgres:17` | Workflows + DB. |
| **InfluxDB** | `influxdb:2.7` | Base de métriques. |
| **OnlyOffice / Collabora** | `onlyoffice/documentserver:latest` | État + config persistante. |
| **Leantime** + **MySQL** | `leantime:latest` + `mysql:8.4` | App + DB. |
| **Affine** + **Postgres** + **Redis** | `affine:stable` + `pgvector:pg16` + `redis:latest` | App + DB + cache. |
| **Joplin** + **Postgres** | `joplin/server:latest` + `postgres:16` | App + DB. |
| **Qdrant** | `qdrant/qdrant:latest` | Vector DB (2ᵉ cerveau) — **critique**. |
**Configuration Watchtower cible (sur chaque machine)** :
```yaml
services:
watchtower:
image: containrrr/watchtower:1 # ⚠️ pin la MAJEURE (pas :latest)
container_name: watchtower
environment:
- TZ=Europe/Paris
- WATCHTOWER_SCHEDULE=0 0 2 * * * # fenêtre de MAJ (nuit)
- WATCHTOWER_CLEANUP=false # ⚠️ GARDER les anciennes images (rollback)
- WATCHTOWER_INCLUDE_STOPPED=false # ne pas toucher les conteneurs arrêtés
# Notifications (n8n / Matrix) — on est averti de chaque MAJ :
- WATCHTOWER_NOTIFICATIONS=matrix
- WATCHTOWER_NOTIFICATION_MATRIX_HOMESERVER=https://matrix.folzocorp.fr
- WATCHTOWER_NOTIFICATION_MATRIX_ROOMID=!SALON-OPS
# (ou WATCHTOWER_NOTIFICATIONS=n8n + URL du webhook)
volumes:
- /var/run/docker.sock:/var/run/docker.sock
restart: unless-stopped
```
> **Pinning des majeures** : les images stateless sont **pinées en majeure** (`:1`, `:2`…) pour éviter un saut mineur surprise ; les **stateful sont exclus** (pas de pinning — MAJ manuelle supervisée, §11.5).
>
> **⚠️ Écart actuel (NAS) = action corrective** : le Watchtower du NAS tourne avec **0 exclusion**, **0 notification**, `WATCHTOWER_CLEANUP=true` (supprime les anciennes images → **pas de rollback**), et l'image `:latest` (non pinnée). C'est **exactement** la config qui a causé l'incident Nextcloud. **Action immédiate** : exclure les briques stateful de Watchtower **sur le NAS actuel** (même **avant** la migration xpenology → G9) — voir S13.1 (A.0) + `DOSSIER-RECREATION.md` R7.
### 11.5 Procédure de MAJ **manuelle** des services **stateful**
> **Règle absolue** : **pas de MAJ stateful sans backup en place** (le 3-2-1 est fonctionnel, §S10). C'est la leçon de l'incident Nextcloud.
| Étape | Action | Détail |
|-------|--------|--------|
| **1** | **Backup** DB + config | `pg_dump` / `mysqldump` (ou snapshot restic) de la **base** + copie du **dossier de config** (ex. `nextcloud/config`). **Vérifier** que le dump est **complet** (taille, lignes). |
| **2** | **Maintenance mode ON** | `docker exec nextcloud_app php occ maintenance:mode --on` (ou équivalent). **Bloque** les écritures pendant la migration. |
| **3** | **Pull + up** | `docker compose pull <service>` puis `docker compose up -d <service>` (image **pinée** à la nouvelle version). |
| **4** | **Migration de schéma** | `docker exec nextcloud_app php occ upgrade` (laisse **terminer** — c'est l'étape que Watchtower a **sauté**). |
| **5** | **Vérification** | `status.php` (version, `needsDbUpgrade: false`, maintenance `false`) + `occ status` + test fonctionnel (login, 1 fichier). |
| **6** | **Maintenance mode OFF** | `docker exec nextcloud_app php occ maintenance:mode --off`. |
| **7** | **Alerte** | n8n → **Matrix** : « MAJ stateful <service> <vX> → <vY> OK » (ou échec → **rollback** depuis l'ancienne image, conservée car `CLEANUP=false`). |
> **Rollback** : si l'étape 5 échoue → `occ maintenance:mode --on` + restaurer le **dump** de l'étape 1 + relancer l'**ancienne** image (conservée). C'est la raison de `WATCHTOWER_CLEANUP=false` + du **pinning**.
---
## S12 — Enfants & MDM
> **Statut** : ✅ **Tranché** (05/10/2026) — **AdGuardHome** (DNS + parental) + **Google Family Link** (maintenant) + **MicroMDM / Android Enterprise** (option future).
### 12.1 La protection enfants (actuelle)
| Brique | Rôle | Machine |
|--------|------|---------|
| **AdGuard Home** | **DNS filtré** + **profils enfants** (restriction par appareil) | J5040 |
| **Google Family Link** | **Contrôle parental** (temps d'écran, apps) sur Android | (cloud Google, **usage parental** uniquement) |
> **Note honnêteté** : **Family Link** est un service **Google** (cloud). C'est le **seul** service GAFAM **gardé** — mais pour un **usage parental** (contrôle des enfants), pas pour les **données famille**. C'est **pragmatique** (pas d'alternative OSS MDM mature pour Android familial) et **limité** (pas de données familiales, juste du contrôle).
### 12.2 L'option future : MDM Android (si besoin)
| Option | Détail |
|--------|--------|
| **MicroMDM** | **MDM** iOS (Apple) — OSS. Si on a des **iPad/iPhone**. |
| **Android Enterprise** | **MDM** Android — via un **gestionnaire** (ex. **MicroMDM** équivalent Android, ou **Geeks**). **Option future** si le besoin de **contrôle à distance** des téléphones enfants se précise. |
> **Décision** : **Family Link** pour l'instant (simple, gratuit, parental). **MDM OSS** en **option future** si le besoin de **contrôle à distance** (rester, verrouiller, effacer) se précise. **Pas de sur-ingénierie** maintenant.
### 12.3 L'usage enfants (résilience = usage)
| Utilisateur | Appareil | Usage quotidien |
|-------------|----------|-----------------|
| **Enfants** | Téléphone Android + PC | **AdGuard** (DNS filtré) + **Family Link** (contrôle) + **Jellyfin** (contenus) + **PhotoPrism** (auto-upload) |
---
## S13 — Plan de migration R&D → production
> **Statut** : ✅ **Posé** (05/10/2026) — **phased**, avec **critères de succès** par phase. Répond à l'**AC7**. S'appuie sur les **phases R5** de `DOSSIER-RECREATION.md` (plan machine) ; cette section ajoute la **couche logicielle**.
### 13.0 Règle de lecture
- **R5 = plan machine** (fondations, cœur famille, finitions). **Cette section = couche logicielle** (quelle brique, dans quel ordre).
- **Fondations d'abord** : on ne déploie pas de brique sur un système **sans backup** (risque #1).
- **On ne passe à la phase N+1 qu'après validation de la phase N.**
- **Statut** : ⬜ à faire → 🟡 en cours → ✅ validé.
> **🔴 Prérequis absolu de TOUTE migration** : un **backup 3-2-1 fonctionnel** (restic/borg chiffré, **restauration testée**, données IA incluses — S10) **AVANT** toute migration (xpenology → G9, ou MAJ d'une brique stateful). **Aucune migration sans backup = aucun risque pris.** L'**incident Nextcloud** (06/10/2026) est la **preuve** que le risque « **MAJ stateful non supervisée** » est **réel** : Watchtower a mis à jour l'app + la DB sans laisser la migration de schéma se terminer → service HS. La **sélectivité** (S11.4) + la **procédure manuelle** (S11.5) + le **backup en place** sont la réponse.
> **🔴 Action corrective immédiate (sur le NAS actuel, AVANT la migration)** : **exclure les briques stateful de Watchtower** sur le xpenology (label `com.centurylinklabs.watchtower.enable=false` — liste S11.4) + passer `WATCHTOWER_CLEANUP=false` + notifier via n8n/Matrix. C'est **urgent** (le NAS actuel est toujours en production, sans exclusion) et **indépendant** de la migration xpenology → G9. → **Phase A.0** ci-dessous.
### 13.1 Phase A — Fondations logicielles (avec R5 Phase 0)
**Objectif** : l'**edge** + le **cœur** + le **bloc IA** en place, **backup 3-2-1 fonctionnel**.
| # | Action logicielle | Dépendance | Critère de succès (vérifiable) |
|---|-------------------|------------|--------------------------------|
| **A.0** | **Sécuriser le NAS actuel** : **exclure les briques stateful** de Watchtower (S11.4) + `WATCHTOWER_CLEANUP=false` + notifications n8n/Matrix + **backup 3-2-1 fonctionnel** (prérequis) | — | **0 stateful** sous Watchtower sur le NAS ; **backup 3-2-1 opérationnel** (restauration testée) ; **aucune MAJ stateful non supervisée** possible |
| A.1 | **J5040 edge** : firewall + DHCP + AdGuard + **SearXNG** + **Caddy** (TLS auto) + WireGuard | R5 0.3 | Edge fonctionnel ; certificats **auto-renew** ; sites accessibles ; firewall strict ; recherche locale fonctionnelle |
| A.2 | **G9 cœur** : OS + RAID1 + USB + **SMB** | R5 0.4 | G9 monté ; IP .25 ; partages SMB accessibles |
| A.3 | **PC IA** : **Ollama** (Qwen 3.8max) + **Qdrant** + **hermes** + **OpenWebUI** (1 seul) | R5 0.5 | LLM fonctionnel ; conso idle mesurée < 80 W ; J5040 < 30% |
| A.4 | **Backup 3-2-1** : **restic** (G9+PC IA → Freebox quotidien → 218play hebdo) + **LUKS** | R5 0.6 | 3 copies en place + **restauration testée** (données IA incluses) |
| A.5 | **Ops** : **Compose** dans **Gitea** + **Watchtower** + **Beszel** | A.1–A.4 | Tous les services déclarés en Compose ; MAJ auto ; monitoring + alertes |
**Statut** : ⬜
**Dépendances** : R5 Phase 0
### 13.2 Phase B — Cœur famille (dégafamisation, avec R5 Phase 1)
**Objectif** : le **G9** devient le **cœur famille** (les briques famille en place).
| # | Action logicielle | Dépendance | Critère de succès |
|---|-------------------|------------|-------------------|
| B.1 | **PhotoPrism** (photos, in-place) + migration 10 ans | Phase A + R5 1.1 | Photos accessibles, IA OK, auto-upload Android |
| B.2 | **Nextcloud** (Drive + Collabora + Groupware + Deck + Notes) | Phase A | Fichiers + Office + agenda + contacts + tâches accessibles |
| B.3 | **Vaultwarden** (mots de passe) + 2FA + emergency access | Phase A | Coffre accessible famille, 2FA actif |
| B.4 | **Jellyfin** (vidéo+musique) + **\*arr** + **qBittorrent** | Phase A | Films/séries/musique accessibles |
| B.5 | **Gitea** (vault second cerveau) + **Obsidian** | Phase A | Vault versionné, accessible |
| B.6 | **n8n** (automatisation + alertes → Matrix) | Phase A | Workflows fonctionnels, alertes en Matrix |
| B.7 | **DNS** (AdGuard) + **sous-domaines** folzocorp.fr | Phase A | Sous-domaines résolus, TLS auto |
**Statut** : ⬜
**Dépendances** : Phase A + R5 Phase 1
### 13.3 Phase C — Finitions (dégafamisation complète, avec R5 Phase 2)
**Objectif** : la famille **n'utilise plus de GAFAM** (sauf Family Link parental).
| # | Action logicielle | Dépendance | Critère de succès |
|---|-------------------|------------|-------------------|
| C.1 | **Matrix + Jitsi + Element** (remplace WhatsApp) | Phase B | Famille messagerie + visio en local |
| C.2 | **Home Assistant** (enceintes Google) + **Frigate** (caméras) | Phase B | Assistant vocal + vision caméras |
| C.3 | **Bot hermes ↔ Matrix** (Effi famille) | Phase B + 2ᵉ cerveau | Effi répond dans Matrix |
| C.4 | **Enfants** : AdGuard profils + Family Link | Phase B | Enfants protégés (DNS + parental) |
| C.5 | **Décommissionner** le ballast R&D (Dify, RAGFlow, conteneurs morts…) | Phase B | ~70 conteneurs → ~20 briques ; plus de dupliqué |
| C.6 | **Valheim** (jeu famille) | Phase B | Monde accessible |
**Statut** : ⬜
**Dépendances** : Phase B + R5 Phase 2
### 13.4 Vue d'ensemble
```
Phase A (Fondations logicielles) ──► Phase B (Cœur famille) ──► Phase C (Finitions)
edge + cœur + IA + 3-2-1 + ops PhotoPrism + NC + Jellyfin Matrix + HA +
(avec R5 Ph0) + Vaultwarden + Gitea + n8n enfants + décommission
(avec R5 Ph1) (avec R5 Ph2)
```
> **Lien avec R5** : R5 = **machine** (fondations, cœur, finitions). Cette section = **logiciel** (les briques, dans l'ordre). **Un seul plan, deux couches** (comme le guide culture, C1-C5).
---
## S14 — Grille des décisions + recommandations
> **Statut** : ✅ **Tranchées** (05/10/2026) — chaque décision a une **grille d'évaluation remplie** + une **recommandation**. Répond à l'**AC9**.
### 14.1 Photos (R6.1)
| Critère | PhotoPrism ✅ | Immich | Nextcloud Photos |
|---------|---------------|--------|------------------|
| Lecture in-place | ✅ | ❌ (importe) | 🟡 |
| IA (visages, sémantique) | ✅ | ✅ | ❌ |
| Partage famille | ✅ | ✅ | ✅ |
| Auto-upload Android | ✅ | ✅ | 🟡 |
| Sobriété (G9 16 Go) | ✅ | 🟡 | ✅ |
| Licence | AGPL-3.0 | AGPL-3.0 | AGPL-3.0 |
| Sortie | ✅ (originaux intacts) | 🟡 | ✅ |
| **Verdict** | **✅ Recommandé** | ❌ (désorganise) | ❌ (pas d'IA) |
> **Recommandation** : **PhotoPrism** (lecture in-place + IA + sobre).
### 14.2 Notes / second cerveau (R6.2)
| Critère | Obsidian+Gitea+Qdrant ✅ | Nextcloud Notes | Joplin | Affine |
|---------|--------------------------|-----------------|--------|--------|
| Versioning | ✅ (git) | ❌ | 🟡 | 🟡 |
| Structure liée (wikilinks) | ✅ | ❌ | 🟡 | ✅ |
| Lisible par l'IA (RAG) | ✅ (md + Qdrant) | 🟡 | 🟡 | ❌ |
| Format ouvert | ✅ (md) | 🟡 | ✅ | 🟡 |
| Échelle (une vie) | ✅ | ❌ | 🟡 | 🟡 |
| **Verdict** | **✅ Recommandé** | ❌ (mauvais fit store) | ❌ (silo) | ❌ (R&D) |
> **Recommandation** : **Obsidian** (viewer) + **Gitea** (store) + **Qdrant** (index) pour le **second cerveau** ; **Nextcloud Notes** pour les **notes rapides famille**.
### 14.3 Mail
| Critère | Nextcloud Mail (IMAP) ✅ | Serveur SMTP local |
|---------|--------------------------|--------------------|
| Fiabilité livraison | ✅ (Gmail/Hotmail) | ❌ (SPF/DKIM/DMARC, spam) |
| Complexité | ✅ (faible) | ❌ (élevée) |
| Pérennité | ✅ | 🟡 (risque spam) |
| **Verdict** | **✅ Recommandé** | ❌ (trop risqué en prod) |
> **Recommandation** : **Nextcloud Mail** (lecture IMAP) + **livraison externe** (Gmail/Hotmail). Dégafamisation complète du mail = **horizon > 1 an**.
### 14.4 Frontend IA
| Critère | OpenWebUI (1 seul) ✅ | Dify | mcpo+pipelines |
|---------|----------------------|------|----------------|
| Sobriété | ✅ | ❌ (14 conteneurs) | 🟡 |
| Simplicité famille | ✅ | 🟡 | ❌ |
| Pérennité | ✅ | 🟡 (R&D) | 🟡 |
| **Verdict** | **✅ Recommandé** | ❌ | ❌ |
> **Recommandation** : **1 seul OpenWebUI** (front famille) + **hermes** (harnais agent).
### 14.5 Communication (Matrix)
| Critère | Matrix+Jitsi+Element ✅ | WhatsApp | Signal |
|---------|------------------------|----------|--------|
| OSS / souverain | ✅ | ❌ | ✅ |
| Visio intégrée | ✅ (Jitsi) | ✅ | 🟡 |
| Fédéré / auto-hébergé | ✅ | ❌ | 🟡 |
| **Verdict** | **✅ Recommandé** | ❌ (GAFAM) | 🟡 (pas d'auto-hébergement facile) |
> **Recommandation** : **Matrix (Synapse) + Jitsi + Element** (remplace WhatsApp).
### 14.6 MDM enfants
| Critère | Family Link ✅ (maintenant) | MicroMDM / Android Enterprise (future) |
|---------|------------------------------|----------------------------------------|
| Simplicité | ✅ | 🟡 |
| OSS | ❌ (Google) | ✅ |
| Contrôle parental | ✅ | ✅ |
| **Verdict** | **✅ Recommandé (maintenant)** | 🟡 (option future) |
> **Recommandation** : **Google Family Link** (maintenant, usage parental) + **MDM OSS** (MicroMDM/Android Enterprise) en **option future**.
---
## S15 — Critères d'acceptance (AC1–AC10)
> **Statut** : ✅ **Tous remplis** par ce dossier. C'est la **preuve** que la table rase est **complète**.
| AC | Critère | Où c'est rempli |
|----|---------|-----------------|
| **AC1** | Chaque besoin famille/perso mappé sur **UNE brique + UNE machine** (pas d'orphelin, pas de doublon) | **S3.1** (tableau 28 briques, 1 besoin = 1 brique = 1 machine — **F9 recherche web = SearXNG** (S3.1 #28) ; **F15 RSS = suppression explicite** (S3.2, chemin de ré-add documenté)) + **S2.2** (rôle par machine) |
| **AC2** | Stack consolidée **≤ ~20 briques** + **liste de suppression explicite** | **S3.1** (~20 briques) + **S3.2** (liste de suppression : Dify, RAGFlow, STT/OCR dupliqués, conteneurs morts, OpenWebUI multiples → 1 seul) |
| **AC3** | Chaque outil **justifié** (maturité, maintenance, ressource, licence OSS, chemin de sortie) | **S3.1** (colonnes licence + sortie) + **S3.3** (justification détaillée par brique) |
| **AC4** | **Budget ressource** par machine tenable (J5040 ~7-9/16, G9 ~8-11/16, PC IA ~53/62, Freebox léger) | **S2.4** (tableau budget ressource par machine) |
| **AC5** | **3-2-1** avec **restic/borg** (chiffré, append-only) + **procédure de restore testée** (LUKS source abandonné 10/10/2026 — chiffrement porté par restic) | **S10** (restic/borg + procédure de restore testée §10.4) |
| **AC6** | Modèle **sécurité** (Caddy+TLS auto, WireGuard, firewall strict, copies chiffrées, 2FA, sous-domaines folzocorp.fr) | **S9** (modèle de sécurité couche par couche + sous-domaines §9.2) |
| **AC7** | **Plan de migration** R&D→production, **phased**, avec **critères de succès** par phase | **S13** (Phase A/B/C, critères de succès par action) |
| **AC8** | **Résilience/adoption** — chaque service a un **utilisateur nommé + appareil + usage quotidien** | **S3.4** (briques edge & infra) + **S5.3, S6.4, S7.3, S8.2, S12.3** (tableaux d'usage par utilisateur/appareil/usage) |
| **AC9** | **Décisions** (photos, sync notes, mail, frontend IA, Matrix, MDM) = **grille d'évaluation remplie + recommandation** | **S14** (6 grilles d'évaluation + recommandations) |
| **AC10** | **100% open source**, **licence notée par service** | **S3.1** (colonne licence par brique) + **S3.1 note** (honnêteté : n8n = fair-code, non-OSI, noté) |
> **Bilan** : les **10 critères** sont **tous remplis** par ce dossier. La **table rase** est **complète** : architecture ciblée, décisions tranchées, plan de migration posé, licences notées, budget ressource validé, sécurité + backup + ops + enfants couverts.
---
## Changelog
| Date | Version | Changement |
|------|---------|------------|
| 10/10/2026 | **v1.5** | **S10.2 — copie 2 (Freebox) EN PLACE sur le G9** : restic 0.19.1 + rclone 1.75.2, repo `rclone:freeboxftp:Freebox/backup/g9` (FTP local 21), snapshot 4.18 GiB, **append-only prouvé**, **restore testée** (sha256), cron 03:30/04:00/09:15 + alerte Matrix ; **mont CIFS abandonné** (restic/`chmod`, FUSE) → backend rclone, **persistance post-reboot sans montage**. **Phase 5 planifiée** (non réalisée) : VM Freebox + `rest-server --append-only` (autostart à valider), `restic copy` → 218play, identifiants `NoAccess`, chroot durci. |
| 06/10/2026 | **v1.4** | **Cohérence inter-dossiers (source unique)** : (1) **S10.2** — le schéma 3-2-1 ne **re-déclare plus** le tableau → **pointeur** vers `DOSSIER-RECREATION.md` §R4 (source unique) + note outil (restic, quotidien/hebdo) ; (2) **S4.3** — le modèle 2 couches ne **re-déclare plus** le principe → **pointeur** vers `PROJET-SECOND-CERVEAU.md` S4.1 (source unique) + tableau **mapping briques/machines** (spécificité du dossier logiciel) ; (3) **S3.1** — le tableau est **cadré autour des briques fonctionnelles** : la stack \*arr #10 = **1 brique**, les apps Nextcloud 13-15 marquées **↳ apps du socle NC #12** ; note de comptage précisée (**28 lignes = 25 briques fonctionnelles**, dans la cible ≤ ~20 briques de consolidation). |
| 06/10/2026 | **v1.3** | **Correction mainteneur hermes** : S3.1 #24 — « maintenu (Nous Research) » → « **maintenu (nesquena — `ghcr.io/nesquena/hermes-webui`)** », aligné sur l'image réellement en production (snapshot `ETAT-DE-LART-2026-09-30.md` §2.1). |
| 06/10/2026 | **v1.2** | **S11 Ops — Watchtower sélectif** (après l'**incident Nextcloud** du 06/10, documenté dans `ETAT-DE-LART-2026-10-06.md` §3) : correction de la règle **stateless = auto / stateful = exclu + MAJ manuelle** (S11.4) — suppression de l'adjectif « testées » (Watchtower ne teste rien) ; **liste des briques stateful à exclure** (Nextcloud, MariaDB, Redis, Vaultwarden, Gitea, n8n+Postgres, InfluxDB, OnlyOffice/Collabora, Leantime+MySQL, Affine+Postgres+Redis, Joplin+Postgres, Qdrant) ; config Watchtower cible (**pinning des majeures**, `CLEANUP=false`, notifications n8n/Matrix) ; **écart actuel NAS** (0 exclusion, 0 notification, `CLEANUP=true`, `:latest`) = action corrective. **Nouvelle procédure de MAJ manuelle stateful** (S11.5, 7 étapes + rollback, règle « pas de MAJ stateful sans backup »). **S13** : prérequis absolu « backup 3-2-1 fonctionnel AVANT toute migration » + **action corrective immédiate** (exclure les stateful de Watchtower sur le NAS actuel) + **Phase A.0** (sécuriser le NAS + backup). S11 statut + cohérence interne vérifiés. |
| 06/10/2026 | **v1.1** | **Correction AC1** (besoins orphelins) : **SearXNG** mappé dans la stack consolidée (S3.1 #28, edge J5040 — S2.2, S2.4, S3.4, S13 A.1) ; **FreshRSS** ajouté à la liste de suppression explicite (S3.2 — pas d'usage quotidien nommé, podcasts RSS couverts côté client par AntennaPod, chemin de ré-add documenté). S15 (AC1/AC8) mis à jour. |
| 05/10/2026 | **v1.0** | **Création du dossier Partie 5 (couche logicielle)** — table rase : 6 principes (S1), architecture cible **Option A** (S2, PC IA garde la RAM, Matrix+Jitsi sur J5040, HA sur Freebox), **stack consolidée ~20 briques** (S3) + **liste de suppression** (Dify, RAGFlow, dupliqués, morts), **bloc IA** (S4, Ollama+Qwen 3.8max+Qdrant+hermes+OpenWebUI), **PhotoPrism** (S5), **Nextcloud** socle (S6), **Jellyfin+\*arr+AntennaPod** (S7), **Matrix+Jitsi+Element** (S8), **sécurité** (S9, Caddy+WireGuard+LUKS+2FA+sous-domaines), **backup 3-2-1 restic/borg** (S10), **ops** (S11, Compose+Gitea+Watchtower+Beszel), **enfants/MDM** (S12), **plan de migration A/B/C** (S13), **grille des décisions** (S14), **AC1–AC10** (S15). Licences notées par brique (honnêteté : n8n = fair-code). |
+349
View File
@@ -0,0 +1,349 @@
# État de l'art — Infrastructure Folzo Home
**Dernière mise à jour** : 30/09/2026
**Réseau** : `192.168.1.0/24`
**Objectif** : Audit complet (hardware, disques, services, réseau, problèmes) avant définition des besoins et restructuration.
> ⚠️ **Document supplanté** : cet état de l'art est un **snapshot daté** (référence matérielle à la date du scan). Pour les **décisions, corrections et le plan de (re)création**, voir **`DOSSIER-RECREATION.md`** (v2, source de vérité courante, vivante). Les specs matérielles détaillées (disques, conteneurs) restent ici comme référence ; les rôles cibles et le plan y sont supplantés. *(Le dossier v1 `archive/DOSSIER-INFRA.md` est lui-même supplanté par la v2.)*
> **Modèle de document** : cet état de l'art est un **snapshot daté** (le nom de fichier porte la date du scan). Il fait référence **à la date indiquée**, pas en continu. Après la restructuration, chaque évolution majeure produit un **nouveau fichier daté** ; le plus récent est la référence courante. Les données proviennent d'un scan live (30/09/2026) : `lscpu`, `free`, `df`, `docker ps`, `nvidia-smi`, `systemctl`, sondes réseau, et commandes DSM exécutées manuellement sur le xpenology. **Ce snapshot est figé à la date du scan (30/09/2026)** — les faits mesurés après (diagnostic USB 01/10, incident Nextcloud + état HyperBackup + accès SSH 06/10) sont dans le snapshot suivant : **`ETAT-DE-LART-2026-10-06.md`**.
---
## Sommaire
1. [Topologie réseau](#1-topologie-réseau)
2. [Serveurs](#2-serveurs)
3. [Inventaire disques](#3-inventaire-disques)
4. [Freebox Delta](#4-freebox-delta--1921681254)
5. [PC & périphériques](#5-pc--périphériques)
6. [Services IA cartographiés](#6-services-ia-cartographiés)
7. [Problèmes identifiés](#7-problèmes-identifiés-par-priorité)
8. [Culture de la donnée](#8-culture-de-la-donnée)
9. [Vision cible](#9-vision-cible)
10. [Migration xpenology → Mini PC i3-13100T](#10-migration-xpenology--mini-pc-i3-13100t-à-étudier)
11. [Prochaines étapes](#11-prochaines-étapes-phase-2--à-définir)
---
## 1. Topologie réseau
### 1.1 Mapping IP → équipement
| IP | OUI / Fabricant | Nom | Rôle |
|----|-----------------|-----|------|
| 192.168.1.1 | — | **AdGuard Home** | DNS interne, blocage pubs, filtre enfants |
| 192.168.1.2 | Netgear | Switch | Commutateur |
| 192.168.1.3 | ASUS | Switch | Commutateur |
| 192.168.1.5 | Xikestor | Switch | Commutateur |
| 192.168.1.11 | — | **folzo-corp** | Station GPU — IA (llama.cpp, Ollama, STT, OCR) |
| 192.168.1.15 | — | PC Ewan | Poste famille |
| 192.168.1.16 | — | PC Noah | Poste famille |
| 192.168.1.17 | — | PC famille | Poste famille |
| 192.168.1.20 | — | **Home Assistant** | VM Freebox (IP fixe .20) |
| 192.168.1.21 | Diagral | **Alarma** | Centrale d'alarme |
| 192.168.1.25 | Synology | **xpenology / NVR** | NVR DVA1622 + conteneurs + disques |
| 192.168.1.26 | Synology | **218play** | NAS backup (2× 2 To) |
| 192.168.1.27 | — | **effi-server** | Serveur Docker principal (IA/RAG/Dify) |
| 192.168.1.41 | Reolink | **NVR Reolink #1** | Enregistreur vidéo (RTSP :554, :8000) |
| 192.168.1.123 | Reolink | **NVR Reolink #2** | Enregistreur vidéo |
| 192.168.1.191 | Reolink | **NVR Reolink #3** | Enregistreur vidéo (RTSP :554, :8000) |
| 192.168.1.51 | — | Shield TV | Android TV |
| 192.168.1.52 | — | Shield TV | Android TV |
| 192.168.1.53 | — | Freebox Player Pop | Boîtier TV |
| 192.168.1.54 | — | Denon 2400H | Ampli home-cinéma |
| 192.168.1.55 | — | Mi Box | Boîtier TV |
| 192.168.1.56–.59 | — | Périphériques | TV / boîtiers / divers |
| 192.168.1.254 | Free | **Freebox Delta** | Routeur / box (VMs HA + AdGuard) |
**Notes topologiques**
- La **Freebox Delta** est le routeur (`.254`). **AdGuard Home** est un service séparé sur `.1` (DNS).
- **3 NVR Reolink** (`.41`, `.123`, `.191`) — découverts lors de l'audit. Ce sont des enregistreurs vidéo autonomes, **pas** des VMs oubliées.
- **Home Assistant** tourne en VM sur la Freebox, IP fixe `.20`.
- **Valheim ×2 n'est PAS une erreur** : un monde est la sauvegarde du monde pré-1.0, l'autre est le monde actuel. Les joueurs conseillaient d'en recréer un.
---
## 2. Serveurs
### 2.1 effi-server — `192.168.1.27`
**Hardware**
- OS : Ubuntu 24.04.4 LTS
- CPU : Intel Pentium Silver J5040 @ 2.00 GHz — 4 cœurs / 4 threads
- RAM : 7.6 GiB (3.9 GiB utilisés au scan)
- Disque : LVM `ubuntu--vg-ubuntu--lv` — **98 Go, 87 Go utilisés, 6.0 Go libres, 94%**
**Rôle** : Serveur Docker principal — IA / RAG / Dify / orchestration.
**Conteneurs Docker (34)**
| Conteneur | Image | Port(s) | Statut | Rôle |
|-----------|-------|---------|--------|------|
| open-webui | ghcr.io/open-webui/open-webui:main | 3000 | Up (healthy) | Interface LLM |
| llm-proxy | effi-llm-proxy | 3012 (interne, non publié) | Up | Proxy LLM (LiteLLM) |
| qdrant | qdrant/qdrant:latest | 6333-6334 | Up | Vector DB (RAG) |
| n8n | docker.n8n.io/n8nio/n8n:latest | 5678 | Up | Automatisation |
| hermes-webui | ghcr.io/nesquena/hermes-webui:latest | 8787 | Up (healthy) | WebUI agent |
| effi-searxng | searxng/searxng:latest | 11112 | Up | Moteur de recherche |
| mcpo | ghcr.io/open-webui/mcpo:main | 8000 | Up | MCP orchestrator |
| pipelines | ghcr.io/open-webui/pipelines:main | 9099 | Up | Pipelines OpenWebUI |
| whisper | fedirz/faster-whisper-server:latest-cpu | 8200 | Up | STT |
| openedai-speech | ghcr.io/matatonic/openedai-speech | 8100 | Up | TTS |
| effi-ocr | effi-ocr-effi-ocr | 8301 | Up | OCR |
| effi-leantime-mcp | effi-leantime-mcp:latest | 8079 | Up | MCP Leantime |
| couchdb | couchdb:3.3 | 5984 | Up | Obsidian LiveSync |
| obsidian | lscr.io/linuxserver/obsidian:latest | 3100 | Up | Obsidian |
| watchtower | nickfedor/watchtower:latest | — | Up (healthy) | Mises à jour auto |
| beszel-agent | henrygd/beszel-agent:latest | — | Up | Monitoring |
| **Dify (14 conteneurs)** | langgenius/dify-* :1.16.1 | 3001 (nginx), 5003 | Up | Plateforme IA/agents |
| pterodactyl-panel-1 | ghcr.io/pterodactyl/panel:v1.15.1 | 8085 | Up | Panel de jeux |
| pterodactyl-database-1 | mariadb:11 | 3306 | Up | DB Pterodactyl |
| pterodactyl-cache-1 | redis:alpine | 6379 | Up | Cache Pterodactyl |
| valheim (aff5199c…) | valheim-pterodactyl:1 | 2456-2458 | Up (healthy) | **Valheim monde actuel** |
**Dify** : `docker-api-1`, `docker-worker-1`, `docker-worker_beat-1`, `docker-web-1`, `docker-agent_backend-1`, `docker-plugin_daemon-1`, `docker-db_postgres-1`, `docker-redis-1`, `docker-pgvector-1`, `docker-sandbox-1`, `docker-local_sandbox-1`, `docker-agent_ssrf_proxy-1`, `docker-ssrf_proxy-1`, `docker-nginx-1`.
---
### 2.2 folzo-corp — `192.168.1.11`
**Hardware**
- OS : Fedora Linux 44 (Workstation)
- CPU : AMD Ryzen 7 5800X3D — 8 cœurs / 16 threads
- RAM : 62 GiB (27 GiB utilisés au scan)
- Disque : NVMe `nvme0n1p3` (Samsung 990 PRO) — **1.9 To, 885 Go utilisés, 974 Go libres, 48%**
- GPU : **2× NVIDIA GeForce RTX 3060 (12288 MiB chacune)** — 11052 MiB et 11016 MiB utilisés au scan
> ⚠️ Les docs existantes indiquaient « AMD RX 7800 XT (16 Go) » et « 32 Go RAM ». La réalité : **2× RTX 3060 12 Go** et **62 Go RAM**. → Voir section « Culture de la donnée ».
**Rôle** : Station GPU dédiée IA.
**Services (systemd / natifs)**
| Service | Port | Statut | Rôle |
|---------|------|--------|------|
| llama-server (llama.cpp) | 8081 | **active** (health OK) | LLM principal (Swift-1.5 Qwen 27B IQ4_XS) — 10.5 Go VRAM |
| Ollama | 11434 | **active** (v0.30.10) | 28 modèles (vision, embeddings, legacy) |
| ComfyUI | 8188 | **inactive** (port mort) | Génération d'images — **à vérifier** |
| effi-services.service | — | **failed** | Bundle de services — **à réparer** |
**Conteneurs Docker (9)**
| Conteneur | Image | Port(s) | Statut | Rôle |
|-----------|-------|---------|--------|------|
| effi-stt | effi-stt-effi-stt | 8200 | Up (**unhealthy**) | STT |
| effi-ocr | effi-ocr-effi-ocr | 8301 | Up (**unhealthy**) | OCR |
| kreuzberg | ghcr.io/kreuzberg-dev/kreuzberg:latest | 8300 | Up (healthy) | OCR PaddleOCR FR |
| docker-ragflow-cpu-1 | infiniflow/ragflow:v0.26.4 | — | **Restarting (126)** | RAG — **crash loop** |
| docker-mysql-1 | mysql:8.0.39 | 3306 | Up (healthy) | DB RAGFlow |
| docker-es01-1 | elasticsearch:8.11.3 | 1200 | Up (healthy) | Recherche RAGFlow |
| docker-minio-1 | pgsty/minio | 9000-9001 | Up (healthy) | Stockage RAGFlow |
| docker-redis-1 | valkey/valkey:8 | 6379 | Up (healthy) | Cache RAGFlow |
| beszel-agent | henrygd/beszel-agent-nvidia:latest | — | Up | Monitoring |
- **Modèles GGUF** : `/home/folzo/models` — **98 Go**.
- **GPU en cours** : 2× `llama-server` (llama.cpp) ~10.5 Go chacun, Ollama llama-server résiduel.
- **Arrêté** : `openedai-speech` (*Exited* 137, il y a 6 semaines).
---
### 2.3 xpenology / NVR — `192.168.1.25`
**Hardware**
- OS : Synology DSM (NVR)
- Modèle : **Synology NVR DVA1622** (système épuré — pas de shell interactif SSH, accès via terminal DSM)
- CPU : Intel Core i5-7500T — 4 cœurs / 8 threads
- RAM : 15 Go
- Disques : voir §3
> **Accès SSH** (documenté 06/10/2026) : voir le snapshot suivant **`ETAT-DE-LART-2026-10-06.md` §1**.
**Rôle** : NVR (Surveillance Station) + conteneurs + stockage.
**Conteneurs Docker (~30)**
| Conteneur | Port | Rôle |
|-----------|------|------|
| Leantime | 8380 | Gestion de projet |
| Nextcloud | 8087 | Cloud / drive |
| Affine | 3010 | Workspace |
| Joplin | 22300 | Notes |
| OnlyOffice | 8088 | Bureautique |
| Vaultwarden | 3013 | Gestionnaire de mots de passe |
| Gitea | 3033 | Git |
| n8n | 5678 | Automatisation |
| InfluxDB | 8086 | Métriques |
| Beszel Hub | 8090 | Monitoring |
| OpenWebUI | — | Interface LLM |
| Valheim | — | **Valheim monde backup pré-1.0** |
| Sonarr / Radarr / Prowlarr / Jackett | — | Media management |
| qBittorrent / Deluge | — | Torrents |
| Watchtower | — | Mises à jour |
| Flaresolverr | — | Anti-Cloudflare |
| mini-qr | 8014 | QR |
| **Morts** : wg-easy, mcpo, youtubedl, wol-proxy, xteve | — | À trier |
**Shares Docker** : docker, docker-REC, Download, Favoris, gitea, homes, music, NetBackup, photo, rr, Save, surveillance, web, web_packages.
---
### 2.4 218play — `192.168.1.26`
**Hardware**
- OS : Synology DSM
- Disques : **2× 2 To** (à confirmer en RAID)
- Ports ouverts : 139, 445 (SMB), 80/443 → redirection DSM (port 14351)
**Rôle** : **NAS de backup**.
---
## 3. Inventaire disques
### 3.1 Par machine
| Machine | Disque | Taille | Utilisé | Libre | Usage | Mount |
|---------|--------|--------|---------|-------|-------|-------|
| effi-server | LVM ubuntu-lv | 98 Go | 87 Go | 6.0 Go | **94%** | `/` |
| folzo-corp | NVMe 990 PRO | 1.9 To | 885 Go | 974 Go | 48% | `/` |
| xpenology | md0 (système) | 7.9 Go | 3.7 Go | 4.1 Go | 48% | `/` |
| xpenology | volume1 (vg1) | 1.8 To | 1.1 To | 711 Go | 61% | `/volume1` |
| xpenology | volume2 (vg2) | 1.8 To | 729 Go | 1.1 To | 41% | `/volume2` |
| xpenology | **USB1** (usb1p1) | **15 To** | **14 To** | **1.3 To** | **92%** | `/volumeUSB1/usbshare` |
| xpenology | **USB2** (usb2p2) | **5.5 To** | **5.1 To** | **456 Go** | **92%** | `/volumeUSB2/usbshare` |
| xpenology | **USB3** (sda2) | **3.6 To** | **3.2 To** | **519 Go** | **87%** | **NON MONTÉ** (secteurs HS) |
| 218play | 2× 2 To | ~4 To | à confirmer | à confirmer | — | — |
| Freebox Delta | 3× 1 To RAID5 | 2 To | à confirmer | à confirmer | — | — |
| Laptop | SSD 1 | 2 To | à confirmer | à confirmer | — | — |
| Laptop | SSD 2 | 1 To | à confirmer | à confirmer | — | — |
| Mini PC HP | (à préciser) | — | — | — | — | — |
> **⚠️ Suivi post-scan (01/10 & 06/10/2026)** : le diagnostic du **disque USB 3 backup hors service** (01/10), l'**incident Nextcloud** causé par Watchtower (06/10), l'**état du backup HyperBackup** (échec données depuis 01/10/2024) et l'**accès SSH** au xpenology (06/10) sont documentés dans le snapshot suivant : **`ETAT-DE-LART-2026-10-06.md`** (§1–§4). Ce snapshot 30/09 est figé à la date du scan.
---
## 4. Freebox Delta — `192.168.1.254`
- **Rôle** : Routeur / box (IP `.254`).
- **VMs** : Home Assistant (`.20`) + AdGuard Home (`.1`).
- **Disques** : 3× 1 To en RAID5 = 2 To (backup, peu utilisé).
- **⚠️ Problème thermique** : la Freebox monte à **85°C** — usure / risque de throttling. À optimiser (ventilation, placement, profils thermiques).
---
## 5. PC & périphériques
| Équipement | Hardware | Rôle actuel | Rôle cible |
|------------|----------|-------------|------------|
| **Laptop** | Ryzen 7 7800H, 32 Go, 2 To + 1 To SSD, RTX 3050 Ti | Workstation principale | Workstation principale |
| **BC 250** | (à préciser) | — | **Steam Machine** (jeu) |
| **Mini PC HP** | i3-13100T, 16 Go RAM | Non branché | **Remplacement xpenology** (cœur famille) |
| Shield TV ×2 | — | Streaming | — |
| Freebox Player Pop | — | TV | — |
| Denon 2400H | — | Audio | — |
| Mi Box | — | TV | — |
| Alarma Diagral (.21) | — | Alarme | — |
| Switches (.2/.3/.5) | Netgear / ASUS / Xikestor | Réseau | — |
> **BC 250** : nom de code d'un PC de la maison, candidat au rôle de **Steam Machine** (jeu). Hardware à préciser lors de l'audit.
>
> Le **Mini PC HP Pro 400 G9 (i3-13100T)** est le remplaçant du xpenology (cœur famille). À étudier — voir §10. *(Spec corrigée : les docs héritées disaient i3-12100.)*
---
## 6. Services IA cartographiés
| Service | Machine | Port | Modèle / Détail |
|---------|---------|------|-----------------|
| llama-server (llama.cpp) | folzo-corp | 8081 | Swift-1.5 Qwen 27B IQ4_XS — 10.5 Go VRAM |
| Ollama | folzo-corp | 11434 | v0.30.10 — 28 modèles (vision, embeddings, legacy) |
| effi-stt (Whisper) | folzo-corp | 8200 | STT (unhealthy) |
| whisper (faster-whisper) | effi-server | 8200 | STT |
| effi-ocr | folzo-corp + effi-server | 8301 | OCR |
| kreuzberg | folzo-corp | 8300 | PaddleOCR FR |
| openedai-speech | effi-server | 8100 | TTS (voix Effi) |
| llm-proxy (LiteLLM) | effi-server | 3012 (interne, non publié) | Point d'entrée LLM unique |
| OpenWebUI | effi-server | 3000 | Interface |
| Qdrant | effi-server | 6333 | Vector DB (RAG, bge-m3) |
| Dify | effi-server | 3001 | Plateforme agents |
| RAGFlow | folzo-corp | — | RAG (crash loop) |
| ComfyUI | folzo-corp | 8188 | Images (à vérifier) |
| Modèles GGUF | folzo-corp | — | 98 Go dans `/home/folzo/models` |
---
## 7. Problèmes identifiés (par priorité)
| # | Priorité | Problème | Machine | Détail |
|---|----------|----------|---------|--------|
| 1 | 🔴 CRITIQUE | **Disque de backup USB non reconnu** | xpenology | Le 3e USB (backup du Synology) n'est pas monté → **pas de backup** |
| 2 | 🔴 CRITIQUE | **Freebox Delta à 85°C** | Freebox | Usure thermique, risque throttling |
| 3 | 🟠 Haute | **RAGFlow crash loop** | folzo-corp | `Restarting (126)` — entrypoint permission denied |
| 4 | 🟠 Haute | **effi-services.service FAILED** | folzo-corp | Bundle de services en échec |
| 5 | 🟠 Haute | **effi-server disque à 94%** | effi-server | 6.0 Go libres seulement |
| 6 | 🟡 Moyenne | **ComfyUI port mort** | folzo-corp | Port 8188 inactif — à vérifier (probablement OK) |
| 7 | 🟡 Moyenne | **effi-stt / effi-ocr unhealthy** | folzo-corp | Conteneurs up mais statut unhealthy |
| 8 | 🟡 Moyenne | **Conteneurs morts NAS** | xpenology | wg-easy, mcpo, youtubedl, wol-proxy, xteve |
| 9 | 🟡 Moyenne | **~18 modèles Ollama legacy** | folzo-corp | À trier (98 Go de GGUF au total) |
| 10 | 🟡 Moyenne | **USB1/USB2 à 92%** | xpenology | 15 To + 5.5 To quasi pleins |
| 11 | 🟢 Basse | **Docs obsolètes** | — | GPU / RAM inexactes → culture de la donnée |
| 12 | 🟢 Basse | **Token HA perdu** | — | Non relocalisable. À régénérer dans HA (Profil → Jetons d'accès longue durée). |
---
## 8. Culture de la donnée
- **Docs existantes inexactes** : `effi/README.md` et mémoires indiquent « GPU RX 7800 XT (16 Go) » et « 32 Go RAM » pour folzo-corp. Réel : **2× RTX 3060 12 Go** + **62 Go RAM**. → Corriger les sources.
- **Token Home Assistant** : perdu (non relocalisable). À régénérer dans HA (Profil → Jetons d'accès longue durée).
- **Dispersion** : infos éparpillées entre `effi/README.md`, mémoires Hermes, docs ad-hoc (`DIAGNOSTIC-KREUZBERG.md`, etc.). Ce document doit devenir la **source de vérité unique**.
- **Principe** : noter la réalité mesurée (scan live) plutôt que la documentation héritée.
---
## 9. Vision cible
> ⚠️ **Supplanté** : la vision cible et les rôles machines sont désormais dans `DOSSIER-RECREATION.md` §R2. Cette section est conservée comme trace du scan initial.
| Machine | Rôle cible |
|---------|-----------|
| **folzo-corp** (2× 3060) | **100% dédié IA** |
| **Laptop** (7800H + 3050 Ti) | **Workstation principale** |
| **BC 250** | **Steam Machine** (jeu) |
| **Mini PC HP** (i3-13100T) | **Cœur famille** (photos, fichiers, média) — remplace le xpenology |
| **218play** | **Backup** |
| **Freebox Delta** | **HA + AdGuard** (optimisation thermique) |
| **xpenology / NVR** | NVR + apps (jusqu'à migration) |
| **effi-server** | IA / RAG / Dify |
---
## 10. Migration xpenology → Mini PC i3-13100T (à étudier)
**Objectif** : migrer le xpenology vers le **Mini PC HP Pro 400 G9 (i3-13100T, 16 Go RAM)** sur la même IP (.25). Le G9 devient le **cœur famille** (photos, fichiers, notes, mots de passe, média, domotique, caméras). Le xpenology est **décommissionné** à l'issue de la migration (plus de DSM, plus de risque MAJ, plus de zone grise légale). La surveillance migre vers le NAS (Frigate) si souhaité. *(Supplanté par `DOSSIER-RECREATION.md` §R2.)*
**Conteneurs candidats à la migration (non-IA)**
- Leantime, Nextcloud, Affine, Joplin, OnlyOffice, Vaultwarden, Gitea, InfluxDB, Watchtower, Flaresolverr, mini-qr
- Sonarr / Radarr / Prowlarr / Jackett / qBittorrent / Deluge (multimédia)
- **Jellyfin (nouveau sur le G9 — transcodage)** — *note : Jellyfin n'est PAS dans l'inventaire des conteneurs xpenology actuels (§2.3) ; c'est une installation neuve sur le G9*
**Fin de vie du xpenology**
- Le xpenology est **décommissionné** après migration (plus de DSM).
- La surveillance (NVR Reolink autonomes) reste ; la vision IA migre vers le G9 (Frigate) si souhaité.
**Points d'attention**
- 16 Go RAM du Mini PC : dimensionner la charge (Nextcloud + OnlyOffice + Jellyfin = gourmands).
- Disques : où stocker les données multimédia (USB 15 To / 5.5 To ?).
- Le backup USB HS (snapshot `ETAT-DE-LART-2026-10-06.md` §2) doit être résolu **avant** toute migration.
---
## 11. Prochaines étapes (Phase 2 — à définir)
> ⚠️ **Supplanté** : le plan de (re)création est désormais dans `DOSSIER-RECREATION.md` §R5. Cette section est conservée comme trace du scan initial.
1. **Résoudre les 2 critiques** : disque backup USB + thermique Freebox.
2. **Définir les besoins** : projet de culture de la donnée, projet Effi, optimisations.
3. **Trier les ballasts** : RAGFlow, ComfyUI, Ollama legacy, conteneurs morts NAS.
4. **Planifier la restructuration** : migration xpenology → i3-13100T (G9), spécialisation folzo-corp en IA.
5. **Réconcilier la documentation** : ce document = source de vérité, corriger les sources héritées.
+113
View File
@@ -0,0 +1,113 @@
# État de l'art — Infrastructure Folzo Home (suivi 06/10/2026)
**Dernière mise à jour** : 06/10/2026
**Réseau** : `192.168.1.0/24`
**Base** : `ETAT-DE-LART-2026-09-30.md` (scan live 30/09/2026 — specs hardware, disques, conteneurs, réseau).
> **Modèle de document** : **snapshot daté** — référence matérielle à la date indiquée (06/10/2026). Il **complète** le snapshot 30/09 avec les faits mesurés entre le 30/09 et le 06/10 (diagnostic USB, incident Nextcloud, état HyperBackup, accès SSH). Chaque évolution majeure produit un **nouveau fichier daté** ; le plus récent est la référence courante. Pour les **décisions, corrections et le plan de (re)création**, voir **`DOSSIER-RECREATION.md`** (v2) et **`DOSSIER-SOFTWARE.md`** (Partie 5).
---
## Sommaire
1. [Accès SSH au xpenology](#1-accès-ssh-au-xpenology)
2. [Disque USB 3 — BACKUP HORS SERVICE](#2-disque-usb-3--backup-hors-service)
3. [Incident Nextcloud — mise à jour non supervisée par Watchtower](#3-incident-nextcloud--mise-à-jour-non-supervisée-par-watchtower)
4. [État du backup — HyperBackup en échec](#4-état-du-backup--hyperbackup-en-échec)
---
## 1. Accès SSH au xpenology
**Documenté** : 06/10/2026 (scan live)
| Champ | Valeur |
|-------|--------|
| **Connexion** | `ssh -p 222 Effi@192.168.1.25` (mot de passe) |
| **Utilisateur** | `Effi` (uid 1043) — groupe **`administrators`** → **`sudo` disponible** (mot de passe) |
| **Docker** | **Pas dans le PATH** : binaire à `/var/packages/ContainerManager/target/usr/bin/docker` (symlink ContainerManager). À invoquer **via `sudo`** (socket `/var/run/docker.sock` = root). |
| **Gestion des conteneurs** | **Majorité gérée via la GUI** (ContainerManager) — seulement **12 fichiers `compose`** dans `/volume1/docker/` (Freshrss, immish-app, Docmost, openwebui, watchtower, mini-qr, postgres16, Gitea, beszel, beszel-agent, Leantime). Les conteneurs **stateful** (Nextcloud, MariaDB, Redis, Vaultwarden, n8n, InfluxDB, OnlyOffice, Affine, Joplin…) sont **créés via GUI** (pas de `compose` versionné). |
> **Conséquence ops** : l'absence de `compose` versionné sur la majorité des briques stateful est **exactement** ce qui a permis l'incident Nextcloud (§3) — pas de déclaration, pas de pinning, pas de migration supervisée. → **Action** : **re-déclarer** les briques stateful en **`docker-compose.yml`** (pinning + labels Watchtower + backup) dans le cadre de la migration (voir `DOSSIER-SOFTWARE.md` S11.4/S11.5 + `DOSSIER-RECREATION.md` R5).
---
## 2. Disque USB 3 — BACKUP HORS SERVICE
**Diagnostiqué** : 01/10/2026 (scan live effi-server)
Le **3e disque USB est branché mais non reconnu** par le xpenology. Or c'est **lui qui assure le backup du Synology**. Le backup est donc **inopérant** actuellement.
**Identification** :
| Champ | Valeur |
|-------|--------|
| Périphérique | `/dev/sda` (partition `sda2`) |
| Marque / modèle | **Seagate Expansion Portable** (réf. USB `0bc2:231a`) |
| Serial | **NAAXB11T** |
| Taille | **3.6 To** |
| Système de fichiers | **NTFS** (version 3.1), label **"Folzo Save"** |
| Occupation | **3.2 To utilisés / 519 Go libres (87%)** |
| Contenu | Sauvegarde Hyper Backup (`Save FolzoCorp..hbk`, 2.0 To) + `save NAS/` (copies USB quotidiennes) + snapshots DSM (`.dss`) |
**Diagnostic technique** :
- **Système de fichiers NTFS : COHÉRENT** (`ntfsfix -n` OK — MFT, MFTMirr, boot sector validés).
- **SMART : inaccessible** via le pont USB Seagate (limitation connue de ces disques — pas de conclusion S.M.A.R.T. possible).
- **2 zones de secteurs défectueux (erreurs I/O physiques)** détectées sur 1 058 485 entrées scannées :
1. `Save FolzoCorp..hbk/Config/@Share/docker-REC/` → `12.db-shm` + `12.db-wal` (fichiers SQLite temporaires — impact faible).
2. `save NAS/USB Copy_2026-08-18_040106/2008/Anni stephe blonde/` → **répertoire illisible** (photos famille 2008 — **impact élevé**).
- **Conclusion** : la corruption est **physique** (bad blocks), **pas** une fragmentation logicielle. Le non-détection par Synology est très probablement causé par ces secteurs HS (DSM refuse de monter un volume avec secteurs illisibles) + le format NTFS (lecture seule sans paquet `ntfs-3g`).
**Actions recommandées (ordre strict)** :
1. **🔴 SAUVEGARDER IMMÉDIATEMENT les données saines** (3.2 To) vers un autre support (218play / G9) — le disque se dégrade, chaque jour augmente le risque de perte totale.
2. **Ne PAS défragmenter** : la défrag ne traite pas la cause (bad blocks) et est risquée sur un disque défaillant (réécrit des données sur des secteurs potentiellement HS).
3. **Rebrancher sur le xpenology** + installer `ntfs-3g` (écriture) — mais sans l'étape 1, le backup restera inopérant tant que les secteurs HS bloquent le montage.
4. À terme : **remplacer le disque** (Seagate Expansion = disque portable, pas conçu pour du 24/7 de backup).
---
## 3. Incident Nextcloud — mise à jour non supervisée par Watchtower
**Date** : 06/10/2026 (détecté et corrigé)
**Symptôme** : Nextcloud inaccessible — **503**, **mode maintenance** actif, `needsDbUpgrade: true` (migration de schéma **incomplète**).
**Cause racine** : **Watchtower** (config non sélective — `CLEANUP=true`, 0 exclusion, 0 notification, image `:latest` ; détail `DOSSIER-SOFTWARE.md` S11.4) a mis à jour **l'application** `nextcloud_app` (image `nextcloud:latest`, non pinnée) le **25/09/2026**, puis **la base** `nextcloud_db` (MariaDB) le **03/10/2026**, **sans laisser la migration de schéma se terminer**. Le swap d'image a relancé l'app sur un schéma **non migré** → `needsDbUpgrade: true` → maintenance mode.
**Actions correctives (06/10/2026)** :
| # | Action | Détail |
|---|--------|--------|
| 1 | **Dump SQL** de la base MariaDB | ~**31 Mo** (snapshot de schéma + données). |
| 2 | **Suppression de l'index résiduel** `tta_throom_attendee` | Index **orphelin** laissé par la migration incomplète (bloquait la re-migration). |
| 3 | **`occ upgrade`** | Migration de schéma **relancée manuellement** → **OK**. |
| 4 | **Maintenance mode OFF** | `occ maintenance:mode --off` → service **restauré**. |
**Version finale** : **Nextcloud 35.0.1.1** (confirmé via `occ --version` le 06/10/2026).
> **Leçon** : une **MAJ stateful non supervisée** (app + DB mises à jour la nuit, sans migration, sans backup récent) = **service HS**. C'est la preuve concrète du risque « Watchtower non sélectif ». → **Règle corrective** : **Watchtower sélectif** (stateless auto / stateful exclu) + **procédure de MAJ manuelle stateful** + **backup en place** — voir `DOSSIER-SOFTWARE.md` **S11.4 / S11.5** et `DOSSIER-RECREATION.md` **R7**.
---
## 4. État du backup — HyperBackup en échec
**Confirmé** : 06/10/2026 (scan live)
Le **backup de données du xpenology est inopérant**. Confirmation live de l'état §2 :
| Tâche HyperBackup | Contenu | Dernier état | Date |
|-------------------|---------|--------------|------|
| **task_8** (backup **données** — `.hbk` ~2 To) | Volumes + apps (le vrai backup) | **`failed_checking`** — `error: status_dest_file_not_exist` (code 2008) → **destination absente** (USB3 3,6 To débranché) | **échec depuis 01/10/2024** (tâche créée 29/09/2024) |
| **task_9** (backup **apps/config** DSM — ~1 Go) | Configs DSM + quelques apps | `done` (succès) | **06/10/2026** (quotidien) |
**Faits mesurés (06/10/2026)** :
- **Aucun `/dev/sd*`** : le **disque USB3 (3,6 To, NTFS)** est **débranché / absent** → c'est la **destination** du backup données (task_8) → **aucun backup de données depuis 01/10/2024**.
- **`synobackup.log`** : **108 erreurs** pour la tâche « Synology NVR 1 » (task_8), dernière entrée **03/11/2024** (`Failed to access the backup destination` / `Failed to run backup task`). Première erreur : **01/10/2024 03:00:05** — la destination `/volume1/Save/Save_Folzo-NAS2.hbk/Guard/config.json` n'existe plus.
- Le fichier `last_result/backup.last` de task_8 porte un `end_time` de **25/04/2026** (stale) — ce n'est **pas** la date de début de l'échec, mais le dernier timestamp écrit avant que la tâche ne cesse d'être planifiée.
- **Pas de `restic` / `borg`** installés sur le NAS (le 3-2-1 cible, `DOSSIER-SOFTWARE.md` S10, n'existe pas encore).
- **USB1 (15 To) + USB2 (5,5 To)** montés (média, **92%**), **pas** des destinations de backup.
- Seul le **backup apps/config** (task_9, ~1 Go) réussit quotidiennement — **insuffisant** (ne couvre ni les **données** ni les **bases**).
> **Risque #1 renforcé** : **AUCUN backup de données fonctionnel** sur le xpenology **depuis 01/10/2024**. C'est le **prérequis absolu** de toute migration (voir `DOSSIER-RECREATION.md` **R7** risque #1 + `DOSSIER-SOFTWARE.md` **S13** A.0). **Aucune migration ni MAJ stateful sans un backup 3-2-1 fonctionnel + restauration testée.**
+799
View File
@@ -0,0 +1,799 @@
# Guide — Culture de la donnée, de la dispersion à la souveraineté
**Date** : 30/09/2026
**Auteur** : Jean-Henry Foltzenlogel (Folzo)
**Réseau** : `192.168.1.0/24`
**Statut global** : 🟡 **Guide de transformation** — à exécuter par phases (partie 10).
**Document** : **vivant** — versionné, mis à jour à chaque évolution.
> **Nature du document** : ce n'est pas un rapport. C'est un **guide de transformation** — comment amener, transformer et adapter (mes habitudes, mon infra, ma famille) à une **culture de la donnée**, pour redevenir **maître de mes données** et faire de la donnée le **carburant du projet Effi** (mon Jarvis).
>
> **Position dans le projet** : c'est le **3ᵉ point** du renouvellement de l'infrastructure — le plus important, car **tout tourne autour**. Le dossier infra (`DOSSIER-RECREATION.md`, v2) décrit la **machine** ; ce guide décrit **l'esprit** qui fait tourner la machine.
>
> **Source de vérité** : les faits matériels proviennent du scan live du 30/09/2026 (`ETAT-DE-LART-2026-09-30.md`). L'inventaire des données (partie 2) est **préliminaire** — les lacunes sont marquées `🔎` à compléter au scan live.
>
> **Racine** : CAS IA & transformation digitale (thèse : **culture de la donnée + données non structurées**), défendu le 28/09/2026. Ce guide est l'application concrète de cette thèse à ma vie.
---
## Sommaire
- [0 — Manifeste](#0--manifeste)
- [1 — Fondations](#1--fondations)
- [2 — État des lieux des données](#2--état-des-lieux-des-données)
- [3 — Architecture cible de la donnée](#3--architecture-cible-de-la-donnée)
- [4 — Du non-structuré au structuré pour l'IA locale](#4--du-non-structuré-au-structuré-pour-lia-locale)
- [5 — Adapter mes habitudes](#5--adapter-mes-habitudes)
- [6 — Adapter la famille](#6--adapter-la-famille)
- [7 — Adapter l'infrastructure](#7--adapter-linfrastructure)
- [8 — Gouvernance, sécurité & continuité](#8--gouvernance-sécurité--continuité)
- [9 — Mesure & amélioration continue](#9--mesure--amélioration-continue)
- [10 — Roadmap « prendre le large »](#10--roadmap--prendre-le-large)
- [Annexe A — Catalogue des actifs de données](#annexe-a--catalogue-des-actifs-de-données)
- [Annexe B — Conventions de nommage & métadonnées](#annexe-b--conventions-de-nommage--métadonnées)
- [Annexe C — Schéma de métadonnées (proposé)](#annexe-c--schéma-de-métadonnées-proposé)
- [Annexe D — Glossaire](#annexe-d--glossaire)
- [Changelog](#changelog)
---
## 0 — Manifeste
> **Statut** : ✅ Validé (intention)
### 0.1 Pourquoi la culture de la donnée est le point central
J'ai fait un **CAS IA & transformation digitale**. J'ai appris trois choses que je n'oublie plus :
1. **La donnée est la matière première de la valeur.** Sans donnée de qualité, pas d'IA utile.
2. **La donnée non structurée est un gisement inexploré.** C'est là que se trouve le potentiel (et c'est le sujet de ma thèse).
3. **La transformation digitale échoue sans culture de la donnée.** Les outils ne suffisent pas ; il faut changer **comment on pense, capture, organise et utilise** la donnée.
Aujourd'hui, mes données sont **éparpillées** : une partie cherchable sur le NAS, le reste dispersé entre Synology Photos, Joplin, Affine, Nextcloud, Obsidian, Gmail, Google Drive, des USB quasi pleins, et des machines qui n'ont rien en commun. J'ai passé beaucoup de temps sur les **photos** (seul actif réellement utilisable aujourd'hui, où je trouve de la plus-value). Le reste est en friche.
**Le problème n'est pas technique. C'est culturel.** Tant que je ne change pas **comment je traite mes données au quotidien**, chaque nouvel outil va juste créer un nouveau silo.
### 0.2 Le lien avec le projet Effi (mon Jarvis)
> **La culture de la donnée est l'activateur du projet Effi.**
Effi (mon Jarvis) ne sera pas un chatbot générique. Ce sera **un système qui connaît ma vie** — mes notes, mes photos, mes projets, mes décisions, mes habitudes. Et il ne pourra le que **si mes données sont** :
- **Maîtrisées** (je sais ce que j'ai, où c'est, qui y a accès),
- **Trouvables** (je peux les retrouver en secondes, moi **et** l'IA),
- **Utilisables** (structurées, indexées, exploitables),
- **Améliorées** (la qualité augmente avec le temps),
- **Pérennes** (elles survivent à un disque, à un OS, à un accident).
**Sans culture de la donnée, Effi est un Jarvis amnésique.** Avec, c'est un Jarvis qui **grandit avec moi**. C'est ça, le pari.
### 0.3 Les 5 engagements (mes 5 objectifs)
| # | Engagement | Question qu'il répond |
|---|-----------|------------------------|
| 1 | **Maîtrise / souveraineté** | *De qui sont mes données ? Qui y a accès ?* → redevenir **maître** de mes données. |
| 2 | **Trouvabilité** | *Où est cette donnée ?* → la **trouver facilement**, moi et l'IA. |
| 3 | **Utilité** | *À quoi ça me sert ?* → des données qui **m'aident**, pas qui m'enterrent. |
| 4 | **Amélioration** | *Ça devient-il meilleur ?* → la qualité qui **s'améliore** avec le temps. |
| 5 | **Pérennité** | *Ça survit-il ?* → **perpétuelle** s'il m'arrive quelque chose. |
> **Règle d'or** : toute décision (outil, flux, habitude) est évaluée contre ces 5 engagements. Si elle n'améliore aucun, on ne la fait pas.
---
## 1 — Fondations
> **Statut** : ✅ Validé (cadre conceptuel)
### 1.1 Qu'est-ce qu'une « culture de la donnée »
Une culture de la donnée, c'est un **système de croyances + pratiques** qui fait que la donnée est traitée comme un **actif** (comme de l'argent), pas comme un **déchet** (comme des fichiers qu'on jette dans un dossier).
Concrètement, ça se traduit en **reflexes** :
| Reflexe | Avant (silo) | Après (culture) |
|---------|--------------|-----------------|
| **Je reçois une info** | je la laisse dans la messagerie / le chat | je la **capture** au point d'entrée unique |
| **Je crée un fichier** | je le nomme `screenshot 2026-09-30.png` | je le **nomme + je le classe** |
| **J'ai une note** | elle dort dans une app | elle est **structurée + indexée** |
| **Je cherche** | je fouille 6 apps | je **cherche une fois**, dans un moteur |
| **Ça ne sert plus** | ça dort éternellement | je l'**archive ou je le supprime** |
| **Un truc casse** | je perds des données | j'ai un **backup vérifié** |
### 1.2 Le cycle de vie de la donnée
Chaque donnée passe par **6 stades**. Une culture de la donnée, c'est **gérer chaque stade** :
```
┌─────────┐ ┌───────────┐ ┌──────────┐ ┌─────────┐ ┌─────────┐ ┌──────────┐
│ 1.CAPTURE│──►│2.ORGANISE │──►│3.STOCKAGE│──►│4.UTILISE│──►│5.ARCHIVE│──►│6.SUPPRIME│
└─────────┘ └───────────┘ └──────────┘ └─────────┘ └─────────┘ └──────────┘
point nommage + backup 3-2-1 recherche si inactif si obsolète
d'entrée métadonnées + indexation + IA (froid) (inutile)
```
| Stade | Question | Outil (direction) |
|-------|----------|-------------------|
| **1. Capture** | Où est le point d'entrée unique ? | Inbox unique (app + dossier) |
| **2. Organisation** | Comment je nomme + je classe ? | Conventions (Annexe B) |
| **3. Stockage** | Où c'est, est-ce sauvegardé ? | NAS G9 + 3-2-1 |
| **4. Utilisation** | Comment je la retrouve + je m'en sers ? | Recherche + RAG (Effi) |
| **5. Archivage** | Que faire de l'inactif ? | Zone froide (USB/offsite) |
| **6. Suppression** | Que jeter, quand ? | Politique (partie 8) |
> **C'est le point que les gens oublient** (y compris moi) : on parle toujours de **stocker**, presque jamais de **supprimer**. Une culture de la donnée complète **gère la fin de vie** — sinon le disque se remplit et la donnée devient du bruit.
### 1.3 Les 5 propriétés, détaillées
| Propriété | Définition | Indicateur (mesurable) |
|-----------|------------|------------------------|
| **Maîtrise** | Je sais **ce que j'ai**, **où**, **qui y accède**. | Inventaire à jour (partie 2) + contrôle d'accès. |
| **Trouvabilité** | Je retrouve une donnée en **< 30 s** (moi et l'IA). | Temps de recherche moyen ; taux de « trouvé du 1er coup ». |
| **Utilité** | La donnée **sert** (décision, création, mémoire). | % de données réellement consultées (vs dormant). |
| **Amélioration** | La **qualité** augmente avec le temps (métadonnées, dédup, structure). | % de données avec métadonnées complètes ; dédups faits. |
| **Pérennité** | La donnée **survit** (disque, OS, accident, décès). | Backup 3-2-1 **vérifié** + continuité famille. |
### 1.4 Les 4 niveaux de structure (du gisement au carburant)
C'est le cœur de ma thèse — les **données non structurées** sont un gisement. Voici la hiérarchie :
| Niveau | Type | Exemple | Traitements pour « monter » |
|--------|------|---------|------------------------------|
| **L0 — Brut** | Non structuré, non exploitable | Photo, scan PDF, enregistrement audio, capture | OCR, STT, extraction |
| **L1 — Textualisé** | Textes extraits, non organisés | Transcription, OCR brut | Nettoyage, segmentation |
| **L2 — Structuré** | Champ, métadonnées, index | Notes taguées, photos géotaguées, fiches | Nommage, métadonnées, index |
| **L3 — Sémantique** | Exploitable par l'IA | Embeddings, graph, RAG | Vectorisation (bge-m3 → Qdrant) |
> **L'objectif** : faire **monter** mes données de L0 → L3. C'est ce qui rendra Effi utile. Aujourd'hui, mes données sont majoritairement **L0** (photos, scans, audio) — d'où le potentiel inexploré.
---
## 2 — État des lieux des données
> **Statut** : 🟡 **Inventaire préliminaire** — basé sur l'état de l'art du 30/09/2026. Les valeurs marquées `🔎` sont à **compléter au scan live** (tâche d'exécution, partie 10 étape C1).
### 2.1 Règle de lecture
- Chaque actif de données est décrit : **localisation, volume, format, structure, qualité, sensibilité, valeur, priorité**.
- **Sensibilité** : 🟢 publique / 🟡 privée / 🔴 critique (perte = désastre).
- **Valeur** : 🔴 patrimoine (irremplaçable) / 🟠 haute / 🟡 moyenne / ⚪ faible.
- **Priorité de structuration** (pour l'IA) : P0 (d'abord) → P3 (plus tard).
### 2.2 Cartographie par support (où sont mes données)
| Support | Machine / IP | Contenu | Volume | État |
|---------|--------------|---------|--------|------|
| **Synology Photos** | xpenology .25 | Photos famille ~10 ans | 🔎 à estimer | 🟡 fiable mais enchaîné XPEnology |
| **Nextcloud** | xpenology .25 | Fichiers famille | 🔎 à estimer | 🟡 à migrer G9 |
| **Joplin** | xpenology .25 | Notes | 🔎 à estimer | 🟡 dispersé |
| **Affine** | xpenology .25 | Workspace | 🔎 à estimer | 🟡 à trancher (R6.2) |
| **Obsidian** | effi-server .27 (CouchDB LiveSync) | Notes | 🔎 à estimer | 🟡 à trancher (R6.2) |
| **Vaultwarden** | xpenology .25 | Mots de passe | — | 🔴 critique, à migrer G9 |
| **Gitea** | xpenology .25 | Projets / docs / code | 🔎 à estimer | 🟠 à fiabiliser |
| **Média USB** | xpenology USB1/USB2 | Films / séries | 15 To + 5.5 To | 🟡 à 92%, quasi pleins |
| **Modèles LLM** | folzo-corp .11 | GGUF (28 modèles) | 98 Go | 🟡 dont ~18 legacy à trier |
| **Gmail** | Google (cloud) | Mail famille | 🔎 à estimer | 🟠 à dégafamiser |
| **Google Drive/Cal/Contacts** | Google (cloud) | Fichiers, agenda, contacts | 🔎 à estimer | 🟠 à dégafamiser |
| **Enregistrements NVR** | Reolink .41/.123/.191 | Vidéos surveillance | 🔎 à estimer | 🟡 à trier (durée de rétention) |
| **Laptop** | — | Workstation, clones locaux, docs | 2 To + 1 To | 🟡 à cartographier |
| **218play** | .26 | Backup (2×2 To) | ~4 To | 🟡 offsite non exploité |
| **Freebox RAID5** | .254 | Backup intermédiaire (3×1 To) | 2 To | 🟡 peu utilisé |
| **WhatsApp / iMessage** | Cloud / téléphone | Messagerie | 🔎 à estimer | 🟡 à dégafamiser (Matrix) |
### 2.3 Inventaire détaillé des actifs
| # | Actif | Localisation | Volume | Format | Structure | Qualité | Sensibilité | Valeur | Prio IA |
|---|-------|-------------|--------|--------|-----------|---------|-------------|--------|---------|
| D1 | **Photos famille** | Synology Photos (.25) | 🔎 ~10 ans | JPG/HEIC | L1 (IA Syno) | 🟠 bonne | 🟡 privée | 🔴 patrimoine | **P0** |
| D2 | **Mots de passe** | Vaultwarden (.25) | — | JSON (chiffré) | L2 | 🟠 | 🔴 critique | 🔴 critique | P2 |
| D3 | **Notes** | Joplin/Affine/Obsidian (dispersé) | 🔎 | Markdown/DB | L1-L2 | 🟡 moyenne | 🟡 privée | 🟠 haute | **P0** |
| D4 | **Fichiers famille** | Nextcloud (.25) | 🔎 | divers | L0-L2 | 🟡 | 🟡 privée | 🟠 haute | P1 |
| D5 | **Projets / docs** | Gitea + clones | 🔎 | Markdown/code | L2 | 🟠 | 🟡 | 🟠 haute | **P0** |
| D6 | **Média (films/séries)** | USB xpenology | 20.5 To | MKV/MP4 | L0 | 🟠 | 🟢 | 🟡 moyenne | P3 |
| D7 | **Modèles LLM** | folzo-corp | 98 Go | GGUF | L3 (outils) | 🟠 | 🟢 | 🟡 moyenne | — (outils) |
| D8 | **Mail** | Gmail | 🔎 | EML/HTML | L0-L1 | 🟡 | 🟡 | 🟠 haute | P1 |
| D9 | **Agenda / contacts** | Google | 🔎 | ICS/VCF | L2 | 🟠 | 🟡 | 🟠 haute | P2 |
| D10 | **Enregistrements NVR** | Reolink ×3 | 🔎 | MP4 | L0 | ⚪ | 🟡 | ⚪ faible | P3 |
| D11 | **Musique** | (à localiser) | 🔎 | MP3/FLAC | L0-L2 | 🟠 | 🟢 | 🟡 moyenne | P3 |
| D12 | **Messagerie** | WhatsApp/iMessage | 🔎 | binaire | L0 | ⚪ | 🟡 | ⚪ faible | P3 |
### 2.4 Diagnostic de l'état des lieux
**Constat** :
1. **Les photos (D1) sont le seul actif mature** — c'est là que je trouve de la plus-value aujourd'hui. C'est le modèle à **répliquer** sur les autres actifs.
2. **Les notes (D3) sont le 2ᵉ gisement** — dispersées en 4 apps, c'est le plus gros frein à la culture de la donnée. C'est la décision R6.2.
3. **Le non-structuré domine** — photos, scans, audio, vidéo = L0. C'est **exactement** le sujet de ma thèse : un gisement inexploré.
4. **La dispersion est le problème n°1** — 6+ apps de notes, 3+ emplacements de fichiers, 2 clouds GAFAM. **Un actif, un endroit, une source de vérité.**
5. **Le backup est inopérant** (USB HS) → la pérennité (engagement 5) est **au sol** aujourd'hui. C'est le risque #1 du dossier.
**Principe** : **un actif = un endroit = une source de vérité.** Pas de duplication, pas de « c'est peut-être ici, peut-être là ».
---
## 3 — Architecture cible de la donnée
> **Statut** : ✅ Cadré — à affiner au scan live (volumes, partitions).
### 3.1 Les 2 zones de données
| Zone | Contenu | Machine | Accès | Sensibilité max |
|------|---------|---------|-------|-----------------|
| **Famille** | Photos, fichiers, média, agenda, mots de passe, domotique | **NAS G9 (.25)** | Famille (postes enfants filtrés) | 🔴 (mots de passe) |
| **Personnel / IA** | Notes, projets, RAG, Effi, modèles | **PC IA (.11)** | Moi | 🟡 |
> **Règle** : rien ne quitte la maison. Les données de la famille et les données personnelles restent **100% locales** — c'est le principe de souveraineté (partie 0).
### 3.2 Topologie de stockage (sur le G9, cœur famille)
```
NAS G9 (.25)
├── /data/famille/
│ ├── photos/ ← D1 (patrimoine, IA)
│ ├── fichiers/ ← D4 (Nextcloud)
│ ├── media/ ← D6 (films/séries, Jellyfin)
│ ├── musique/ ← D11
│ └── partages/ ← partages famille
├── /data/perso/
│ ├── notes/ ← D3 (source de vérité unique)
│ ├── projets/ ← D5 (Gitea)
│ └── documents/ ← scans, PDF, admin
├── /vault/
│ └── motsdepasse/ ← D2 (Vaultwarden, chiffré)
└── /backup/ ← point de départ du 3-2-1
```
### 3.3 Le flux de la donnée (capture → pérennité)
```
CAPTURE ORGANISER STOCKER UTILISER ARCHIVER
│ │ │ │ │
Inbox unique → Nommer + classer → NAS G9 → Recherche + → Zone froide
(1 app, 1 (conventions (3-2-1) IA (Effi/RAG) (USB/offsite)
dossier) Annexe B)
│
▼
INDEXER (L0→L3)
OCR / STT / embeddings
(partie 4)
```
### 3.4 Conventions (résumé — détail en Annexe B)
| Règle | Exemple |
|-------|---------|
| **Nommage** | `AAAAMMJJ-description.ext` (pas `screenshot 1.png`) |
| **Un actif = un endroit** | Les notes → `/data/perso/notes/` uniquement |
| **Métadonnées** | Chaque doc important a : auteur, date, tags, statut |
| **Format ouvert** | Markdown, PDF, JPG — pas de format propriétaire verrouillé |
| **Dédup** | Pas de 2 copies de la même donnée (sauf backup) |
---
## 4 — Du non-structuré au structuré pour l'IA locale
> **Statut** : ✅ Cadré — c'est le cœur de ma thèse. À exécuter par priorité (P0 d'abord).
### 4.1 Le principe
Mes données sont majoritairement **L0** (non structurées). Pour qu'Effi en profite, il faut les **monter** à **L3** (sémantique). Le pipeline :
```
L0 (brut) L1 (textualisé) L2 (structuré) L3 (sémantique)
───────── ────────────── ────────────── ───────────────
Photo ──OCR──► texte ──tags/métadonnées──► embeddings
Audio ──STT──► transcription ──diarisation──► ──bge-m3──► Qdrant
PDF ──OCR──► texte ──segmentation──► (vector DB)
Vidéo ──frames──► images ──scènes──►
```
### 4.2 Les briques existantes (déjà dans mon infra)
| Étape | Outil | Machine | État |
|-------|-------|---------|------|
| **OCR** | `effi-ocr` / `kreuzberg` (PaddleOCR FR) | folzo-corp / effi-server | 🟡 unhealthy (à réparer) |
| **STT** | `whisper` (faster-whisper) | effi-server | ✅ up |
| **STT** | `effi-stt` | folzo-corp | 🟡 unhealthy (à réparer) |
| **Embeddings** | `bge-m3` | effi-server | ✅ |
| **Vector DB** | `Qdrant` | effi-server | ✅ |
| **RAG** | `RAGFlow` | folzo-corp | ❌ crash loop (à réparer) |
| **RAG** | `Dify` | effi-server | ✅ |
| **LLM** | Swift-1.5 Qwen 27B (llama.cpp) | folzo-corp | ✅ stable |
| **Interface** | `OpenWebUI` | effi-server | ✅ |
| **Automatisation** | `n8n` | effi-server + xpenology | ✅ |
> **Bonne nouvelle** : j'ai **toute la chaîne** déjà en place. Le problème n'est pas de construire, c'est de **fiabiliser** (partie 10, étape C3) et de **l'alimenter** en données structurées.
### 4.3 Priorisation de ce à structurer en premier
| Prio | Actif | Pourquoi d'abord | Pipeline |
|------|-------|------------------|----------|
| **P0** | **Notes** (D3) | C'est mon 2ᵉ cerveau, déjà textuel (L1) → rapide à monter à L3 | Nettoyage → segmentation → bge-m3 → Qdrant |
| **P0** | **Projets/docs** (D5) | Markdown, déjà structuré (L2) → le plus rentable | Indexation directe → Qdrant |
| **P0** | **Photos** (D1) | Patrimoine, IA déjà en place (Syno) → à répliquer | OCR légendes + embeddings visuels |
| **P1** | **Fichiers** (D4) | Hétérogène, valeur haute | OCR (PDF) → indexation |
| **P1** | **Mail** (D8) | Volume élevé, valeur variable | Tri → extraction → indexation (sensible) |
| **P2** | **Agenda/contacts** (D9) | Structuré (L2) → facile | Indexation |
| **P3** | **Média/NVR/musique** | Volume énorme, valeur faible | À la demande seulement |
> **Règle** : on ne vectorise **pas** 20 To de films. On structure ce qui a de la **valeur cognitive** (notes, docs, photos de mémoire), on laisse le reste en **stockage passif**.
### 4.4 Le pipeline « capture → Effi » (boucle)
```
Nouvelle donnée (note/photo/doc)
│
▼
n8n détecte l'arrivée (watcher)
│
├──► si image/PDF → OCR (kreuzberg)
├──► si audio → STT (whisper)
│
▼
Nettoyage + segmentation
│
▼
Embeddings (bge-m3)
│
▼
Qdrant (vector DB)
│
▼
Effi peut maintenant y répondre (RAG via Dify/OpenWebUI)
```
> **C'est la boucle qui rend Effi « grandir avec moi »** : chaque nouvelle donnée structurée = Effi devient un peu plus utile. C'est la **culture de la donnée en action**.
---
## 5 — Adapter mes habitudes
> **Statut** : ✅ Cadré — c'est le plus important (la culture, pas la techno).
### 5.1 Les 6 réflexes à installer
| # | Réflexe | Action concrète | Fréquence |
|---|---------|-----------------|-----------|
| 1 | **1 point de capture** | Tout entre par **une** inbox (app + dossier `/inbox/`). Rien ne dort dans un chat/mail. | quotidien |
| 2 | **Nommer au moment** | Je nomme le fichier **quand je le crée** (convention `AAAAMMJJ-description`). | à la création |
| 3 | **Classeer en fin de journée** | Je vide l'inbox : je déplace chaque élément à sa place (ou je le supprime). | quotidien (10 min) |
| 4 | **1 actif = 1 endroit** | Je ne duplique pas. Les notes → notes, les docs → docs. | permanent |
| 5 | **Revue hebdo** | 30 min : je cherche ce qui dort, je dédups, j'archive, je structure. | hebdo |
| 6 | **Documenter** | Chaque décision/évo majeure → mise à jour du dossier (source de vérité). | à chaque écho |
### 5.2 Le rituel quotidien (10 min)
```
┌─────────────────────────────────────────────┐
│ FIN DE JOURNÉE — 10 MIN │
├─────────────────────────────────────────────┤
│ 1. Vide l'inbox (app + /inbox/) │
│ 2. Nomme + classe chaque élément │
│ 3. Supprime ce qui ne sert pas │
│ 4. (optionnel) capture une note pour Effi │
└─────────────────────────────────────────────┘
```
### 5.3 La revue hebdo (30 min)
| Étape | Action | Durée |
|-------|--------|-------|
| 1 | **Trouver** : je cherche 3 choses aléatoires (test de trouvabilité) | 5 min |
| 2 | **Dédup** : je repère les doublons | 5 min |
| 3 | **Structurer** : je monte 1 actif de L0→L2 (tags, métadonnées) | 15 min |
| 4 | **Archiver** : je déplace l'inactif en zone froide | 5 min |
### 5.4 L'automatisation (n8n) — ce qui ne doit pas être manuel
| Tâche | Automatisation | Gain |
|-------|----------------|------|
| **Indexation** | n8n watche `/inbox/` → OCR/STT → Qdrant | 0 min (auto) |
| **Backup** | Snapshot quotidien NAS → Freebox, hebdo → 218play | 0 min (auto) |
| **Alertes** | Beszel alerte si disque > 90%, service down | 0 min (auto) |
| **Dédup photos** | Scan hebdo des doublons | quasi auto |
| **Mise à jour docs** | Rappels (n8n) quand un dossier n'a pas été touché | 0 min (auto) |
> **Principe** : **automatiser ce qui est répétitif** (indexation, backup, alertes), **garder humain ce qui est judgment** (classer, archiver, supprimer). L'IA aide, elle ne décide pas à ma place.
---
## 6 — Adapter la famille
> **Statut** : ✅ Cadré — la culture de la donnée est une affaire **familiale**, pas solo.
### 6.1 Le principe
La famille ne va pas adopter un « système de données ». Elle va adopter **des réflexes simples** qui, agrégés, créent la culture. **Moins c'est plus** : 3 règles max, pas un manuel.
### 6.2 Les 3 règles famille (simples)
| # | Règle | Pourquoi |
|---|-------|----------|
| 1 | **Les photos vont sur le NAS** (pas dans le cloud) | Pérennité + souveraineté |
| 2 | **Les documents vont dans le dossier famille** | Trouvabilité |
| 3 | **On ne supprime pas, on demande** | Évite la perte d'un patrimoine |
### 6.3 L'onboarding (par personne)
| Personne | Ce qu'on lui montre | Effort |
|----------|---------------------|--------|
| **Moi** | Tout (admin) | — |
| **Conjoint(e)** | App photos + dossier famille + partage | 30 min |
| **Enfants** | App photos + règles simples (pas de cloud) | 15 min |
### 6.4 Ce qui est automatisé (la famille ne se soucie de rien)
| Tâche | Qui s'en occupe |
|-------|-----------------|
| **Backup** | n8n + 3-2-1 (invisible) |
| **Indexation photos** | IA (reconnaissance, albums auto) |
| **Sécurité** | DNS filtré (AdGuard) + profils enfants |
| **Partage** | Liens (pas de transfert manuel) |
> **La famille vit la culture sans la subir** : elle pose les photos, elle retrouve ses docs, et le reste (backup, indexation, sécurité) est **transparent**.
---
## 7 — Adapter l'infrastructure
> **Statut** : ✅ Cadré — chaque service infra sert un engagement.
### 7.1 Mapping engagement → service
| Engagement | Service(s) | Machine |
|------------|-----------|---------|
| **Maîtrise** | Inventaire (ce dossier) + contrôle d'accès (VPN, profils) | toutes |
| **Trouvabilité** | Recherche plein texte + RAG (Qdrant) + DNS (AdGuard) | PC IA + J5040 |
| **Utilité** | RAG (Effi) + médiathèque (Jellyfin) + agenda (CalDAV) | PC IA + G9 |
| **Amélioration** | OCR/STT + n8n (indexation auto) + dédup | PC IA |
| **Pérennité** | Backup 3-2-1 + chiffrement + continuité | NAS + Freebox + 218play |
### 7.2 Le backup 3-2-1 (pérennité)
> **Schéma des 3 copies** (originale NAS G9 / intermédiaire Freebox / offsite 218play) : voir `DOSSIER-RECREATION.md` §R4 (source unique du plan).
> **Règle culture** : un backup **non testé** = pas de backup. **Test de restauration au moins 1×/trimestre** (critère de succès, partie 10).
### 7.3 La sécurité (souveraineté)
| Mesure | Détail |
|--------|--------|
| **Chiffrement** | Mots de passe (Vaultwarden) + disque (LUKS) + en transit (HTTPS) |
| **Accès** | VPN pour l'extérieur, profils famille filtrés (AdGuard) |
| **Isolation** | Réseau local, accès contrôlé (VPN, profils) |
| **Mots de passe** | Vaultwarden (coffre unique), 2FA |
| **MAJ** | Watchtower + suivi (mais testé, pas « on verra ») |
---
## 8 — Gouvernance, sécurité & continuité
> **Statut** : ✅ Cadré — c'est ce qui manquait (le « déployer la voile »).
### 8.1 La gouvernance (qui décide de quoi)
| Question | Règle |
|----------|-------|
| **De qui sont les données ?** | De la famille (photos, fichiers) / de moi (notes, projets). |
| **Qui y a accès ?** | Famille → zone famille. Moi → perso. |
| **Qui peut supprimer ?** | Moi (admin). La famille demande, je décide (règle 6.2). |
| **Qui est responsable ?** | Moi (unique responsable de l'infra + des données). |
### 8.2 Les niveaux de sensibilité & règles associées
| Niveau | Exemple | Règle |
|--------|---------|-------|
| 🟢 Publique | Films, musique | Accès libre |
| 🟡 Privée | Photos, notes, mail | Zone famille/perso, pas de cloud |
| 🔴 Critique | Mots de passe | Chiffré, accès moi seul, backup dédié |
### 8.3 La politique de suppression & archivage
> **C'est le point que j'avais oublié** — et c'est essentiel pour la **qualité** (pas de bruit) et la **pérennité** (pas de saturation).
| Type | Règle |
|------|-------|
| **Photos** | Jamais supprimées (patrimoine). Dédup + archive des redondantes. |
| **Notes** | Suppression si obsolète (je décide, en revue hebdo). |
| **Mail** | Archivage après 2 ans, suppression si spam/doublon. |
| **NVR** | Rétention limitée (ex. 30 jours) → suppression auto. |
| **Média** | Suppression si inactif > 2 ans (je décide). |
> **Principe** : **tout a une fin de vie**, sauf le patrimoine. Une donnée sans fin de vie = du bruit qui noie la valeur.
### 8.4 La continuité (si « il m'arrive quelque chose »)
> **C'est l'engagement 5, sa partie la plus sérieuse.** Mes données doivent survivre à **moi**.
| Mesure | Détail |
|--------|--------|
| **Accès famille** | La famille sait **où** sont les données + **comment y accéder** (doc simple). |
| **Mots de passe** | Vaultwarden : accès d'urgence famille (emergency access). |
| **Documentation** | Ce dossier + les docs = source de vérité **lisible par un tiers**. |
| **Backup offsite** | 218play = copie qui survit à un incendie du NAS. |
| **Test** | La famille sait restaurer (testé 1×). |
> **Objectif** : si je disparais demain, ma famille a **accès** à ses photos, ses fichiers, ses mots de passe, et sait **réparer** le système de base. C'est la **pérennité** au sens fort.
---
## 9 — Mesure & amélioration continue
> **Statut** : ✅ Cadré — sans mesure, pas de culture (juste de l'intention).
### 9.1 Les KPI (à mesurer mensuellement)
| KPI | Définition | Cible (1 an) |
|-----|------------|--------------|
| **Trouvabilité** | % de recherches « trouvées du 1er coup » | > 90% |
| **Structure** | % de données avec métadonnées complètes | > 70% (actifs P0) |
| **Indexation** | % des actifs P0 montés à L3 (dans Qdrant) | > 80% |
| **Pérennité** | Backup 3-2-1 vérifié (restauration testée) | 100% (trimestriel) |
| **Dispersion** | Nombre de silos (apps de notes, emplacements) | 1 par actif |
| **Sobriété** | Conso idle des 4 boîtiers 24/7 | stable (Freebox+J5040+G9 ~30 W + PC IA ~80-100 W) |
| **Dégafamisation** | % de services GAFAM remplacés | > 80% |
### 9.2 Le cycle PDCA
```
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ PLAN │──►│ DO │──►│ CHECK │──►│ ACT │
│ Définir │ │ Appliquer│ │ Mesurer │ │ Améliorer│
│ la cible │ │ les │ │ les KPI │ │ (boucle) │
│ du mois │ │ réflexes │ │ │ │ │
└──────────┘ └──────────┘ └──────────┘ └──────────┘
```
| Étape | Fréquence | Action |
|-------|-----------|--------|
| **Plan** | mensuel | Fixer 1-2 cibles (ex. « structurer les notes ») |
| **Do** | quotidien | Appliquer les 6 réflexes (partie 5) |
| **Check** | mensuel | Mesurer les KPI (9.1) |
| **Act** | trimestriel | Ajuster les conventions, les automatisations |
### 9.3 Le « data health check » trimestriel
| Vérification | Outil |
|--------------|-------|
| Backup restaurable ? | Test restauration |
| Disques < 80% ? | `df` / Beszel |
| Services up ? | Beszel |
| KPIs au vert ? | Tableau 9.1 |
| Docs à jour ? | Ce dossier + état de l'art |
---
## 10 — Roadmap « prendre le large »
> **Statut** : 🟡 **Plan phasé** — calé sur les phases **R5** du dossier (plan canonique). Ce guide n'ajoute que les **actions spécifiques à la culture de la donnée** ; les actions « machine » sont dans R5 et **non dupliquées** ici.
### 10.0 Règle de lecture
- **R5 = plan canonique** (machine) : fondations (edge + cœur + IA + 3-2-1), cœur famille, finitions.
- **Cette partie = couche culture** : elle référence R5 par ID et n'ajoute que ce qui est **spécifique à la donnée** (état des lieux, structuration, indexation, gouvernance).
- **Fondations d'abord** : on ne structure pas des données sur un système sans backup (risque #1 R7).
- **On ne passe à l'étape N+1 qu'après validation de l'étape N.**
- **Statut** : ⬜ à faire → 🟡 en cours → ✅ validé.
### 10.1 Étape 1 — Fondations + état des lieux
**R5** : **Phase 0** (réparer backup USB, thermique Freebox, installer J5040 edge + G9, migrer l'IA, 3-2-1, G7 spare).
**Ajout culture** : l'état des lieux complet (la partie 2 du guide est préliminaire).
| # | Action | Dépendance | Critère de succès |
|---|--------|------------|-------------------|
| C1.1 | **Scanner tous les supports** (volumes, formats, counts) | R5 Ph0 | Partie 2 complétée (plus de `🔎`) |
| C1.2 | **Cartographier les silos** (chaque actif → 1 endroit) | C1.1 | Matrice « 1 actif = 1 endroit » |
| C1.3 | **Qualifier** (sensibilité, valeur, priorité) | C1.1 | Inventaire annoté (partie 2.3) |
**Statut** : ⬜
> **Sans R5 Phase 0, la culture repose sur du sable** (pas de backup = pas de pérennité). **Sans C1, on structure à l'aveugle.**
### 10.2 Étape 2 — Cœur famille + structuration des actifs P0
**R5** : **Phase 1** (installer le G9, migrer photos/fichiers/mots de passe/média/notes).
**Ajout culture** : la structuration (L0→L3) des actifs P0, qui ne peut se faire **qu'après** la migration sur le G9.
| # | Action | Dépendance | Critère de succès |
|---|--------|------------|-------------------|
| C2.1 | **Notes** : 1 app de référence (trancher R6.2) | R5 Ph1 | Notes dans 1 seul endroit |
| C2.2 | **Notes** : indexation (→ Qdrant) | C3.1, C2.1 | Effi répond sur mes notes |
| C2.3 | **Projets/docs** : indexation (→ Qdrant) | C3.1 | Effi répond sur mes projets |
| C2.4 | **Photos** : indexation (embeddings) | R5 Ph1 (1.1) | Recherche sémantique photos |
**Statut** : ⬜
> **C'est là que la culture devient tangible** : Effi commence à « connaître » ma vie.
### 10.3 Étape 3 — Fiabiliser la chaîne IA
**R5** : **Phase 0** (0.5 — migrer l'IA sur le PC IA, power limit, mesurer conso) + **Phase 2** (finitions IA).
**Ajout culture** : valider que le pipeline L0→L3 (partie 4) est **opérationnel de bout en bout**.
| # | Action | Dépendance | Critère de succès |
|---|--------|------------|-------------------|
| C3.1 | **Pipeline testé** : 1 doc → OCR → Qdrant → Effi répond | R5 Ph0 (0.5) | Effi répond sur un doc nouveau |
**Statut** : ⬜
### 10.4 Étape 4 — Automatiser les réflexes
**Ajout culture** : les 6 réflexes (partie 5) portés par n8n.
| # | Action | Dépendance | Critère de succès |
|---|--------|------------|-------------------|
| C4.1 | **n8n** : watcher `/inbox/` → indexation | C2.2 | Nouvelle note indexée auto |
| C4.2 | **n8n** : backup quotidien + hebdo | R5 Ph0 | 3-2-1 automatique |
| C4.3 | **n8n** : alertes (disque, services) | — | Alertes fonctionnelles |
| C4.4 | **Rituels** : inbox + revue hebdo en place | — | 2 semaines de pratique |
**Statut** : ⬜
### 10.5 Étape 5 — Gouvernance & continuité
**Ajout culture** : les règles (partie 8) en place et testées.
| # | Action | Dépendance | Critère de succès |
|---|--------|------------|-------------------|
| C5.1 | **Politique de suppression** écrite + appliquée | C1.2 | Règles de fin de vie actives |
| C5.2 | **Sécurité** vérifiée (VPN, profils, 2FA) | R5 Ph1 | Accès contrôlés fonctionnels |
| C5.3 | **Continuité** : doc famille + test restauration | R5 Ph0 | Famille sait restaurer |
| C5.4 | **KPI** : tableau de mesure en place | C2.x | 1er mois de KPI mesurés |
**Statut** : ⬜
### 10.6 Vue d'ensemble
```
R5 Ph0 ─► R5 Ph1 ─► R5 Ph2
(fonda- (cœur (finitions)
tions) famille)
│ │ │
▼ ▼ ▼
C1 C2 C3 ← couche culture (ce guide)
(état (structurer (pipeline
des P0) testé)
lieux)
│
▼
C4 (automatiser) ─► C5 (gouvernance & continuité)
```
> **Lien avec R5** : R5 est le **plan canonique** (machine). Les étapes C1-C5 sont la **couche culture** qui s'y greffe — C1 après Ph0, C2 après Ph1, C3 après Ph0 (0.5), C4/C5 transverses. **Un seul plan, deux couches** : plus de duplication, plus de divergence.
---
## Annexe A — Catalogue des actifs de données
> Version consolidée de la partie 2.3. À compléter au scan live (D1).
| ID | Actif | Zone | Machine | Volume | Format | Niveau | Sensibilité | Valeur | Prio IA | Statut |
|----|-------|------|---------|--------|--------|--------|-------------|--------|---------|--------|
| D1 | Photos famille | Famille | xpenology→G9 | 🔎 | JPG/HEIC | L1 | 🟡 | 🔴 | P0 | 🟡 |
| D2 | Mots de passe | Famille | xpenology→G9 | — | JSON | L2 | 🔴 | 🔴 | P2 | 🟡 |
| D3 | Notes | Perso | dispersé→G9 | 🔎 | MD/DB | L1-L2 | 🟡 | 🟠 | P0 | 🟡 |
| D4 | Fichiers famille | Famille | xpenology→G9 | 🔎 | divers | L0-L2 | 🟡 | 🟠 | P1 | 🟡 |
| D5 | Projets/docs | Perso | Gitea→G9 | 🔎 | MD/code | L2 | 🟡 | 🟠 | P0 | 🟡 |
| D6 | Média | Famille | USB→G9 | 20.5 To | MKV | L0 | 🟢 | 🟡 | P3 | 🟡 |
| D7 | Modèles LLM | Perso | folzo-corp | 98 Go | GGUF | L3 | 🟢 | 🟡 | — | 🟡 |
| D8 | Mail | Famille | Gmail→local | 🔎 | EML | L0-L1 | 🟡 | 🟠 | P1 | 🟠 |
| D9 | Agenda/contacts | Famille | Google→local | 🔎 | ICS/VCF | L2 | 🟡 | 🟠 | P2 | 🟠 |
| D10 | Enregistrements NVR | Famille | Reolink | 🔎 | MP4 | L0 | 🟡 | ⚪ | P3 | 🟡 |
| D11 | Musique | Famille | à localiser | 🔎 | MP3/FLAC | L0-L2 | 🟢 | 🟡 | P3 | 🟡 |
| D12 | Messagerie | Famille | WhatsApp/iMsg | 🔎 | binaire | L0 | 🟡 | ⚪ | P3 | 🟡 |
---
## Annexe B — Conventions de nommage & métadonnées
### B.1 Nommage des fichiers
| Règle | Format | Exemple |
|-------|--------|---------|
| **Date** | `AAAAMMJJ` (ISO) | `20260930` |
| **Description** | kebab-case, 3-6 mots | `recette-tarte-tatin` |
| **Extension** | d'origine | `.pdf` |
| **Complet** | `AAAAMMJJ-description.ext` | `20260930-recette-tarte-tatin.pdf` |
**Interdits** : `screenshot 1.png`, `nouveau dossier`, `copie de`, `final (2)`.
### B.2 Dossiers (structure)
```
/data/
├── famille/
│ ├── photos/AAA/ ← par année
│ ├── fichiers/
│ │ ├── admin/ ← factures, contrats
│ │ ├── sante/
│ │ └── projets/
│ ├── media/
│ └── musique/
├── perso/
│ ├── notes/ ← 1 source de vérité
│ ├── projets/
│ └── documents/
├── vault/
└── backup/
```
### B.3 Tags (pour la recherche + l'IA)
| Tag | Usage |
|-----|-------|
| `#personne/<nom>` | Personnes (photos, notes) |
| `#lieu/<lieu>` | Lieux |
| `#projet/<nom>` | Projets |
| `#type/<type>` | admin, sante, recette, … |
| `#statut/<statut>` | actif, archive, à-trier |
| `#sensibilité/<niveau>` | publique, privee, critique |
---
## Annexe C — Schéma de métadonnées (proposé)
> Pour chaque actif important (doc, photo, note). Stocké en front-matter (Markdown) ou EXIF (photos).
```yaml
---
id: 20260930-recette-tarte-tatin
titre: Recette tarte tatin
auteur: Jean-Henry
date_creation: 2026-09-30
date_modification: 2026-09-30
type: recette
tags:
- type/recette
- statut/actif
sensibilite: privee
zone: famille
source: capture-manuelle
ia:
indexe: true
embedding: qdrant/notes
date_indexation: 2026-09-30
---
```
| Champ | Obligatoire | Rôle |
|-------|-------------|------|
| `id` | ✅ | Identifiant unique (nommage) |
| `titre` | ✅ | Recherche |
| `date_creation` | ✅ | Pérennité / tri |
| `type` | ✅ | Classement |
| `tags` | ✅ | Recherche + IA |
| `sensibilite` | ✅ | Gouvernance |
| `zone` | ✅ | Isolation |
| `ia.indexe` | ✅ | Suivi de l'indexation |
---
## Annexe D — Glossaire
| Terme | Définition |
|-------|------------|
| **Culture de la donnée** | Système de croyances + pratiques qui traite la donnée comme un actif. |
| **Souveraineté** | Maîtrise totale de mes données (local, contrôle, export). |
| **Dégafamisation** | Remplacer les services GAFAM par des équivalents locaux. |
| **Donnée non structurée** | Donnée sans format imposé (photo, audio, texte libre) — le gisement. |
| **L0→L3** | Hiérarchie de structure : brut → textualisé → structuré → sémantique. |
| **RAG** | Retrieval-Augmented Generation (l'IA répond sur MES données). |
| **Embedding** | Représentation vectorielle d'une donnée (bge-m3). |
| **Qdrant** | Base de données vectorielle (stocke les embeddings). |
| **3-2-1** | 3 copies, 2 supports, 1 offsite (backup). |
| **PDCA** | Plan-Do-Check-Act (amélioration continue). |
| **KPI** | Indicateur clé de performance. |
| **G9** | HP Pro Mini 400 G9 (futur NAS, cœur famille). |
| **Effi / Jarvis** | Mon assistant IA personnel, qui « connaît » ma vie grâce à mes données. |
| **CAS** | Certificat d'Aptitude Spéciale (IA & transformation digitale). |
---
## Changelog
| Date | Version | Changement |
|------|---------|------------|
| 30/09/2026 | v1.0 | Création du guide — manifeste, fondations, état des lieux (préliminaire), architecture, pipeline IA, habitudes, famille, infra, gouvernance, mesure, roadmap, annexes. |
| 30/09/2026 | v1.1 | Renommage sans date (doc vivant) ; suppression du périmètre pro CASU (2 zones, plus de zone isolée) ; roadmap réalignée sur R5 (plan canonique, couche culture C1-C5, plus de duplication) ; typo « s'il m'arrive ». |
+486
View File
@@ -0,0 +1,486 @@
# Projet — Second Cerveau & Inventaire des Données Numériques
**Date** : 05/10/2026
**Auteur** : Jean-Henry Foltzenlogel (Folzo)
**Réseau** : `192.168.1.0/24`
**Statut global** : 🟡 **Cahier des charges rédigé** — réflexion terminée, architecture cible posée. **Aucune implémentation** : l'infra est en pleine (re)création (`DOSSIER-RECREATION.md` v2) et ce projet démarre **après** les fondations.
**Document** : **vivant** — versionné, mis à jour à chaque étape. Chaque section porte son statut (✅ / 🟡 / ⬜).
> **Nature du document** : c'est le **cahier des charges + plan** du projet « Second Cerveau & Inventaire ». Il succède à la réflexion de l'échange du 05/10/2026. Il **ne duplique pas** les specs hardware (→ snapshot `ETAT-DE-LART-2026-09-30.md`) ni l'architecture d'infra (→ `DOSSIER-RECREATION.md` v2). En cas de conflit sur un fait matériel, c'est le **snapshot** qui fait foi ; pour l'architecture d'infra, c'est **`DOSSIER-RECREATION.md`** ; pour **ce projet** (second cerveau, inventaire, analyse de valeur), c'est **ce document** qui fait foi.
---
## Sommaire
- [Résumé exécutif](#résumé-exécutif)
- [S1 — Besoin & périmètre](#s1--besoin--périmètre)
- [S2 — Les 9 exigences du second cerveau](#s2--les-9-exigences-du-second-cerveau)
- [S3 — Sources réelles](#s3--sources-réelles)
- [S4 — Architecture](#s4--architecture)
- [S5 — MVP (Second Cerveau Tier A de bout en bout)](#s5--mvp-second-cerveau-tier-a-de-bout-en-bout)
- [S6 — Roadmap & phases](#s6--roadmap--phases)
- [S7 — Décisions ouvertes](#s7--décisions-ouvertes)
- [S8 — Risques](#s8--risques)
- [S9 — Lien avec l'infrastructure](#s9--lien-avec-linfrastructure)
- [S10 — Montée en compétence](#s10--montée-en-compétence)
- [Annexe A — Critères d'acceptance](#annexe-a--critères-dacceptance)
- [Annexe B — Glossaire](#annexe-b--glossaire)
- [Changelog](#changelog)
---
## Résumé exécutif
**Le problème** : ma donnée numérique est **dispersée** (plusieurs machines, protocoles, 50 To+), et surtout ma **connaissance** (notes, écrits IA, mails, fichiers précieux) est **éparpillée et oubliable** — chaque IA (OpenFox, OpenCode, Hermes) a sa propre mémoire, oublie ce qu'on lui a dit, et on se répète.
**La vision** : un **second cerveau de famille** — une mémoire **partagée, versionnée, lisible par l'humain et puisée d'elle-même par l'IA** — plus un **inventaire** du reste (où est quoi, doublons, corruption) pour prendre conscience de l'ensemble de la donnée.
**La décision clé** : le second cerveau n'est **ni** un outil de notes, **ni** un RAG opaque. C'est un **modèle à deux couches** :
| Couche | Nature | Consommateur |
|--------|--------|--------------|
| **1 — La connaissance** | Fichiers **Markdown**, versionnés git | **Toi** (Obsidian) + source de vérité |
| **2 — L'index** | Embeddings + index vectoriel (**cache jetable**) | **L'IA** (recherche rapide via MCP) |
> **Principe fondateur** : **la couche 1 est la vérité, la couche 2 est jetable** (reconstruisible à tout moment depuis la couche 1). Un seul artefact de connaissance (le vault md), deux consommateurs (toi + l'IA). C'est ce qui découple le **besoin** de l'**outil** et garantit de ne pas changer dans 5 mois.
**Le périmètre se scinde en deux tiers** :
| | **Tier A — le cœur** (analyse IA profonde) | **Tier B — le bulk** (analyse cheap, sans IA) |
|---|---|---|
| **Contenu** | ~10 Go fichiers précieux + **mails** + **notes** + **écrits IA** | Photos, films, archives, le reste |
| **Traitement** | LLM profond → fiches md structurées + index vectoriel | Pass 1 seul : métadonnées, doublons, corruption |
| **Volume** | Petit, borné | Tens de To |
| **Valeur** | **C'est ici que réside l'intérêt** | Juste savoir où est quoi / doublons / corrompu |
**Le succès** (mesurable) : poser une question naturelle à l'IA sur mes données et obtenir une réponse précise avec références (machine + chemin) ; l'IA **récupère d'elle-même** le contexte quand un sujet revient ; la famille (femme, enfants) bénéficie de la même aide, **automatisée et non intrusive**, en alternative souveraine aux GAFAM et aux IA cloud.
---
## S1 — Besoin & périmètre
> **Statut** : ✅ Validé (05/10/2026)
### 1.1 Le besoin
**Objectif principal** : prendre conscience de l'ensemble de ma donnée numérique dispersée, identifier la **valeur** de chaque donnée (et du corpus global), et faire de ma **connaissance** un actif **navigable** — par moi et par l'IA.
**Ce que l'utilisateur veut en fin de compte** :
- **Une vue d'ensemble** : où est quoi, combien, en double, en déshérence, corrompu.
- **Une évaluation de valeur** : ce qui est précieux, sensible, obsolète, dupliqué.
- **Un second cerveau** : un index consultable par un LLM local, pour qu'une IA puisse **se repérer d'elle-même** dans l'ensemble de mes données (recherche sémantique, notes, références précises) **sans que je réexplique**.
- **Une assistante quotidienne** : « ma petite assistante Effi » qui m'aide au quotidien sur n'importe quel sujet, en puisant dans le cerveau + l'inventaire.
- **Un outil de famille** : utilisable par ma femme (surtout mails / organisation) et mes enfants, **automatisé et non intrusif**, en **alternative souveraine** aux GAFAM et aux IA cloud (ChatGPT, Claude) qui ciblent la jeune génération.
### 1.2 Périmètre & contraintes
- **Périmètre** : plusieurs machines/serveurs (PC, NAS, serveurs) sur le réseau local ; protocoles multiples (SMB, SSH/SFTP, local, WebDAV) ; tous types de fichiers.
- **Volume réel inconnu** (c'est le but de l'inventaire) — estimé ~50 To+ (voir S3).
- **100% local / auto-hébergé** : aucune donnée ne quitte la maison (données personnelles, sensibles).
- **IA locale** (Ollama / llama.cpp sur PC IA) — pas d'API cloud.
- **Économique en temps** : approche par **passes / entonnoir** (le moins cher d'abord).
- **Incrémental** : l'inventaire se maintient dans le temps (re-scan des seuls nouveaux/modifiés).
### 1.3 Le reframe Tier A / Tier B (décision clé de périmètre)
> Le périmètre n'est **pas** « analyser 50 To ». C'est **deux choses distinctes** :
| | **Tier A — le cœur** | **Tier B — le bulk** |
|---|---|---|
| **Contenu** | ~10 Go fichiers précieux (Google Drive + Synology) + **mails** + **notes** + **écrits IA** (OpenFox, OpenCode, Hermes) | Photos, films, archives, le reste |
| **Traitement** | **LLM profond** → fiche structurée (résumé, valeur, sensibilité, tags) + index vectoriel | **Pass 1 seul** : métadonnées, doublons, corruption. **Zéro IA.** |
| **Volume** | Petit, borné | Tens de To |
| **Valeur** | **C'est ici que réside l'intérêt principal** | Juste savoir où est quoi / doublons / corrompu |
**Conséquence majeure** : le coût IA devient **trivial**. L'analyse profonde ne touche que ~10 Go + mails + notes + écrits — très tractable sur 2× RTX 3060. Les 50 To ne touchent **que** le Pass 1 (mécanique, pas cher). L'entonnoir fonctionne : son extrémité étroite (IA profonde) est **petite et curée**, pas « 5% de 10M fichiers ».
> **Le vrai nom de la valeur** : ce n'est pas juste un « inventaire ». C'est un **index de connaissance personnelle** — rendre ma pensée, mes projets, mes écrits (surtout l'historique IA, trace de tout ce que j'ai fait) **navigables et interrogeables** par un LLM. C'est un **second cerveau**, pas un catalogue de disques.
### 1.4 Ce que l'utilisateur ne veut PAS
- **Pas** d'Immich (désorganise les fichiers) → penche pour **Moments + Nextcloud** (→ S6 Phase 4, Outillage).
- **Pas** de déplacement des données brutes : le projet **produit** des notes/fiches, il ne déplace pas les sources.
- **Pas** de suppression / dédoublonnage d'actions au MVP : **lecture seule**, identification uniquement.
- **Pas** de nouveau stack RAG : s'appuyer sur les briques existantes (voir S9).
---
## S2 — Les 9 exigences du second cerveau
> **Statut** : ✅ Validé (05/10/2026) — **cœur du document**. Ce tableau est le **cahier des charges** du second cerveau. La **Phase 4 (Outillage, S6)** n'aura qu'à le cocher outil par outil.
> **Principe** : les **besoins** (ce qu'il doit faire) sont découplés des **outils** (avec quoi). Le modèle 2 couches (S4) est **outil-agnostique** : il ne dit ni Gitea ni Nextcloud ni Obsidian. On pourra changer de host, d'éditeur, de vector DB **sans toucher au contenu** (markdown universel + index reconstruisible).
| # | Exigence | Ce que ça veut dire concrètement |
|---|----------|-----------------------------------|
| 1 | **Persistance cross-harnais** | Ce qui est dit avec OpenFox doit être lisible par OpenCode et Hermes. Une mémoire **partagée**, pas 3 silos. |
| 2 | **Récupération autonome** | L'IA sait *qu'elle doit* chercher et le fait *d'elle-même* quand le sujet revient. Pas « l'utilisateur colle le contexte ». |
| 3 | **Lisible + éditable par l'humain** | Tu peux ouvrir, lire, éditer, lier. Pas une boîte noire. |
| 4 | **Versionné / non destructif** | Historique, pas de perte, on revient en arrière. |
| 5 | **Structuré et navigable** | Organisé (liens, tags, arborescence), pas un tas. |
| 6 | **Scalable à une vie + la famille** | De quelques Mo aujourd'hui à des dizaines de Go, et **partitionnable par personne** plus tard (moi, femme, enfants). |
| 7 | **100% local / souverain** | Rien ne quitte la maison. |
| 8 | **Rapide pour l'IA** | Recherche sémantique instantanée, pas un grep sur 10 Go. |
| 9 | **Ingestion ouverte** | N'importe quelle source (fichiers, mails, notes, sessions IA, futurs) peut **alimenter** le cerveau. Système ouvert. |
**Ces 9 lignes sont le cahier des charges du second cerveau.** C'est ce qui rend le choix des outils (S6 Phase 4) définitif et évite de « changer dans 5 mois ».
---
## S3 — Sources réelles
> **Statut** : 🟡 **Partiellement vérifié** — les sources Tier A du PC IA sont localisées (05/10/2026, via SSH `jeuxpc`). Les écrits IA sur `effi-server` sont **à localiser** (pas de clé SSH depuis l'environnement de rédaction).
### 3.1 Sources Tier A (analyse IA profonde)
| Source | Emplacement | Nature / taille | Statut |
|--------|-------------|-----------------|--------|
| **Fichiers précieux** | Google Drive → copie chaude sur NAS (Cloud Sync Synology) → **SMB** | ~10 Go | ✅ localisé (SMB) |
| **Fichiers précieux** | Synology (xpenology / G9) | ~10 Go | ✅ localisé (SMB) |
| **Mails** | **Gmail** + **Hotmail** (20-30 ans) via **Thunderbird** sur PC | 2 boîtes, volumineux | ✅ localisé (Thunderbird) — **point difficile** (dégafamisation vs attachement) |
| **Notes** | `~/Documents/Notes/CAS IA` (PC IA) + Joplin (`~/Documents/Joplin/`) | md | ✅ localisé (PC IA) |
| **Écrits IA — OpenCode sessions** | `~/.local/share/opencode/opencode.db` (PC IA) | **SQLite, 218 Mo** | ✅ localisé — **extracteur dédié requis** |
| **Écrits IA — OpenCode projets** | `~/Documents/OpenCode/Projets` (PC IA) | git repo | ✅ localisé |
| **Écrits IA — OpenFox** | `effi-server` (J5040, .1) | à localiser | ⚠️ **à vérifier** |
| **Écrits IA — Hermes** | `effi-server` (J5040, .1) | à localiser | ⚠️ **à vérifier** |
> **Note OpenCode** : les sessions sont dans un **SQLite** (`opencode.db`, 218 Mo), pas des fichiers texte. Un **extracteur dédié** (ou l'API OpenCode) est requis pour les rendre indexables. C'est une brique du MVP (C1.1).
### 3.2 Sources Tier B (analyse cheap, sans IA)
| Source | Emplacement | Contenu |
|--------|-------------|---------|
| **Photos** | xpenology (USB1 15 To, USB2 5,5 To), 218play, Laptop | Photos famille (dont 2008 sur disque USB HS) |
| **Films / séries** | xpenology, 218play, Laptop | Médias |
| **Archives** | divers | Dumps, ISO, backups |
| **Le reste** | G9 (RAID1 2 To + USB 20 To), Laptop (2 To + 1 To), Freebox (2 To RAID5) | Divers |
> **Volume estimé total** : ~50 To+ (USB1 15 To + USB2 5,5 To + USB3 3,6 To + 218play 4 To + Freebox 2 To + Laptop 3 To + G9 20 To). **À re-dimensionner précisément en Phase 0** (C0.1).
### 3.3 Sources exclues / hors périmètre
- **Disque USB 3,6 To HS** (xpenology, secteurs défectueux) : hors périmètre — l'utilisateur a **218play** + un **disque 4 To 3,5" retrouvé** (à brancher sur le PC IA) comme copies. Voir snapshot `ETAT-DE-LART-2026-10-06.md` §2.
- **NVR Reolink** (surveillance) : hors périmètre (enregistreurs autonomes).
---
## S4 — Architecture
> **Statut** : ✅ Validé (05/10/2026) — modèle 2 couches + découpage des rôles + mécanisme MCP.
### 4.1 Le modèle 2 couches (colonne vertébrale)
```
┌─────────────────────────────────────────────────────────┐
│ COUCHE 1 — LA CONNAISSANCE (source de vérité) │
│ Fichiers Markdown, lisibles, versionnés git │
│ = le vault │
│ → TOI tu lis/édites ici (Obsidian) │
├─────────────────────────────────────────────────────────┤
│ COUCHE 2 — L'INDEX (dérivé, JETABLE) │
│ Embeddings + index vectoriel (sqlite-vec / Qdrant) │
│ → L'IA recherche ici (rapidité, via MCP) │
│ → se RECONSTRUIT à tout moment depuis la couche 1 │
└─────────────────────────────────────────────────────────┘
```
**Le principe qui débloque tout** : **la couche 1 est la vérité, la couche 2 est jetable.**
- La connaissance = des **fichiers Markdown dans un repo git** : à la fois le vault (tu lis, édites, liens `[[wikilinks]]`) **et** la source que l'IA indexe. **Un seul artefact, deux consommateurs.**
- L'index vectoriel n'est qu'un **cache**. S'il est corrompu/obsolète → on le **reconstruit** depuis les md. Il n'est **jamais** la source de vérité.
- Ça élimine le dilemme « rapide pour l'IA **ou** consultable par moi » : c'est rapide **et** consultable, parce que la vitesse vient de l'index et la vérité vient des md.
### 4.2 Pourquoi pas « juste RAG » ni « juste Obsidian »
- **Pas un RAG pur** (chunking + embeddings seul) : tu ne peux **pas le lire**. Tes données précieuses sont de la **connaissance que tu veux aussi relire et faire vivre**, pas juste « interroger ». Un RAG opaque, tu ne peux pas l'éditer, le lier, le versionner. Les md, si.
- **Pas « juste Obsidian, sans index »** : sur un corpus qui grandit, la recherche sémantique (« où est-ce que j'ai parlé de X ? ») devient lente/floue en plein texte. L'index vectoriel rend la consultation LLM **rapide et fiable** (exigence #8).
### 4.3 Le découpage des rôles (outil-agnostique)
> Le second cerveau n'est **ni** Gitea, **ni** Obsidian, **ni** Nextcloud Notes. C'est **les fichiers markdown + l'index**. Les outils sont juste des **rôles** autour :
| Rôle | Exigences servies | Candidate(s) | Note |
|------|-------------------|--------------|------|
| **Store** (la vérité) | 1,3,4,5,6,7 | **Repo git de markdown** | Gitea = *host canonique* du repo. Le store = les fichiers, **pas** Gitea. |
| **Viewer / éditeur** | 3 | **Obsidian** (pointe sur le dossier md) | C'est un *front-end*, **pas** le store. |
| **Index + retrieval** | 2,8 | embeddings + sqlite-vec / Qdrant + **MCP** | Le cache jetable. |
| **Ingestion** | 9 | extracteurs (OpenCode.db, mails, notes, fichiers) | Les portes d'entrée. |
**Réponses aux doutes** :
- **Gitea** : bon choix pour le **versioning** (host du repo) ; apprendre git rentre dans le pilier « monter en compétence ». Mais ne pas le sur-vendre : Gitea est juste *où le repo vit*. Le cerveau, c'est les md. Si on change de host, les md ne bougent pas.
- **Nextcloud Notes** : c'est une **app de notes simple**, pas un store de connaissance. Il ne fait **pas** le versioning (#4), la structure liée (#5), ni l'échelle d'une vie (#6). Comme *store* du second cerveau, **mauvais fit**. Comme *viewer* d'un sous-ensemble, OK. → **constat à noter pour la Phase 4 (Outillage, S6)**, pas une décision maintenant.
### 4.4 Le mécanisme de récupération autonome (exigence #2)
> C'est le mécanisme qui transforme « l'IA oublie et on répète » en « l'IA se souvient parce qu'elle va chercher ».
1. L'index (vector + SQL) est exposé via **MCP** (un outil « second cerveau »).
2. Chaque harnais (OpenFox, OpenCode, Hermes) est configuré avec cet outil + une instruction : *« tu as accès au second cerveau de l'utilisateur ; quand un sujet revient, cherche-le avant de répondre. »*
3. En cours de conversation, l'IA détecte « on parle du projet X » → appelle l'outil → récupère les fiches → intègre le contexte **sans que tu réexpliques**.
> ⚠️ **Dépendance** : ça exige que chaque harnais **supporte MCP** et soit **prompté** pour l'utiliser. OpenFox : oui. OpenCode : oui. Hermes : **à vérifier**.
### 4.5 Le flux global (sources → cerveau)
```
Sources brutes (immuables, in situ)
├── Fichiers précieux (Google Drive/SMB + Synology) ─┐
├── Mails (Thunderbird) ├─→ [INGESTION] ─→ [ANALYSE LLM]
├── Notes / écrits IA (OpenCode.db, notes, md) ─┘ │
└── (fichiers précieux : fiches, PAS de déplacement) ──────────────────── ▼
Vault md
(Gitea canon + G9 working)
│
┌─────────┴─────────┐
▼ ▼
Index vectoriel Toi (Obsidian)
(cache, jetable)
│
▼
Consultation LLM (MCP)
→ réponse + références
```
Le vault contient **la connaissance distillée** (fiches, résumés, tags, index des mails) — **pas** les 10 Go bruts. Les bruts sont **référencés par chemin** (machine + path) dans les fiches.
### 4.6 Synthèse des briques
| Brique | Rôle | Où |
|--------|------|-----|
| **Scanner** (Tier B) | métadonnées + doublons + corruption, pas d'IA | sur **G9** (là où seront les données) |
| **Extracteurs** | sessions OpenCode (SQLite→texte), mails, notes, écrits | **PC IA** |
| **Analyse** (Tier A) | LLM profond → fiches md structurées | **PC IA** (2×3060) |
| **Vault** | md versionné git = source de vérité, lisible + éditable | **Gitea** (canon) + **G9** (working) |
| **Index** | embeddings + sqlite-vec/Qdrant, jetable, reconstruisible | **G9** (ou PC IA) |
| **Consultation** | requête NLP → réponse + références, exposé en **MCP** | **PC IA** |
---
## S5 — MVP (Second Cerveau Tier A de bout en bout)
> **Statut** : 🟡 **Cahier des charges posé** — à implémenter **après** les fondations infra (`DOSSIER-RECREATION.md` Phase 0).
**Définition** : le MVP = la plus petite version qui livre la **valeur centrale** : *poser une question naturelle à un LLM sur mes données précieuses et obtenir une réponse précise avec références (machine + chemin), avec récupération autonome.*
**Le MVP = le Tier A de bout en bout** (le scanner Tier B est une **piste séparée**, pas dans le MVP) :
1. **Ingestion** des sources Tier A (fichiers précieux, mails, notes, écrits IA) → store local.
2. **Analyse profonde** : chaque doc → fiche structurée LLM (résumé, valeur, sensibilité, tags).
3. **Vault** : md versionné git (Gitea canon + G9 working).
4. **Index** : embeddings + index vectoriel (sqlite-vec ou Qdrant), reconstruisible.
5. **Consultation** : requête NLP → réponse + références, exposée en **MCP** avec récupération autonome.
### 5.1 Critères d'acceptance du MVP
Voir **Annexe A** (C1.1 → C1.13 + transverses CT.1 → CT.4).
---
## S6 — Roadmap & phases
> **Statut** : 🟡 **Cadrée** — l'ordre est posé ; le démarrage est **conditionné aux fondations infra**.
| Phase | Contenu | IA ? | Dépendance |
|-------|---------|------|------------|
| **0 — Dimensionnement** | Estimer le volume réel par source (rclone size / échantillonnage), identifier les sources prioritaires. **Dérisk tout le reste.** | non | infra (re)créée |
| **1 — MVP (Second Cerveau Tier A)** | Ingestion → analyse → vault → index → consultation MCP (de bout en bout) | oui (2×3060) | Phase 0 |
| **2 — Scanner (Tier B)** | Pass 1 métadonnées + dédoublonnage par taille + corruption + stats + dashboard + incrémental | **non** | Phase 0 |
| **3 — Famille & assistante** | Partitionnement par personne ; 2ᵉ cerveau de la femme (automatisé, non intrusif) ; protection enfants ; assistante Effi | oui | Phases 1+2 |
| **4 — Outillage** | Choix des outils (notes, viewer, vector DB, host) en cochant les 9 exigences (S2) | — | Phase 1 (pour valider le besoin) |
> **Ordre de valeur** : le **MVP (Phase 1)** est la priorité car il livre la valeur centrale (second cerveau). Le **Scanner (Phase 2)** est utile mais secondaire. La **Famille (Phase 3)** est long terme. L'**outillage (Phase 4)** se décide **après** avoir validé le besoin sur soi.
> ⚠️ **Timing** : ce projet démarre **après** que l'infra est en place (migration xpenology→G9, backup 3-2-1 fonctionnel, disque J5040 allégé). Les données sont **en flux** pendant la (re)création ; un inventaire fait trop tôt serait obsolète. Le re-scan post-migration est systématique (d'où l'incrémental).
---
## S7 — Décisions ouvertes
> **Statut** : 🟡 **À trancher** — chacune a sa liste de critères (S2). On ne tranche jamais à l'arrache.
| # | Décision | Statut | Critères / note |
|---|----------|--------|-----------------|
| D1 | **Notes** : quel outil ? (Nextcloud Notes vs Obsidian vs autre) | → **DOSSIER-SOFTWARE.md** (Partie 5) | Les 9 exigences (S2) guident le choix. Nextcloud Notes = mauvais fit comme *store* (S4.3). |
| D2 | **Mails** : Thunderbird+MCP (validé en test) vs n8n vs autre | À évaluer | Thunderbird+MCP a marché en test (« du bricolage mais ça a marché »). n8n = alternative à étudier. |
| D3 | **Écrits IA** : localiser OpenFox/Hermes sur `effi-server` | ⚠️ **À vérifier** | Pas de clé SSH depuis l'environnement de rédaction. À faire manuellement. |
| D4 | **Store** : Gitea (host canon) confirmé ? | À valider | Bon pour le versioning ; à apprendre git (pilier 4). Le store = les md, pas Gitea. |
| D5 | **Vector DB** : sqlite-vec (simple) vs Qdrant (déjà installé) | ✅ **Tranché : Qdrant** | Qdrant (bge-m3) tourne déjà. Décision actée dans `DOSSIER-SOFTWARE.md` S4.2. |
| D6 | **Famille** : partitionnement dès le design ou post-MVP ? | Long terme | Hors MVP. Exigence #6 (S2). Déploiement progressif : moi → femme → enfants. |
| D7 | **Photos Tier B** : Moments + Nextcloud vs autre | → **DOSSIER-SOFTWARE.md** (Partie 5) | Pas d'Immich (désorganise). Le scanner (Tier B) ne dépend pas de ce choix. |
---
## S8 — Risques
> **Statut** : 🟡 **Identifiés** — matrice à évaluer en Phase 0.
| # | Risque | Prob. | Impact | Mitigation |
|---|--------|-------|--------|------------|
| 1 | **Faux négatifs au triage** (fichier précieux au nom générique) | 3 | 3 | Triage sur **contenu** (extrait/EXIF), seuils réglables, re-passe possible |
| 2 | **Volume Tier B inconnu** → passes longues | 3 | 3 | **Phase 0 dimensionnement d'abord** (C0.1) ; entonnoir (IA seulement sur Tier A) |
| 3 | **Sessions OpenCode en SQLite** (pas texte) | 2 | 2 | **Extracteur dédié** (C1.1) |
| 4 | **Harnais sans support MCP** (Hermes ?) | 3 | 3 | À vérifier ; **fallback** = lecture directe du vault md |
| 5 | **Maintenance du vault** (git) | 3 | 2 | Montée en compétence (S10) + conventions simples |
| 6 | **Non-intrusivité pour la famille** | 3 | 3 | Ingestion **automatisée**, pas de saisie manuelle |
| 7 | **Échelle** (une vie + famille) | 2 | 3 | Markdown = format universel, index **reconstruisible** |
| 8 | **Mails volumineux** (20-30 ans) | 3 | 2 | Index + triage (pas ingestion brute) ; sous-projet à part |
| 9 | **Dépendance aux fondations infra** | 4 | 4 | Démarrage **conditionné** à la (re)création (S6) ; backup 3-2-1 d'abord |
---
## S9 — Lien avec l'infrastructure
> **Statut** : ✅ **Aligné** avec `DOSSIER-RECREATION.md` v2 (architecture cible).
### 9.1 Rôle de chaque machine pour ce projet
| Machine | Rôle (infra) | Rôle (ce projet) |
|---------|--------------|------------------|
| **G9** (.25) | Cœur famille | **Truth** : vault working copy + scanner (les données) + index (option) |
| **PC IA** (.11) | IA 24/7 (2×3060) | **Calcul** : extracteurs + analyse LLM + consultation MCP ; **4 To backup** local des données précieuses |
| **Gitea** (sur G9) | Versioning | **Host canonique** du vault |
| **effi-server / J5040** (.1) | Edge / IA | Source des écrits IA (OpenFox, Hermes) — **à localiser** |
| **218play** (.26) | Offsite (3ᵉ copie) | Backup du vault + données précieuses |
### 9.2 Règle de placement des données
- **G9 = vérité canonique** (cœur famille).
- **PC IA = calcul** (2×3060, Ollama) + **4 To 3,5" retrouvé = copie de backup locale** des données précieuses.
- **Stage** les ~10 Go du Tier A sur le NVMe du PC IA pour l'analyse (10 Go = négligeable) ou lecture réseau.
- **Gitea = host canon** du vault ; **G9 = working copy**.
### 9.3 S'appuyer sur les briques existantes (pas de nouveau stack)
> L'infra a **déjà** : Qdrant (bge-m3), Ollama (28 modèles, embeddings), mcpo (orchestrateur MCP), Dify, n8n. Ce projet **expose** le second cerveau via **MCP** vers ces briques existantes. **Pas de nouveau stack RAG.**
> ⚠️ **Note** : Qdrant a été mis en place pour le projet Effi ; OpenWebUI ne marche plus très bien ; RAGFlow et Dify servent de **maquette** pour la présentation. L'infra a grandi de manière empirique — **l'ensemble des applications à garder/rejeter sera revu dans la Phase 4 (Outillage, S6)**, une fois les données traitées. Ce projet **ne préjuge pas** de ce choix.
---
## S10 — Montée en compétence
> **Statut** : ✅ **Cadré** — l'infra + ce projet comme terrain d'apprentissage (pilier 4).
| Compétence | Service / action dans ce projet | Phase |
|------------|---------------------------------|-------|
| **Git** | Vault versionné (Gitea), conventions de commit, branches | 1 |
| **MCP** | Exposition du second cerveau en outil MCP, configuration des harnais | 1 |
| **RAG** | Index vectoriel, embeddings, recherche sémantique, chunking | 1 |
| **SQLite / SQL** | Base de l'inventaire, requêtes, sqlite-vec | 1, 2 |
| **OCR** | Tesseract / kreuzberg (PDF scannés) | 1 |
| **Dédoublonnage** | Hash exact, pHash, pré-filtre par taille | 2 |
| **Ingestion** | Extracteurs (SQLite→texte, mails, notes) | 1 |
| **Orchestration** | Passes, files de priorité, quotas | 1 |
> **Lien CAS** : ce projet est un **portfolio réel** du CAS (IA & transformation digitale) — la culture de la donnée appliquée (second cerveau, source de vérité, 3-2-1) + la méthode (état de l'art → besoins → architecture → plan).
---
## Annexe A — Critères d'acceptance
> **Statut** : ✅ **Posés** (05/10/2026) — 31 critères, par phase.
### Phase 0 — Dimensionnement
| ID | Critère |
|----|---------|
| C0.1 | Volume réel par source (nb fichiers + Go), table des volumes |
| C0.2 | Sources **Tier A** identifiées et localisées (fichiers précieux, mails, notes, écrits IA) |
| C0.3 | Sources **Tier B** identifiées (photos, films, archives) |
### Phase 1 — MVP (Second Cerveau Tier A de bout en bout)
**Ingestion**
| ID | Critère |
|----|---------|
| C1.1 | Extracteur sessions OpenCode (`opencode.db` SQLite → texte) |
| C1.2 | Ingestion des notes (md) |
| C1.3 | Ingestion des écrits IA (OpenFox, Hermes) — à localiser sur effi-server |
| C1.4 | Ingestion fichiers précieux (Google Drive/SMB + Synology) → fiches |
| C1.5 | Ingestion mails (Thunderbird) → index + triage |
**Analyse**
| ID | Critère |
|----|---------|
| C1.6 | Analyse LLM → fiches md structurées (résumé, valeur, sensibilité, tags) |
**Vault (couche 1 — vérité)**
| ID | Critère |
|----|---------|
| C1.7 | Vault markdown versionné git (Gitea canon + G9 working copy) |
| C1.8 | Fiches liées (wikilinks, tags, arborescence) |
**Index (couche 2 — jetable)**
| ID | Critère |
|----|---------|
| C1.9 | Embeddings + index vectoriel (sqlite-vec **ou** Qdrant) |
| C1.10 | Index **reconstruisible** à tout moment depuis le vault |
**Consultation**
| ID | Critère |
|----|---------|
| C1.11 | Requête NLP → réponse + références précises (machine + chemin) |
| C1.12 | Exposition **MCP** (outil « second cerveau ») |
| C1.13 | **Récupération autonome** : le harnais appelle l'outil seul quand le sujet revient |
### Phase 2 — Scanner (Tier B, sans IA)
| ID | Critère |
|----|---------|
| C2.1 | Config N sources (SMB, SFTP, local), identifiants chiffrés |
| C2.2 | Pass 1 métadonnées (chemin, taille, mtime, type) |
| C2.3 | Dédoublonnage (pré-filtre taille, hash exact **uniquement** sur doublons) |
| C2.4 | Détection corruption (photos : EXIF/validité ; films : intégrité) |
| C2.5 | Stats volumétriques (par source / type / doublons) |
| C2.6 | Dashboard minimal (volumes, doublons, top dossiers) |
| C2.7 | Re-scan incrémental (mtime + size) |
### Phase 3 — Famille & assistante (long terme)
| ID | Critère |
|----|---------|
| C3.1 | **Partitionnement par personne** (moi → femme → enfants) |
| C3.2 | Second cerveau de la **femme** : ingestion **automatisée, non intrusive** (mails, organisation) |
| C3.3 | **Protection enfants** : alternative souveraine aux GAFAM / IA cloud (anti-dépendance) |
| C3.4 | **Assistante Effi** quotidienne : répond à tout sujet en puisant dans le cerveau + l'inventaire |
### Transverses (toutes phases)
| ID | Critère |
|----|---------|
| CT.1 | **100% local** — aucune donnée ne quitte la maison |
| CT.2 | **Non destructif / versionné** — historique, pas de perte |
| CT.3 | **Lisible + éditable par l'humain** — pas de boîte noire |
| CT.4 | **Index = cache** — jamais la source de vérité |
---
## Annexe B — Glossaire
| Terme | Définition |
|-------|------------|
| **Second cerveau** | Mémoire partagée, versionnée, lisible par l'humain et puisée d'elle-même par l'IA |
| **Vault** | La couche 1 : fichiers markdown versionnés git (source de vérité) |
| **Index** | La couche 2 : embeddings + index vectoriel (cache jetable, reconstruisible) |
| **Tier A** | Le cœur : ~10 Go précieux + mails + notes + écrits IA (analyse IA profonde) |
| **Tier B** | Le bulk : photos, films, archives (analyse cheap, sans IA) |
| **MCP** | Model Context Protocol — expose le second cerveau comme outil pour les harnais IA |
| **Harnais** | L'outil IA (OpenFox, OpenCode, Hermes) qui consomme le second cerveau |
| **RAG** | Retrieval-Augmented Generation |
| **pHash** | Hash perceptuel (images) pour détection de doublons approximatifs |
| **Gitea** | Host git canonique du vault (versioning) |
| **Dégafamisation** | Remplacer les services GAFAM par des équivalents locaux |
| **CAS** | Certificat d'Aptitude Spéciale (IA & transformation digitale) |
---
## Changelog
| Date | Version | Changement |
|------|---------|------------|
| 06/10/2026 | **v1.1** | **Correction IP** : `effi-server` / J5040 standardisé sur l'IP **cible .1** (post-rebuild) au lieu de l'ancienne .27 — S3.1 (écrits IA OpenFox/Hermes) + S9.1 (rôle machine). Le projet démarre **après** la (re)création où J5040 = .1. |
| 05/10/2026 | **v1.0** | **Création du dossier** — synthèse de la réflexion du 05/10/2026. Périmètre Tier A/B, **9 exigences du second cerveau** (S2), sources réelles vérifiées (S3), **architecture 2 couches** (S4), MVP Tier A (S5), roadmap (S6), décisions ouvertes (S7), risques (S8), lien infra (S9), montée en compétence (S10), **31 critères d'acceptance** (Annexe A). |
+16
View File
@@ -0,0 +1,16 @@
# Infrastructure Folzo — Docs
Documents complémentaires. Lis-les dans cet ordre :
| Fichier | Rôle | Nature |
|---------|------|--------|
| **`DOSSIER-RECREATION.md`** | Le **plan vivant** (v2) : vision, architecture cible (edge / cœur famille / IA / spare), 3-2-1 (données IA incluses), plan de (re)création. | **Source de vérité courante** — à maintenir à jour. |
| **`DOSSIER-SOFTWARE.md`** | La **Partie 5 — couche logicielle** (table rase) : stack consolidée ~20 briques, décisions tranchées (photos, notes, mail, IA, comms, MDM), sécurité, backup restic/borg, ops, plan de migration R&D→production, AC1–AC10. | **Cahier des charges logiciel** — à implémenter pendant le rebuild. |
| **`ETAT-DE-LART-2026-09-30.md`** | **Snapshot daté** du scan 30/09/2026 : specs hardware, disques, conteneurs, réseau. **Figé** à la date du scan. | **Référence immuable** — source unique des faits matériels à la date du scan. |
| **`ETAT-DE-LART-2026-10-06.md`** | **Snapshot daté** (suivi 06/10/2026) : diagnostic disque USB backup HS, incident Nextcloud (Watchtower), état HyperBackup, accès SSH xpenology. | **Référence immuable** — source unique des faits matériels de la période 30/09–06/10. |
| **`GUIDE-CULTURE-DONNEE.md`** | La **culture de la donnée** : comment traiter la donnée comme un actif (naming, backup, sécurité, gouvernance). | **Couche de transformation** — principes + pratiques. |
| **`archive/DOSSIER-INFRA.md`** | Le plan v1 (30/09/2026) — **supplanté** par `DOSSIER-RECREATION.md`. | **Trace historique** — conservé en `archive/`, plus mis à jour. |
| **`PROJET-SECOND-CERVEAU.md`** | Le **cahier des charges** du projet « Second Cerveau & Inventaire » : 9 exigences, architecture 2 couches, MVP, 31 critères d'acceptance. | **Cahier des charges** — à implémenter après les fondations infra. |
| **`g9-migration/README.md`** | Le **kit d'exécution** de la migration G9 : scripts + configs + compose, charte G1–G10, phases. `g9-migration/journal.md` = journal d'exécution (trace de ce qui a réellement été fait). | **Runbook opérationnel** — à exécuter et tenir à jour pendant la migration. |
> **Règle** : le dossier est vivant (il évolue), le snapshot est figé (il date), le guide est la culture. En cas de conflit sur les faits matériels, c'est le **snapshot** qui fait foi ; pour l'**architecture machine** et le plan, c'est **`DOSSIER-RECREATION.md`** (v2) qui fait foi ; pour le **logiciel** (stack, briques, licences), c'est **`DOSSIER-SOFTWARE.md`** (Partie 5) qui fait foi.
+642
View File
@@ -0,0 +1,642 @@
# Dossier — (Re)création de l'infrastructure locale Folzo Home (v1)
> ⚠️ **Document supplanté** — ce dossier v1 (30/09/2026) est **supplanté** par [`DOSSIER-RECREATION.md`](DOSSIER-RECREATION.md) (v2, 02/10/2026), qui est la **source de vérité actuelle** pour l'architecture et le plan d'exécution. Ce document est conservé comme **trace historique** (méthode, état de l'art, besoins). **Ne pas l'utiliser comme référence opérationnelle.**
**Date** : 30/09/2026
**Auteur** : Jean-Henry Foltzenlogel (Folzo)
**Réseau** : `192.168.1.0/24`
**Statut global** : 🔒 **Gelé (trace historique)** — supplanté par `DOSSIER-RECREATION.md` (v2). Plus de mise à jour.
**Document** : **figé** — conservé comme trace de la méthode (état de l'art → besoins → architecture → plan).
> **Nature du document** : ce n'est pas un rapport à lire une fois. C'est un **cahier des charges + plan de (re)création + portfolio**. Il (1) structure la réflexion sur les décisions ouvertes, (2) sert de plan de travail pendant le rebuild, (3) met en valeur la méthode et la compétence : *état de l'art → besoins → architecture → plan*, en raisonnement, pas en liste.
>
> **Source de vérité** : les données matérielles proviennent du scan live du 30/09/2026 (`lscpu`, `free`, `df`, `docker ps`, `nvidia-smi`, `systemctl`, sondes réseau, commandes DSM) — voir `ETAT-DE-LART-2026-09-30.md`. Ce dossier **corrige** les docs héritées inexactes (GPU, RAM, IP du NAS).
---
## Sommaire
- [Résumé exécutif](#résumé-exécutif)
- [G1 — Vision & intention](#g1--vision--intention)
- [G2 — État de l'art](#g2--état-de-lart)
- [G3 — Besoins](#g3--besoins)
- [G4 — Architecture cible](#g4--architecture-cible)
- [G5 — Décisions ouvertes](#g5--décisions-ouvertes)
- [G6 — Risques & priorités](#g6--risques--priorités)
- [G7 — Plan de (re)création](#g7--plan-de-recréation)
- [G8 — Montée en compétence](#g8--montée-en-compétence)
- [G9 — Qualité & mise en valeur](#g9--qualité--mise-en-valeur)
- [Annexe A — Matrice de traçabilité besoin → décision → action](#annexe-a--matrice-de-traçabilité)
- [Annexe B — Glossaire & abréviations](#annexe-b--glossaire--abréviations)
---
## Résumé exécutif
**Le problème** : l'informatique de la maison est aujourd'hui un système que je **subis** — pas de backup fonctionnel, un routeur qui surchauffe, un serveur à 94% de disque, un NAS XPEnology dont je redoute chaque mise à jour, des services GAFAM (Google, Apple, Netflix, Spotify, WhatsApp…) qui détiennent la vie digitale de la famille.
**La vision** : une infrastructure **100% locale** qui me **dégafamise**, équipée des **bons outils** pour la **qualité de vie digitale** de la famille, en **réutilisant intelligemment** tout le matériel existant, en **montant en compétence** (appliquer concrètement mon CAS IA & transformation digitale), et en **ne subissant plus l'informatique** : un système fiable, redondant, sobre, que je comprends et que je contrôle.
**La méthode** :
1. **État de l'art** honnête (G2) — ce que j'ai, ce qui marche, ce qui est cassé.
2. **Besoins** par usage quotidien (G3) — au niveau besoin, pas par techno.
3. **Architecture cible** (G4) — un rôle clair par machine, sur 4 axes (sécurité / fiabilité / redondance / conso).
4. **Décisions ouvertes** (G5) — photos & notes cadrées, **sans trancher**.
5. **Risques & priorités** (G6) — cadre fourni, ordre laissé ouvert (analyse en cours).
6. **Plan phasé** (G7) — fondations d'abord, structuré pour être **utilisé pendant** le rebuild.
7. **Montée en compétence** (G8) — l'infra comme terrain d'apprentissage et portfolio.
**Le succès** (mesurable) : dans 1 an, la famille n'utilise plus de service GAFAM pour photos, fichiers, notes, mots de passe, média, agenda ; le système est sauvegardé (3-2-1), sobre (~3 boîtiers 24/7), et je sais réparer n'importe quelle brique en 5 minutes parce que c'est **mon** système, documenté.
---
## G1 — Vision & intention
> **Statut** : ✅ Validé (cadrage partagé)
### 1.1 Contexte & intention
Après 7 ans d'infirmier (HUG), une reconversion en informatique, et un **CAS en IA & transformation digitale** défendu le 28/09/2026 (thèse sur la culture de la donnée et les données non structurées), je souhaite **appliquer concrètement** ce que j'ai validé à ma vie de tous les jours.
L'intention n'est pas de "réparer des pannes" (même si c'est un moyen). C'est de **devenir acteur** de mon environnement informatique :
- **Ne plus subir** : un système qui tourne tout seul, que je comprends, que je peux réparer.
- **Devenir acteur** : des choix raisonnés, documentés, que je maîtrise.
- **Appliquer mon CAS** : l'infrastructure comme terrain d'apprentissage et comme **portfolio réel**.
### 1.2 Les 5 piliers
| # | Pilier | Définition (1 phrase) |
|---|--------|------------------------|
| 1 | **Se dégafamiser** | Remplacer chaque service GAFAM de la famille par un besoin satisfait en local, sur du matériel que je possède déjà. (100% = horizon long terme ; le target 1 an est un sous-ensemble défini, voir G1.4.) |
| 2 | **Les bons outils** | Choisir, **au cas par cas**, les outils qui améliorent la vie quotidienne — par usage, pas par techno. |
| 3 | **Réutiliser le matériel** | Donner un rôle clair à chaque machine existante, en tenant compte de la sobriété et de la redondance. |
| 4 | **Monter en compétence** | Utiliser l'infra comme terrain d'apprentissage (Docker, réseau, DNS, backup, sécurité) + une source de vérité. |
| 5 | **Être acteur** | Un système fiable, observable, documenté → ne plus subir l'informatique. |
### 1.3 Principes directeurs
| Principe | Signification |
|----------|---------------|
| **100% local** | Les données de la famille ne quittent pas la maison (sauf choix explicite). |
| **Souveraineté** | Indépendance vis-à-vis des GAFAM et des dépendances fragiles (XPEnology). |
| **Réutilisation** | Pas d'achat pour l'achat — donner un rôle au matériel existant d'abord. |
| **Sobriété** | Consommation électrique maîtrisée : 3 boîtiers 24/7 sobres, le reste on-demand. |
| **Fiabilité** | Le système tourne sans intervention constante (watchdogs, services stables). |
| **Redondance** | Aucune donnée unique : backup 3-2-1 (3 copies, 2 supports, 1 offsite). |
| **Compréhension** | Chaque brique est documentée et comprise — sinon on ne la garde pas. |
### 1.4 Définition du succès (mesurable, horizon 1 an)
| Critère de succès | Mesure |
|-------------------|--------|
| **Dégafamisation** | Photos, fichiers, notes, mots de passe, média, agenda de la famille accessibles **sans** GAFAM. (Mail, messagerie, streaming = horizon > 1 an.) |
| **Fiabilité** | Backup 3-2-1 **vérifié** (restauration testée au moins une fois). |
| **Sobriété** | Seulement 3 boîtiers 24/7 (Freebox + NAS + effi-server), conso idle mesurée et stable. |
| **Maîtrise** | Je peux réparer n'importe quelle brique en < 5 min (doc + procédure). |
| **Compétence** | Dossier + docs projet maintenus à jour = portfolio démontrable. |
---
## G2 — État de l'art
> **Statut** : ✅ Validé (scan live 30/09/2026) — **corrections** appliquées vs docs héritées
### 2.1 Inventaire des machines (rôles)
> **Référence matérielle** : les specs hardware détaillées (CPU, RAM, disques, conteneurs) sont dans le **snapshot daté** `ETAT-DE-LART-2026-09-30.md` §2/§3/§5 — **source unique des faits matériels**. Ce tableau ne retient que le **rôle** de chaque machine (la valeur ajoutée du dossier) pour éviter la duplication et la dérive.
| Machine | IP | Rôle actuel | Statut |
|---------|-----|-------------|--------|
| **Freebox Delta** | .254 | Passerelle + DNS + domotique | actif (thermique à corriger) |
| **NAS (futur G9)** | **.25** | **Cœur famille (cible)** | prêt sur le bureau, non branché |
| **xpenology / NVR** | .25 (actuel) | NVR + ~30 conteneurs + stockage | à décommissionner |
| **218play** | .26 | NAS backup | actif (offsite non exploité) |
| **effi-server** | .27 | Cerveau IA (34 conteneurs) | actif (disque 94%) |
| **folzo-corp** | .11 | Muscle IA (LLM, Ollama, ComfyUI…) | actif (quelques services cassés) |
| **Laptop** | — | Workstation principale | actif |
| **BC 250** | — | Gaming (candidat Steam Machine) | — |
| **Postes enfants** | .15/.16/.17 | Accès stack famille | actifs |
| **Média salon** | .51–.59 | Streaming / audio | actifs |
| **Alarme** | .21 | Alarme maison | active |
| **Switches** | .2/.3/.5 | Réseau | actifs |
| **NVR Reolink ×3** | .41/.123/.191 | Surveillance (RTSP) | actifs |
> ⚠️ **Corrections vs docs héritées** (appliquées dans le snapshot comme ici) :
> - Le NAS cible est sur **.25** (le G9 remplace xpenology **sur place**), pas .10.
> - folzo-corp = **2× RTX 3060 12 Go + 62 Go RAM** (les docs `Effi`/`Hermes`/`Folzo Corp.` disaient « RX 7800 XT 16 Go / 32 Go » → **obsolète**).
> - Le futur NAS = **HP Pro Mini 400 G9, i3-13100T** (les docs disaient « i3-12100 » → corrigé).
### 2.2 Par machine : ce qui marche / ce qui est cassé
| Machine | ✅ Marche | ❌ Cassé / fragile |
|---------|-----------|---------------------|
| **Freebox** | Routeur, DNS (AdGuard .1), domotique (HA .20), RAID5 | **85°C** (usure thermique, risque throttling) |
| **xpenology** | NVR, Nextcloud, Vaultwarden, Joplin, Gitea, n8n, \*arr | **Backup USB non reconnu** (pas de backup — disque Seagate 3.6 To à secteurs HS, diagnostiqué 01/10), 5 conteneurs morts, **légalité + peur MAJ** |
| **218play** | Stockage backup (2×2To) | **Non utilisé** comme offsite (pas de flux) |
| **effi-server** | OpenWebUI, Qdrant, n8n, Hermes, Dify, SearXNG, STT/TTS | **Disque 94%** (6 Go libres), 34 conteneurs (surcharge) |
| **folzo-corp** | **LLM stable** (Swift-1.5 Qwen 27B, 57 tok/s, 50/50), Ollama, kreuzberg | **ComfyUI mort** (port 8188), **RAGFlow crash loop**, effi-services FAILED, effi-stt/ocr unhealthy, 18 modèles legacy (98 Go) |
| **G9** | Matériel prêt | **Non branché, non installé** |
### 2.3 Dépendances GAFAM actuelles (à dégafamiser)
| Service GAFAM | Usage famille | Criticité |
|---------------|---------------|-----------|
| **Synology Photos** (via XPEnology) | Photos famille (~10 ans) | ⚠️ **haute** — fiable mais **enchaîné à XPEnology** |
| **Google** (Drive/Cal/Contacts/Keep) | Fichiers, agenda, contacts, notes | haute |
| **Gmail** | Mail famille | haute |
| **Netflix / YouTube** | Films / séries / vidéo | moyenne |
| **Spotify / Apple Music** | Musique | moyenne |
| **WhatsApp / iMessage / FaceTime / Meet** | Messagerie / vidéo | moyenne |
| **ChatGPT / Gemini** | IA (remplacé par Effi/Hermes local) | basse (déjà en cours) |
### 2.4 Actifs de données identifiés
| Actif | Volume estimé | Importance | Où aujourd'hui |
|-------|---------------|------------|----------------|
| **Photos famille** | **~10 ans** | 🔴 **patrimoine** | Synology Photos (XPEnology) |
| **Notes** | variable | haute | Joplin / Affine / NC / Obsidian (dispersé) |
| **Fichiers famille** | variable | haute | Nextcloud (XPEnology) |
| **Modèles LLM (GGUF)** | 98 Go | moyenne | folzo-corp `/home/folzo/models` |
| **Média (films/séries)** | 15To + 5.5To USB | moyenne | XPEnology USB |
| **Projets / docs** | variable | haute | Gitea + clones locaux |
| **Mots de passe** | — | 🔴 **critique** | Vaultwarden (XPEnology) |
### 2.5 Fragilité XPEnology (moteur de la migration)
Le xpenology (DSM sur hardware non officiel) pose **deux problèmes structurels** :
1. **Légalité** : DSM sur un NVR reflashé = zone grise légale.
2. **Peur des MAJ** : chaque mise à jour DSM risque de **casser le système** (pas de support officiel, pas de retour en arrière garanti).
→ C'est le **moteur** de la migration vers le **G9** (OS neuf, supporté, sous mon contrôle) **sur la même IP .25**. Le xpenology sera **décommissionné** une fois la migration faite.
---
## G3 — Besoins
> **Statut** : 🟡 **À affiner au cas par cas** — les besoins sont posés ; le choix des outils se fera **ultérieurement**, par usage, **sans prescription** ici.
### 3.1 Règle de lecture
- Chaque besoin est au **niveau besoin** (ce qu'on veut obtenir), **pas** au niveau techno.
- **1 à 2 exemples max** de direction, **aucune prescription d'app**.
- Besoins séparés : **famille** / **personnel**.
- Chaque besoin est rattaché à un **pilier** (G1).
- Les **décisions ouvertes** (photos, notes) sont marquées ⚠️ et **non résolues** (→ G5).
### 3.2 Besoins — Famille
| # | Besoin (usage quotidien) | Situation actuelle | Direction à étudier (1-2 ex. max) | Pilier | Statut |
|---|--------------------------|--------------------|-------------------------------------|--------|--------|
| F1 | **Photos** : mémoire familiale, IA, partage, **sans dépendance XPEnology** | Synology Photos (~10 ans, fiable) | "gère tout" (ex. Immich) **ou** "stockage + accès" (ex. Nextcloud) | 1, 2 | ⚠️ **ouvert** (G5.1) |
| F2 | **Notes** : aligné sur la culture de la donnée (local, portable, structuré) | Joplin / Affine / NC / Obsidian (dispersé) | à étudier en profondeur | 1, 2, 4 | ⚠️ **ouvert** (G5.2) |
| F3 | **Fichiers** : centralisation famille, accès multi-appareils | Nextcloud (XPEnology) | centralisation + sync | 1, 2 | à étudier |
| F4 | **Agenda + contacts** : synchro tous téléphones | Google Cal/Contacts | CalDAV/CardDAV local | 1 | à étudier |
| F5 | **Mots de passe** : coffre local, partage famille | Vaultwarden (XPEnology) | coffre auto-hébergé | 1 | à étudier |
| F6 | **Films / séries** : médiathèque + auto-download | Netflix / YouTube | Jellyfin + \*arr | 2 | à étudier |
| F7 | **Musique** : médiathèque audio | Spotify / Apple | Jellyfin / Navidrome | 2 | à étudier |
| F8 | **Mail** : serveur local + client | Gmail | serveur mail + Thunderbird | 1 | à étudier |
| F9 | **Recherche web** : sans Google | Google | méta-moteur local (ex. SearXNG) | 1 | à étudier |
| F10 | **Domotique** : centralisation maison | Home Assistant (Freebox) | centralisation + automatisations | 2 | à étudier |
| F11 | **Caméras** : vision IA locale | NVR Reolink + Surveillance Station | vision IA (ex. Frigate) | 2 | à étudier |
| F12 | **Anti-ads / DNS** : filtre famille (enfants) | AdGuard Home (.1) | filtre DNS | 2 | à étudier |
| F13 | **Messagerie / vidéo** : alternatives ouvertes | WhatsApp / FaceTime / Meet | messagerie ouverte (ex. Matrix) + visio (ex. Jitsi) | 1 | à étudier |
| F14 | **Jeux** : maison + rétro | Steam | serveur de jeu + rétro | 2 | à étudier |
| F15 | **Lecture / RSS** : veille | — | agrégateur local (ex. FreshRSS) | 2 | à étudier |
### 3.3 Besoins — Personnel
| # | Besoin | Situation actuelle | Direction | Pilier | Statut |
|---|--------|--------------------|-----------|--------|--------|
| P1 | **IA personnelle** : assistant + RAG sur mes docs | Effi / Hermes + Jean (en place) | fiabiliser la chaîne | 2, 5 | ✅ en cours |
| P2 | **Gestion de projet** : suivi des tâches | Leantime | local | 2 | à étudier |
| P3 | **Versioning** : code + docs | Gitea (down) | fiabiliser | 4, 5 | à étudier |
| P4 | **Automatisation** : workflows | n8n | local | 2, 5 | à étudier |
### 3.4 Mapping besoin → pilier
| Pilier | Besoins couverts |
|--------|------------------|
| 1 — Dégafamisation | F1–F5, F8, F9, F13 |
| 2 — Bons outils | F1–F15, P1–P4 |
| 3 — Réutilisation | (tous — chaque besoin mappé sur une machine existante, G4) |
| 4 — Montée en compétence | F2, P2, P3 |
| 5 — Être acteur | P1, P3, P4 (+ fiabilité globale) |
---
## G4 — Architecture cible
> **Statut** : ✅ Cadré — chaque machine a un **rôle clair**, évalué sur 4 axes.
### 4.1 Rôle de chaque machine (4 axes)
| Machine | **Rôle cible** | Sécurité | Fiabilité | Redondance | Conso |
|---------|----------------|----------|-----------|------------|-------|
| **Freebox .254** | Passerelle + DNS (filtre) + domotique + **backup intermédiaire** | gateway + VPN + DNS famille | 24/7 | RAID5 (3×1To) = 2ᵉ copie | **85°C à corriger** |
| **NAS .25 (G9)** | **Cœur famille** : photos, fichiers, notes, mots de passe, média, domotique, caméras | tout le patrimoine famille | 24/7 = **point critique** → doit être sauvegardé | 2 disques internes + USB + offsite | i3-13100T 35W, **~10-15W idle** (sobre) |
| **effi-server .27** | **Cerveau IA** : orchestration, RAG, assistant | données RAG | **disque 94% → à alléger** | — | ~10W (sobre, 24/7) |
| **folzo-corp .11** | **Muscle IA** : LLM, génération, STT/TTS | LLM 100% local (rien ne sort) | gestion VRAM | 2 GPU | **grosse conso en charge → on-demand (WoL), pas 24/7** |
| **xpenology .25** | **À décommissionner** (remplacé par le G9 sur .25) | légalité + peur MAJ → moteur migration | — | — | — |
| **218play .26** | **Backup offsite** (3ᵉ copie) | 3-2-1 | — | — | **éteinte le plus souvent** (on-demand) |
| **Laptop** | Poste principal + dev | — | — | — | on-demand |
| **BC 250** | Gaming / média salon | — | — | — | on-demand |
| **Postes enfants** | Accèdent à la stack famille | DNS filtré, profils | — | — | — |
### 4.2 Principe 24/7 vs on-demand
| Mode | Machines | Raison |
|------|----------|--------|
| **24/7** (toujours allumées) | **Freebox** + **NAS** (+ effi-server) | Seuls les services critiques famille + cerveau IA. Tous **sobres** (i3-13100T, J5040). |
| **On-demand** (WoL / allumage à la demande) | **folzo-corp** (IA lourde), **BC 250** (gaming), **218play** (backup offsite), **Laptop** | Grosse conso en charge → ne les allumer que quand c'est nécessaire. |
> **Sobriété** : l'objectif est **3 boîtiers 24/7 sobres** (Freebox + NAS + effi-server, tous ~10W). Le muscle IA (2× 3060) est **on-demand** pour ne pas gaspiller.
### 4.3 Stratégie de backup 3-2-1
**3-2-1** = **3** copies, sur **2** supports différents, dont **1** offsite.
| Copie | Support | Machine | Contenu | Fréquence |
|-------|---------|---------|---------|-----------|
| **1** (originale) | Disques internes (NVMe + SATA) | **NAS .25** | Photos, fichiers, notes, mots de passe, média | continu |
| **2** (intermédiaire) | RAID5 (3×1To) | **Freebox** | Snapshot NAS quotidien | quotidien |
| **3** (offsite) | 2×2To | **218play** | Copie des données critiques (photos, mots de passe, docs) | hebdo / on-demand |
> **Prérequis** : le backup 3-2-1 ne peut être mis en place **qu'après** la résolution du backup USB HS (G6, risque #1) et l'installation du G9.
### 4.4 Topologie réseau (schéma)
```
INTERNET (Fibre Free)
│
┌───────▼────────────────────────────────────────┐
│ FREEBOX DELTA (.254) — gateway + VPN │
│ ├─ AdGuard Home (.1) — DNS filtre famille │
│ ├─ Home Assistant (.20) — domotique │
│ └─ RAID5 (3×1To) — backup intermédiaire │
└───┬───────────┬───────────┬───────────┬────────┘
│ │ │ │
┌───▼───┐ ┌────▼────┐ ┌───▼────┐ ┌───▼─────────┐
│ NAS │ │ effi- │ │ folzo- │ │ 218play │
│ .25 │ │ server │ │ corp │ │ .26 │
│ (G9) │ │ .27 │ │ .11 │ │ (offsite) │
│ cœur │ │ cerveau │ │ muscle │ │ 3ᵉ copie │
│ famille│ │ IA │ │ IA │ │ │
└───────┘ └─────────┘ └────────┘ └─────────────┘
│
├─ Postes enfants (.15/.16/.17)
├─ Média salon (.51–.59)
├─ NVR Reolink ×3 (.41/.123/.191)
└─ Alarme (.21)
```
### 4.5 Décommissionnement xpenology
- Le **G9** prend l'IP **.25** (même adresse) → migration **transparente** (DNS, sous-domaines conservés).
- Le **xpenology** est **décommissionné** une fois la migration validée (plus de DSM, plus de risque MAJ, plus de zone grise légale).
- Les **NVR Reolink** (autonomes) restent tels quels ; la surveillance migre vers le NAS (Frigate) si souhaité.
---
## G5 — Décisions ouvertes
> **Statut** : 🟡 **Cadrées, non tranchées** — à étudier **au cas par cas**, dans un second temps. Ce dossier fournit le **cadre de réflexion + critères d'évaluation**, pas la décision.
### 5.1 Décision ouverte — Photos
**Contexte** : 10 ans de photos sur Synology Photos, **fiable**, mais **enchaîné à XPEnology** (légalité + peur MAJ). C'est LA décision qui justifie le NAS G9.
**Besoin** : la même **fiabilité** + **IA** + **partage famille**, **sans dépendance XPEnology**.
**Axes de réflexion** :
| Axe | Option A — "gère tout" | Option B — "stockage + accès" |
|-----|------------------------|-------------------------------|
| Exemple | Immich | Nextcloud (photos) |
| IA (reconnaissance, recherche) | intégrée | à ajouter |
| Partage famille | intégrée | à configurer |
| Migration des 10 ans | export + re-upload | rsync / sync |
| Complexité | plus élevée | plus simple |
| Souveraineté | forte | forte |
**Critères d'évaluation (à remplir lors de l'étude)** :
- [ ] Fiabilité sur le long terme (track record)
- [ ] Qualité de l'IA (reconnaissance personnes/lieux, recherche sémantique)
- [ ] Facilité de partage famille (liens, albums)
- [ ] Effort de migration des 10 ans de photos
- [ ] Consommation (CPU/RAM/disk) sur le G9 (16 Go)
- [ ] Maintenance (MAJ, backups)
- [ ] Sortie / export (rester libre)
**Volume à estimer** : ~10 ans de photos → **estimer le volume** (Go / nombre de fichiers) avant de choisir.
### 5.2 Décision ouverte — Notes
**Contexte** : essai de **Joplin / Affine / Nextcloud Notes / Obsidian**… toujours pas fixé. C'est un **gros point de réflexion** lié à la **culture de la donnée**.
**Besoin** : un outil de notes **local, portable, structuré, souverain**, aligné sur ma culture de la donnée (thèse CAS).
**Critères d'évaluation (à remplir lors de l'étude)** :
- [ ] **Local** : données sur le NAS (pas dans le cloud)
- [ ] **Multi-appareils** : synchro laptop / téléphone / tablette
- [ ] **Recherche** : plein texte + (idéalement) sémantique
- [ ] **Structure** : hiérarchie, liens, tags, graph
- [ ] **Format** : ouvert (Markdown) → pas de verrouillage
- [ ] **Export** : rester libre (exporter tout à tout moment)
- [ ] **Collaboration** : partage famille (si besoin)
- [ ] **Intégration** : avec le reste de la stack (RAG, Effi)
**Point de réflexion clé** : quelle est la **philosophie** de mes notes — "capture rapide" (type Joplin) vs "second cerveau structuré" (type Obsidian) vs "workspace collaboratif" (type Affine) ? La réponse oriente le choix.
### 5.3 Règle pour les décisions ouvertes
> Chaque décision ouverte a une **liste de critères d'évaluation prêts à l'emploi** (5.1, 5.2). Lors de l'étude ultérieure, on **remplit la grille**, on **note le choix**, et on **met à jour ce dossier** (G3 + G4). **On ne tranche jamais à l'arrache.**
---
## G6 — Risques & priorités
> **Statut** : 🟡 **Cadre fourni, ordre LAISSÉ OUVERT** (analyse en cours). Le cadre d'évaluation est prêt ; les priorités seront fixées **après** l'analyse complète.
### 6.1 Cadre d'évaluation des risques
**Formule** : `Risque = Probabilité (1-5) × Impact (1-5)` → score 1-25.
| Score | Niveau | Action |
|-------|--------|--------|
| 15-25 | 🔴 Critique | Traiter **en premier** (fondations) |
| 8-14 | 🟠 Élevé | Traiter tôt |
| 4-7 | 🟡 Moyen | Traiter au fil de l'eau |
| 1-3 | 🟢 Faible | Surveiller |
**Probabilité** : 1 = quasi impossible, 5 = quasi certain.
**Impact** : 1 = négligeable, 5 = perte de données / service famille à l'arrêt.
### 6.2 Risques actuels connus
| # | Risque | Machine | Prob. | Impact | Score | Niveau | Lien vision |
|---|--------|---------|-------|--------|-------|--------|-------------|
| 1 | **Pas de backup** (USB HS — **secteurs HS détectés 01/10**) | xpenology | 4 | 5 | **20** | 🔴 | Pilier 5 (fiabilité) — sans backup, tout le reste repose sur du sable. **Diagnostic** : Seagate Expansion 3.6 To NTFS, 2 zones de bad blocks (dont photos 2008 illisibles). **À sauvegarder immédiatement** avant dégradation totale. Voir `ETAT-DE-LART-2026-10-06.md` §2 |
| 2 | **Freebox 85°C** | Freebox | 3 | 4 | **12** | 🟠 | Pilier 5 — usure du routeur = tout le réseau |
| 3 | **Disque effi-server 94%** | effi-server | 4 | 3 | **12** | 🟠 | Pilier 5 — risque de saturation du cerveau IA |
| 4 | **Gitea down** | xpenology | 3 | 3 | **9** | 🟠 | Pilier 4 — versioning des docs/projets |
| 5 | **XPEnology (légalité + MAJ)** | xpenology | 3 | 4 | **12** | 🟠 | Pilier 1, 5 — moteur de la migration G9 |
| 6 | **ComfyUI mort** | folzo-corp | 3 | 2 | **6** | 🟡 | Pilier 2 — génération d'images |
| 7 | **RAGFlow crash loop** | folzo-corp | 4 | 2 | **8** | 🟠 | Pilier 2 — RAG |
| 8 | **18 modèles legacy (98 Go)** | folzo-corp | 2 | 2 | **4** | 🟡 | Pilier 3 — nettoyage |
| 9 | **Docs obsolètes (GPU/RAM)** | — | 2 | 2 | **4** | 🟡 | Pilier 4 — culture de la donnée |
| 10 | **Backup intermédiaire Freebox non validé** | Freebox | 3 | 3 | **9** | 🟠 | Pilier 5 — utiliser le RAID5 interne de la Freebox comme cible de snapshot NAS est non standard ; à valider avant Phase 0 |
### 6.3 Principe "fondations d'abord"
> **On ne construit pas sur du sable.** Avant toute migration ou nouveau service, les **fondations** doivent être solides :
> 1. **Backup 3-2-1** fonctionnel (risque #1)
> 2. **Freebox thermique** corrigée (risque #2)
> 3. **Disque effi-server** allégé (risque #3)
>
> Sans ces 3, tout le reste (NAS G9, dégafamisation) repose sur un système fragile.
#### 6.3.1 Pourquoi Synology ne détecte plus le disque backup (diagnostic 01/10/2026)
Le 3ᵉ USB (backup du Synology) est un **Seagate Expansion Portable 3.6 To** (serial `NAAXB11T`), NTFS, 3.2 To de sauvegardes. **Diagnostic** : le système de fichiers NTFS est cohérent, mais **2 zones de secteurs défectueux (bad blocks)** bloquent le montage par DSM (dont un répertoire de **photos famille 2008 illisible**). Causes secondaires : format NTFS (lecture seule sans `ntfs-3g`) + disque branché sur effi-server.
> **Diagnostic complet + zones exactes + recommandations détaillées** : voir `ETAT-DE-LART-2026-10-06.md` §2 (source unique).
**Action prioritaire absolue** : **copier immédiatement les 3.2 To de données saines** vers 218play / G9 — le disque se dégrade, le risque de perte totale augmente chaque jour. **Ne PAS défragmenter** (cause physique, défrag risquée). À terme : **remplacer le disque** (portable non conçu pour du 24/7), formaté **ext4** ou **Btrfs**.
### 6.4 Ordre de priorité — LAISSÉ OUVERT
> **L'analyse n'est pas terminée.** L'ordre de priorité sera fixé **après** :
> - l'étude des décisions ouvertes (G5),
> - le remplissage complet de la matrice de risques (6.2),
> - la validation de la vision par l'utilisateur.
>
> **Cadre de décision fourni** : on traite d'abord les 🔴 (score 15+), puis les 🟠 (8-14), en respectant le principe "fondations d'abord" (6.3).
---
## G7 — Plan de (re)création
> **Statut** : 🟡 **Phasé, à affiner** — les phases et dépendances sont posées ; les détails d'exécution seront précisés au fur et à mesure. **Structuré pour être utilisé PENDANT le rebuild** (checklist + statut par phase).
### 7.0 Règle de lecture
- Chaque phase a : **objectif**, **actions**, **dépendances**, **critères de succès vérifiables**.
- On démarre par les **fondations** (fiabilité/backup), on finit par les **optimisations**.
- **Statut par phase** : ⬜ à faire → 🟡 en cours → ✅ validé.
- **On ne passe à la phase N+1 qu'après validation de la phase N.**
### 7.1 Phase 0 — Fondations (fiabilité)
**Objectif** : un système qui ne repose plus sur du sable.
| # | Action | Dépendance | Critère de succès (vérifiable) |
|---|--------|------------|-------------------------------|
| 0.1 | **Sauvegarder + remplacer** le **disque backup USB** (diagnostiqué 01/10 : Seagate Expansion 3.6 To NTFS, 2 zones de secteurs HS) | — | **3.2 To de données saines copiées** sur 218play/G9 + disque remplacé + backup testé (restauration OK) |
| 0.2 | Corriger la **thermique Freebox** (85°C) | — | Température < 70°C en charge |
| 0.3 | Alléger le **disque effi-server** (94%) | — | < 70% d'utilisation |
| 0.4 | Remettre **Gitea** en service | 0.1 | `gitea.folzocorp.fr` accessible |
**Statut** : ⬜ à faire
**Dépendances** : aucune (c'est le départ)
### 7.2 Phase 1 — Cœur famille (NAS G9)
**Objectif** : le NAS G9 sur .25 devient le **cœur famille** (dégafamisation).
| # | Action | Dépendance | Critère de succès |
|---|--------|------------|-------------------|
| 1.1 | Installer **Ubuntu Server 24.04** sur le G9 | Phase 0 | OS + réseau OK (IP .25) |
| 1.2 | Monter les disques (NVMe + SATA + USB) | 1.1 | Disques montés, partitions OK |
| 1.3 | Installer **Docker** + base services | 1.2 | Docker fonctionnel |
| 1.4 | Migrer **photos** (Synology → NAS) | 1.3 + **G5.1 tranché** | Photos accessibles, IA OK |
| 1.5 | Migrer **fichiers** (Nextcloud) | 1.3 | Fichiers accessibles multi-appareils |
| 1.6 | Migrer **mots de passe** (Vaultwarden) | 1.3 | Coffre accessible famille |
| 1.7 | Installer **média** (Jellyfin + \*arr) | 1.2 | Films/séries accessibles |
| 1.8 | Installer **notes** | 1.3 + **G5.2 tranché** | Notes accessibles |
| 1.9 | Configurer **DNS** (AdGuard) + sous-domaines | 1.3 | Sous-domaines résolus en local |
**Statut** : ⬜ à faire
**Dépendances** : Phase 0 + décisions G5.1/G5.2
### 7.3 Phase 2 — Cerveau IA (fiabiliser)
**Objectif** : la chaîne IA est stable et complète.
| # | Action | Dépendance | Critère de succès |
|---|--------|------------|-------------------|
| 2.1 | Réparer **ComfyUI** (port 8188) | — | Génération d'images OK |
| 2.2 | Réparer **RAGFlow** (crash loop) | — | RAG fonctionnel |
| 2.3 | Réparer **effi-services** (FAILED) | — | Services up |
| 2.4 | Trier les **18 modèles legacy** (98 Go) | — | Disk folzo-corp < 60% |
| 2.5 | Corriger les **docs obsolètes** (GPU/RAM) | — | Docs = réalité |
**Statut** : ⬜ à faire
**Dépendances** : Phase 0 (disque allégé)
### 7.4 Phase 3 — Finitions (dégafamisation complète)
**Objectif** : la famille n'utilise plus de GAFAM.
| # | Action | Dépendance | Critère de succès |
|---|--------|------------|-------------------|
| 3.1 | **Agenda + contacts** (CalDAV/CardDAV) | 1.3 | Synchro tous téléphones |
| 3.2 | **Mail** (serveur local) | 1.3 | Mail famille local |
| 3.3 | **Domotique** (HA → NAS) | 1.3 | Domotique centralisée |
| 3.4 | **Caméras** (Frigate) | 1.3 | Vision IA locale |
| 3.5 | **Messagerie / vidéo** (Matrix/Jitsi) | 1.3 | Alternatives ouvertes |
| 3.6 | **Jeux** (Valheim + rétro) | 1.2 | Jeux accessibles |
| 3.7 | **Décommissionner xpenology** | 1.4–1.8 validés | xpenology éteint, G9 seul sur .25 |
**Statut** : ⬜ à faire
**Dépendances** : Phase 1
### 7.5 Vue d'ensemble des phases
```
Phase 0 (Fondations) ──► Phase 1 (Cœur famille G9) ──► Phase 2 (Cerveau IA) ──► Phase 3 (Finitions)
backup/Freebox/ NAS + photos/fichiers/ ComfyUI/RAGFlow/ agenda/mail/
disque/Gitea média/notes docs caméras/jeux
└──────────────► Décommissionner xpenology
```
> **Note** : les phases 1 et 2 sont **partiellement parallélisables** (le cerveau IA ne dépend pas du NAS), mais la **phase 0 est toujours en premier**.
---
## G8 — Montée en compétence
> **Statut** : ✅ Cadré — l'infra comme terrain d'apprentissage et portfolio.
### 8.1 L'infra comme terrain d'apprentissage
Chaque service installé = **une compétence** :
| Compétence | Service / action | Phase |
|------------|------------------|-------|
| Docker | Installation + conteneurs | 1 |
| Réseau / DNS | AdGuard + sous-domaines | 1 |
| Reverse proxy | Nginx Proxy Manager | 1 |
| Backup | 3-2-1 | 0, 1 |
| Sécurité | VPN, firewall, mots de passe | 1, 3 |
| Stockage | Partitions, montages | 1 |
| IA | LLM, RAG, STT/TTS | 2 |
| Domotique | Home Assistant | 3 |
| Supervision | Beszel | 0 |
### 8.2 Principe "une seule source de vérité"
> **Ce dossier + les docs projet** = la source de vérité unique. Plus de dispersion entre `effi/README.md`, mémoires Hermes, docs ad-hoc.
>
> **Règle** : noter la **réalité mesurée** (scan live) plutôt que la documentation héritée. Après chaque évolution majeure → **mettre à jour ce dossier**.
### 8.3 Approche de documentation progressive
- **Documenter au fur et à mesure** (pas "à la fin").
- Chaque phase validée → mise à jour du dossier (statut + décisions).
- Les décisions ouvertes (G5) → remplissage de la grille + note du choix.
### 8.4 Lien avec le CAS (IA & transformation digitale)
> L'infrastructure est le **portfolio réel** du CAS :
> - **Culture de la donnée** (thèse) → appliquée dans la décision "notes" (G5.2) + la source de vérité (8.2).
> - **IA & transformation digitale** → appliquée dans la chaîne IA (Phase 2).
> - **Méthode** → état de l'art → besoins → architecture → plan (ce dossier).
---
## G9 — Qualité & mise en valeur
> **Statut** : ✅ En cours d'application — ce groupe décrit les règles de qualité du dossier lui-même.
### 9.1 Cohérence interne
| Point | Valeur unique (pas de contradiction) |
|-------|--------------------------------------|
| IP du NAS | **.25** (partout) |
| GPU folzo-corp | **2× RTX 3060 12 Go** (partout) |
| RAM folzo-corp | **62 Go** (partout) |
| CPU du G9 | **i3-13100T** (partout) |
| Rôles machines | cohérents G2 ↔ G4 ↔ G7 |
### 9.2 Structure & lisibilité
- **Français**, hiérarchie claire (H1/H2/H3), tableaux.
- Sommaire avec ancres.
- Chaque section porte son **statut** (✅ / 🟡 / ⬜).
### 9.3 Mise en valeur du travail
> Ce dossier ne liste pas : il **raisonne**.
> - **Méthode** : état de l'art → besoins → architecture → plan (démontrable).
> - **Profondeur** : scan live, corrections de docs héritées, matrice de risques, cadre de décision.
> - **Compétence** : chaque choix est justifié (pas d'assertion sans raison).
### 9.4 Document vivant
- **Versionné** : les documents vivants (ce dossier, le guide) ont un **nom sans date** ; les dates sont dans le **changelog**. Les **snapshots** (état de l'art) sont **datés et immuables** — chaque évolution majeure produit un nouveau fichier daté.
- **Statut par section** : ✅ validé / 🟡 en cours / ⬜ à faire.
- **Traçabilité** : besoin → décision → action (Annexe A).
### 9.5 Traçabilité
> Voir **Annexe A** : chaque besoin (G3) est rattaché à une décision (G5) et à une action (G7). Pas d'orphelin.
---
## Annexe A — Matrice de traçabilité
| Besoin (G3) | Pilier (G1) | Décision (G5) | Action (G7) | Machine (G4) |
|-------------|-------------|---------------|-------------|--------------|
| F1 Photos | 1, 2 | **G5.1 (ouvert)** | 1.4 | NAS .25 |
| F2 Notes | 1, 2, 4 | **G5.2 (ouvert)** | 1.8 | NAS .25 |
| F3 Fichiers | 1, 2 | — | 1.5 | NAS .25 |
| F4 Agenda/Contacts | 1 | — | 3.1 | NAS .25 |
| F5 Mots de passe | 1 | — | 1.6 | NAS .25 |
| F6 Films/Séries | 2 | — | 1.7 | NAS .25 |
| F7 Musique | 2 | — | 1.7 | NAS .25 |
| F8 Mail | 1 | — | 3.2 | NAS .25 |
| F9 Recherche | 1 | — | (effi-server) | effi-server .27 |
| F10 Domotique | 2 | — | 3.3 | NAS .25 |
| F11 Caméras | 2 | — | 3.4 | NAS .25 |
| F12 Anti-ads/DNS | 2 | — | 1.9 | Freebox .254 |
| F13 Messagerie/vidéo | 1 | — | 3.5 | NAS .25 |
| F14 Jeux | 2 | — | 3.6 | NAS / BC 250 |
| F15 Lecture/RSS | 2 | — | (à planifier) | NAS .25 |
| P1 IA perso | 2, 5 | — | 2.1–2.3 | folzo-corp .11 |
| P2 Gestion projet | 2 | — | (à planifier) | NAS .25 |
| P3 Versioning | 4, 5 | — | 0.4 | NAS .25 (Gitea) |
| P4 Automatisation | 2, 5 | — | (à planifier) | NAS .25 |
| Fiabilité (global) | 5 | — | 0.1–0.4 | toutes |
> **Lecture** : chaque besoin a un pilier, une décision (si ouverte), une action, et une machine. **Pas d'orphelin.**
---
## Annexe B — Glossaire & abréviations
| Terme | Définition |
|-------|-----------|
| **GAFAM** | Google, Apple, Facebook (Meta), Amazon, Microsoft |
| **Dégafamisation** | Remplacer les services GAFAM par des équivalents locaux |
| **3-2-1** | 3 copies, 2 supports, 1 offsite (stratégie de backup) |
| **WoL** | Wake-on-LAN (allumer une machine à distance) |
| **NAS** | Network Attached Storage |
| **NVR** | Network Video Recorder |
| **XPEnology** | DSM (Synology) installé sur hardware non officiel |
| **RAG** | Retrieval-Augmented Generation |
| **LLM** | Large Language Model |
| **STT / TTS** | Speech-To-Text / Text-To-Speech |
| **\*arr** | Stack de gestion média (Sonarr, Radarr, Prowlarr) |
| **CAS** | Certificat d'Aptitude Spéciale (IA & transformation digitale) |
| **G9** | HP Pro Mini 400 G9 (futur NAS) |
| **4 axes** | Sécurité, fiabilité, redondance, conso électrique |
---
## Changelog
| Date | Version | Changement |
|------|---------|------------|
| 30/09/2026 | v1.0 | Création du dossier — vision, état de l'art, besoins, architecture, décisions ouvertes, risques, plan, montée en compétence, qualité. |
| 30/09/2026 | v1.1 | Renommage sans date (doc vivant) ; suppression du périmètre pro CASU (ne rien ramener du travail) ; clarification scope dégafamisation (1 an = sous-ensemble) ; risque #10 (backup intermédiaire Freebox à valider) ; « 4 axes » (ex 4 lens). |
+112
View File
@@ -0,0 +1,112 @@
# Compte rendu — PHASE 1 : Prérequis & sécurisation
**Date de clôture :** 2026-10-10
**Statut :** ✅ TERMINÉE — 12/12 critères validés, GO utilisateur donné, tâche Kanban « PHASE 1 » déplacée en Done.
**Traçabilité complète :** `g9-migration/journal.md` (18 entrées, 6 colonnes, G8).
---
## 1. Périmètre et objectifs
| Étape Kanban | Objectif | Résultat |
|---|---|---|
| T1.1 | Sécuriser le xpenology (.25) : stateful hors Watchtower, CLEANUP=false, notifications | ✅ Fait sur .25 **et** répliqué sur .24 |
| T1.2 | Inventaire des stateful xpenology + plan d'export | ✅ `INVENTAIRE-STATEFUL.md` |
| T1.3 | Thermique Freebox < 70 °C | ⏭️ Hors périmètre agent (action manuelle utilisateur, hors G10 sans validation) |
| T1.4 | J5040 edge / délestage avant formatage | ✅ Stack IA迁移 .11, Valheim + n8n/notes sur G9, OpenFox .27→.11, GO formatage |
Charte G1–G10 respectée : aucune suppression de données source (G4), backup vérifié avant toute migration (G2), aucune action destructive (G3 — formatage SSD exécuté **par l'utilisateur**), journal tenu (G8), aucun secret commité (G9).
---
## 2. T1.1 — Durcissement Watchtower (.25 + .24)
### T1.1-a · Inventaire conteneurs .25
- 30 conteneurs (`sudo docker ps -a` DSM), cross-check 12 projets compose `/volume1/docker` + conteneurs GUI.
- Livrable : `conteneurs-xpenology.csv` (name, image:tag, mode GUI/compose, stateful, label, data_paths).
- 22 stateful, 2 stateless (flaresolverr, mini-qr), 1 watchtower. 15 yml compose récupérés.
### T1.1-b · Labels `watchtower.enable=false`
- **.24** : n8n, couchdb, obsidian, valheim → label `false` (`docker inspect`), watchtower `true`.
- **.25** : **28/28 stateful = false**, vérifié par boucle `docker inspect` (0 exception).
- 13 projets compose : labels dans les yml + `compose up -d` (backups `.bak-labels` sur le NAS, InfluxDB recréé).
- 8 conteneurs GUI (postgres16, vaultwarden-server-1, qbittorrent, sonarr, radarr, jackett, prowlarr, deluge) : `docker update --label-add` **indisponible sur docker 24.0.2 (DSM)** → clonage via API unix socket : stop → rename `-old-lbl` → create avec `Labels` **au niveau racine du payload** (les labels nichés dans `Config` sont épurés par le daemon Synology) → start. Données intactes (mêmes binds/env), restauration automatique en cas d'échec.
- Incident résolu en cours de route : accès .25 bloqué (clé retirée + compte `effi` bloqué par Synology) → déblocage par l'utilisateur, nouvel accès via compte `Master` port 222.
### T1.1-c · Watchtower durci (digest-pinné)
- Image : `nickfedor/watchtower@sha256:45b54f6dc15227cfb38dbb826f2881aa0f56aaf0c255cdbb38bd80e598bdca9a` (1.23.0 ; digest vérifié = `:1`/`:1.23`/`:latest` via API registry — `containrrr/watchtower:1` n'existe pas).
- Env sur .24 **et** .25 : `CLEANUP=false`, `INCLUDE_STOPPED=false`, `POLL_INTERVAL=3600`, `LABEL_ENABLE=true`, label `true`.
- .25 était `containrrr/watchtower:latest` CLEANUP=true (dangereux) → remplacé ; projet compose `watchpower` (`COMPOSE_PROJECT_NAME` requis).
- Contrainte fork nickfedor : refuse `SCHEDULE` + `POLL` simultanés → POLL 3600 conservé.
### T1.1-d · Notifications Watchtower → n8n → Matrix
- Chaîne : watchtower (shoutrrr generic) → n8n .24 workflow `w1pfhX0A7KvaDrtk` (Webhook → Build log → Matrix → Respond) → Synapse .24:8008, salon ops `!mp7MFx9QbrJ0ARtzw…`, user `ops-bot`.
- **Preuves** : notifs .24 (exec #10, #31, #32) + **notif startup réelle du watchtower .25 reçue** (« Watchtower 1.23.0 using Docker API v1.43 … Next scheduled run 02:48 »), event_ids vérifiés dans `event_json` de Synapse.
- Correctif : payload shoutrrr generic = `title`/`message` (le workflow lisait `event_name`/`image_name` → affichait « undefined »).
- Points techniques notés : n8n v2.42 exige publication du workflow ; URL Matrix en expression full `={{ }}` ; txnId = image+timestamp (évite les doublons).
---
## 3. T1.2 — Inventaire stateful .25
Livrable : `INVENTAIRE-STATEFUL.md` — pour chaque stateful : image:tag, data paths, taille `du -sh` mesurée, type DB, mode GUI/compose, dépendances, méthode d'export et destination (G9 / .11 / .25 / supprimé).
Tailles clés : NextCloud 2.3G, valheim 2.8G, openwebui 4.7G, Joplin 706M, Leantime 298M, AFFiNE 94M, postgres16 70M, n8n 75M, gitea 2M, vaultwarden 8.8M, beszel 11M, InfluxDB 188K.
Vigilances relevées : conteneurs GUI sans CLI labels (passage par API), flux .25→.11 (AFFiNE/OpenWebUI → LLM proxy), credentiels DB d'origine conservés à l'import (les seeds G9 sont ignorés sur base initialisée — documenté dans `07-migrate-stateful.sh`).
---
## 4. T1.4 — J5040 (.27) : délestage, migrations, GO formatage
### Backups (G2, avant toute migration)
- Valheim : world 53.6M + backups + env + pterodactyl.tar 4.7G → Folzo Save, SHA256 vérifiés.
- Inventaire complet J5040 : 34 conteneurs, 116 images, dumps DB (dify 671M, n8n sqlite, pterodactyl wings 4.7G), volumes nommés (open-webui 3.4G, whisper-cache 2.0G), home 1.8G, cron 6 jobs → **12 Go sur `.11:/run/media/folzo/Folzo Save/migration-j5040/` avec SHA256SUMS.txt** (`sha256sum -c` : 23/23 « Réussi » — l'unique « Échec » est l'auto-référence de SHA256SUMS.txt).
- OpenFox : `openfox-share.tar.gz` 942M + `node-bin.tar.gz` 52M archivés + SHA256 vérifiés.
### Migrations réalisées
| Cible | Contenu | Preuve |
|---|---|---|
| PC IA .11 | Stack IA complète (22 images save/load) : open-webui, searxng, pipelines, qdrant (6 collections), couchdb, mcpo, tts, dify (14 healthy), llm-proxy 3011/3012/3017, ollama 28 modèles, whisper large-v3 | Test LLM bout en bout → « MIGRATION-OK » ; endpoints 200 |
| G9 .24 | **Valheim** : monde restauré, 37 chunks sha256 identiques source, conteneur healthy, UDP 2456 joignable | Double-run G4 : source redémarrée healthy |
| G9 .24 | **n8n + Obsidian + CouchDB** : `database.sqlite` sha `693ee229` IDENTIQUE source, vault 79M | n8n 5678→200, couchdb 5984→200, obsidian 3100 OK |
| PC IA .11 | **OpenFox** (66 sessions, thread préservé) : systemd `openfox.service`, HTTP 200 sur .11:10369, symlink `/home/effi→/home/folzo`, SELinux `chcon -R -t bin_t` | Thread de migration repris sur .11 après bascule proxy |
### T1.4-e · GO utilisateur
Vérification **sans .27** (éteint) : OpenFox .11 200, IA .11 200, n8n/couchdb/obsidian/Matrix .24 OK, NAS .25 31/31 Up + labels 28/28 false + watchtower digest healthy. Backup 24 Go SHA256 23/23. **GO donné par l'utilisateur — formatage SSD J5040 exécuté par l'utilisateur lui-même** (conforme G3).
---
## 5. Livrables produits
| Fichier | Contenu |
|---|---|
| `g9-migration/journal.md` | 18 entrées 6 colonnes (G8), toutes les actions Phase 1 |
| `g9-migration/conteneurs-xpenology.csv` | Inventaire 30 conteneurs .25 |
| `g9-migration/INVENTAIRE-STATEFUL.md` | Inventaire stateful + plan d'export (base Phase 4) |
| `g9-migration/INVENTAIRE-J5040.md` | Inventaire J5040 avant formatage |
| Backups `.11:/run/media/folzo/Folzo Save/migration-j5040/` | 24 Go, SHA256SUMS vérifié |
| `/tmp/nas-yml/` (session) | yml compose .25 modifiés + `.orig` (répliqués sur le NAS avec `.bak-labels`) |
## 6. Améliorations post-revue (5 findings, tous résolus)
1. `README.md` racine : ligne ajoutée pour `g9-migration/README.md` dans le tableau de lecture.
2. `01-hardening.sh` : garde anti-lockout SSH (refuse `PasswordAuthentication no` si `authorized_keys` vide).
3. `07-migrate-stateful.sh` : multiplexage SSH (`ControlMaster/ControlPath/ControlPersist`) → 1 seule session par brique au lieu de 4+ ; pré-test `command -v rsync` sur le NAS.
4. `.gitignore` : règle no-op `!*.env.example` supprimée.
5. `restic.env.example` : suppression du faux backend `smb://` ; voies supportées documentées (sftp:, rclone `smb:`, montage CIFS).
---
## 7. Restes et points d'attention pour la Phase 2
- **T1.3 (thermique Freebox)** : non traitée par l'agent (matériel, hors périmètre) — à valider manuellement par l'utilisateur (< 70 °C en charge).
- **Cron BomberFox** (J5040) : spécifique J5040, à reconfigurer après formatage.
- **Proxy inverse openfox.folzocorp.fr** : bascule .27→.11 à confirmer définitivement côté utilisateur.
- **Clés SSH** : la clé `.25` utilisée est `id_ed25519_openfox11` (nouvelle, ajoutée aux `authorized_keys` du compte Master) ; les clés de l'ancien bundle .27 sont à reconstituer/archiver.
- Les imports Phase 4 conservent les **credentiels DB d'origine** du xpenology (documenté).
- Rollback global Phase 1 : rien de destructif sur les sources (.25 lecture seule + labels réversibles via `.bak-labels`, .27 éteint intact, backups Folzo Save).
## 8. Dépendances levées pour la suite
Phase 2 (Base G9) peut démarrer : docker est déjà opérationnel sur .24 (T2.3 de fait), durcissement SSH/ufw/fail2ban reste à faire (scripts `01-hardening.sh`, `02-disk-luks.sh`, `03-docker.sh` prêts et conformes à la charte).
+45
View File
@@ -0,0 +1,45 @@
# Inventaire J5040 → destinations (.11 / .24)
Backup complet : `.11:/run/media/folzo/Folzo Save/migration-j5040/` (12 Go, SHA256SUMS.txt vérifié, tars testés lisibles).
## Plateforme IA → PC IA .11
| Service J5040 | Image | Data (source J5040) | Backup |
|---|---|---|---|
| Dify (14 conteneurs) | dify:0.15.1 | `RAG/dify/docker/volumes/{db,pgvector,redis,app/storage,sandbox,plugin_daemon}` + `.env` | `platform/dify-compose.tar` |
| Open WebUI | open-webui:main | volume `effi_open-webui-data` | `platform/effi_open-webui-data.tar` (3.4 G) |
| Pipelines | pipelines:main | volume `effi_pipelines-data` | `platform/effi_pipelines-data.tar` |
| Whisper | faster-whisper:latest-cpu | volume `effi_whisper-cache` | `platform/effi_whisper-cache.tar` (2.0 G) |
| Valkey/Redis 2020 | valkey:8 | volume `valkey-data` | `platform/valkey-data.tar` |
| SearXNG | searxng:latest | bind `effi/searxng-data` (settings.yml +密钥) | `notes/effi-data.tar` + `config/searxng-settings.yml` |
| llm-proxy | effi-llm-proxy | build local, sans data | image `llm-proxy` + dockerfile |
| MCP-O | mcporter-idealabs | .env secrets | `config/effi-compose.yml` |
| OCR | effi-ocr-effi-ocr | bind .env OCR_SECRET | `config/ocr-compose.yml` |
| Hermes CLI | nosmo/hermes:2026.5.23.1 | bind `effi/openfox-hermes` | `notes/effi-data.tar` |
| Hermes WebUI | nesquena/hermes-webui | bind `/home/effi/.hermes` | `home/home-projects.tar` (.config) |
| Open Voice / TTS / Beszel / leantime-mcp | — | `effi/{open-voice,tts,beszel}` beszel db 32K | `notes/effi-data.tar` |
## Notes & automatisation → G9 .24
- n8n : `effi/n8n-data` (2.7 Mo, `database.sqlite`) → `notes/effi-data.tar`
- Obsidian (bind mount `effi/obsidian` = dossier partagé G9 déjà existant sur `.24:/home/folzo/Documents/Obsidian/notes` — S13.2, pas de backup redondant nécessaire)
- CouchDB `/home/folzo/obsidian/couchdb` sur .24 (déjà en place S13.3)
- Cron root : aucun. Cron effi : 6 jobs `*/15 * * * *` → `config/crontab-effi.txt`
## Pterodactyl → supprimé (S3.2)
- `/home/effi/pterodactyl` (4.7 G) backup au cas où : `pterodactyl/pterodactyl.tar`
- Panel + DB + cache + Valheim → **Ne PAS recréer**. Valheim seul migré (fait, voir 2026-10-09).
## Inventaires bruts
- `config/j5040-inventory.txt` : 34 conteneurs (name, image, state, mounts)
- `config/mounts-map.txt` : mapping complet bind/volume
- `config/images.txt` : 116 images
- `config/effi-compose.yml`, `config/hermes-compose.yml`, `config/ocr-compose.yml`, `config/effi-env` (secrets .env)
## Dépendances réseau J5040 (récits 2026-09-30)
- `172.20.0.0/16` → `172.20.0.10` proxy sur G9 (SearXNG)
- `llm-5040` réseau interne, `127.0.0.1:8080`
- `host.docker.internal → PC-IA (192.168.1.11)` pour Hermes
+55
View File
@@ -0,0 +1,55 @@
# Inventaire des conteneurs stateful xpenology .25 (Folzo-NAS DS240j)
> Source : `conteneurs-xpenology.csv` + 15 compose yml récupérés via `ssh cat` (avant blocage SSH .25, 2026-10-09).
> Périmètre T1.2-a : chaque conteneur de `docker ps -a` (30) est couvert ci-dessous.
> Destinations = **recommandations à valider au Go/No-Go T1.4-e** (user). Tailles = `du -sh` mesurées sur .25 le 2026-10-09.
## Couverture : 30 conteneurs de `docker ps -a`
Stateful (22) — 21 via compose, 7 via GUI DSM (labels applicables uniquement via GUI).
| # | Conteneur | Image:tag | Mode | Data path | Taille | Type DB | Dépendances | Export | Destination |
|---|---|---|---|---|---|---|---|---|---|
| 1 | n8n | docker.n8n.io/n8nio/n8n:latest | compose | `/volume2/docker-REC/n8n/data` + `files` | 75 M (project) | SQLite (`database.sqlite`) | n8n-postgresql | tar des 2 dirs | **G9 .24 — migré T1.4-c ✅** (sha 693ee229 identique) |
| 2 | n8n-postgresql | postgres:17 | compose | `/volume2/docker-REC/n8n/postgresql` | ~40 M inclus ci-dessus | Postgres | n8n | `pg_dump` | **G9 .24 — migré** (n8n .24 = SQLite, PG non migré, redondance) |
| 3 | joplin-server | joplin/server:latest | compose | via joplin-db | — | client Joplin | joplin-db | API Joplin export | G9 .24 (stack notes) |
| 4 | joplin-db | postgres:16 | compose | `/volume2/docker-REC/Joplin/postgres` | 706 M (project) | Postgres | joplin-server | `pg_dump --format=custom` | G9 .24 |
| 5 | postgres16 | postgres:16 | compose | `/volume1/docker/postgres16/data` (+init) | 70 M | Postgres (infra partagée) | clients externes | `pg_dumpall` | **.25 reste** (infra NAS) |
| 6 | affine_server | ghcr.io/toeverything/affine:stable | compose | `${UPLOAD_LOCATION}` (storage) + `${CONFIG_LOCATION}` (config) | 94 M (project) | client AFFiNE | affine_postgres, affine_redis, LLM `http://192.168.1.25:3011/v1` | tar dirs + AFFiNE export | .25 reste (dépend LLM proxy .25) |
| 7 | affine_postgres | pgvector/pgvector:pg16 | compose | `${DB_DATA_LOCATION}` | inclus ci-dessus | Postgres+pgvector | affine_server | `pg_dump` | .25 reste |
| 8 | affine_redis | redis:latest | compose | volume nommé | négligeable | Redis (cache) | affine_server | RDB snapshot | .25 reste |
| 9 | nextcloud_app | nextcloud:latest | compose | `/volume2/docker-REC/NextCloud/data` + `config` | 2.3 G (project) | client Nextcloud | nextcloud_db, nextcloud_redis, onlyoffice-ds | `occ export` + tar data | **.25 reste** (NAS = stockage fichiers) |
| 10 | nextcloud_db | mariadb:10.6 | compose | `/volume2/docker-REC/NextCloud/db` | inclus ci-dessus | MariaDB | nextcloud_app | `mysqldump` | .25 reste |
| 11 | nextcloud_redis | redis:latest | compose | `/volume2/docker-REC/NextCloud/redis` | quelques Mo | Redis (cache) | nextcloud_app | RDB | .25 reste |
| 12 | gitea | gitea/gitea:latest | compose | `/volume1/docker/Gitea/data` | 2 M | SQLite/Gitea | — | `gitea dump -c` | **.25 reste** |
| 13 | vaultwarden-server-1 | vaultwarden/server:latest | **GUI DSM** | `/volume1/docker/vaultwarden` | 8.8 M | SQLite (SQLite+env) | — | tar dir (RO, service stoppé) | **.25 reste** (secrets, critique) |
| 14 | leantime | leantime/leantime:latest | compose | `./data/{userfiles,public_userfiles,plugins,logs}` | 298 M (project) | client Leantime | leantime-db | tar dirs + API Leantime | G9 .24 (stack PM) |
| 15 | leantime-db | mysql:8.4 | compose | `./data/mysql` | inclus ci-dessus | MySQL | leantime | `mysqldump` | G9 .24 |
| 16 | beszel | henrygd/beszel:latest | compose | `./beszel_data` | 11 M | SQLite | beszel-agent, docker.sock | tar dir | **.25 reste** (monitoring NAS) |
| 17 | beszel-agent | henrygd/beszel-agent:latest | compose | `./beszel_agent_data` | quelques Mo | agent (socket) | beszel | tar dir | **.25 reste** |
| 18 | valheim | mbround18/valheim:latest | compose | `./saves` + `./server` + `./backups` | 2.8 G (project) | fichiers monde | — | tar des 3 dirs (fait J5040 → .11) | **G9 .24 — migré T1.4-b1 ✅** |
| 19 | openwebui | ghcr.io/open-webui/open-webui:latest | compose | `/volume1/docker/openwebui` | 4.7 G | SQLite (app data) | Ollama `192.168.1.10:11434` | tar dir | **PC IA .11 — migré T1.4-b ✅** |
| 20 | InfluxDB | influxdb:2.7 | compose | `/volume2/docker-REC/InfluxDB` + volume nommé | 188 K | InfluxDB v2 | beszel (metrics) | `influxd backup` | G9 .24 |
| 21 | onlyoffice-ds | onlyoffice/documentserver:latest | compose | `./data` + `./logs` + 2 volumes nommés | 160 K | DocumentServer | nextcloud_app | tar dirs | **.25 reste** (couplé NextCloud) |
| 22 | qbittorrent | linuxserver/qbittorrent:latest | **GUI DSM** | `/volume2/docker-REC/Multimedia/qbittorrent` | sessions/torrents | config qBittorrent | deluge, flaresolverr | tar config (fastresume) | **.25 reste** (NAS média) |
| 23 | sonarr | linuxserver/sonarr:latest | **GUI DSM** | `/volume2/docker-REC/Multimedia/Sonarr` | db SQLite | Sonarr | prowlarr, qbittorrent | `db backup` + tar | **.25 reste** |
| 24 | radarr | linuxserver/radarr:latest | **GUI DSM** | `/volume2/docker-REC/Multimedia/Radarr` | db SQLite | Radarr | prowlarr, qbittorrent | `db backup` + tar | **.25 reste** |
| 25 | jackett | linuxserver/jackett:latest | **GUI DSM** | `/volume2/docker-REC/Multimedia/Jackett` | IndexTor + API key | Jackett | Sonarr/Radarr | tar config (indexers.yml) | **.25 reste** |
| 26 | prowlarr | linuxserver/prowlarr:latest | **GUI DSM** | `/volume2/docker-REC/Multimedia/Prowlarr` | db SQLite + grants | Prowlarr | Jackett, Sonarr/Radarr | `db backup` + tar | **.25 reste** |
| 27 | deluge | linuxserver/deluge:latest | **GUI DSM** | `/volume2/docker-REC/Multimedia/Deluge` | state + auth | Deluge | qbittorrent | tar `state` | **.25 reste** |
| 28 | flaresolverr | flaresolverr/flaresolverr:latest | **GUI DSM** | volume nommé (cache) | cache jetzturbine | Stateless | Jackett/Sonarr | non requis | .25 reste (recréable) |
| 29 | mini-qr | ghcr.io/lyqht/mini-qr:latest | compose | (aucun bind, stateless) | — | Stateless | — | non requis | .25 reste (recréable) |
| 30 | watchtower | containrrr/watchtower:latest | compose | docker.sock (ro) | — | Stateless (n/a) | tous conteneurs .25 | n/a | **.25 durci T1.1-c en attente (bloqué)** |
## Récapitulatif des destinations (à valider Go/No-Go)
- **G9 .24** (déjà migré) : n8n+postgres, valheim, openwebui(.11) ✅ ; stack notes/PM cibles Joplin, Leantime, InfluxDB.
- **PC IA .11** : openwebui ✅, whisper/ocr/stt (doublons supprimés ✅).
- **.25 (NAS) reste** : media stack qbittorrent/sonarr/radarr/jackett/prowlarr/deluge (stockage NAS natif), NextCloud + onlyoffice (couplé fichiers NAS), Gitea, vaultwarden, postgres16, AFFiNE (dépend LLM proxy .25), beszel.
- **Supprimés** : Pterodactyl (S3.2, fait sur J5040), flaresolverr/mini-qr stateless (recréables sans backup).
## Points de vigilance
1. **7 conteneurs GUI** (vaultwarden, qbittorrent, sonarr, radarr, jackett, prowlarr, deluge) : labels watchtower **inapplicables en CLI DSM** (`docker update --label-add` absent sur docker DSM 24.0.2) → à mettre via **Panneau DSM → Container Manager** (labels par conteneur).
2. **Flux de dépendances** .25 → .11 : AFFiNE et OpenWebUI pointent sur LLM proxy .25 (port 3011) / Ollama .10. Si le proxy .25 disparaît au formatage J5040, ces services cassent → à recâbler sur .11 (`http://192.168.1.11:3011/v1`).
3. **Accès .25 perçu** (2026-10-09 11:43) : clé `id_ed25519_effi_server` retirée + compte `effi` bloqué par Synology. Les 15 yml durcis sont prêts dans `/tmp/nas-yml/` (labels `false` + watchtower pinné + CLEANUP=false) à pousser dès déblocage.
+121
View File
@@ -0,0 +1,121 @@
# Migration G9 — Kit d'exécution
**Objectif** : tout le matériel (scripts + configs + compose) prêt à exécuter la migration
xpenology → G9, phase par phase, quand le G9 sera branché (IP temporaire `.24`).
> **Règle d'or** : chaque script est **idempotent** et **sécurisé**. Les opérations
> destructives (partitionnement, LUKS, wipe) sont **bloquées** sans le drapeau
> `--i-understand` et la validation explicite de l'utilisateur (garde-fou G3).
## Charte d'autonomie (garde-fous G1–G10)
| # | Garde-fou |
|---|-----------|
| G1 | Lecture seule libre ; toute modif délibérée et documentée. |
| G2 | Aucune modif de données sans backup vérifié en place. |
| G3 | Op. destructives (`rm -rf`, `mkfs`, `wipe`, `dd`, partition, RAID/LUKS, suppression conteneur) = **validation explicite** avant exécution. |
| G4 | Double-run : xpenology = source de vérité ; aucune suppression dessus tant que le contrepartie G9 n'est pas validé. |
| G5 | IP G9 = `.24` tant que le xpenology n'est pas décommissionné. |
| G6 | Aucun changement réseau impactant d'autres appareils sans validation. |
| G7 | Chaque modif a un **rollback** documenté. |
| G8 | **Journalisation** de chaque action (quoi, pourquoi, résultat). |
| G9 | Aucun secret loggué ou commité. |
| G10 | Périmètre = G9 (+ lecture xpenology) ; autres machines sur validation. |
### Correspondance garde-fou → point d'application
| Garde-fou | Point d'application |
|-----------|---------------------|
| G1 | Scripts idempotents, en-têtes documentant chaque modification. |
| G2 | `06-restic-backup.sh` : backup + **test de restauration** avant toute migration. |
| G3 | `06b-usb-media.sh`, `08-decommission.sh` : drapeau `--i-understand` + saisie du nom du disque / `WIPE XPENOLOGY`. |
| G4 | `07-migrate-stateful.sh` : lecture seule du xpenology + intégrité sha256 ; `08-decommission.sh` exige la validation de toutes les briques avant wipe. |
| G5 | README (ordre d'exécution) + `08-decommission.sh` : IP `.24`→`.25` uniquement au décommissionnement. |
| G6 | `01-hardening.sh` : ufw LAN-only (`192.168.1.0/24`), pas d'exposition WAN. |
| G7 | Chaque script : section **Rollback** en en-tête + colonne Rollback du journal. |
| G8 | `journal()` de chaque script → `journal.md` (6 colonnes : date, phase, action, raison, résultat, rollback). |
| G9 | `.gitignore` (`*.env`, `secrets/`, configs remplis) + `03b-secrets.sh` (chmod 600, non loggué). |
| G10 | Scripts ne modifient que le G9 ; xpenology en lecture seule (SSH `Master@192.168.1.25:222`, clé SSH — compte `effi` bloqué par Synology 10/2026). |
## Arborescence
```
g9-migration/
├── README.md ← ce fichier (runbook)
├── journal.md ← journal d'exécution (G8) — à remplir pendant la migration
├── scripts/
│ ├── 01-hardening.sh ← T2.1 : SSH keys, ufw (LAN-only), unattended-upgrades, fail2ban
│ ├── 02-data.sh ← T2.2 : espace /data (NON destructif — LUKS abandonné 10/10/2026)
│ ├── 03-docker.sh ← T2.3 : Docker + compose + groupe docker
│ ├── 03b-secrets.sh ← T2.3b : bootstrap des fichiers secrets des briques (chmod 600)
│ ├── 04-smb.sh ← T2.4 : partage SMB (données famille)
│ ├── 05-beszel.sh ← T2.5 : agent Beszel
│ ├── 06-restic-backup.sh ← T3.1 : restic G9 → Freebox + test restauration
│ ├── 06b-usb-media.sh ← T6.1 : montage des disques USB média → /mnt/usb-media
│ ├── 07-migrate-stateful.sh← T4.x : export/import des stateful (xpenology → G9)
│ └── 08-decommission.sh ← T6.4 : décommissionnement xpenology [DESTRUCTIF]
├── compose/ ← docker-compose.yml versionnés (stockés aussi dans Gitea)
│ ├── vaultwarden/ ← T4.1
│ ├── nextcloud/ ← T4.2
│ ├── photoprism/ ← T4.3
│ ├── gitea/ ← T4.4
│ ├── n8n/ ← T4.5
│ ├── jellyfin/ ← T6.2
│ └── watchtower/ ← S11.4 : Watchtower sélectif
└── configs/
├── fail2ban.local.conf ← config fail2ban (T2.1)
└── restic.env.example ← template restic (T3.1) — REMPLIR les secrets
```
## Ordre d'exécution (phase par phase)
| Phase | Script / action | Garde-fou |
|-------|-----------------|-----------|
| 1 | (xpenology) exclure stateful de Watchtower + inventaire | G4 |
| 2 | `01-hardening.sh` → `02-data.sh` → `03-docker.sh` → `03b-secrets.sh` → `04-smb.sh` → `05-beszel.sh` | — |
| 3 | `06-restic-backup.sh` (backup + test restauration) | G2 |
| 4 | `07-migrate-stateful.sh` (par brique, double-run) — **après** `03b-secrets.sh` | G4 |
| 5 | restic 3-2-1 complet (G9 + PC IA → Freebox → 218play) | G2 |
| 6 | `06b-usb-media.sh` (T6.1) → Jellyfin (T6.2) → IP .24→.25 → `08-decommission.sh` | G3, G4, G5 |
| 7 | (G7) wipe DSM + Ubuntu neuf + boot test | G3, G10 |
> **Note disque (décision 10/10/2026 — Option 0)** : le G9 est **mono-disque**
> (Samsung 990 Pro 2 To, **seul disque interne**, aucun RAID prévu). `02-data.sh`
> crée l'espace `/data` en **simple dossier** sur le LV racine existant :
> **ni partitionnement, ni LUKS, ni réinstallation** (LUKS abandonné — données
> familiales non critiques ; protection = ufw LAN-only + backups **restic chiffrés**,
> voir `DOSSIER-SOFTWARE.md` S9/S10 changelog 10/10/2026). La tolérance panne
> disque est assurée par le **3-2-1** (Phases 3/5) et le **spare G7** (Phase 7).
## Phase 3 — backup restic : état au 10/10/2026
Repo `rclone:freeboxftp:Freebox/backup/g9` (Freebox Delta, partage `Save/Freebox`,
1.79 To, 1.785 To libres). Snapshot initial `21de0787` : 4 564 fichiers,
6.956 GiB lus / **4.178 GiB stockés** (sources `/data`, `/home/folzo`, `/etc`).
- **Transport & append-only** : backend **rclone** de restic, remote FTP local
`freeboxftp:` = 192.168.1.254:21 (SMB `freebox:` en secours), **aucun montage** ;
append-only **prouvé**. Transports testés/écartés et limitations :
**source unique → `RESTORE-G9.md` §6**.
- **Test de restauration** : fichier restauré depuis la Freebox, sha256 identique
(`9352a905d9bb…`, 470 637 o, restore < 2 s) — voir `RESTORE-G9.md` §2/§5 et `journal.md`.
- **Automatisation** (cron root, logs `/var/log/restic-*.log`) : 03:30 backup (conteneurs
stateful stoppés ~1–3 min : valheim, n8n, synapse, couchdb, obsidian), 04:00 prune
(7 quotidiens / 4 hebdo / 12 mensuels / 5 annuels), 09:15 health (alerte si snapshot > 26 h).
Les 2 jobs ont été lancés à la main (rc=0, snapshot `cccdb2c7`).
- **Alerte Matrix** : **directe** via Synapse (`/etc/restic/matrix.env`, token ops-bot, salon
ops) — **testée**, messages réellement reçus. Voie de secours webhook n8n conservée
(`RESTIC_NOTIFY_WEBHOOK`) ; **accès n8n à recréer** (clé API à générer dans l'UI).
- **Persistance post-reboot** : validée par un vrai reboot (21:36) — `scripts/06c-postboot-check.sh`
= **15 contrôles OK / 0 KO** (binaires, env/clé/config en 600, **aucun montage requis**,
cron, jobs, repo lisible + `check` OK, conteneurs relancés). À rejouer après tout changement.
- **Secrets** : `/etc/restic/repo.key` (clé du repo, **jamais affichée** — **à consigner dans
Vaultwarden**, item « G9 – clé repo restic »), `/etc/restic/cifs.cred`, `/etc/restic/matrix.env`,
`/root/.config/rclone/{rclone,ftp}.conf`, `/etc/restic.env` → tous `600` root ; **aucun secret
dans le repo git** (`.gitignore` : `*.env`, `*.cred`, `*.key`).
## Convention
- Chaque script affiche ce qu'il fait, est idempotent, et écrit dans `journal.md`.
- Les secrets (mots de passe, clés restic) vont dans `configs/*.env` **hors git** (G9).
- Un script échoue → il s'arrête (`set -euo pipefail`) → on lit le journal → on corrige.
+144
View File
@@ -0,0 +1,144 @@
# Restauration G9 depuis la Freebox (T3.1 — AC5 / S10.4)
> **Règle** : un backup non testé n'est pas un backup. Ce document donne les commandes exactes.
> Testé le : **2026-10-10** (voir `journal.md`, ligne « Test restauration OK »). À re-tester **1×/trimestre**.
## 0. Ce qu'il faut avoir
| Élément | Où | Remarque |
|---------|-----|----------|
| Mot de passe du repo restic | `/etc/restic/repo.key` (G9, root, 600) — **consigné dans Vaultwarden** | **Sans lui, les backups sont irrécupérables** (G9) |
| Identifiants FTP Freebox | `/root/.config/rclone/rclone.conf` (G9, root, 600) — remote `[freeboxftp]`, secours `[freebox]` (SMB) | Serveur FTP activé dans Freebox OS ; copie FTP-only : `ftp.conf` |
| Config backup | `/etc/restic.env` (G9, root, 600) | `RESTIC_REPOSITORY=rclone:freeboxftp:Freebox/backup/g9` |
Toutes les commandes s'exécutent **en root sur le G9** (`sudo -i`), avec `restic` = `/usr/local/bin/restic` (version épinglée **0.19.1**).
## 1. Lister ce qui est sauvegardé
```bash
set -a; source /etc/restic.env; set +a
restic -r "$RESTIC_REPOSITORY" snapshots # tous les snapshots
restic -r "$RESTIC_REPOSITORY" ls latest | head # contenu du dernier snapshot
restic -r "$RESTIC_REPOSITORY" find 'rapport.pdf' # retrouver un fichier dans tous les snapshots
```
## 2. Restaurer **un fichier** (le cas du quotidien)
**restic 0.19** : les arguments positionnels de `restore` sont des **IDs de snapshot** (pas des
fichiers). On filtre avec `--include`, et les chemins sont **absolus** (tels que sauvegardés) :
```bash
restic -r "$RESTIC_REPOSITORY" restore latest --target /tmp/restore \
--include "/home/folzo/obsidian-vault/Notes/Exemple.md"
# le fichier restauré est dans : /tmp/restore/home/folzo/obsidian-vault/Notes/Exemple.md
```
Restaurer une **version antérieure** : remplacer `latest` par l'ID du snapshot voulu (vu par `snapshots`) :
```bash
restic -r "$RESTIC_REPOSITORY" restore 1a2b3c4d --target /tmp/restore \
--include "/home/folzo/obsidian-vault"
```
Vérifier ensuite : `cmp -s <original> <restauré>` ou `sha256sum` des deux.
> `restic -r "$RESTIC_REPOSITORY" ls latest` affiche l'arborescence du snapshot ; `find <motif>`
> retrouve un fichier dans tous les snapshots (et renvoie le chemin à passer à `--include`).
## 3. Restaurer **tout** (sinistre, disque neuf)
```bash
# 1) réinstaller Ubuntu Server + docker, créer /data, restaurer les fichiers de config
restic -r "$RESTIC_REPOSITORY" restore latest --target /rebuild
# -> /rebuild/data/... /rebuild/home/folzo/... /rebuild/etc/...
# 2) recoller les fichiers aux emplacements d'origine (rsync -a /rebuild/etc/ /etc/ par ex.)
# 3) conteneurs : docker compose up -d (compose versionné dans Gitea), puis vérifier les services
```
## 4. Contrôles d'intégrité
```bash
restic -r "$RESTIC_REPOSITORY" check # intégrité du repo (le plus complet)
restic -r "$RESTIC_REPOSITORY" check --read-data-subset=5% # lit 5 % des données (plus strict)
restic -r "$RESTIC_REPOSITORY" stats # volumétrie
```
## 5. Journalisation du test (à chaque test)
| Date | Fichier testé | Snapshot | Durée | Résultat |
|------|---------------|----------|-------|----------|
| 2026-10-10 | `/data/famille/Test/PXL_20261009_130502121.jpg_compressed.JPEG` (470 637 o) | `21de0787` puis `cccdb2c7` | **1.8 s** / **< 2 s** (restore + `sha256sum`) | ✅ sha256 `9352a905d9bb…` identique source/restore |
| 2026-10-10 (post-reboot) | repo entier | `snapshots` + `check --read-data-subset=1%` | ~4 s | ✅ 15 contrôles OK / 0 KO (`06c-postboot-check.sh`) |
Le test est **rejouable** : `sudo bash /home/folzo/g9-migration/scripts/06-restic-backup.sh`
(init → backup si besoin → preuve append-only → restauration testée → jobs/cron), idempotent.
## 6. Transport retenu, transports écartés, limitations — **section canonique**
> Les autres documents (README §Phase 3, `configs/restic.env.example`, en-tête de
> `06-restic-backup.sh`) pointent vers cette section ; ne pas y recopier le détail.
**Retenu** : backend **rclone** de restic sur le **FTP local de la Delta** (`freeboxftp:` =
192.168.1.254:21), config unique `/root/.config/rclone/rclone.conf` (600 root, `[freeboxftp]`
FTP + `[freebox]` SMB en secours). **Aucun montage** → rien à dépendre d'un mount au boot.
| Transport testé | Résultat |
|-----------------|----------|
| `mount.cifs` du noyau (vers 1.0, SMB1) | ❌ restic échoue : `chmod … no such file or directory` (chmod impossible sur le partage) → fstab + unité systemd retirées |
| `rclone mount` (FUSE) | ❌ sémantique correcte mais cache d'écriture → objets du repo perdus au redémarrage |
| rclone backend `smb` (1.60 d'apt) | ❌ backend absent ; rclone 1.75.2 fonctionne → gardé en **secours** (`rclone:freebox:…`) |
| FTPES distant (port 13366) | ❌ joignable uniquement depuis l'extérieur de la LAN |
| **FTP local (21) via rclone** | ✅ **retenu**, validé après reboot |
## 6.1 Limitations assumées (Phase 3)
- **Append-only = enforcement côté client** : il est imposé par le rclone local du G9
(`-o rclone.args="serve restic --stdio --append-only"`), **prouvé** le 10/10/2026 (`forget --prune`
refusé, rc=1, snapshots intacts). Un G9 rooté peut retirer l'option et supprimer le repo sur la
Freebox. La cible **WORM** (append-only **côté serveur**) est la **VM Freebox + `rest-server
--append-only`** exposé en **HTTPS/TLS** avec **`htpasswd`** (compte `g9-backup` écriture,
`g9-restore` lecture), prévue en **Phase 5** (autostart VM à valider — la Freebox ne fait pas
tourner restic). Repo cible `rest:https://…` à la place de `rclone:`.
- Conséquences pratiques : `restic unlock` tourne **sans** l'option append-only (les locks sont des
objets supprimables) ; le prune **refusé par append-only est le comportement attendu** → il est
**journalisé seulement** (pas d'alerte Matrix, sinon fatigue du canal), et l'alerte n'est envoyée
que si la purge **sans** append-only échoue aussi (rétention réellement bloquée).
- La copie 3 (hebdo, **218play**, via `restic copy`) arrive en Phase 5 : sans elle, G9 + Freebox restent
2 copies sur le même site.
- Le backup quotidien **arrête les conteneurs stateful** (valheim, n8n, synapse, couchdb, obsidian) pendant
~1 à 3 min à 03:30 (`stop → backup → start`, règle écrite dans les jobs `restic-daily.sh` et
`06-restic-backup.sh`) pour garder les bases sqlite/couchdb cohérentes. **Aucune suppression de la
source** (G4) : le backup est une lecture.
## 7. Alerte
Le job `restic-health.sh` (cron 09:15) envoie une alerte Matrix **« BACKUP KO : aucun snapshot depuis N h »**
si aucun snapshot ne date de moins de 26 h. `restic-daily.sh` alerte si le FTP est injoignable ou si
`restic backup` échoue. Contrôle manuel : `sudo /usr/local/bin/restic-health.sh`,
`sudo /usr/local/bin/restic-notify.sh "message de test"`.
- **Voie utilisée** : **Matrix en direct** via Synapse (`http://192.168.1.24:8008`, salon ops, token
ops-bot dans `/etc/restic/matrix.env`, 600 root) — **testée le 10/10/2026** : messages réellement
reçus dans le salon ops.
- **Voie de secours** : webhook n8n (`RESTIC_NOTIFY_WEBHOOK`, `POST /webhook/backup-alert`) — nécessite
un workflow n8n + une clé API : **accès n8n à recréer** (1 workflow existant, « Watchtower Notification »).
## 8. Restaurer depuis une autre machine (sinistre, G9 perdu)
Il faut **3 éléments** (sans le n°1, les backups sont illisibles) :
| # | Élément | Source |
|---|---------|--------|
| 1 | **Mot de passe du repo** | item Vaultwarden « G9 – clé repo restic » (copie de `/etc/restic/repo.key`) |
| 2 | Accès au partage | FTP/SMB de la Freebox, `Freebox/backup/g9` (identifiants du compte Freebox) |
| 3 | restic + rclone | `restic` 0.19.1 + `rclone` 1.75.2 (mêmes versions que le G9) |
```bash
export RESTIC_REPOSITORY=rclone:freeboxftp:Freebox/backup/g9 # remote rclone équivalent sur la machine de restore
export RESTIC_PASSWORD_FILE=/chemin/vers/repo.key
restic -r "$RESTIC_REPOSITORY" snapshots
restic -r "$RESTIC_REPOSITORY" restore latest --target /rebuild
```
Alternative sans rclone : `restic -r smb:freebox-server.local/Freebox/backup/g9` (backend SMB natif) ou
`restic -r ftp:192.168.1.254:21/Freebox/backup/g9` — mêmes données, même clé.
@@ -0,0 +1,70 @@
# Gitea — T4.4 (vault 2e cerveau + versioning des compose, G9)
# Stateful (repos + DB) → exclu de Watchtower (S11.4).
# C'est aussi le repo qui versionne tous les docker-compose.yml (S11.1).
#
# BACKEND = POSTGRES (corrigé 11/10/2026) : la source xpenology est le paquet App Center
# DSM « gitea » v1.27.3, DB postgres gitea_db (13 repositories, 3 users). Un import d'une
# base Postgres dans une installation SQLite est IMPOSSIBLE → le compose G9 doit être Postgres.
# Version épinglée sur la version source (1.27.3) : pas de migration de schéma surprise.
services:
gitea:
image: gitea/gitea:1.27.3
container_name: gitea
restart: unless-stopped
labels:
- "com.centurylinklabs.watchtower.enable=false"
env_file:
- ./secrets/gitea.env # GITEA__database__PASSWD (600, hors git — G9)
environment:
- TZ=Europe/Paris
- USER_UID=1000
- USER_GID=1000
- GITEA__database__DB_TYPE=postgres
- GITEA__database__HOST=gitea-db:5432
- GITEA__database__NAME=gitea_db
- GITEA__database__USER=gitea_folzo
- GITEA__database__SSL_MODE=disable
- GITEA__security__INSTALL_LOCK=true
- GITEA__server__DOMAIN=git.folzocorp.fr
- GITEA__server__ROOT_URL=https://git.folzocorp.fr
- GITEA__server__SSH_DOMAIN=git.folzocorp.fr
- GITEA__server__SSH_PORT=2222
- GITEA__server__LFS_START_SERVER=true
- GITEA__repository__ROOT=/data/git/repositories
- GITEA__lfs__PATH=/data/git/lfs
- GITEA__service__DISABLE_REGISTRATION=true
- GITEA__service__DISABLE_OPENID=true
# Création de repo au premier push (S11.1 : versionner un stack = `git push`, pas de clic UI).
- GITEA__service__ENABLE_PUSH_CREATE_USER=true
- GITEA__repository__DEFAULT_PUSH_CREATE_PRIVATE=false
volumes:
- ./data:/data
- ./repositories:/data/git/repositories
- ./lfs:/data/git/lfs
- ./secrets:/run/secrets:ro
# Bind 0.0.0.0 : accessible depuis le LAN (edge/Caddy le proxy en T4.6).
# ufw (01-hardening.sh) restreint 3000 et 2222 au LAN 192.168.1.0/24 → pas d'exposition WAN.
ports:
- "0.0.0.0:3000:3000"
- "0.0.0.0:2222:22"
depends_on:
- gitea-db
healthcheck:
test: ["CMD", "curl", "-sf", "http://localhost:3000/api/healthz"]
interval: 30s
timeout: 10s
retries: 3
gitea-db:
image: postgres:16
container_name: gitea-db
restart: unless-stopped
labels:
- "com.centurylinklabs.watchtower.enable=false" # DB stateful → exclu
environment:
- POSTGRES_DB=gitea_db
- POSTGRES_USER=gitea_folzo
- POSTGRES_PASSWORD_FILE=/run/secrets/dbpass
volumes:
- ./db:/var/lib/postgresql/data
- ./secrets:/run/secrets:ro
@@ -0,0 +1,26 @@
# Jellyfin — T6.2 (vidéo + musique, G9, USB média)
# Lit les fichiers sources sur les USB (15 To + 5,5 To) — montés par 06b-usb-media.sh (T6.1).
# Stateful (config + métadonnées) → exclu de Watchtower (S11.4).
# Le *arr + qBittorrent sont sur le J5040 (edge), pas ici (S7.2).
services:
jellyfin:
image: jellyfin/jellyfin:10.9
container_name: jellyfin
restart: unless-stopped
labels:
- "com.centurylinklabs.watchtower.enable=false"
environment:
- TZ=Europe/Paris
- UID=1000
- GID=1000
- PUID=1000
- PGID=1000
volumes:
- ./config:/config
- /mnt/usb-media:/media:ro # USB 15 To + 5,5 To (montés en T6.1)
# Bind 0.0.0.0 : accessible depuis le LAN (J5040 edge/Caddy le proxy).
# ufw (01-hardening.sh) restreint le port au LAN 192.168.1.0/24 → pas d'exposition WAN.
ports:
- "0.0.0.0:8096:8096"
devices:
- /dev/dri:/dev/dri # accélération vidéo (si dispo)
@@ -0,0 +1,44 @@
# n8n — T4.5 (automatisation + alertes → Matrix, G9)
# Le conteneur n8n est stateless (image) mais sa DB (Postgres) est stateful → exclu.
# Workflows exportables (JSON). Licence fair-code (notée, S3.1).
services:
n8n:
image: n8nio/n8n:1.90
container_name: n8n
restart: unless-stopped
labels:
- "com.centurylinklabs.watchtower.enable=true" # image stateless → auto
environment:
- TZ=Europe/Paris
- N8N_HOST=n8n.folzocorp.fr
- N8N_PROTOCOL=https
- N8N_PORT=5678
- DB_TYPE=postgresdb
- DB_POSTGRESDB_HOST=n8n_db
- DB_POSTGRESDB_DATABASE=n8n
- DB_POSTGRESDB_USER=n8n
- DB_POSTGRESDB_PASSWORD_FILE=/run/secrets/dbpass
- GENERIC_TIMEZONE=Europe/Paris
volumes:
- ./data:/home/node/.n8n
- ./secrets:/run/secrets:ro
# Bind 0.0.0.0 : accessible depuis le LAN (J5040 edge/Caddy le proxy).
# ufw (01-hardening.sh) restreint le port au LAN 192.168.1.0/24 → pas d'exposition WAN.
ports:
- "0.0.0.0:5678:5678"
depends_on:
- n8n_db
n8n_db:
image: postgres:17
container_name: n8n_db
restart: unless-stopped
labels:
- "com.centurylinklabs.watchtower.enable=false" # DB stateful → exclu
environment:
- POSTGRES_DB=n8n
- POSTGRES_USER=n8n
- POSTGRES_PASSWORD_FILE=/run/secrets/dbpass
volumes:
- ./db:/var/lib/postgresql/data
- ./secrets:/run/secrets:ro
@@ -0,0 +1,53 @@
# Nextcloud — T4.2 (socle famille, G9)
# Stateful (app + MariaDB + Redis) → exclus de Watchtower (S11.4).
# MAJ manuelle supervisée (S11.5) : backup DB → maintenance ON → pull/up → occ upgrade → vérif → OFF.
# L'incident Nextcloud (06/10) est la raison de ce pinning + exclusion.
services:
nextcloud:
image: nextcloud:35
container_name: nextcloud
restart: unless-stopped
labels:
- "com.centurylinklabs.watchtower.enable=false"
environment:
- TZ=Europe/Paris
- POSTGRES_HOST=nextcloud_db
- POSTGRES_DB=nextcloud
- POSTGRES_USER=nextcloud
- POSTGRES_PASSWORD_FILE=/run/secrets/dbpass
- NEXTCLOUD_TRUSTED_PROXIES=172.16.0.0/12
volumes:
- ./data:/var/www/html
- ./config:/var/www/html/config
- ./secrets:/run/secrets:ro
depends_on:
- nextcloud_db
- redis
# Bind 0.0.0.0 : accessible depuis le LAN (J5040 edge/Caddy le proxy).
# ufw (01-hardening.sh) restreint le port au LAN 192.168.1.0/24 → pas d'exposition WAN.
ports:
- "0.0.0.0:8080:80"
nextcloud_db:
image: mariadb:10.11
container_name: nextcloud_db
restart: unless-stopped
labels:
- "com.centurylinklabs.watchtower.enable=false" # DB stateful → exclu
environment:
- MARIADB_DATABASE=nextcloud
- MARIADB_USER=nextcloud
- MARIADB_PASSWORD_FILE=/run/secrets/dbpass
- MARIADB_ROOT_PASSWORD_FILE=/run/secrets/rootpass
volumes:
- ./db:/var/lib/mysql
- ./secrets:/run/secrets:ro
redis:
image: redis:7
container_name: nextcloud_redis
restart: unless-stopped
labels:
- "com.centurylinklabs.watchtower.enable=false" # cache persistant → exclu
volumes:
- ./redis:/data
@@ -0,0 +1,46 @@
# PhotoPrism — T4.3 (photos, G9)
# Lecture IN-PLACE des originaux (S5) : pointe sur /data/famille/photos (pas de déplacement).
# Stateful (base + index) → exclu de Watchtower (S11.4). Image PINNÉE (S11.4, pas :latest).
services:
photoprism:
image: photoprism/photoprism:260919
container_name: photoprism
restart: unless-stopped
labels:
- "com.centurylinklabs.watchtower.enable=false"
environment:
- TZ=Europe/Paris
- PHOTOPRISM_URL=https://photos.folzocorp.fr
- PHOTOPRISM_SITE_URL=https://photos.folzocorp.fr
- PHOTOPRISM_ADMIN_USER=admin
- PHOTOPRISM_ADMIN_PASSWORD_FILE=/run/secrets/admin
- PHOTOPRISM_LIBRARY_PATH=/originals:/import
- PHOTOPRISM_DATABASE_USER=photoprism
- PHOTOPRISM_DATABASE_PASSWORD_FILE=/run/secrets/dbpass
- PHOTOPRISM_LOCAL_NETWORKS=172.16.0.0/12,192.168.0.0/16
- PHOTOPRISM_SIDECAR_ENABLED=true
volumes:
- /data/famille/photos:/originals:ro # in-place (lecture seule)
- ./storage:/storage
- ./secrets:/run/secrets:ro
# Bind 0.0.0.0 : accessible depuis le LAN (J5040 edge/Caddy le proxy).
# ufw (01-hardening.sh) restreint le port au LAN 192.168.1.0/24 → pas d'exposition WAN.
ports:
- "0.0.0.0:2342:2342"
depends_on:
- photoprism_db
photoprism_db:
image: mariadb:10.11
container_name: photoprism_db
restart: unless-stopped
labels:
- "com.centurylinklabs.watchtower.enable=false"
environment:
- MARIADB_ROOT_PASSWORD_FILE=/run/secrets/rootpass
- MARIADB_DATABASE=photoprism
- MARIADB_USER=photoprism
- MARIADB_PASSWORD_FILE=/run/secrets/dbpass
volumes:
- ./db:/var/lib/mysql
- ./secrets:/run/secrets:ro
@@ -0,0 +1,32 @@
# Vaultwarden — T4.1 (cœur famille, G9)
# Stateful (coffre) → exclu de Watchtower (S11.4). MAJ manuelle supervisée (S11.5).
# Backup : le dossier ./data (SQLite) est couvert par restic (T3.1/T5.1).
services:
vaultwarden:
image: vaultwarden/server:1
container_name: vaultwarden
restart: unless-stopped
labels:
- "com.centurylinklabs.watchtower.enable=false" # stateful → exclu
environment:
- TZ=Europe/Paris
- DOMAIN=https://vault.folzocorp.fr
- SIGNUPS_ALLOWED=false
# Secrets via fichiers (hors git, G9) créés par 03b-secrets.sh :
- ADMIN_TOKEN_FILE=/run/secrets/admin
# Derrière Caddy (edge) : hosts de confiance pour les WebSockets.
- TRUSTED_HOSTS=192.168.1.0/24
volumes:
# DATA_FOLDER par défaut = /data → le volume DOIT pointer sur /data (sinon la
# base écrit dans la couche conteneur, non persistée, et l'import 07 n'est jamais lu).
- ./data:/data
- ./secrets:/run/secrets:ro
# Bind 0.0.0.0 : accessible depuis le LAN (edge/Caddy le proxy).
# ufw (01-hardening.sh) restreint le port au LAN 192.168.1.0/24 → pas d'exposition WAN.
ports:
- "0.0.0.0:8081:80"
healthcheck:
test: ["CMD", "curl", "-sf", "http://localhost:80/alive"]
interval: 30s
timeout: 10s
retries: 3
@@ -0,0 +1,25 @@
# Watchtower — S11.4 (MAJ sélective, sur chaque machine)
# Règle : stateful = exclu (label com.centurylinklabs.watchtower.enable=false).
# Leçon de l'incident Nextcloud (06/10) : CLEANUP=false (garder les anciennes images = rollback).
#
# Aligné sur le déploiement RÉEL validé Phase 1 (journal T1.1-c/.1.1-d, 2026-10-10) :
# - image = fork nickfedor ÉPINGLÉE PAR DIGEST (containrrr/watchtower:1 n'existe PAS, vérifié API registry).
# - nickfedor refuse SCHEDULE + POLL simultanés → POLL=3600 conservé (pas de SCHEDULE ici).
# - notifications : shoutrrr generic → webhook n8n → Matrix (chaîne testée, payload title/message).
services:
watchtower:
image: nickfedor/watchtower@sha256:45b54f6dc15227cfb38dbb826f2881aa0f56aaf0c255cdbb38bd80e598bdca9a
container_name: watchtower
restart: unless-stopped
environment:
- TZ=Europe/Paris
- WATCHTOWER_CLEANUP=false # GARDER les anciennes images (rollback)
- WATCHTOWER_INCLUDE_STOPPED=false # ne pas toucher les conteneurs arrêtés
- WATCHTOWER_POLL_INTERVAL=3600 # nickfedor : SCHEDULE et POLL exclusifs
- WATCHTOWER_LABEL_ENABLE=true # seul ce qui est labelé true est mis à jour
# Notifications shoutrrr generic → webhook n8n → salon Matrix ops.
# ⚠️ PLACEHOLDER : remplacer par l'URL webhook réelle de la machine (workflow n8n w1pfhX0A7KvaDrtk).
- WATCHTOWER_NOTIFICATIONS=shoutrrr
- WATCHTOWER_NOTIFICATION_URL=generic:https://n8n.folzocorp.fr/webhook/watchtower
volumes:
- /var/run/docker.sock:/var/run/docker.sock
+18
View File
@@ -0,0 +1,18 @@
# /etc/restic/excludes.txt — exclusions du backup restic du G9 (T3.1)
# Copié par 06-restic-backup.sh si le fichier est absent (le fichier existant n'est JAMAIS écrasé).
# Syntaxe restic (un motif par ligne, glob). Fichier 644, aucun secret.
# Politique : exclure uniquement ce qui est RECONSTRUIBABLE à l'identique. Les médias
# familiaux (/data/famille, photos, vidéos) ne sont PAS exclus : ce sont les données précieuses.
# Caches
*/cache/*
*/.cache/*
*/.npm/*
*/node_modules/.cache/*
*/.cache/thumbnails/*
# Fichiers temporaires
*/tmp/*
# Logs de rotation de watchtower (sans valeur historique)
/home/folzo/watchtower/log/*
+13
View File
@@ -0,0 +1,13 @@
# fail2ban.local.conf — T2.1
# Copié vers /etc/fail2ban/jail.local par 01-hardening.sh.
[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 5
banaction = ufw
[sshd]
enabled = true
port = ssh
logpath = /var/log/auth.log
maxretry = 3
+61
View File
@@ -0,0 +1,61 @@
# Template restic — T3.1 / T5.1
# Copier vers /etc/restic.env et REMPLIR.
# ⚠️ G9 : secrets hors git — chmod 600, root, JAMAIS commité.
#
# TRANSPORT RETENU (10/10/2026, après tests) : backend **rclone** de restic sur le
# **FTP local de la Freebox Delta (port 21)**. Aucun montage : pas de CIFS, pas de FUSE,
# donc rien à dépendre d'un mount au boot (validé après reboot, 06c-postboot-check.sh).
#
# Transports testés/écartés et limitations append-only : **source unique = RESTORE-G9.md §6**.
# Cible = repo restic via rclone (chemin = partage `Save/Freebox` de la Delta).
RESTIC_REPOSITORY=rclone:freeboxftp:Freebox/backup/g9
RESTIC_RCLONE_REMOTE=freeboxftp:
RESTIC_RCLONE_PATH=Freebox/backup
# Config rclone (remote [freeboxftp] type=ftp host=192.168.1.254 port=21 user=freebox pass=…)
# Le mot de passe vit DANS ce fichier (600, root), jamais ici.
RCLONE_CONFIG=/root/.config/rclone/rclone.conf
# Append-only anti-ransomware, porté par le rclone local du G9 (limitation documentée,
# RESTORE-G9.md §6) : 1 = les jobs utilisent `-o rclone.args="serve restic --stdio --append-only"`.
RESTIC_RCLONE_APPENDONLY=1
# Repo de preuve utilisé par 06-restic-backup.sh (créé puis purgé pendant le test) :
SCRATCH_REPO=rclone:freeboxftp:Freebox/backup/_appendonly-test
# Mot de passe du repo restic (fichier, jamais en variable) : openssl rand -base64 32 > /etc/restic/repo.key
# A CONSIGNER DANS VAULTWARDEN (item « G9 – clé repo restic ») : sans lui, les backups sont irrécupérables.
RESTIC_PASSWORD_FILE=/etc/restic/repo.key
# Sources (T3.1) : /data + les données réelles des briques (/home/folzo) + config système.
# NB : le stateful du G9 vit dans /home/folzo (valheim, n8n, couchdb, obsidian, matrix), pas dans /data.
RESTIC_SOURCES="/data /home/folzo /etc"
RESTIC_EXCLUDE_FILE=/etc/restic/excludes.txt
RESTIC_TAG=g9
# Conteneurs stoppés pendant le backup (cohérence des bases) puis relancés.
RESTIC_STATEFUL="valheim n8n synapse couchdb obsidian"
# Alerte ops. Voie principale = Matrix en direct (salon ops) : token ops-bot dans
# /etc/restic/matrix.env (600 root : MATRIX_SERVER / MATRIX_ROOM / MATRIX_TOKEN), lu par
# restic-notify.sh. Voie de secours = webhook n8n (vide = pas d'alerte).
RESTIC_NOTIFY_WEBHOOK=http://127.0.0.1:5678/webhook/backup-alert
# Politique de rétention (T5.1 : 3-2-1)
# Copies: 3 (G9+PC IA originaux / Freebox quotidien / 218play hebdo)
RESTIC_RETAIN_DAILY=7
RESTIC_RETAIN_WEEKLY=4
RESTIC_RETAIN_MONTHLY=12
RESTIC_RETAIN_YEARLY=5
# Binaires épinglés par 06-restic-backup.sh
RESTIC_VERSION=0.19.1
RESTIC_SHA256=f415415624dcc452f2a02b8c33641791a8c6d6d3b65bbb3543fcf9a25151585c
# Journal du kit sur le G9 (G8)
G9_JOURNAL=/root/g9-migration/journal.md
# --- Transports alternatifs (non retenus) ---
# Secours : RESTIC_REPOSITORY=rclone:freebox:Freebox/backup/g9 (remote [freebox] type=smb)
# Natifs restic : smb:freebox-server.local/Freebox/backup/g9 ou ftp:192.168.1.254:21/Freebox/backup/g9
# Phase 5 (cible WORM) : voir RESTORE-G9.md §6.
+31
View File
@@ -0,0 +1,31 @@
name,image_tag,mode,stateful,watchtower_label,data_paths
n8n,docker.n8n.io/n8nio/n8n:latest,compose,yes,missing,/volume2/docker-REC/n8n/data (75M total project)
n8n-postgresql,postgres:17,compose,yes,missing,/volume2/docker-REC/n8n/postgresql
joplin-server,joplin/server:latest,compose,yes,missing,(stateful via joplin-db)
joplin-db,postgres:16,compose,yes,missing,/volume2/docker-REC/Joplin/postgres (706M project)
postgres16,postgres:16,compose,yes,missing,/volume1/docker/postgres16/data (70M)
affine_server,ghcr.io/toeverything/affine:stable,compose,yes,missing,/volume2/docker-REC/AFFiNE/config+storage
affine_postgres,pgvector/pgvector:pg16,compose,yes,missing,/volume2/docker-REC/AFFiNE/postgres
affine_redis,redis:latest,compose,yes,missing,(named volume)
nextcloud_app,nextcloud:latest,compose,yes,missing,/volume2/docker-REC/NextCloud/config+data (2.3G project)
nextcloud_db,mariadb:10.6,compose,yes,missing,/volume2/docker-REC/NextCloud/db
nextcloud_redis,redis:latest,compose,yes,missing,/volume2/docker-REC/NextCloud/redis
gitea,gitea/gitea:latest,compose,yes,missing,/volume1/docker/Gitea/data (2M)
vaultwarden-server-1,vaultwarden/server:latest,gui,yes,missing,/volume1/docker/vaultwarden (8.8M)
leantime,leantime/leantime:latest,compose,yes,missing,/volume1/docker/Leantime/data/userfiles+plugins (298M)
leantime-db,mysql:8.4,compose,yes,missing,/volume1/docker/Leantime/data/mysql
beszel,henrygd/beszel:latest,compose,yes,missing,/volume1/docker/beszel/beszel_data (11M)
beszel-agent,henrygd/beszel-agent:latest,compose,yes,missing,/volume1/docker/beszel-agent/beszel_agent_data
valheim,mbround18/valheim:latest,compose,yes,missing,/volume2/docker-REC/valheim/saves+backups+server (2.8G)
openwebui,ghcr.io/open-webui/open-webui:latest,compose,yes,missing,/volume1/docker/openwebui (4.7G)
InfluxDB,influxdb:2.7,compose,yes,missing,/volume2/docker-REC/InfluxDB + named volume (188K)
onlyoffice-ds,onlyoffice/documentserver:latest,compose,yes,missing,2 named volumes + /volume2/docker-REC/onlyoffice (160K)
qbittorrent,linuxserver/qbittorrent:latest,gui,yes,missing,/volume2/docker-REC/Multimedia/qbittorrent
sonarr,linuxserver/sonarr:latest,gui,yes,missing,/volume2/docker-REC/Multimedia/Sonarr
radarr,linuxserver/radarr:latest,gui,yes,missing,/volume2/docker-REC/Multimedia/Radarr
jackett,linuxserver/jackett:latest,gui,yes,missing,/volume2/docker-REC/Multimedia/Jackett
prowlarr,linuxserver/prowlarr:latest,gui,yes,missing,/volume2/docker-REC/Multimedia/Prowlarr
deluge,linuxserver/deluge:latest,gui,yes,missing,/volume2/docker-REC/Multimedia/Deluge
flaresolverr,flaresolverr/flaresolverr:latest,gui,no,ok-n/a,(named volume cache only)
mini-qr,ghcr.io/lyqht/mini-qr:latest,compose,no,ok-n/a,(stateless)
watchtower,containrrr/watchtower:latest,compose,watchtower,n/a,/var/run/docker.sock
1 name image_tag mode stateful watchtower_label data_paths
2 n8n docker.n8n.io/n8nio/n8n:latest compose yes missing /volume2/docker-REC/n8n/data (75M total project)
3 n8n-postgresql postgres:17 compose yes missing /volume2/docker-REC/n8n/postgresql
4 joplin-server joplin/server:latest compose yes missing (stateful via joplin-db)
5 joplin-db postgres:16 compose yes missing /volume2/docker-REC/Joplin/postgres (706M project)
6 postgres16 postgres:16 compose yes missing /volume1/docker/postgres16/data (70M)
7 affine_server ghcr.io/toeverything/affine:stable compose yes missing /volume2/docker-REC/AFFiNE/config+storage
8 affine_postgres pgvector/pgvector:pg16 compose yes missing /volume2/docker-REC/AFFiNE/postgres
9 affine_redis redis:latest compose yes missing (named volume)
10 nextcloud_app nextcloud:latest compose yes missing /volume2/docker-REC/NextCloud/config+data (2.3G project)
11 nextcloud_db mariadb:10.6 compose yes missing /volume2/docker-REC/NextCloud/db
12 nextcloud_redis redis:latest compose yes missing /volume2/docker-REC/NextCloud/redis
13 gitea gitea/gitea:latest compose yes missing /volume1/docker/Gitea/data (2M)
14 vaultwarden-server-1 vaultwarden/server:latest gui yes missing /volume1/docker/vaultwarden (8.8M)
15 leantime leantime/leantime:latest compose yes missing /volume1/docker/Leantime/data/userfiles+plugins (298M)
16 leantime-db mysql:8.4 compose yes missing /volume1/docker/Leantime/data/mysql
17 beszel henrygd/beszel:latest compose yes missing /volume1/docker/beszel/beszel_data (11M)
18 beszel-agent henrygd/beszel-agent:latest compose yes missing /volume1/docker/beszel-agent/beszel_agent_data
19 valheim mbround18/valheim:latest compose yes missing /volume2/docker-REC/valheim/saves+backups+server (2.8G)
20 openwebui ghcr.io/open-webui/open-webui:latest compose yes missing /volume1/docker/openwebui (4.7G)
21 InfluxDB influxdb:2.7 compose yes missing /volume2/docker-REC/InfluxDB + named volume (188K)
22 onlyoffice-ds onlyoffice/documentserver:latest compose yes missing 2 named volumes + /volume2/docker-REC/onlyoffice (160K)
23 qbittorrent linuxserver/qbittorrent:latest gui yes missing /volume2/docker-REC/Multimedia/qbittorrent
24 sonarr linuxserver/sonarr:latest gui yes missing /volume2/docker-REC/Multimedia/Sonarr
25 radarr linuxserver/radarr:latest gui yes missing /volume2/docker-REC/Multimedia/Radarr
26 jackett linuxserver/jackett:latest gui yes missing /volume2/docker-REC/Multimedia/Jackett
27 prowlarr linuxserver/prowlarr:latest gui yes missing /volume2/docker-REC/Multimedia/Prowlarr
28 deluge linuxserver/deluge:latest gui yes missing /volume2/docker-REC/Multimedia/Deluge
29 flaresolverr flaresolverr/flaresolverr:latest gui no ok-n/a (named volume cache only)
30 mini-qr ghcr.io/lyqht/mini-qr:latest compose no ok-n/a (stateless)
31 watchtower containrrr/watchtower:latest compose watchtower n/a /var/run/docker.sock
+76
View File
@@ -0,0 +1,76 @@
# Journal d'exécution — Migration G9 (G8)
> Chaque action est journalisée : date, action, raison, résultat, rollback.
> Rempli pendant la migration. Ce fichier est la trace de ce qui a réellement été fait.
| Date | Phase | Action | Raison | Résultat | Rollback |
|------|-------|--------|--------|----------|----------|
| 2026-10-06 | — | Kit d'exécution préparé (scripts + configs + compose) | Migration G9 à venir | Prêt à exécuter | — |
| 2026-10-06 | — | Conformisation des scripts à la charte G1–G10 | Audit des garde-fous (C1–C5) | `journal()` 6 colonnes (G8) dans les 10 scripts ; renvois garde-fous en en-tête (G1) ; tableau G→script dans README (G2) ; secrets hors git confirmés (G9) | `git checkout` des fichiers modifiés |
| 2026-10-06 | — | Corrections post-revue (6 findings) | Revue de code du kit G9 | Binds compose → 0.0.0.0 (accessibles LAN via J5040, ufw restreint) ; retrait mount /etc/timezone (Gitea) ; user DB dédié photoprism ; fstab USB = vrai FSTYPE ; docs credentiels import ; suppression fichiers morts (ufw.rules, smb.conf, RESTIC_APPEND_ONLY) | `git checkout` des fichiers modifiés |
| 2026-10-09 | 1 (T1.4-a1) | Backup Valheim J5040 → `.11:/run/media/folzo/Folzo Save/migration-j5040/valheim/` | G2 : filet avant migration ; serveur arrêté proprement (shutdown backup 20261009-074115) | 3 artefacts : valheim-world.tar.gz (53.6 Mo), valheim-backups.tar.gz (53.6 Mo), valheim-steam.tar.gz (2.13 Go) + valheim-env.json (chmod 600) ; **sha256 source == destination vérifiés** | Suppression du dossier `migration-j5040/valheim/` sur Folzo Save (aucune donnée source touchée) |
| 2026-10-09 | 1 (T1.4-b1) | Valheim UP sur G9 .24 (conteneur direct, sans Pterodactyl) | Ami joueur — priorité ; serveur source J5040 redémarré (double-run G4) | Docker installé sur G9 (T2.3 de fait) ; image `valheim-pterodactyl:1` transférée par `docker save/load` (ghcr denied) ; monde restauré : **37 chunks sha256 identiques source** ; conteneur healthy, ports 2456-2458 ouverts, lobby enregistré (ZDOS:84), UDP 2456 joignable depuis J5040 | `docker compose down` + `docker rmi` sur G9 ; J5040 intact (serveur source redémarré et healthy) |
| 2026-10-09 | 1 (T1.4-a) | Inventaire J5040 + backup stateful → Folzo Save | G2 : filet complet avant formatage | 34 conteneurs + mounts + 116 images inventoriés ; dumps DB (dify 671M, n8n sqlite, pterodactyl wings 4.7G), volumes nommés (open-webui 3.4G, whisper-cache 2.0G), home 1.8G, cron 6 jobs, compose+env. **12 Go sur `.11:/run/media/folzo/Folzo Save/migration-j5040/` avec SHA256SUMS.txt** (`sha256sum -c` : Réussi). Inventaire → `g9-migration/INVENTAIRE-J5040.md` | Suppression du dossier `migration-j5040/` (données sources J5040 intactes) |
| 2026-10-09 | 1 (T1.4-b) | Migration stack IA J5040 → PC IA .11 | Délester J5040 avant formatage ; IA sur GPU .11 | 22 images transférées `docker save/load` ; stack monté dans `~/migration-j5040` (binds `:z` SELinux) ; endpoints vérifiés : open-webui/searxng/pipelines/qdrant (6 collections)/couchdb/mcpo/tts 200, dify 307 (14 healthy), llm-proxy 3011/3012/3017, ollama 28 modèles, whisper large-v3. Test LLM bout en bout : proxy 3012 → **« MIGRATION-OK »**. Doublons supprimés (whisper/ocr/stt déjà présents) | `docker compose down` + `docker rmi` sur .11 ; J5040 inchangé (source des images) |
| 2026-10-09 | 1 (T1.1-a) | Inventaire des conteneurs xpenology .25 : 30 conteneurs (`sudo docker ps -a` DSM), cross-check 12 projects compose /volume1/docker + 7 conteneurs GUI | Base du durcissement watchtower et de l'inventaire stateful | `g9-migration/conteneurs-xpenology.csv` (nom, image:tag, mode, stateful, label, data_paths) ; 15 yml compose récupérés via `ssh cat` dans `/tmp/nas-yml/` (+ `.orig`) ; tailles `du -sh` mesurées par projet | Fichiers locaux, aucune action destructive sur .25 |
| 2026-10-09 | 1 (T1.4-c) | Migration n8n + notes → G9 .24 | Continuité des services notes/automatisation | 3 images (n8n/obsidian/couchdb) transférées save/load ; données : n8n database.sqlite sha **693ee229 IDENTIQUE** source, obsidian-vault 79M, couchdb-data. Compose `~/docker-compose.yml`+`g9.env` (600) : n8n 5678→200, couchdb 5984→200, obsidian 3100→200 | `docker compose down` sur .24 ; J5040 intact, backups Folzo Save intacts |
| 2026-10-09 | 1 (T1.1-b) | Labels `watchtower.enable=false` — stack .24 appliqué, .25 **BLOQUÉ** | Durcissement : exclure les stateful du auto-update | .24 : n8n, couchdb, obsidian, valheim → label false vérifié `docker inspect` ; watchtower → label true. **.25 bloqué** : clé `id_ed25519_effi_server` retirée de ~/.ssh (11:43) + mot de passe refusé (Synology a bloqué le compte effi). 22 conteneurs .25 + 7 GUI = labels non appliqués, yml préparés localement `/tmp/nas-yml/` prêts à pousser. **Action requise côté utilisateur** : débloquer `effi` (Panneau → Utilisateurs, blocage IP) | Retirer les labels avec `docker compose up -d` (yml .orig sauvegardés sur .25 en `.bak`) |
| 2026-10-09 | 1 (T1.1-c) | Watchtower durci sur .24 : image **pinnée par digest** + CLEANUP=false + INCLUDE_STOPPED=false | Durcissement S11.4 (pas de :latest, pas de cleanup des états) | Image `nickfedor/watchtower@sha256:45b54f6d...` (digest épinglé, 1.23.0, fork maintenu — `containrrr/watchtower:1` **n'existe pas** sur Docker Hub, vérifié API). Env vérifiés : CLEANUP=false, INCLUDE_STOPPED=false, POLL=3600, label-scope. Statut running, API Docker v1.52. À répliquer sur .25 (watchtower .25 = `:latest` CLEANUP=true → yml durci prêt `/tmp/nas-yml/watchtower.yaml`) | Rester sur `containrrr/watchtower:latest` avec CLEANUP=true (state actuel .25, non durable) |
| 2026-10-09 | 1 (T1.1-d) | Notification watchtower → webhook n8n → **Matrix** — chaîne complète testée sur .24 | Exigence ops : tracer chaque MAJ d'image | Watchtower (shoutrrr generic) → webhook n8n `POST /webhook/watchtower` (workflow `w1pfhX0A7KvaDrtk`, Webhook→Build log→Matrix→Respond) → Synapse .24:8008 salon ops. Notification réelle de startup watchtower reçue (exec #10) + 2 notifs de test distinctes (exec #31/#32, event_ids `$OSVIb…`/`$XcR0…`/`$bu5f…` vérifiés dans `event_json` de Synapse). Utilisateurs : ops-bot (token), n8n login owner recréé. Points techniques : n8n v2.42 exige publication (workflow_history+published_version) ; URL Matrix = expression full `={{ }}` (URL statique non évaluée en prod) ; txnId = image+timestamp Build log (ids sandbox déterministes → doublons sinon). Notification sur .25 : yml prêt (watchtower .25 durci + URL webhook → n8n .24) | Désactiver WATCHTOWER_NOTIFICATION_URL sur .24 ; supprimer salon/ops-bot via Synapse admin |
| 2026-10-09 | 1 (T1.1-d) | Ouverture salon Matrix ops sur Synapse .24 + user ops-bot | Forwarding notifications (Synapse absent de .11, déjà déployé sur .24) | Room `!mp7MFx9QbrJ0ARtzw…` ("ops") créée via `createRoom` (`room_creation: true` ajouté à homeserver.yaml via sudo, restart synapse) ; user `ops-bot` enregistré ; 6 messages de probe redactés | Supprimer room + user via administration Synapse ; retirer `room_creation: true` |
| 2026-10-10 | 1 (T1.1-b) | Labels `watchtower.enable=false` .25 : **28/28 stateful labellés** | Accès .25 débloqué (compte `Master`, port 222, clé id_ed25519_synology + NOPASSWD sudo). Compose (13 projets) : yml avec labels installés (backups `.bak-labels`) + `compose up -d` (12/13 OK, postgres16 géré GUI). GUI (8 conteneurs dont postgres16) : `docker update --label-add` **indisponible sur docker 24.0.2** → clonage via API unix socket (rename `-old-lbl`, create avec labels racine, start ; backup auto si échec). | Vérif `docker inspect` loop : les 28 stateful = `false`, watchtower = `true`. Preuves dans /tmp/clone_labels.py + logs session | Supprimer label : restaurer `.bak-labels` + `compose up -d` ; conteneurs clonés = mêmes binds/env (données intactes) |
| 2026-10-10 | 1 (T1.1-c/.25) | Watchtower durci sur .25 : `nickfedor/watchtower@sha256:45b54f6d…` + CLEANUP=false + INCLUDE_STOPPED=false + POLL=3600 + label-scope | Réplication du durcissement .24 sur NAS (watchtower .25 était `:latest` CLEANUP=true). Projet compose `watchpower` (COMPOSE_PROJECT_NAME requis, `up -d` OK). Nickfedor refuse SCHEDULE+POLL ensemble → POLL 3600 conservé. | Notification startup réelle reçue dans Matrix : « Watchtower 1.23.0 using Docker API v1.43 … Next scheduled run 02:48 » (event vérifié). Nœud n8n corrigé (payload shoutrrr generic = title/message, pas event_name/image_name) | Restaurer `compose.yaml.bak-labels` + `compose up -d` (containrrr/watchtower:latest CLEANUP=true) |
| 2026-10-10 | 1 (T1.4-e) | **GO utilisateur** — vérification finale services sans .27, avant formatage | Condition posée : .27 éteint + tout vérifié OK = GO (formatage SSD fait par l'utilisateur). | Vérifiés depuis .11 : OpenFox .11:10369 200 (thread survit à la bascule), dify/open-webui/llm-proxy 3011-3012 .11 200, n8n/couchdb/obsidian/matrix .24 OK (API 200), NAS .25 : 31/31 conteneurs Up, labels stateful 28/28 false (0 exception), watchtower digest healthy (notif Matrix confirmée). Clé SSH .25 recréée sur .11 (id_ed25519_openfox11, authorized_keys Master). Backup Folzo Save 24 Go SHA256 23/23. **GO donné, formatage J5040 par l'utilisateur** | Rollback : rallumer .27 (données intactes, rien de destructive n'a été fait sur .27) |
| 2026-10-09 | 1 (T1.2-a) | Inventaire stateful .25 → `g9-migration/INVENTAIRE-STATEFUL.md` | Préparer l'export/migration des données sans perte | 30 conteneurs `docker ps -a` couverts (cross-check CSV), pour chaque stateful : image:tag, data paths, `du -sh` mesuré, type DB, mode GUI/compose, dépendances, méthode d'export + destination (G9/.11/.25/supprimé). Tailles : n8n 75M, Joplin 706M, NextCloud 2.3G, valheim 2.8G, openwebui 4.7G, beszel 11M, InfluxDB 188K, gitea 2M, vaultwarden 8.8M, Leantime 298M, AFFiNE 94M, postgres16 70M. Points de vigilance : 7 GUI sans CLI labels, flux .25→.11 | Suppression du fichier `INVENTAIRE-STATEFUL.md` (aucune action sur .25) |
| 2026-10-10 | 1 (T1.4-f) | OpenFox recopié J5040 (.27) → PC IA .11 : thread + sessions préservés avant formatage | Les sessions OpenFox vivent dans `.27:~/.local/share/openfox/sessions.db` (66 sessions, 107 Mo) → formatage = perte sans copie. | Bundle `openfox-share.tar.gz` 942M (projets ~/openfox 1.1G, .config/openfox 486M auth+config, .local/share/openfox 922M sessions, node v24 global) + `node-bin.tar.gz` 52M vers .11, **SHA256 vérifiés « Réussi »**, archivés sur Folzo Save. Déploiement .11 : extract, symlink `/home/effi → /home/folzo`, launcher corrigé, SELinux `chcon -R -t bin_t`, unit systemd `openfox.service` activé → HTTP 200 sur .11:10369 (LAN), thread OpenFox re-pris sur .11 après bascule proxy. Vérifié sans .27 (éteint) : endpoints .11/.24/.25 tous OK | `systemctl disable --now openfox` + suppression `~/.config/openfox` et `~/.local/share/openfox` sur .11 ; .27 non touché (données intactes avant formatage) |
| 2026-10-10 12:52 | 2 | hardening : SSH keys-only activé | Durcissement SSH (G6) | PasswordAuthentication no, PermitRootLogin prohibit-password | Re-activer PasswordAuthentication yes dans sshd_config |
| 2026-10-10 12:52 | 2 | hardening : ufw activé | Firewall LAN-only (G6) | deny incoming, allow outgoing, OpenSSH + services LAN-only 192.168.1.0/24 (TCP 2342 3000 3100 5678 5984 8008 8009 8080 8081 8096 + Valheim 2456-2458 TCP/UDP) | ufw disable |
| 2026-10-10 12:52 | 2 | hardening : unattended-upgrades activé | Mises à jour de sécurité auto (G7) | installé + activé | systemctl disable --now unattended-upgrades |
| 2026-10-10 12:52 | 2 | hardening : fail2ban activé | Anti brute-force SSH (G7) | installé + activé | systemctl disable --now fail2ban |
| 2026-10-10 12:53 | 2 | data : Espace /data créé (Option 0) | Données famille sur mono-disque 990 Pro, LUKS abandonné (protection = ufw LAN + restic chiffré) | /data, /data/famille, /data/docker (owner folzo, 2775) | rmdir /data/famille /data/docker /data |
| 2026-10-10 12:53 | 2 | docker : Docker + compose installés | Runtime conteneurs du G9 | folzo dans le groupe docker | apt-get remove docker.io docker-compose-v2 |
| 2026-10-10 12:53 | 2 | smb : Samba installé, partage [famille] (invité LAN) | Accès SMB données famille sans maintenance par appareil | partage [famille] → /data/famille, guest ok, force user folzo, ufw SMB LAN-only | systemctl disable --now smbd nmbd ; suppression [famille] ; ufw delete règles SMB |
| 2026-10-10 13:05 | 2 | hardening : correctif drop-in `50-cloud-init.conf` | Ubuntu 26 : Include sshd_config.d en tête → 1re occurrence gagne → PasswordAuthentication yes gagnait | `sshd -T` : passwordauthentication no effectif ; script 01 patché (boucle drop-ins) ; anti-lockout vérifié (session clés OK, auth mot de passe refusée) | Restaurer `.bak` du drop-in + restart ssh |
| 2026-10-10 13:10 | 2 | smb : netbios activé + smbclient + tests R/W | Ubuntu default `disable netbios=Yes` bloque la découverte par nom | Partage `\\192.168.1.24\famille` visible depuis .11 (LAN) ; put/get/rm testés avec diff sha → SMB_RW_OK ; invité (guest ok, force user folzo) | `disable netbios = Yes` + restart smbd/nmbd |
| 2026-10-10 13:20 | 2 | docs : bascule G9 **mono-disque 990 Pro** + **abandon LUKS** (Option 0, validation utilisateur) | 2ᵉ disque interne prévu = aucun ; données familiales non critiques ; fuite internet = ufw/edge, pas LUKS | `DOSSIER-RECREATION.md` v2.3 (R1/R3/3.1/R4/R5/R8/plan 0.4 + changelog) ; `DOSSIER-SOFTWARE.md` S9/S10/AC5/AC6 + statut révisé 10/10 ; `g9-migration/README.md` (02-data.sh, note disque) ; `02-disk-luks.sh` supprimé → `02-data.sh` (non destructif) ; prompt Kanban Phase 2 T2.2 réaligné | `git checkout` des docs (historique non destructif, aucune donnée touchée) |
| 2026-10-10 16:50 | 2 | beszel : agent v0.21.0 installé (hub .25:8090) | Monitoring G9 (T2.5) — token universel fourni par l'utilisateur, transmis hors journal (G9), fichiers temporaires supprimés | service `beszel-agent` **active**, logs : « WebSocket connected host=192.168.1.25:8090 » → G9 visible dans le hub ; script 05 réécrit (get.beszel.dev, version épinglée, token par env) | systemctl disable --now beszel-agent ; install-agent.sh -u |
| 2026-10-10 16:58 | 2 | smb : correctif nmbd (`disable netbios = No`) | Ubuntu 26 pose `disable netbios = yes` par défaut → nmbd inactive (découverte par nom impossible) | smbd+nmbd **active**, `testparm` OK, partage `famille` visible, guest map OK ; script 04 patché | `disable netbios = yes` + restart |
| 2026-10-10 17:30 | 2 | corrections post-revue code (8 findings) | Revue de code du kit (diff Phase 2) | 1 watchtower compose aligné déploiement réel (nickfedor digest + POLL 3600 + shoutrrr→n8n, placeholder marqué) ; 2 vaultwarden compose : ADMIN_TOKEN_FILE/TRUSTED_HOSTS, ./data:/data, healthcheck curl /alive ; 3 restic : append-only déplacé après 1er backup avec `latest` ; 4 07-migrate : stop/start conteneurs source autour du rsync (cohérence bases + manifeste stable, G4 respecté) ; 5 secrets 644 (n8n uid 1000) ; 6 scripts chmod +x ; 7 accès xpenology → Master/clé (07/08/README) ; 8 06b : grep \|\| true + fstab fsck 0 pour ntfs/exfat. Vérif : `bash -n` 10/10 OK, YAML compose OK | `git checkout` des fichiers modifiés |
| 2026-10-10 18:40 | 2 | stack .24 : correctifs tests utilisateur (Element, n8n) | Tests Phase 2 : Element tournait en boucle (lisait /app/config.json défaut matrix.org, le mount /config.json n'est pas lu) ; n8n refusait l'accès http par IP (secure cookie) | element recréé avec `./config.json:/app/config.json:ro` → homeserver 192.168.1.24:8008 chargé ; n8n : N8N_SECURE_COOKIE=false + N8N_HOST=192.168.1.24 + N8N_PROTOCOL=http ; vérif HTTP 200 sur 8009 et 5678 ; backups `.bak.$TS` des 2 compose. Obsidian 3100 : « secure connection required » = attendu (web viewer exige HTTPS) → résolu en Phase 4 via Caddy TLS (obsidian.folzocorp.fr), pas de fix Phase 2 | restaurer les `.bak` + `compose up -d` |
| 2026-10-10 17:55 | 2 | valheim : diagnostic connexion jeu (ufw FORWARD + port de jeu) | Utilisateur : « serveurs indisponibles » en jeu | Cause 1 : ufw FORWARD DROP cassait l'UDP docker (TCP passait via docker-proxy) → règle `DOCKER-USER -A -p udp --dport 2456:2458 -j ACCEPT` posée (+ RETURN, à persister dans /etc/ufw/before.rules). Cause 2 : le serveur Valheim lie le **port de jeu = UDP 2457** (2456 = steam query, non bindé) — tcpdump prouve que les paquets 2457 arrivent bien au conteneur. → En jeu, rejoindre **192.168.1.24:2457**. Amis hors LAN : redirection Freebox UDP 2456-2458 → .24 (action utilisateur, G6 ; l'ancienne redirection pointait .27) | `iptables -D DOCKER-USER 1` |
| 2026-10-10 20:05 | 2 | valheim : restauration du VRAI monde + persistance | Le serveur tournait dans un monde NEUF : le SAVE_LOCATION de l'image (valheim-pterodactyl) pointe /home/steam/.config (couche conteneur, non persistée) → le monde restauré Phase 1 a été perdu à la recréation du conteneur (09/10 20:34). Sauvegarde saine unique = backup .11 Folzo Save (172 chunks, 848 saves, sha 8e68a82f vérifié) | sha vérifié sur .24 ; monde neuf archivé (`monde-neuf-20261010-archivage`) ; vrai monde extrait dans le bind `data/save` (33 chunks, _main.848.db2) ; compose: `SAVE_LOCATION=/home/container/save` (persistant) ; serveur relancé avec `-savedir /home/container/save`. À faire: l'utilisateur rejoint 192.168.1.24:2457 et valide la base. Note: le job AUTO_BACKUP de l'image regarde l'ancien chemin (backups auto à reconfigurer sur /home/container/save) | restaurer compose `.bak` + monde neuf depuis l'archivage |
| 2026-10-10 20:15 | 2 | valheim : restauration du monde depuis le SSD J5040 (USB) | La build de l'ami (10/10 matin) était sur .27, postérieure au backup .11 (848). SSD branché USB sur .24, LVM intact (non formaté) | VG USB renommé `j5040-vg` (nom dupliqué avec le VG système, collision) ; monté RO ; monde trouvé `wings/data/aff5199c…/steam/.config/.../worlds_local/LeRepaireDuGrandFrak` = **_main.897 du 10/10 06:20** ; serveur stoppé, version 848 archivée (`monde-848-20261010-archivage`), 897 + player.list/prefs/lists déployés dans le bind persistant `data/save` (chown 1000) ; serveur redémarré, world chargé puis save 898. USB démonté, VG désactivé. À valider en jeu : base de l'ami visible. | `mv` des dossiers d'archivage (`monde-848-20261010-archivage`) vers `data/save` ; redémarrer le serveur |
| 2026-10-10 20:22 | 2 | backup .11 Folzo Save : valheim-world.tar.gz écrasé par la version 897 (10/10 06:20) | Demande utilisateur expresse : la copie de référence doit être la plus récente (build de l'ami) | Nouveau tar 64.6 Mo (worlds_local 897/898 + backups auto + player.list/prefs/lists), sha256 `e766ae2c…` (ancien `8e68a82f…` remplacé dans SHA256SUMS.txt, `sha256sum -c` OK) | L'archive `valheim-backups.tar.gz` sur .11 conserve les versions antérieures |
| 2026-10-10 20:30 | 2 | **reboot test G9 validé** (GO utilisateur) | Valider la persistance post-reboot (durcissement, montages, services) | G9 up en ~75 s ; ufw actif + règle DOCKER-USER valheim persistée (before.rules) ; passwordauthentication no effectif ; smbd/nmbd/fail2ban/unattended/beszel-agent actifs ; 7 conteneurs up (valheim healthy) ; /data/famille présent ; partage famille visible ; endpoints 5678/8008/8009/5984/3100 OK ; monde valheim 897/898 intact | — |
| 2026-10-10 20:32 | 2 | G9 éteint (poweroff) sur demande — déplacement physique sur onduleur | Fin de Phase 2, tâche Kanban déplacée en Done par l'utilisateur | G9 éteint proprement (ping KO après poweroff) ; données saines (valheim 898, world .11 sha e766ae2c). Prochain boots : tout est persistant (validé par reboot test). Prévu : onduleur USB → installer NUT (nut-server) en Phase 3 pour arrêt propre sur batterie | rallumer = action physique utilisateur |
| 2026-10-10 20:12 | 3 | restic : premier backup OK | Backup chiffré du G9 (G2) | snapshot 21de0787 — 4 564 fichiers, 6.956 GiB lus / 4.178 GiB stockés, sources /data /home/folzo /etc ; conteneurs stateful stoppés (valheim, n8n, synapse, couchdb, obsidian) puis relancés | `restic forget 21de0787 && restic prune` |
| 2026-10-10 20:20 | 3 | transport backup : SMB/CIFS écarté, rclone retenu | `mount.cifs` (vers 1.0) + restic → `chmod … no such file or directory` (chmod impossible sur le partage) ; rclone mount FUSE → cache d'écriture qui a perdu des objets du repo ; rclone 1.60 (apt) sans backend smb | Transport final = backend rclone de restic (FTP/SMB), repo `rclone:freeboxftp:Freebox/backup/g9` ; 3 repos de test ont été créés puis purgés (4.176 GiB libérés) | retirer `-o rclone.args=… --append-only` des jobs ; restaurer le mount CIFS depuis `/etc/fstab.bak.*` + unité `rclone-freebox.service` |
| 2026-10-10 20:22 | 3 | repo restic initialisé sur la Freebox | Backup chiffré + versionné du G9 | Repo `Freebox/backup/g9` (Delta, 1.79 To, 1.785 To libres) ; clé de repo générée par openssl dans `/etc/restic/repo.key` (600, jamais affichée) | `rclone purge freeboxftp:Freebox/backup/g9` (DESTRUCTIF, volontaire) |
| 2026-10-10 20:53 | 3 | append-only vérifié | Anti-ransomware (G2) | `forget --prune` REFUSÉ par `rclone serve restic --stdio --append-only` (rc=1), snapshots toujours lisibles ; repo de preuve `_appendonly-test` créé puis purgé | retirer `-o rclone.args=… --append-only` des jobs |
| 2026-10-10 20:53 | 3 | test de restauration OK | Vérification G2 (restauration testée) | `/data/famille/Test/PXL_20261009_130502121.jpg_compressed.JPEG` restauré depuis la Freebox, sha256 9352a905d9bb… identique à l'original | restauration dans un dossier temporaire, aucun impact sur les données sources |
| 2026-10-10 20:55 | 3 | jobs + cron installés | Backup automatique quotidien + alerte | `/usr/local/bin/restic-{daily,prune,health,notify}.sh` (root 755) ; cron root : 03:30 backup, 04:00 prune, 09:15 health (alerte webhook n8n → Matrix) | supprimer les 4 lignes du crontab root + les 4 scripts |
| 2026-10-10 20:31 | 3 | artefact secret supprimé | G9 : les secrets ne doivent pas traîner | dossier racine créé par erreur (heredoc collé) contenant le mot de passe SMB, mode 600 root → supprimé ; le mot de passe n'a jamais été affiché ; `cifs.cred` / `rclone-smb.cred` / `ftp.conf` / `repo.key` restent en 600 (root) | rien (suppression) |
| 2026-10-10 20:47 | 3 | unité systemd `rclone-freebox` retirée | Mount CIFS abandonné au profit du backend rclone : plus besoin de montage persistant | service désactivé, unit et `/etc/systemd/system/rclone-*.d/override.conf` supprimés, daemon-reload ; ligne fstab CIFS ajoutée puis retirée (sauvegardes `/etc/fstab.bak.*`) | recréer l'unit + l'override |
| 2026-10-10 21:12 | 3 | alerte Matrix en place (directe, sans n8n) | Prévenir l'ops si le backup échoue (C3) | `restic-notify.sh` poste sur Synapse (`http://192.168.1.24:8008`, salon ops, token ops-bot récupéré du workflow n8n « Watchtower Notification », écrit dans `/etc/restic/matrix.env` 600 root, jamais affiché) ; **2 messages de test envoyés et reçus** ; fallback webhook n8n conservé | retirer les 3 lignes de `/etc/restic/matrix.env` + les 3 lignes cron |
| 2026-10-10 21:12 | 3 | accès n8n : à recréer (bloquant pour la voie n8n → Matrix) | L'utilisateur n'a plus accès à n8n | 1 workflow présent (« Watchtower Notification »), CLI n8n 2.42.5 sans création d'utilisateur → recréer l'accès par l'UI (propriétaire) puis créer une clé API (Settings → n8n API) ; la voie directe Matrix est opérationnelle en attendant | — |
| 2026-10-10 21:19 | 3 | jobs lancés à la main + logs | Vérifier que le backup et la purge s'exécutent hors kit (C3) | `restic-daily.sh` rc=0 → snapshot `cccdb2c7` (+5.175 MiB stockés, parent 21de0787) ; `restic-prune.sh` rc=0 ; logs `/var/log/restic-{daily,prune,health}.log` ; `restic check --read-data-subset=1%` sans erreur | `restic forget cccdb2c7 && restic prune` |
| 2026-10-10 21:19 | 3 | rétention appliquée (7/4/12/5) | Rétention et espace (C5) | `forget --prune` exécuté (append-only refusé → purge appliquée sans append-only, alerte envoyée), 2 snapshots conservés ; repo **4.19 GiB** pour **1.785 TiB libres** sur le RAID5 de la Delta (1.79 TiB) — marge conforme aux 2 To | rien (rétention non destructrice à ce stade) |
| 2026-10-10 21:20 | 3 | lock stale supprimé + `unlock` sans append-only | Le lock laissé par une exécution interrompue bloquait le prune (rc=11) | `restic unlock` (1 lock retiré) ; **règle intégrée aux jobs** : `unlock` tourne **sans** l'option append-only (les locks sont supprimables), ajout en tête de `restic-daily.sh` et `restic-prune.sh` | retirer les lignes `unlock` des jobs |
| 2026-10-10 21:25 | 3 | **incident : crontab root effacé par le kit (corrigé)** | Le bloc cron du kit pipait `crontab -l \| grep -vE …` dans `crontab -` ; avec `set -e`, le `grep` sans correspondance tuait le sous-shell → stdin vide → crontab vidé | détecté et **documenté** : le crontab root ne contenait que les 3 lignes restic (aucune ligne utilisateur perdue) ; kit corrigé (`grep … \|\| true` + écriture dans un fichier + **contrôle « 3 lignes attendues » avant installation**, refus et crontab intact sinon) ; crontab re-créé puis vérifié après reboot | restaurer le crontab : `sudo crontab -l` (3 lignes restic) |
| 2026-10-10 21:36 | 3 | **reboot G9 validé (persistance post-reboot)** | Valider C1 après le changement de transport (plus de montage CIFS) | `systemctl reboot` (21:36:32), retour SSH en ~75 s ; `06c-postboot-check.sh` : **15 contrôles OK / 0 KO** (binaires, env 600, clé repo, config rclone, **aucun montage CIFS requis**, 3 lignes cron, 4 jobs, `restic snapshots`, `check --read-data-subset=1%`, health < 26 h, 5 conteneurs stateful relancés) | rien (validation en lecture seule) |
| 2026-10-10 21:40 | 3 | `.gitignore` élargi (secrets) | Renforcer G9 côté dépôt local | ajout de `*.cred`, `*.key`, `repo.key`, `matrix.env` (les patterns `*.env` et le chemin explicite couvraient déjà) ; vérifié : **aucun secret dans le repo** (recherche du mot de passe SMB et du token Matrix = 0 occurrence), `git check-ignore` OK, aucun fichier suivi par git de type env/cred/key | retirer les lignes ajoutées |
| 2026-10-10 21:52 | 3 | `configs/restic.env.example` rechargé sur le transport retenu | Revue T3.1 : l'exemple restait sur le mount CIFS/rclone-FUSE abandonné (C8) | exemple aligné sur `RESTIC_REPOSITORY=rclone:freeboxftp:Freebox/backup/g9` + `RESTIC_RCLONE_REMOTE/PATH`, `RCLONE_CONFIG`, `RESTIC_RCLONE_APPENDONLY=1`, `SCRATCH_REPO`, `RESTIC_VERSION/SHA256`, `G9_JOURNAL`, alerte Matrix directe (`/etc/restic/matrix.env`) + webhook n8n de secours, transports écartés documentés (CIFS/FUSE/SMB/FTPES) ; aucun secret dans le fichier | restaurer l'exemple depuis git (fichier versionné) |
| 2026-10-10 21:55 | 3 | sous-point « thermique Freebox < 70 °C » **déchargé de T3.1** | Revue T3.1 (C0) : jamais mesuré, et non mesurable par l'agent | **décision utilisateur (10/10/2026)** : API Freebox OS = appairage requis (mot de passe Freebox OS + code saisi sur l'écran de la Freebox) ; SSH Delta fermé (port 22 refused, testé) → relevé reporté en **Phase 1 / T1.3** (COMPTE-RENDU-PHASE-1.md, DOSSIER-RECREATION plan 0.2 : seuil 85 °C) ; à lire dans Freebox OS (app Sapeur) puis journaliser | réintégrer le sous-point dès qu'un relevé est disponible |
| 2026-10-10 21:55 | 3 | énoncé C1 réaligné sur le transport réellement retenu | Revue T3.1 : C1 décrivait un montage CIFS (`/mnt/freebox`, `vers=2.x`, `x-systemd.automount`) exécutée en rclone/FTP | critère réécrit : cible = `rclone:freeboxftp:Freebox/backup/g9`, aucun montage, `snapshots` + `check --read-data-subset=1%` OK, persistance prouvée par reboot réel (15/15) ; abandon CIFS/FUSE tracé (20:20, 20:47) | réécrire l'énoncé si la cible change (Phase 5 : `rest:` en VM) |
| 2026-10-10 22:05 | 3 | format du journal corrigé (6 colonnes) | Revue T3.1 : la ligne « transport SMB/CIFS écarté » (20:20) n'avait que 5 colonnes (Rollback absent) — violation G8 | **19 lignes phase 3** toutes en 6 colonnes (vérifié par rendu markdown, 0 ligne non conforme) ; rollback ajouté sur la ligne 20:20 (`retirer -o rclone.args=… --append-only` / restaurer le mount CIFS depuis `/etc/fstab.bak.*` + unité) ; rollback isolé en 6ᵉ cellule sur la ligne valheim 20:15 (phase 2) | restaurer les lignes depuis git (journal versionné) |
| 2026-10-10 22:08 | 3 | `RCLONE_CONFIG` harmonisé sur un seul fichier | Revue T3.1 : docs annonçaient `rclone.conf`, le live pointait `ftp.conf` (2 fichiers, 2 remotes séparés) | les 2 remotes réunis dans `/root/.config/rclone/rclone.conf` (`[freeboxftp]` FTP + `[freebox]` SMB secours, 600 root, `.bak.1791670085` daté) ; `/etc/restic.env` → `RCLONE_CONFIG=/root/.config/rclone/rclone.conf` ; vérifié : FTP liste le repo, SMB lit 4.184 GiB, `restic snapshots` = 2, kit rejoué rc=0 ; `ftp.conf` conservé en copie FTP-only (600) ; docs (kit + RESTORE-G9.md) alignés | `cp /root/.config/rclone/rclone.conf.bak.1791670085` à l'emplacement + remettre l'ancien `RCLONE_CONFIG` |
| 2026-10-10 22:09 | 3 | repo de preuve append-only purgé | Artefact du test append-only laissé sur la Freebox (`_appendonly-test`) | `rclone purge freeboxftp:Freebox/backup/_appendonly-test` → il ne reste que `g9/` sur le partage ; kit rejoué rc=0 (append-only prouvé, restore testée) | re-créé automatiquement par le kit à chaque exécution (puis purgé) |
| 2026-10-10 22:40 | 3 | corrections post-revue code (12 findings) | Revue de code du kit (diff T3.1) | 1 `jq` installé par le kit + garde `command -v jq` avec repli JSON dans `restic-notify.sh` ; 2 `configs/excludes.txt.example` créé et **installé par le kit s'il est absent** (jamais écrasé) + contrôle dans `06c` ; 3 `restic-notify.sh \|\| true` dans le job prune ; 4 le refus d'append-only au prune est **journalisé seulement** (plus d'alerte « normale » quotidienne ; vérifié : 0 ligne PURGE dans le log, alerte uniquement si la purge sans append-only échoue) ; 5 `03b-secrets.sh` : wording **644** aligné sur le code (3 endroits) ; 6 lignes vides intra-tableau supprimées dans `journal.md` ; 7 branche `restic ls \| awk` morte supprimée (fichier test = `find` déterministe) ; 8 **rclone épinglé** v1.75.2 + sha256 `349ac8fb…3702` vérifié ; 9 transport/append-only **canonique dans RESTORE-G9.md §6** (tableau des transports testés), README/env.example/en-tête du script = pointeurs ; 10 liste stateful **dynamique** (`RESTIC_STATEFUL`) dans `06c` ; 11 arbre vide `Save/migration-j5040/` supprimé + `.gitignore` dégraissé ; 12 **règles ufw sauvegardées avant `ufw --force reset`** (`/etc/ufw/{before,user}.rules.bak.<epoch>`) + note « liste de ports à aligner sur les compose ». Vérif live : kit rc=0, prune rc=0 (0 alerte), notify rc=0, `06c` = **17 OK / 0 KO**, excludes intacts (6 lignes) | `git checkout` des fichiers du kit ; restaurer `/etc/ufw/*.rules.bak.<epoch>` |
| 2026-10-11 07:32 | 4 | migrate:gitea : backup restic pré-migration | Garde-fou G2 avant import Gitea | snapshot restic Freebox (voir logs /var/log/restic-daily.log) | restic forget <snapshot> && restic prune |
| 2026-10-11 07:35 | 4 | migrate:gitea : backup restic pré-migration | Garde-fou G2 avant import Gitea | snapshot restic Freebox (voir logs /var/log/restic-daily.log) | restic forget <snapshot> && restic prune |
| 2026-10-11 07:36 | 4 | migrate:gitea : backup restic pré-migration | Garde-fou G2 avant import Gitea | snapshot restic Freebox (voir logs /var/log/restic-daily.log) | restic forget <snapshot> && restic prune |
| 2026-10-11 07:36 | 4 | migrate:gitea : import ÉCHEC intégrité (repositories) | Vérification G4 | manifeste sha256 source ≠ stage | purger stage/repositories et re-exécuter |
| 2026-10-11 07:37 | 4 | migrate:gitea : backup restic pré-migration | Garde-fou G2 avant import Gitea | snapshot restic Freebox (voir logs /var/log/restic-daily.log) | restic forget <snapshot> && restic prune |
| 2026-10-11 07:38 | 4 | migrate:gitea : backup restic pré-migration | Garde-fou G2 avant import Gitea | snapshot restic Freebox (voir logs /var/log/restic-daily.log) | restic forget <snapshot> && restic prune |
| 2026-10-11 07:39 | 4 | migrate:gitea : backup restic pré-migration | Garde-fou G2 avant import Gitea | snapshot restic Freebox (voir logs /var/log/restic-daily.log) | restic forget <snapshot> && restic prune |
| 2026-10-11 07:40 | 4 | migrate:gitea : Gitea migré + lancé sur le G9 | T4.4 (double-run G4, .25 lecture seule) | pg_dump fcab4f2231ee… restauré ; dépôts+LFS+appdata sha256 identiques ; healthz ; .25 intact | docker compose -f /data/docker/gitea/docker-compose.yml down + rm -rf /data/docker/gitea (G9 seul) |
+100
View File
@@ -0,0 +1,100 @@
#!/usr/bin/env bash
#
# 01-hardening.sh — T2.1 : Durcissement Ubuntu Server 26.04 du G9
# Idempotent. Exécuter en root (sudo).
#
# Garde-fous : G6 (réseau LAN-only), G7 (rollback), G8 (journalisation).
#
# Fait :
# - SSH : clés uniquement (Pas de mot de passe), port par défaut 22
# - ufw : actif, par défaut deny incoming / allow outgoing
# - unattended-upgrades : activé
# - fail2ban : actif
#
# Rollback :
# - Re-activer PasswordAuthentication yes dans /etc/ssh/sshd_config
# - ufw disable ; restaurer les règles sauvegardées avant le reset
# (`cp /etc/ufw/before.rules.bak.<epoch> /etc/ufw/before.rules`, idem user.rules, puis `ufw reload`)
# - systemctl disable --now fail2ban unattended-upgrades
#
# ⚠️ Maintenance : la liste de ports ouverts (TCP/UDP, LAN-only) est **en dur** dans ce script
# et doit rester **alignée sur `compose/*/docker-compose.yml`** (source des ports publiés).
# Ajouter une brique = ajouter son port ici, sinon le service sera injoignable depuis la LAN.
set -euo pipefail
log() { echo -e "\n[hardening] $*"; }
journal() {
local action="$1" raison="$2" resultat="$3" rollback="$4"
printf '| %s | 2 | hardening : %s | %s | %s | %s |\n' "$(date '+%Y-%m-%d %H:%M')" "$action" "$raison" "$resultat" "$rollback" >> "$(dirname "$0")/../journal.md"
}
[[ $EUID -eq 0 ]] || { echo "ERREUR : exécuter en root (sudo)"; exit 1; }
# --- 1. SSH : clés uniquement ---
log "SSH : désactivation de l'authentification par mot de passe"
SSHD=/etc/ssh/sshd_config
# Guard anti-lockout (G1) : couper PasswordAuthentication sans clé installée = porte SSH fermée.
RUN_USER="${SUDO_USER:-$USER}"
RUN_HOME="$(getent passwd "$RUN_USER" | cut -d: -f6)"
if ! grep -qsE '^(ssh-|ecdsa-|sk-)' "$RUN_HOME/.ssh/authorized_keys" 2>/dev/null; then
echo "ERREUR : aucune clé publique dans $RUN_HOME/.ssh/authorized_keys pour '$RUN_USER'."
echo "Installe d'abord une clé (ssh-copy-id), puis relance. Rien n'a été modifié."
exit 1
fi
cp -n "$SSHD" "$SSHD.bak.$(date +%s)" 2>/dev/null || true
sed -ri 's/^#?PasswordAuthentication.*/PasswordAuthentication no/' "$SSHD"
sed -ri 's/^#?PermitRootLogin.*/PermitRootLogin prohibit-password/' "$SSHD"
sed -ri 's/^#?X11Forwarding.*/X11Forwarding no/' "$SSHD"
# Drop-ins sshd_config.d : la PREMIÈRE occurrence gagne (Include en tête de sshd_config)
# → désactiver le mot de passe aussi dans les drop-ins (ex. 50-cloud-init.conf).
for dropin in "$SSHD".d/*.conf; do
[[ -f "$dropin" ]] || continue
cp -n "$dropin" "$dropin.bak.$(date +%s)" 2>/dev/null || true
sed -ri 's/^#?PasswordAuthentication.*/PasswordAuthentication no/' "$dropin"
done
systemctl restart ssh
journal "SSH keys-only activé" "Durcissement SSH (G6)" "PasswordAuthentication no, PermitRootLogin prohibit-password" "Re-activer PasswordAuthentication yes dans sshd_config"
# --- 2. ufw ---
log "ufw : activation (deny incoming / allow outgoing)"
# G8 : les règles existantes sont sauvegardées AVANT le reset (ufw --force reset est destructif).
ufw_bak="/etc/ufw/before.rules.bak.$(date +%s)"
cp -a /etc/ufw/before.rules "$ufw_bak" 2>/dev/null || true
cp -a /etc/ufw/user.rules "/etc/ufw/user.rules.bak.$(date +%s)" 2>/dev/null || true
log "règles ufw sauvegardées : $ufw_bak"
ufw --force reset >/dev/null
ufw default deny incoming
ufw default allow outgoing
ufw allow OpenSSH
# Services LAN-only (G9 = LAN-only, l'exposition WAN passe par l'edge — S9.2/S9.3).
# Ports des services RÉELS sur .24 (n8n, synapse, element, couchdb, obsidian) +
# briques famille futures + Valheim (TCP+UDP). Accessibles depuis le LAN uniquement.
for port in 2342 3000 3100 5678 5984 8008 8009 8080 8081 8096; do
ufw allow from 192.168.1.0/24 to any port "$port" proto tcp
done
for port in 2456 2457 2458; do
ufw allow from 192.168.1.0/24 to any port "$port" proto tcp
ufw allow from 192.168.1.0/24 to any port "$port" proto udp
done
ufw --force enable
journal "ufw activé" "Firewall LAN-only (G6)" "deny incoming, allow outgoing, OpenSSH + services LAN-only 192.168.1.0/24 (TCP 2342 3000 3100 5678 5984 8008 8009 8080 8081 8096 + Valheim 2456-2458 TCP/UDP)" "ufw disable"
# --- 3. unattended-upgrades ---
log "unattended-upgrades : activation"
apt-get update -qq
apt-get install -y -qq unattended-upgrades
dpkg-reconfigure -f noninteractive unattended-upgrades
systemctl enable --now unattended-upgrades
journal "unattended-upgrades activé" "Mises à jour de sécurité auto (G7)" "installé + activé" "systemctl disable --now unattended-upgrades"
# --- 4. fail2ban ---
log "fail2ban : installation + activation"
apt-get install -y -qq fail2ban
cp -n "$(dirname "$0")/../configs/fail2ban.local.conf" /etc/fail2ban/jail.local
systemctl enable --now fail2ban
journal "fail2ban activé" "Anti brute-force SSH (G7)" "installé + activé" "systemctl disable --now fail2ban"
log "Terminé. Vérif : ss -tlnp | grep :22, ufw status, systemctl is-active fail2ban unattended-upgrades"
echo "AVANT de couper ton accès SSH, ouvre une 2e session et teste 'ssh <user>@G9' avec ta clé."
+42
View File
@@ -0,0 +1,42 @@
#!/usr/bin/env bash
#
# 02-data.sh — T2.2 (Option 0, validée 10/10/2026) : espace données /data sur le G9
#
# Décision tracée : LUKS abandonné sur le G9 (mono-disque Samsung 990 Pro 2 To,
# données familiales non critiques, protection = ufw LAN-only + backups restic
# chiffrés — voir DOSSIER-SOFTWARE S9/S10, changelog 10/10/2026).
# Remplace 02-disk-luks.sh (aucune opération destructive : ni partitionnement,
# ni LUKS, ni réinstallation).
#
# Garde-fous : G1 (idempotent, non destructif), G7 (rollback = rmdir), G8.
#
# Fait :
# /data (root du volume données, sur le LV racine existant)
# /data/famille (partage SMB — T2.4)
# /data/docker (données conteneurs si besoin)
#
# Rollback : rmdir /data/famille /data/docker /data (dossiers vides uniquement)
set -euo pipefail
log() { echo -e "\n[data] $*"; }
journal() {
local action="$1" raison="$2" resultat="$3" rollback="$4"
printf '| %s | 2 | data : %s | %s | %s | %s |\n' "$(date '+%Y-%m-%d %H:%M')" "$action" "$raison" "$resultat" "$rollback" >> "$(dirname "$0")/../journal.md"
}
[[ $EUID -eq 0 ]] || { echo "ERREUR : exécuter en root (sudo)"; exit 1; }
OWNER="${SUDO_USER:-folzo}"
log "Création de /data (dossier sur le LV racine — pas de partition, pas de LUKS)"
mkdir -p /data /data/famille /data/docker
chown "$OWNER":"$OWNER" /data /data/famille /data/docker
chmod 755 /data
chmod 2775 /data/famille
chmod 2775 /data/docker
df -h /data
journal "Espace /data créé (Option 0)" "Données famille sur mono-disque 990 Pro, LUKS abandonné (protection = ufw LAN + restic chiffré)" "/data, /data/famille, /data/docker (owner $OWNER, 2775)" "rmdir /data/famille /data/docker /data"
log "Terminé. Vérif : df -h /data, ls -ld /data /data/famille"
+35
View File
@@ -0,0 +1,35 @@
#!/usr/bin/env bash
#
# 03-docker.sh — T2.3 : Docker + docker compose + groupe docker
# Idempotent. Exécuter en root.
#
# Garde-fous : G7 (rollback), G8 (journalisation).
#
# Rollback : apt-get remove docker.io docker-compose-v2 ; groupe docker supprimé manuellement.
set -euo pipefail
log() { echo -e "\n[docker] $*"; }
journal() {
local action="$1" raison="$2" resultat="$3" rollback="$4"
printf '| %s | 2 | docker : %s | %s | %s | %s |\n' "$(date '+%Y-%m-%d %H:%M')" "$action" "$raison" "$resultat" "$rollback" >> "$(dirname "$0")/../journal.md"
}
[[ $EUID -eq 0 ]] || { echo "ERREUR : exécuter en root (sudo)"; exit 1; }
log "Installation Docker + compose"
apt-get update -qq
apt-get install -y -qq docker.io docker-compose-v2
systemctl enable --now docker
# Utilisateur courant dans le groupe docker (évite sudo pour docker)
USER="${SUDO_USER:-$(whoami)}"
if [[ "$USER" != "root" ]]; then
usermod -aG docker "$USER"
log "Utilisateur '$USER' ajouté au groupe docker (reconnexion SSH requise)"
fi
journal "Docker + compose installés" "Runtime conteneurs du G9" "$USER dans le groupe docker" "apt-get remove docker.io docker-compose-v2"
log "Terminé. Vérif : docker version, docker compose version, docker run --rm hello-world"
+81
View File
@@ -0,0 +1,81 @@
#!/usr/bin/env bash
#
# 03b-secrets.sh — T2.3b : bootstrap des fichiers secrets des briques famille
# Idempotent (ne remplace JAMAIS un secret déjà existant). Exécuter en root.
#
# Garde-fous : G9 (secrets hors git, non loggués), G7 (rollback), G8 (journalisation).
#
# Pourquoi : les compose montent ./secrets:/run/secrets:ro et lisent *_PASSWORD_FILE.
# Rien d'autre ne crée ces fichiers → sans ce script, le premier 'compose up'
# crash-loop sur secret manquant. À exécuter AVANT 07-migrate-stateful.sh (étape 3).
#
# Génère (chmod 644 root:root — voir la note du code : les entrypoints conteneurs ne sont
# pas root, 600 les rendrait illisibles ; le dossier reste hors git, G9 respecté) pour chaque brique :
# nextcloud : dbpass, rootpass
# photoprism : admin, dbpass, rootpass
# vaultwarden : admin
# n8n : dbpass
# gitea : dbpass (backend Postgres — voir compose/gitea)
# (jellyfin, watchtower : pas de secret fichier)
#
# ⚠️ QUELS CREDENTIELS GAGNENT (piège de migration) :
# - INIT FRAÎCHE : ce sont ces secrets générés qui sont utilisés (MARIADB_*/POSTGRES_*
# les seedent au 1er démarrage de la base).
# - IMPORT STATEFUL (07-migrate-stateful.sh) : les env MARIADB_*/POSTGRES_* sont IGNORÉS
# (ils ne seedent qu'une base vierge). La base importée garde les CREDENTIELS
# D'ORIGINE du xpenology, et la config app importée (config.php, storage…) est
# cohérente avec elle. → Les secrets générés ici sont donc INUTILES (dead weight)
# pour une brique importée ; ils ne servent que si la base doit être re-initialisée.
#
# NB : les secrets sont HORS GIT (G9). Ne jamais les logguer ni les commiter.
set -euo pipefail
log() { echo -e "\n[secrets] $*"; }
journal() {
local action="$1" raison="$2" resultat="$3" rollback="$4"
printf '| %s | 2 | secrets : %s | %s | %s | %s |\n' "$(date '+%Y-%m-%d %H:%M')" "$action" "$raison" "$resultat" "$rollback" >> "$(dirname "$0")/../journal.md"
}
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
COMPOSE_ROOT="$(dirname "$SCRIPT_DIR")/compose"
# Génère un secret aléatoire (24 octets base64) dans $1, sans écraser s'il existe.
gen_secret() {
local file="$1"
if [[ -f "$file" ]]; then
log " ⏭ existe déjà : $file (non modifié)"
return 0
fi
umask 077
openssl rand -base64 24 > "$file"
# 644 (root:root) : les entrypoints conteneurs qui lisent /run/secrets/* ne sont
# PAS root (n8n = uid 1000, Nextcloud = www-data) → 600 root = illisible = crash-loop.
# Le dossier reste hors git (G9) ; 644 = compromis lisible par les services.
chmod 644 "$file"
log " ✅ généré : $file"
}
# briques : "brique:s1 s2 s3;..."
declare -A SECRET_MAP=(
[nextcloud]="dbpass rootpass"
[photoprism]="admin dbpass rootpass"
[vaultwarden]="admin"
[n8n]="dbpass"
[gitea]="dbpass"
)
for brique in nextcloud photoprism vaultwarden n8n gitea; do
COMPOSE_DIR="$COMPOSE_ROOT/$brique"
[[ -d "$COMPOSE_DIR" ]] || { echo "ERREUR : $COMPOSE_DIR absent"; exit 1; }
SECRETS_DIR="$COMPOSE_DIR/secrets"
mkdir -p "$SECRETS_DIR"
log "Brique $brique → $SECRETS_DIR"
for s in ${SECRET_MAP[$brique]}; do
gen_secret "$SECRETS_DIR/$s"
done
done
journal "Secrets bootstrap OK" "Briques stateful nécessitent des fichiers secrets" "nextcloud, photoprism, vaultwarden, n8n (chmod 644 root:root, dossier hors git)" "suppression des fichiers secrets générés"
log "Terminé. Vérif : ls -l compose/*/secrets/ (644 root:root, non versionnés — 644 voulu : lus par l'uid du conteneur)."
echo "⚠️ Ces secrets sont HORS GIT (G9). Ne pas les commiter ni les logguer."
+76
View File
@@ -0,0 +1,76 @@
#!/usr/bin/env bash
#
# 04-smb.sh — T2.4 : Partage SMB (données famille) sur le G9
# Idempotent. Exécuter en root.
#
# Garde-fous : G6 (réseau LAN-only), G9 (aucun secret loggué/commité),
# G7 (rollback), G8 (journalisation).
#
# Modèle retenu (10/10/2026 — zéro maintenance par périphérique) :
# accès INVITÉ lecture/écriture (« map to guest »), réservé au LAN (ufw 137-139/445).
# Les écritures sont forcées sur l'utilisateur du G9 (force user) → permissions
# simples, aucun compte à créer par appareil. Un appareil qui exige des
# identifiants accepte n'importe quel utilisateur/mot de passe (mappé en invité).
#
# Partage : /data/famille → //G9/famille
# Rollback : systemctl disable --now smbd nmbd ; suppression du [famille] dans
# /etc/samba/smb.conf ; ufw delete des règles SMB 137/138/139/445
set -euo pipefail
log() { echo -e "\n[smb] $*"; }
journal() {
local action="$1" raison="$2" resultat="$3" rollback="$4"
printf '| %s | 2 | smb : %s | %s | %s | %s |\n' "$(date '+%Y-%m-%d %H:%M')" "$action" "$raison" "$resultat" "$rollback" >> "$(dirname "$0")/../journal.md"
}
[[ $EUID -eq 0 ]] || { echo "ERREUR : exécuter en root (sudo)"; exit 1; }
log "Installation Samba"
DEBIAN_FRONTEND=noninteractive apt-get update -qq
DEBIAN_FRONTEND=noninteractive apt-get install -y -qq samba
OWNER="${SUDO_USER:-folzo}"
# Dossier partagé
mkdir -p /data/famille
chown "$OWNER":"$OWNER" /data/famille
chmod 2775 /data/famille
# Global : accès invité (idempotent)
if ! grep -qiE '^map to guest' /etc/samba/smb.conf; then
sed -ri 's/^\[global\]/[global]\n map to guest = Bad User/' /etc/samba/smb.conf
fi
# Config du partage (idempotent : on n'ajoute [famille] que s'il est absent)
if grep -q '^\[famille\]' /etc/samba/smb.conf; then
log "Partage [famille] déjà présent — on le laisse tel quel (idempotent)."
else
cat >> /etc/samba/smb.conf <<EOF
[famille]
path = /data/famille
browseable = yes
read only = no
guest ok = yes
force user = $OWNER
create mask = 0664
directory mask = 2775
EOF
fi
# Firewall : SMB réservé au LAN (G6) — idempotent
ufw allow from 192.168.1.0/24 to any port 445 proto tcp 2>/dev/null || true
ufw allow from 192.168.1.0/24 to any port 139 proto tcp 2>/dev/null || true
ufw allow from 192.168.1.0/24 to any port 137 proto udp 2>/dev/null || true
ufw allow from 192.168.1.0/24 to any port 138 proto udp 2>/dev/null || true
# Ubuntu >= 24 : « disable netbios = yes » par défaut → nmbd ne démarre pas
# (découverte par nom impossible). L'activer (réservé LAN, filtré par ufw).
sed -ri "s/^([[:space:]]*)disable netbios.*/\1disable netbios = No/I" /etc/samba/smb.conf
systemctl enable --now smbd nmbd
systemctl restart nmbd
journal "Samba installé, partage [famille] (invité LAN)" "Accès SMB données famille sans maintenance par appareil" "partage [famille] → /data/famille, guest ok, force user $OWNER, ufw SMB LAN-only" "systemctl disable --now smbd nmbd ; suppression [famille] ; ufw delete règles SMB"
log "Terminé. Vérif : testparm -s ; smbclient -L //localhost -N ; depuis un poste LAN : \\\\192.168.1.24\\famille"
+50
View File
@@ -0,0 +1,50 @@
#!/usr/bin/env bash
#
# 05-beszel.sh — T2.5 : Agent Beszel sur le G9
# Idempotent. Exécuter en root.
#
# Garde-fous : G7 (rollback), G8 (journalisation), G9 (le token n'est JAMAIS
# loggué ni commité — passé par l'environnement, écrit uniquement dans l'unit
# systemd root-only).
#
# Hub : http://192.168.1.25:8090 (monitoring.folzocorp.fr) — container hub sur .25.
#
# Prérequis : BESZEL_TOKEN = token universel du hub (créé dans le hub :
# /settings/tokens → « nouveau token »). BESZEL_KEY = clé publique du hub
# (affichée dans « Add System ») pour la méthode SSH.
#
# Usage :
# sudo BESZEL_TOKEN=<token> bash 05-beszel.sh
#
# Rollback : /tmp/install-agent.sh -u ; ou systemctl disable --now beszel-agent
set -euo pipefail
log() { echo -e "\n[beszel] $*"; }
journal() {
local action="$1" raison="$2" resultat="$3" rollback="$4"
printf '| %s | 2 | beszel : %s | %s | %s | %s |\n' "$(date '+%Y-%m-%d %H:%M')" "$action" "$raison" "$resultat" "$rollback" >> "$(dirname "$0")/../journal.md"
}
[[ $EUID -eq 0 ]] || { echo "ERREUR : exécuter en root (sudo)"; exit 1; }
BESZEL_HUB="${BESZEL_HUB:-http://192.168.1.25:8090}"
BESZEL_KEY="${BESZEL_KEY:-ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINo6KvTbvXbilsNLqHX+EOZKuZGgpNcfx+c+mbyivBlv}"
BESZEL_VERSION="${BESZEL_VERSION:-v0.21.0}"
[[ -n "${BESZEL_TOKEN:-}" ]] || { echo "ERREUR : BESZEL_TOKEN requis (token universel du hub, /settings/tokens). Rien n'a été installé."; exit 1; }
if systemctl is-active beszel-agent >/dev/null 2>&1; then
log "Agent beszel-agent déjà actif — idempotent, rien à faire."
journal "Agent Beszel déjà actif" "Monitoring du G9" "hub $BESZEL_HUB" "install-agent.sh -u"
exit 0
fi
log "Installation de l'agent Beszel $BESZEL_VERSION (hub: $BESZEL_HUB) — token non loggué"
curl -fsSL https://get.beszel.dev -o /tmp/install-agent.sh
chmod +x /tmp/install-agent.sh
/tmp/install-agent.sh -k "$BESZEL_KEY" -t "$BESZEL_TOKEN" -url "$BESZEL_HUB" -v "$BESZEL_VERSION" --auto-update false
unset BESZEL_TOKEN
journal "Agent Beszel installé ($BESZEL_VERSION)" "Monitoring du G9 (T2.5)" "hub $BESZEL_HUB, agent actif (token non loggué)" "systemctl disable --now beszel-agent ; /tmp/install-agent.sh -u"
log "Terminé. Vérif : systemctl is-active beszel-agent ; journalctl -u beszel-agent -n 5 ; le G9 apparaît dans le hub."
+244
View File
@@ -0,0 +1,244 @@
#!/usr/bin/env bash
#
# 06-restic-backup.sh — T3.1 : restic G9 → Freebox (chiffré, quotidien) + test restauration
# Idempotent. Exécuter en root : sudo bash 06-restic-backup.sh
#
# Transport retenu (10/10/2026, après tests) : **FTP local de la Freebox (port 21)** via le
# backend rclone de restic (`RESTIC_REPOSITORY=rclone:freeboxftp:Freebox/backup/g9`).
#
# Transports testés/écartés + limitations append-only : **source unique = RESTORE-G9.md §6**.
#
# Prérequis :
# - /etc/restic.env (600, root) rempli + /etc/restic/repo.key (600) = mot de passe du repo
# - /root/.config/rclone/rclone.conf (600) = RCLONE_CONFIG : remote [freeboxftp] type=ftp
# host=192.168.1.254 port=21 user=freebox (+ [freebox] type=smb en secours)
# - Serveur FTP de la Freebox activé (Freebox OS → Paramètres → Mode avancé → FTP)
#
# Fait :
# 1. Installe restic (binaire officiel épinglé + sha256) et rclone (backend smb/ftp)
# 2. Vérifie la connectivité FTP vers la Freebox
# 3. Initialise le repo (si absent)
# 4. Premier backup de RESTIC_SOURCES, conteneurs stateful arrêtés (cohérence des bases)
# 5. Append-only 5m APRÈS le premier backup (limitation : enforcement côté client)
# 6. TEST DE RESTAURATION (AC5) sur un fichier réel + comparaison sha256
# 7. Cron : backup 03:30 (stop → backup → start) + prune 04:00 + health 09:15 + alerte Matrix
#
# Rollback : systemctl stop des jobs (crontab -r lignes restic) ; apt-get remove rclone ;
# rm /usr/local/bin/restic ; suppression du repo distant (destructif, manuel).
set -euo pipefail
log() { echo -e "\n[restic] $*"; }
journal() {
local action="$1" raison="$2" resultat="$3" rollback="$4"
printf '| %s | 3 | restic : %s | %s | %s | %s |\n' "$(date '+%Y-%m-%d %H:%M')" "$action" "$raison" "$resultat" "$rollback" >> "${G9_JOURNAL:-/root/g9-migration/journal.md}"
}
ENV_FILE=/etc/restic.env
[[ -f "$ENV_FILE" ]] || { echo "ERREUR : $ENV_FILE absent. Copier configs/restic.env.example et remplir."; exit 1; }
# shellcheck disable=SC1090
set -a; source "$ENV_FILE"; set +a # set -a : exporter, sinon restic/rclone ne voient pas les variables
[[ -n "${RESTIC_REPOSITORY:-}" && -n "${RESTIC_PASSWORD_FILE:-}" ]] || { echo "ERREUR : RESTIC_REPOSITORY / RESTIC_PASSWORD_FILE vides dans $ENV_FILE"; exit 1; }
[[ -f "$RESTIC_PASSWORD_FILE" ]] || { echo "ERREUR : $RESTIC_PASSWORD_FILE introuvable (mot de passe du repo)"; exit 1; }
chmod 600 "$ENV_FILE" "$RESTIC_PASSWORD_FILE"
[[ -n "${RCLONE_CONFIG:-}" && -f "$RCLONE_CONFIG" ]] || { echo "ERREUR : config rclone absente ($RCLONE_CONFIG)"; exit 1; }
chmod 600 "$RCLONE_CONFIG"
RCLONE_BIN=/usr/local/bin/rclone
RESTIC_BIN=/usr/local/bin/restic
STATEFUL="${RESTIC_STATEFUL:-valheim n8n synapse couchdb obsidian}"
AOPT=()
if [[ "${RESTIC_RCLONE_APPENDONLY:-0}" == "1" ]]; then AOPT=(-o "rclone.args=serve restic --stdio --append-only"); fi
log "Installation restic (officiel, épinglé) + rclone"
apt-get update -qq >/dev/null 2>&1 || true
apt-get install -y -qq unzip bzip2 curl jq >/dev/null
RESTIC_VERSION="${RESTIC_VERSION:-0.19.1}"
RESTIC_SHA256="${RESTIC_SHA256:-f415415624dcc452f2a02b8c33641791a8c6d6d3b65bbb3543fcf9a25151585c}"
if ! $RESTIC_BIN version 2>/dev/null | grep -q "$RESTIC_VERSION"; then
tmpd=$(mktemp -d)
( cd "$tmpd" && curl -fsSL -o restic.bz2 "https://github.com/restic/restic/releases/download/v${RESTIC_VERSION}/restic_${RESTIC_VERSION}_linux_amd64.bz2" \
&& printf '%s restic.bz2\n' "$RESTIC_SHA256" | sha256sum -c - \
&& bzip2 -d restic.bz2 && install -m 755 restic "$RESTIC_BIN" ) || { echo "ERREUR : sha256 restic invalide"; exit 1; }
rm -rf "$tmpd"
fi
RCLONE_VERSION="${RCLONE_VERSION:-1.75.2}"
RCLONE_SHA256="${RCLONE_SHA256:-349ac8fba6ff65d6247043f1750cdcb518ec5d500ef91463a10d37c0ccdf3702}"
if ! $RCLONE_BIN version 2>/dev/null | grep -q "v${RCLONE_VERSION}"; then
tmpd=$(mktemp -d)
( cd "$tmpd" \
&& curl -fsSL -o r.zip "https://downloads.rclone.org/v${RCLONE_VERSION}/rclone-v${RCLONE_VERSION}-linux-amd64.zip" \
&& printf '%s r.zip\n' "$RCLONE_SHA256" | sha256sum -c - \
&& unzip -q r.zip && install -m 755 rclone-*-linux-amd64/rclone "$RCLONE_BIN" ) \
|| { echo "ERREUR : sha256 rclone invalide (v${RCLONE_VERSION})"; exit 1; }
rm -rf "$tmpd"
fi
log "restic $($RESTIC_BIN version | head -1) / rclone $($RCLONE_BIN version | head -1)"
$RESTIC_BIN options 2>&1 | grep -q 'rclone.args' || { echo "ERREUR : backend rclone indisponible dans restic"; exit 1; }
EXCLUDES_DEST="${RESTIC_EXCLUDE_FILE:-/etc/restic/excludes.txt}"
if [[ ! -f "$EXCLUDES_DEST" ]]; then
cp "$(dirname "$0")/../configs/excludes.txt.example" "$EXCLUDES_DEST"
chmod 644 "$EXCLUDES_DEST"
log "Installé : $EXCLUDES_DEST (depuis configs/excludes.txt.example)"
fi
log "Connectivité FTP vers la Freebox (${RESTIC_RCLONE_REMOTE:-freeboxftp:})"
$RCLONE_BIN --timeout 30s lsf "${RESTIC_RCLONE_REMOTE:-freeboxftp:}${RESTIC_RCLONE_PATH:-Freebox/backup}" >/dev/null \
|| { echo "ERREUR : FTP Freebox injoignable (serveur FTP activé ? hôte/port dans $RCLONE_CONFIG ?)"; exit 1; }
log "Initialisation du repo : $RESTIC_REPOSITORY"
if ! $RESTIC_BIN -r "$RESTIC_REPOSITORY" snapshots >/dev/null 2>&1; then
$RESTIC_BIN -r "$RESTIC_REPOSITORY" init
$RESTIC_BIN -r "$RESTIC_REPOSITORY" snapshots >/dev/null || { echo "ERREUR : repo illisible après init"; exit 1; }
journal "Repo restic initialisé" "Backup chiffré du G9 (FTP/rclone)" "repo $RESTIC_REPOSITORY, config+clé relues après écriture" "suppression du repo distant (destructif, manuel)"
else
log "Repo déjà initialisé."
fi
if [[ "$($RESTIC_BIN -r "$RESTIC_REPOSITORY" snapshots 2>/dev/null | grep -cE '^[0-9a-f]{8}' || true)" == "0" ]]; then
log "Premier backup de $RESTIC_SOURCES — conteneurs stateful arrêtés (cohérence des bases)"
for c in $STATEFUL; do docker stop -t 20 "$c" >/dev/null 2>&1 && log "stop $c"; done
set +e
$RESTIC_BIN -r "$RESTIC_REPOSITORY" "${AOPT[@]}" backup --exclude-file "${RESTIC_EXCLUDE_FILE:-/etc/restic/excludes.txt}" --tag "${RESTIC_TAG:-g9}" $RESTIC_SOURCES
RC=$?
set -e
for c in $STATEFUL; do docker start "$c" >/dev/null 2>&1 && log "start $c"; done
[[ $RC -eq 0 ]] || { journal "Premier backup ÉCHEC" "Backup chiffré du G9" "rc=$RC" "re-exécuter le script"; echo "ERREUR : backup rc=$RC"; exit 1; }
journal "Premier backup OK" "Backup chiffré du G9 (G2)" "snapshot créé (sources $RESTIC_SOURCES), conteneurs stoppés puis relancés" "restic forget du snapshot + prune"
else
log "Snapshots déjà présents : premier backup sauté."
fi
log "Append-only (après le 1er backup) : rclone serve restic --append-only"
# restic >= 0.18 n'a plus de sous-commande `set` ; l'append-only s'obtient par le backend rclone
# (`rclone serve restic --stdio --append-only` refuse la suppression des objets du repo).
# Limitation assumée : enforcement par le process rclone local (le G9), pas par la Freebox.
APPENDONLY_ARGS=(-o "rclone.args=serve restic --stdio --append-only")
SCRATCH=/tmp/restic-appendonly-test
SCRATCH_REPO="${SCRATCH_REPO:-rclone:${RESTIC_RCLONE_REMOTE}Freebox/backup/_appendonly-test}"
mkdir -p "$SCRATCH"
echo "appendonly-proof-$(date -Is)" > "$SCRATCH/canary.txt"
set +e
$RESTIC_BIN -r "$SCRATCH_REPO" init >/dev/null 2>&1
$RESTIC_BIN -r "$SCRATCH_REPO" backup "$SCRATCH" >/dev/null 2>&1
$RESTIC_BIN -r "$SCRATCH_REPO" "${APPENDONLY_ARGS[@]}" forget --prune --keep-last 1 --force > "$SCRATCH/forget.log" 2>&1
RC_FORGET=$?
$RESTIC_BIN -r "$SCRATCH_REPO" snapshots >/dev/null 2>&1
RC_SNAP=$?
set -e
if [[ $RC_FORGET -ne 0 && $RC_SNAP -eq 0 ]]; then
journal "Append-only vérifié" "Anti-ransomware (G2)" "forget --prune REFUSÉ par rclone --append-only (rc=$RC_FORGET), snapshots toujours lisibles ; jobs backup utilisés avec cet argument" "retirer --append-only de RESTIC_RCLONE_APPENDONLY"
log "Append-only vérifié : suppression refusée (rc=$RC_FORGET), repo intact"
else
journal "Append-only NON vérifié" "Anti-ransomware (G2)" "rc_forget=$RC_FORGET rc_snap=$RC_SNAP — comportement inattendu" "voir journal"
log "AVERTISSEMENT : append-only non confirmé (rc_forget=$RC_FORGET)"
fi
rm -rf "$SCRATCH"
$RCLONE_BIN purge "$SCRATCH_REPO" >/dev/null 2>&1 || true
log "TEST DE RESTAURATION (AC5)"
RESTORE_DIR=$(mktemp -d /restore-test-XXXXXXXX)
# Fichier test = premier fichier > 1k des sources (déterministe ; `restic ls` n'expose pas
# la taille dans une colonne exploitable par awk, la branche `ls` était morte).
TEST_FILE=$(find $RESTIC_SOURCES -type f -size +1k 2>/dev/null | head -n 1 || true)
[[ -n "$TEST_FILE" ]] || { echo "ERREUR : aucun fichier test trouvé"; exit 1; }
RESTIC_PATH="$TEST_FILE"
$RESTIC_BIN -r "$RESTIC_REPOSITORY" restore latest --target "$RESTORE_DIR" --include "$RESTIC_PATH"
SRC_SHA=$(sha256sum "$TEST_FILE" | cut -d' ' -f1)
DST_SHA=$(sha256sum "$RESTORE_DIR/$RESTIC_PATH" 2>/dev/null | cut -d' ' -f1)
if [[ -f "$RESTORE_DIR/$RESTIC_PATH" ]] && cmp -s "$TEST_FILE" "$RESTORE_DIR/$RESTIC_PATH" && [[ "$SRC_SHA" == "$DST_SHA" ]]; then
log "Restauration testée OK : $TEST_FILE (sha256 ${SRC_SHA:0:12}… identique)"
journal "Test restauration OK" "Vérification G2 (restauration testée)" "$TEST_FILE restauré depuis la Freebox, sha256 identique" "—"
else
log "ÉCHEC restauration : $TEST_FILE (src ${SRC_SHA:0:12}… / dst ${DST_SHA:0:12}…)"
journal "Test restauration ÉCHEC" "Vérification G2" "fichier restauré différent de $TEST_FILE" "re-brancher le backup"
rm -rf "$RESTORE_DIR"; exit 1
fi
rm -rf "$RESTORE_DIR"
log "Jobs quotidiens + alerte Matrix"
install -m 755 "$(dirname "$0")/restic-notify.sh" /usr/local/bin/restic-notify.sh
cat > /usr/local/bin/restic-daily.sh <<'EOS'
#!/usr/bin/env bash
set -euo pipefail
set -a; source /etc/restic.env; set +a
RESTIC_BIN=/usr/local/bin/restic
RCLONE_BIN=/usr/local/bin/rclone
STATEFUL="${RESTIC_STATEFUL:-valheim n8n synapse couchdb obsidian}"
AOPT=()
if [[ "${RESTIC_RCLONE_APPENDONLY:-0}" == "1" ]]; then AOPT=(-o "rclone.args=serve restic --stdio --append-only"); fi
if ! $RCLONE_BIN --timeout 30s lsf "${RESTIC_RCLONE_REMOTE:-freeboxftp:}${RESTIC_RCLONE_PATH:-Freebox/backup}" >/dev/null 2>&1; then
/usr/local/bin/restic-notify.sh "BACKUP KO : FTP Freebox injoignable depuis le G9"
exit 1
fi
$RESTIC_BIN -r "$RESTIC_REPOSITORY" unlock >/dev/null 2>&1 || true
for c in $STATEFUL; do docker stop -t 20 "$c" >/dev/null 2>&1 || true; done
set +e
$RESTIC_BIN -r "$RESTIC_REPOSITORY" "${AOPT[@]}" backup --exclude-file "${RESTIC_EXCLUDE_FILE:-/etc/restic/excludes.txt}" --tag "${RESTIC_TAG:-g9}" $RESTIC_SOURCES
RC=$?
set -e
for c in $STATEFUL; do docker start "$c" >/dev/null 2>&1 || true; done
if [[ $RC -ne 0 ]]; then
/usr/local/bin/restic-notify.sh "BACKUP KO : restic a échoué (rc=$RC) sur le G9"
exit $RC
fi
EOS
chmod 755 /usr/local/bin/restic-daily.sh
cat > /usr/local/bin/restic-prune.sh <<'EOS'
#!/usr/bin/env bash
set -euo pipefail
set -a; source /etc/restic.env; set +a
RESTIC_BIN=/usr/local/bin/restic
$RESTIC_BIN -r "$RESTIC_REPOSITORY" unlock >/dev/null 2>&1 || true
AOPT=()
if [[ "${RESTIC_RCLONE_APPENDONLY:-0}" == "1" ]]; then AOPT=(-o "rclone.args=serve restic --stdio --append-only"); fi
RETAIN=(--keep-daily "${RESTIC_RETAIN_DAILY:-7}" --keep-weekly "${RESTIC_RETAIN_WEEKLY:-4}" \
--keep-monthly "${RESTIC_RETAIN_MONTHLY:-12}" --keep-yearly "${RESTIC_RETAIN_YEARLY:-5}")
# Le refus d'append-only est le comportement ATTENDU : log seul, pas d'alerte (sinon alerte
# quotidienne = fatigue du canal où arrivent les vrais « BACKUP KO »).
if ! $RESTIC_BIN -r "$RESTIC_REPOSITORY" "${AOPT[@]}" forget --prune "${RETAIN[@]}"; then
echo "$(date -Is) append-only : suppression des objets refusée (attendu), purge sans --append-only"
if ! $RESTIC_BIN -r "$RESTIC_REPOSITORY" forget --prune "${RETAIN[@]}"; then
/usr/local/bin/restic-notify.sh "PURGE KO : la purge a échoué même sans --append-only (rétention bloquée)" || true
exit 1
fi
fi
EOS
chmod 755 /usr/local/bin/restic-prune.sh
cat > /usr/local/bin/restic-health.sh <<'EOS'
#!/usr/bin/env bash
# Le dernier snapshot doit dater de moins de 26 h.
set -euo pipefail
set -a; source /etc/restic.env; set +a
RESTIC_BIN=/usr/local/bin/restic
LAST=$($RESTIC_BIN -r "$RESTIC_REPOSITORY" snapshots --json 2>/dev/null | grep -o '"time":"[^"]*"' | head -1 | cut -d'"' -f4 || true)
AGE=$(( $(date +%s) - $(date -d "${LAST:-1970-01-01}" +%s) ))
if [[ $AGE -gt $((26*3600)) ]]; then
/usr/local/bin/restic-notify.sh "BACKUP KO : aucun snapshot depuis $((AGE/3600)) h sur le G9"
echo "KO: dernier snapshot = ${LAST:-aucun}"
exit 1
fi
echo "OK: dernier snapshot = $LAST"
EOS
chmod 755 /usr/local/bin/restic-health.sh
BACKUP_LINE="30 3 * * * /usr/local/bin/restic-daily.sh >> /var/log/restic-daily.log 2>&1"
PRUNE_LINE="0 4 * * * /usr/local/bin/restic-prune.sh >> /var/log/restic-prune.log 2>&1"
HEALTH_LINE="15 9 * * * /usr/local/bin/restic-health.sh >> /var/log/restic-health.log 2>&1"
cron_tmp=$(mktemp)
{ crontab -l 2>/dev/null | grep -vE 'restic-daily\.sh|restic-prune\.sh|restic-health\.sh' || true; echo "$BACKUP_LINE"; echo "$PRUNE_LINE"; echo "$HEALTH_LINE"; } > "$cron_tmp"
if [[ "$(grep -c 'restic-' "$cron_tmp")" -ne 3 ]]; then
rm -f "$cron_tmp"
echo "ERREUR : crontab refusé (3 lignes attendues) — crontab existant laissé intact"
exit 1
fi
crontab "$cron_tmp"
rm -f "$cron_tmp"
journal "Cron restic programmé" "Backup quotidien + visibilité" "backup 03:30 (stop/backup/start) + prune 04:00 + health 09:15" "suppression des 3 lignes crontab"
log "Terminé. Vérif : $RESTIC_BIN -r $RESTIC_REPOSITORY snapshots ; check ; stats"
+83
View File
@@ -0,0 +1,83 @@
#!/usr/bin/env bash
#
# 06b-usb-media.sh — T6.1 : montage des disques USB média → /mnt/usb-media
# Prérequis MANUEL avant Jellyfin (T6.2). Exécuter en root.
#
# Garde-fous : G3 (formatage = validation explicite), G7 (rollback), G8 (journalisation).
#
# Pourquoi ce script est interactif : les disques USB (15 To + 5,5 To) ont des
# noms de device variables (sdb, sdc, sdd…) selon l'ordre de branchement. On ne
# peut PAS les détecter automatiquement en toute sécurité → l'utilisateur
# sélectionne les device à formater/monter.
#
# Fait :
# 1. Crée /mnt/usb-media
# 2. Affiche les disques USB détectés (lsblk)
# 3. Demande le device de CHAQUE disque média
# 4. (Si vierge) propose de formater en ext4 — garde-fou G3
# 5. Ajoute les entrées fstab (nofail) + montage
#
# ⚠️ OPÉRATION POTENTIELLEMENT DESTRUCTIVE (formatage) → bloqué sans --i-understand.
set -euo pipefail
log() { echo -e "\n[usb-media] $*"; }
die() { echo -e "\n[ERREUR] $*" >&2; exit 1; }
journal() {
local action="$1" raison="$2" resultat="$3" rollback="$4"
printf '| %s | 6 | usb-media : %s | %s | %s | %s |\n' "$(date '+%Y-%m-%d %H:%M')" "$action" "$raison" "$resultat" "$rollback" >> "$(dirname "$0")/../journal.md"
}
MOUNT_ROOT=/mnt/usb-media
mkdir -p "$MOUNT_ROOT"
log "Disques détectés :"
lsblk -o NAME,SIZE,TYPE,FSTYPE,MODEL | grep -vE 'rom|loop' || true
echo
read -r -p "Nom du 1er disque média (ex. sdb) : " DISK1
read -r -p "Nom du 2e disque média (ex. sdc) : " DISK2
[[ -n "$DISK1" && -n "$DISK2" ]] || die "Deux disques requis."
for DISK in "$DISK1" "$DISK2"; do
DEV="/dev/${DISK}"
[[ -b "$DEV" ]] || die "Device introuvable : $DEV"
if lsblk -no MOUNTPOINT "$DEV" | grep -q .; then
die "$DEV a déjà des partitions montées. Abandon."
fi
FSTYPE=$(blkid -o value -s TYPE "$DEV" 2>/dev/null || true)
if [[ -z "$FSTYPE" ]]; then
[[ "${1:-}" == "--i-understand" ]] || die "$DEV est vierge (formatage requis). Relancer avec --i-understand."
read -r -p "CONFIRMER le formatage ext4 de $DEV (efface tout) : saisir $DEV : " CONF
[[ "$CONF" == "$DEV" ]] || die "Non confirmé. Abandon."
mkfs.ext4 -F "$DEV"
FSTYPE=ext4
fi
# Le fstab doit porter le VRAI type de fs (pas ext4 en dur) : les disques média
# sont souvent NTFS/exFAT. Un mauvais type + nofail = montage silencieux qui échoue.
case "$FSTYPE" in
ext4|xfs|btrfs|ntfs|exfat|vfat) ;;
*) die "$DEV : système de fichiers '$FSTYPE' non supporté pour le montage média. Abandon." ;;
esac
LABEL=$(echo "$DISK" | tr -d '/')
UUID=$(blkid -s UUID -o value "$DEV")
MP="$MOUNT_ROOT/$LABEL"
mkdir -p "$MP"
# fsck (pass 2) ne sait vérifier que ext2/3/4 → 0 pour ntfs/exfat/vfat/xfs.
case "$FSTYPE" in
ext2|ext3|ext4) FSCK=2 ;;
*) FSCK=0 ;;
esac
if ! grep -qE "^[^#]* $MP " /etc/fstab; then
echo "UUID=${UUID} $MP $FSTYPE defaults,noatime,nofail 0 $FSCK" >> /etc/fstab
fi
mount "$MP"
log "Monté : $DEV → $MP ($FSTYPE)"
done
journal "USB média montés" "Jellyfin (T6.2) lit les médias" "$MOUNT_ROOT (disques $DISK1, $DISK2)" "suppression des entrées fstab + umount"
log "Terminé. Jellyfin (T6.2) peut maintenant monter $MOUNT_ROOT."
echo "⚠️ Redémarrer pour valider le montage au boot (fstab nofail)."
@@ -0,0 +1,40 @@
#!/usr/bin/env bash
# 06c-postboot-check.sh — vérifie la persistance du backup restic après reboot du G9.
# Lecture seule : ne modifie rien. Exécuter en root : sudo bash 06c-postboot-check.sh
set -uo pipefail
ok=0; ko=0
say() { printf '%-46s %s\n' "$1" "$2"; }
chk() { if eval "$2" >/dev/null 2>&1; then say "$1" "OK"; ok=$((ok+1)); else say "$1" "KO"; ko=$((ko+1)); fi; }
chk "binaire restic (/usr/local/bin/restic)" "test -x /usr/local/bin/restic"
chk "binaire rclone (/usr/local/bin/rclone)" "test -x /usr/local/bin/rclone"
chk "/etc/restic.env présent et 600" "test \$(stat -c %a /etc/restic.env) = 600"
chk "clé repo /etc/restic/repo.key 600" "test \$(stat -c %a /etc/restic/repo.key) = 600"
chk "config rclone 600" "test \$(stat -c %a /root/.config/rclone/rclone.conf) = 600"
chk "aucun montage CIFS requis (fstab)" "! grep -q cifs /etc/fstab"
chk "cron root : 3 lignes restic" "test \$(crontab -l | grep -c 'restic-') = 3"
chk "job quotidien exécutable" "test -x /usr/local/bin/restic-daily.sh"
chk "job prune exécutable" "test -x /usr/local/bin/restic-prune.sh"
chk "job health exécutable" "test -x /usr/local/bin/restic-health.sh"
chk "job notify exécutable" "test -x /usr/local/bin/restic-notify.sh"
chk "jq présent (payload de l'alerte)" "command -v jq"
set -a; . /etc/restic.env; set +a
chk "repo accessible (restic snapshots)" "restic -r \"\$RESTIC_REPOSITORY\" snapshots"
chk "repo intègre (restic check)" "restic -r \"\$RESTIC_REPOSITORY\" check --read-data-subset=1%"
chk "dernier snapshot < 26 h" "/usr/local/bin/restic-health.sh"
chk "fichier d'exclusions présent" "test -s \"${RESTIC_EXCLUDE_FILE:-/etc/restic/excludes.txt}\""
# Liste stateful lue depuis /etc/restic.env (source unique, pas de liste en dur ici).
RUNNING=$(docker ps --format '{{.Names}}')
stateful_up=0; stateful_total=0
for c in ${RESTIC_STATEFUL:-}; do
stateful_total=$((stateful_total+1))
grep -qx "$c" <<<"$RUNNING" && stateful_up=$((stateful_up+1))
done
chk "conteneurs stateful relancés (${stateful_up}/${stateful_total})" "test $stateful_total -gt 0 -a $stateful_up -eq $stateful_total"
echo
echo "persistance post-reboot : $ok OK / $ko KO"
[[ $ko -eq 0 ]] || exit 1
+209
View File
@@ -0,0 +1,209 @@
#!/usr/bin/env bash
#
# 07-migrate-stateful.sh — T4.x : export/import des stateful (xpenology → G9)
#
# Garde-fous : G4 (double-run, xpenology = source de vérité, lecture seule), G7 (rollback), G8 (journalisation).
#
# Principe (garde-fou G4 — double-run) :
# 1. EXPORT des données depuis le xpenology (source de vérité) → copie sur le G9.
# 2. VÉRIFICATION D'INTÉGRITÉ fichier par fichier (manifeste sha256 trié, diff).
# 3. Le xpenology n'est JAMAIS modifié/supprimé ici — lecture seule.
# 4. Chaque brique est validée sur le G9 AVANT que le xpenology soit décommissionné (T6.4).
#
# Usage :
# ./07-migrate-stateful.sh <brique>
# briques : vaultwarden | nextcloud | photoprism | gitea | n8n
#
# Prérequis :
# - Accès SSH au xpenology : ssh -p 222 Master@192.168.1.25 (clé SSH + sudo NOPASSWD —
# compte `effi` bloqué par Synology depuis 10/2026, voir journal T1.1-b).
# - Sur le G9 : /data monté, Docker opérationnel.
# - Secrets bootstrap (03b-secrets.sh) déjà exécuté.
#
# ⚠️ COHÉRENCE DES BASES (G4) : le rsync d'une base VIVANTE (MariaDB/Postgres en
# service sur .25) produit une copie incohérente et un manifeste sha256 qui
# change pendant le transfert. Le script STOPPE donc les conteneurs source
# (XP_CONTAINERS) avant l'export et les REDÉMARRE juste après — le double-run
# G4 est respecté (la source reprend du service, rien n'est supprimé).
#
# ⚠️ CREDENTIELS DB : l'import est une COPIE de la base existante. Les env
# MARIADB_*/POSTGRES_* (seedés par 03b-secrets.sh) sont IGNORÉS sur une base
# déjà initialisée → la base importée garde les CREDENTIELS D'ORIGINE du
# xpenology, et la config app importée est cohérente avec elle. Le service
# G9 démarre donc avec les identifiants d'origine, PAS les secrets générés.
#
# ⚠️⚠️ À CONFIRMER PENDANT L'INVENTAIRE T1.2 ⚠️⚠️
# Les chemins sources xpenology (XP_MAP) sont des HYPOTHÈSES. Les conteneurs
# stateful ont été créés via la GUI DSM (snapshot ETAT-DE-LART-2026-10-06 §1),
# donc leur arborescence réelle n'est pas garantie. AVANT d'exécuter, vérifier
# sur le xpenology que chaque chemin source existe et contient les bonnes données,
# puis ajuster XP_MAP si besoin. Le script refuse de migrer un chemin source absent.
#
# Rollback : la migration est une COPIE → rien n'est perdu. Sur le G9, supprimer
# le conteneur + les sous-dossiers importés dans compose/<brique>/ si l'import est inacceptable.
set -euo pipefail
log() { echo -e "\n[migrate] $*"; }
journal() {
local action="$1" raison="$2" resultat="$3" rollback="$4"
printf '| %s | 4 | migrate:%s : %s | %s | %s | %s |\n' "$(date '+%Y-%m-%d %H:%M')" "$BRIQUE" "$action" "$raison" "$resultat" "$rollback" >> "$(dirname "$0")/../journal.md"
}
BRIQUE="${1:-}"
[[ -n "$BRIQUE" ]] || { echo "Usage : ./07-migrate-stateful.sh <vaultwarden|nextcloud|photoprism|gitea|n8n>"; exit 1; }
# --- Coordonnées xpenology (lecture seule) ---
XP_USER="Master"
XP_HOST="192.168.1.25"
XP_PORT=222
XP_DOCKER="/var/packages/ContainerManager/target/usr/bin/docker"
XP_SSH="ssh -p $XP_PORT -o ControlMaster=auto -o ControlPath=/tmp/.g9-ctl-%r@%h:%p -o ControlPersist=600 $XP_USER@$XP_HOST"
# Pré-test rapide : connexion SSH + rsync présent sur le xpenology (échec franc avant transfert).
if ! $XP_SSH "command -v rsync >/dev/null 2>&1" 2>/dev/null; then
echo "ERREUR : connexion SSH impossible vers $XP_USER@$XP_HOST:$XP_PORT ou rsync absent du NAS."
echo "Vérifiez les accès SSH et la présence de rsync côté xpenology avant de relancer."
exit 1
fi
# --- Racine des compose G9 ---
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
COMPOSE_ROOT="$(dirname "$SCRIPT_DIR")/compose"
COMPOSE_DIR="$COMPOSE_ROOT/$BRIQUE"
[[ -d "$COMPOSE_DIR" ]] || { echo "ERREUR : $COMPOSE_DIR absent"; exit 1; }
# --- Mapping source (xpenology) → sous-dossier compose (G9) ---
# Format par brique : "xp_dir:g9_subdir;xp_dir:g9_subdir;..."
# g9_subdir est relatif à compose/<brique>/ (il correspond aux volumes relatifs ./data, ./db, ...).
# ⚠️ Chemins sources = HYPOTHÈSES à confirmer à l'inventaire T1.2.
declare -A XP_MAP=(
[vaultwarden]="/volume1/docker/vaultwarden:data"
[nextcloud]="/volume1/docker/nextcloud/data:data;/volume1/docker/nextcloud/config:config;/volume1/docker/nextcloud_db/db:db"
[photoprism]="/volume1/docker/photoprism/storage:storage;/volume1/docker/photoprism_db/db:db"
[gitea]="/volume1/docker/gitea:data"
[n8n]="/volume1/docker/n8n/data:data;/volume1/docker/n8n_db/db:db"
)
# Conteneurs SOURCE à stopper sur .25 pendant l'export (cohérence des bases),
# redémarrés immédiatement après (double-run G4). Noms = inventaire T1.1-a
# (conteneurs-xpenology.csv) — à ajuster si le nom réel diffère.
declare -A XP_CONTAINERS=(
[vaultwarden]="vaultwarden-server-1"
[nextcloud]="nextcloud_app nextcloud_db"
[photoprism]="photoprism photoprism_db"
[gitea]="gitea"
[n8n]="n8n"
)
# Photoprism lit les originaux IN-PLACE (pas de migration) : /data/famille/photos doit
# déjà être en place sur le G9 (T2.2/T2.4). Seuls storage + db sont migrés.
if [[ "$BRIQUE" == "photoprism" ]]; then
log "⚠️ PhotoPrism : les originaux sont lus IN-PLACE sur /data/famille/photos (non migrés)."
log " Vérifiez que /data/famille/photos est bien en place sur le G9 avant 'compose up'."
fi
XP_MAP_LINE="${XP_MAP[$BRIQUE]:-}"
[[ -n "$XP_MAP_LINE" ]] || { echo "ERREUR : mapping inconnu pour $BRIQUE (compléter XP_MAP)"; exit 1; }
# Décomposer le mapping en paires (xp_dir, g9_subdir)
IFS=';' read -ra PAIRS <<< "$XP_MAP_LINE"
XP_DIRS=()
G9_SUBS=()
for pair in "${PAIRS[@]}"; do
[[ -n "$pair" ]] || continue
XP_DIRS+=("${pair%%:*}")
G9_SUBS+=("${pair##*:}")
done
log "=== ÉTAPE 1/3 : VÉRIFICATION DES CHEMINS SOURCES (xpenology, lecture seule) ==="
MISSING=0
for i in "${!XP_DIRS[@]}"; do
if $XP_SSH "test -d '${XP_DIRS[$i]}'" 2>/dev/null; then
log " ✅ source présente : ${XP_DIRS[$i]} → compose/$BRIQUE/${G9_SUBS[$i]}/"
else
log " ❌ source ABSENTE : ${XP_DIRS[$i]} (attendu → compose/$BRIQUE/${G9_SUBS[$i]}/)"
MISSING=1
fi
done
if [[ $MISSING -eq 1 ]]; then
echo
echo "ERREUR : au moins un chemin source xpenology est absent."
echo "Les chemins de XP_MAP sont des hypothèses (conteneurs créés via GUI DSM)."
echo "Vérifiez l'arborescence réelle sur le xpenology (inventaire T1.2) et corrigez XP_MAP."
exit 1
fi
log "=== ÉTAPE 2/3 : TRANSFERT (rsync, chiffré via SSH) + INTÉGRITÉ fichier par fichier ==="
# --- Stop de la source (cohérence des bases vivantes) ---
STOPPED=()
for c in ${XP_CONTAINERS[$BRIQUE]:-}; do
if $XP_SSH "sudo $XP_DOCKER ps -q -f name=^${c}\$" 2>/dev/null | grep -q .; then
if $XP_SSH "sudo $XP_DOCKER stop $c" >/dev/null 2>&1; then
STOPPED+=("$c")
log " ⏸ source stoppée : $c"
else
log " ⚠️ échec du stop de $c — export potentiellement incohérent, À VÉRIFIER."
fi
else
log " ⚠️ conteneur '$c' non trouvé running sur .25 — vérifier l'inventaire (cohérence non garantie)."
fi
done
start_source() {
for c in "${STOPPED[@]:-}"; do
$XP_SSH "sudo $XP_DOCKER start $c" >/dev/null 2>&1 && log " ▶ source redémarrée : $c" || log " ⚠️ ÉCHEC du redémarrage de $c — INTERVENIR SUR .25."
done
}
trap start_source EXIT
TOTAL_FILES=0
for i in "${!XP_DIRS[@]}"; do
XP_DIR="${XP_DIRS[$i]}"
G9_SUB="${G9_SUBS[$i]}"
G9_DIR="$COMPOSE_DIR/$G9_SUB"
mkdir -p "$G9_DIR"
log "Transfert : $XP_DIR/ → $G9_DIR/"
rsync -a --info=progress2 -e "ssh -p $XP_PORT -o ControlMaster=auto -o ControlPath=/tmp/.g9-ctl-%r@%h:%p -o ControlPersist=600" "$XP_USER@$XP_HOST:$XP_DIR/" "$G9_DIR/"
# Manifeste sha256 trié, chemins RELATIFS (identiques des deux côtés) → diff fiable.
log "Intégrité : manifeste sha256 (chemins relatifs, triés) pour $G9_SUB"
REMOTE_MANIFEST=$(mktemp)
LOCAL_MANIFEST=$(mktemp)
$XP_SSH "cd '$XP_DIR' && find . -type f -exec sha256sum {} + 2>/dev/null | sort" > "$REMOTE_MANIFEST"
( cd "$G9_DIR" && find . -type f -exec sha256sum {} + 2>/dev/null | sort ) > "$LOCAL_MANIFEST"
R_COUNT=$(wc -l < "$REMOTE_MANIFEST")
L_COUNT=$(wc -l < "$LOCAL_MANIFEST")
TOTAL_FILES=$(( TOTAL_FILES + R_COUNT ))
log " fichiers : source=$R_COUNT local=$L_COUNT"
if diff -u "$REMOTE_MANIFEST" "$LOCAL_MANIFEST" > "${REMOTE_MANIFEST}.diff"; then
log " ✅ intégrité OK ($G9_SUB)"
else
log " ❌ ÉCHEC intégrité ($G9_SUB) — différences :"
head -n 50 "${REMOTE_MANIFEST}.diff"
rm -f "$REMOTE_MANIFEST" "$LOCAL_MANIFEST" "${REMOTE_MANIFEST}.diff"
journal "import ÉCHEC intégrité sur $G9_SUB" "Vérification G4 (intégrité)" "manifeste sha256 différent" "supprimer le sous-dossier importé, re-transférer"
echo "ERREUR : l'intégrité a échoué sur $G9_SUB. Ne pas lancer le service. Re-vérifier le transfert."
exit 1
fi
rm -f "$REMOTE_MANIFEST" "$LOCAL_MANIFEST" "${REMOTE_MANIFEST}.diff"
done
log "Intégrité globale OK : $TOTAL_FILES fichiers vérifiés."
journal "export+import OK" "Migration G4 (copie, xpenology non modifié)" "$TOTAL_FILES fichiers, manifeste sha256 identique" "suppression du conteneur + sous-dossiers importés"
# --- Redémarrage de la source AVANT de toucher au G9 (double-run G4) ---
start_source
log "=== ÉTAPE 3/3 : LANCEMENT DU SERVICE SUR LE G9 ==="
log "docker compose up -d dans $COMPOSE_DIR"
docker compose -f "$COMPOSE_DIR/docker-compose.yml" up -d
log "Vérification du service (healthcheck)"
sleep 10
docker compose -f "$COMPOSE_DIR/docker-compose.yml" ps
log "Terminé. VALIDER MANUELLEMENT le service (login, 1 fichier, 1 recherche) AVANT de passer à la brique suivante."
journal "service lancé sur le G9" "Double-run G4" "compose up -d, healthcheck à valider" "docker compose down + suppression des données importées"
+166
View File
@@ -0,0 +1,166 @@
#!/usr/bin/env bash
#
# 07a-migrate-gitea.sh — T4.4 : migration Gitea (xpenology .25 → G9 .24)
#
# Garde-fous : G4 (double-run, .25 = source de vérité, LECTURE SEULE), G2 (backup restic
# déclenché avant import), G7 (rollback), G8 (journalisation), G9 (aucun secret loggué).
#
# SOURCE RÉELLE (mesurée le 2026-10-11, voir C-T4-INV) : le Gitea familial est le **paquet
# App Center DSM « gitea » v1.27.3** (compte sc-gitea), PAS le conteneur compose :
# - DB : postgres du conteneur partagé `postgres16` (127.0.0.1:5433), base `gitea_db`,
# rôle `gitea_folzo` → 13 repositories, 3 users
# - dépôts : /volume2/gitea/gitea-repositories (155 Mo)
# - LFS : /volume2/gitea/lfs
# - appdata : /volume2/@appdata/gitea/data (avatars, attachments, jwt, sessions, indexers)
# - conf : /volume2/@appdata/gitea/conf.ini (ROOT = /volume2/gitea/gitea-repositories)
# Le conteneur `gitea` de DSM (compose, ROOT = /data/git/repositories VIDE) partage la MÊME base
# postgres : second frontend, dépôts vides → il n'est PAS la source.
#
# COHÉRENCE (G4) : pg_dump = instantané cohérent (MVCC) → AUCUN stop, AUCUNE écriture,
# AUCUNE suppression sur .25. Les dépôts git (objets) sont quiets.
#
# TRANSIT : **tar sur SSH** (.25 → stage .11 → .24). Mesuré le 2026-10-11 : le sshd de DSM
# refuse `rsync --server` (« Permission denied, please try again. ») et scp (subsystem sftp
# internal-sftp) → `subsystem request failed`. Seuls `ssh 'sudo tar cz'` et `ssh 'sudo cat'`
# fonctionnent. Le stage local est un cache, purgé à la fin.
#
# Usage (machine de pilotage détenant les clés SSH, ex. PC IA .11) : ./07a-migrate-gitea.sh
# Prérequis : clés SSH Master@.25:222 + folzo@.24 ; 03b-secrets.sh exécuté sur le G9.
#
# Rollback : migration = COPIE. G9 : `docker compose -f /data/docker/gitea/docker-compose.yml
# down` + suppression de /data/docker/gitea. .25 : rien (intact).
set -euo pipefail
XP_USER="Master"; XP_HOST="192.168.1.25"; XP_PORT=222
XP_DOCKER="/var/packages/ContainerManager/target/usr/bin/docker"
G9_USER="folzo"; G9_HOST="192.168.1.24"
KEY="${SSH_KEY:-$HOME/.ssh/id_ed25519_openfox11}"
BRIQUE="gitea"
G9_DIR="/data/docker/$BRIQUE"
KIT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
KIT_ON_G9="/home/folzo/g9-migration"
JOURNAL="$KIT_DIR/journal.md"
STAGE="${STAGE:-$HOME/migration-staging/$BRIQUE}"
SSH_XP="ssh -p $XP_PORT -i $KEY -o ControlMaster=auto -o ControlPath=/tmp/.g9-ctl-%r@%h:%p -o ControlPersist=600 $XP_USER@$XP_HOST"
SSH_G9="ssh -i $KEY -o ControlMaster=auto -o ControlPath=/tmp/.g9-ctl-%r@%h:%p -o ControlPersist=600 $G9_USER@$G9_HOST"
RSH_XP="ssh -p $XP_PORT -i $KEY"
RSH_G9="ssh -i $KEY"
log() { echo -e "\n[gitea] $*"; }
journal() {
printf '| %s | 4 | migrate:%s : %s | %s | %s | %s |\n' \
"$(date '+%Y-%m-%d %H:%M')" "$BRIQUE" "$1" "$2" "$3" "$4" >> "$JOURNAL"
}
manifest_in() { ( cd "$1" && find . -type f -exec sha256sum {} + 2>/dev/null | LC_ALL=C sort ); }
# ---------------------------------------------------------------- 0. préflight
log "=== 0/5 PRÉFLIGHT ==="
$SSH_XP "command -v rsync >/dev/null && echo XP_OK" >/dev/null || { echo "ERREUR : SSH/rsync .25 indisponibles"; exit 1; }
$SSH_G9 "command -v rsync >/dev/null && command -v docker >/dev/null && echo G9_OK" >/dev/null || { echo "ERREUR : SSH/rsync/docker .24 indisponibles"; exit 1; }
$SSH_XP "command -v tar >/dev/null && sudo tar --version >/dev/null" || { echo "ERREUR : SSH/tar(sudo) indisponibles sur .25"; exit 1; }
for d in /volume2/gitea/gitea-repositories /volume2/gitea/lfs /volume2/@appdata/gitea/data; do
$SSH_XP "sudo test -d '$d'" >/dev/null || { echo "ERREUR : source absente sur .25 : $d"; exit 1; }
done
$SSH_G9 "test -d $KIT_ON_G9/compose && command -v rsync >/dev/null" || {
echo "ERREUR : kit absent sur le G9 ($KIT_ON_G9) — synchroniser le kit avant de migrer."; exit 1; }
mkdir -p "$STAGE"
# ---------------------------------------------------------------- 1. backup G2
log "=== 1/5 BACKUP restic (G2) ==="
$SSH_G9 "sudo /usr/local/bin/restic-daily.sh >/dev/null 2>&1; sudo restic snapshots 2>/dev/null | tail -2"
journal "backup restic pré-migration" "Garde-fou G2 avant import Gitea" "snapshot restic Freebox (voir logs /var/log/restic-daily.log)" "restic forget <snapshot> && restic prune"
# ---------------------------------------------------------------- 2. export .25
log "=== 2/5 EXPORT pg_dump depuis .25 (lecture seule, MVCC, sans stop) ==="
STAMP="$(date '+%Y-%m-%d-%H%M')"
DUMP_REMOTE="/tmp/gitea_db_$STAMP.dump"
$SSH_XP "sudo $XP_DOCKER exec postgres16 pg_dump -Fc -Z3 -U gitea_folzo gitea_db > $DUMP_REMOTE && sudo chmod 644 $DUMP_REMOTE"
DUMP_SHA=$($SSH_XP "sha256sum $DUMP_REMOTE | cut -d' ' -f1")
DUMP_SIZE=$($SSH_XP "stat -c%s $DUMP_REMOTE")
log "dump ${DUMP_REMOTE##*/} : $DUMP_SIZE o, sha256 ${DUMP_SHA:0:16}…"
$SSH_XP "sudo cat $DUMP_REMOTE" > "$STAGE/${DUMP_REMOTE##*/}"
[[ "$(sha256sum "$STAGE/${DUMP_REMOTE##*/}" | cut -d' ' -f1)" == "$DUMP_SHA" ]] || { echo "ERREUR : dump corrompu au stage"; exit 1; }
$SSH_XP "sudo rm -f $DUMP_REMOTE"
# ---------------------------------------------------------------- 3. transfert + intégrité
log "=== 3/5 TRANSFERT (tar sur SSH : .25 → stage → .24) + INTÉGRITÉ sha256 ==="
copy_pair() { # $1 = src .25, $2 = nom du sous-dossier G9
local src="$1" name="$2"
local stage="$STAGE/$name"
mkdir -p "$stage"
log " hop 1 (tar/ssh) : $src/ → stage/$name/"
$SSH_XP "sudo tar cz -C '$src' ." | tar xz -C "$stage"
log " intégrité $name (source vs stage) :"
local r l rc=0
r=$(mktemp); l=$(mktemp)
$SSH_XP "cd '$src' && sudo find . -type f -exec sha256sum {} + 2>/dev/null | LC_ALL=C sort" > "$r"
manifest_in "$stage" > "$l"
diff -u "$r" "$l" > "$r.diff" || rc=1
if [[ $rc -ne 0 ]]; then
log " ❌ ÉCHEC intégrité $name — 20 premières différences :"; head -20 "$r.diff"
rm -f "$r" "$l" "$r.diff"
journal "import ÉCHEC intégrité ($name)" "Vérification G4" "manifeste sha256 source ≠ stage" "purger stage/$name et re-exécuter"
echo "ERREUR : intégrité $name — arrêt avant le G9."; exit 1
fi
log " ✅ $(wc -l < "$r") fichiers identiques (source == stage)"
rm -f "$r" "$l" "$r.diff"
log " hop 2 (rsync) : stage/$name/ → .24:$G9_DIR/$name/"
$SSH_G9 "mkdir -p $G9_DIR/$name"
rsync -a --info=stats1 -e "$RSH_G9" "$stage/" "$G9_USER@$G9_HOST:$G9_DIR/$name/"
local g rc2=0
g=$(mktemp)
manifest_in "$stage" > "$r"; $SSH_G9 "cd '$G9_DIR/$name' && find . -type f -exec sha256sum {} + 2>/dev/null | LC_ALL=C sort" > "$g"
diff -u "$r" "$g" > "$g.diff" || rc2=1
if [[ $rc2 -ne 0 ]]; then
log " ❌ ÉCHEC intégrité $name sur le G9 :"; head -20 "$g.diff"
rm -f "$r" "$g" "$g.diff"
journal "import ÉCHEC intégrité G9 ($name)" "Vérification G4" "manifeste stage ≠ G9" "supprimer $G9_DIR/$name et re-transférer"
echo "ERREUR : intégrité G9 $name — arrêt."; exit 1
fi
log " ✅ $name : $(wc -l < "$g") fichiers identiques sur le G9"
rm -f "$r" "$g" "$g.diff"
}
copy_pair /volume2/gitea/gitea-repositories repositories
copy_pair /volume2/gitea/lfs lfs
copy_pair /volume2/@appdata/gitea/data data/gitea
log " dump → G9"
$SSH_G9 "mkdir -p $G9_DIR/backup"
rsync -a --info=stats1 -e "$RSH_G9" "$STAGE/${DUMP_REMOTE##*/}" "$G9_USER@$G9_HOST:$G9_DIR/backup/"
[[ "$($SSH_G9 "sha256sum $G9_DIR/backup/${DUMP_REMOTE##*/} | cut -d' ' -f1")" == "$DUMP_SHA" ]] || { echo "ERREUR : sha256 dump ≠ source"; exit 1; }
log " ✅ dump restauré sur le G9, sha256 identique (${DUMP_SHA:0:16}…)"
rm -rf "$STAGE"
# ---------------------------------------------------------------- 4. déploiement + restore
log "=== 4/5 DÉPLOIEMENT compose + RESTORE DB sur le G9 ==="
# Le kit vit sur le G9 dans /home/folzo/g9-migration : les secrets y sont générés par
# 03b-secrets.sh (644 root, hors git), puis le stack est installé sous /data/docker/<brique>
# (source couverte par le repo restic — C-COMMON).
KIT_ON_G9="/home/folzo/g9-migration"
rsync -a --info=stats1 -e "$RSH_G9" --exclude "secrets" "$KIT_DIR/compose/$BRIQUE/" "$G9_USER@$G9_HOST:$KIT_ON_G9/compose/$BRIQUE/"
rsync -a -e "$RSH_G9" "$KIT_DIR/scripts/03b-secrets.sh" "$G9_USER@$G9_HOST:$KIT_ON_G9/scripts/"
$SSH_G9 "sudo bash $KIT_ON_G9/scripts/03b-secrets.sh >/dev/null && sudo ls -l $KIT_ON_G9/compose/$BRIQUE/secrets/"
# secrets/ est root:root 700 (03b-secrets tourne en root) → copie du stack en sudo,
# puis secrets relus en 755/644 (lisibles par l'uid conteneur, dossier hors git — G9).
$SSH_G9 "sudo mkdir -p $G9_DIR && sudo rsync -a $KIT_ON_G9/compose/$BRIQUE/ $G9_DIR/ && sudo chmod 755 $G9_DIR/secrets && sudo chmod 644 $G9_DIR/secrets/dbpass"
$SSH_G9 "cd $G9_DIR && sudo sh -c 'umask 077 && printf \"GITEA__database__PASSWD=%s\n\" \"\$(cat secrets/dbpass)\" > secrets/gitea.env' && sudo chmod 644 secrets/gitea.env"
$SSH_G9 "cd $G9_DIR && docker compose up -d gitea-db && sleep 15 && docker compose ps --format '{{.Name}} {{.Status}}'"
log "restore pg_dump → gitea_db (rôle gitea_folzo, base vierge)"
# Le dump n'est pas monté dans le conteneur postgres → il entre par stdin (format custom).
$SSH_G9 "cd $G9_DIR && docker compose exec -T gitea-db pg_restore -U gitea_folzo -d gitea_db < backup/$(basename "$DUMP_REMOTE") 2>&1 | tail -8"
$SSH_G9 "cd $G9_DIR && docker compose exec -T gitea-db psql -U gitea_folzo -d gitea_db -t -A -c \"select 'repositories='||count(*) from repository; select 'users='||count(*) from \\\"user\\\";\""
$SSH_G9 "sudo chown -R 1000:1000 $G9_DIR/repositories $G9_DIR/lfs $G9_DIR/data && sudo chmod -R u+rwX $G9_DIR/repositories $G9_DIR/lfs $G9_DIR/data"
$SSH_G9 "cd $G9_DIR && docker compose up -d && sleep 20 && docker compose ps --format '{{.Name}} {{.Status}}'"
# ---------------------------------------------------------------- 5. vérification
log "=== 5/5 VÉRIFICATION ==="
$SSH_G9 "curl -sf -o /dev/null -w 'healthz HTTP %{http_code}\n' http://127.0.0.1:3000/api/healthz"
$SSH_G9 "cd $G9_DIR && docker compose exec -T gitea-db psql -U gitea_folzo -d gitea_db -t -A -c \"select owner_name||'/'||lower_name from repository order by 1;\""
log "source .25 (double-run G4) :"
$SSH_XP "sudo $XP_DOCKER ps --format '{{.Names}} {{.Status}}' | grep -E '^gitea '"
journal "Gitea migré + lancé sur le G9" "T4.4 (double-run G4, .25 lecture seule)" "pg_dump ${DUMP_SHA:0:12}… restauré ; dépôts+LFS+appdata sha256 identiques ; healthz ; .25 intact" "docker compose -f $G9_DIR/docker-compose.yml down + rm -rf $G9_DIR (G9 seul)"
log "Terminé. VALIDATION MANUELLE : git ls-remote ≥ 3 repos, clone/push, repo g9-migration."
+64
View File
@@ -0,0 +1,64 @@
#!/usr/bin/env bash
#
# 08-decommission.sh — T6.4 : Décommissionnement du xpenology
#
# Garde-fous : G3 (destructif — validation explicite), G4 (double-run terminé), G5 (IP .25), G7 (rollback), G8 (journalisation).
#
# ⚠️ OPÉRATION DESTRUCTIVE ET IRRÉVERSIBLE (garde-fous G3 + G4) :
# Ce script ARRÊTE et WIPE le xpenology. Il ne s'exécute QUE si :
# 1. Toutes les briques sont validées sur le G9 (double-run terminé, G4).
# 2. Le backup 3-2-1 est fonctionnel + restauration testée (G2).
# 3. L'IP du G9 est passée à .25 (G5).
# 4. Validation explicite de l'utilisateur (drapeau + saisie du mot de passe).
#
# Usage :
# sudo ./08-decommission.sh --i-understand
#
# Le script EXIGE de cocher chaque prérequis avant de continuer.
set -euo pipefail
log() { echo -e "\n[decom] $*"; }
die() { echo -e "\n[ERREUR] $*" >&2; exit 1; }
journal() {
local action="$1" raison="$2" resultat="$3" rollback="$4"
printf '| %s | 6 | decommission : %s | %s | %s | %s |\n' "$(date '+%Y-%m-%d %H:%M')" "$action" "$raison" "$resultat" "$rollback" >> "$(dirname "$0")/../journal.md"
}
[[ "${1:-}" == "--i-understand" ]] || die "Drapeau --i-understand requis (opération irréversible)."
XP_HOST="192.168.1.25"
log "=== PRÉREQUIS (cocher chacun : y/n) ==="
prereq() {
local label="$1"
read -r -p " [ ] $label — confirmé ? (y/N) : " a
[[ "$a" == "y" || "$a" == "Y" ]] || die "Prérequis non confirmé : $label"
}
prereq "Toutes les briques (Vaultwarden, Nextcloud, PhotoPrism, Gitea, n8n, Jellyfin) validées sur le G9"
prereq "Backup 3-2-1 fonctionnel + restauration testée (données IA incluses)"
prereq "IP du G9 passée à .25, tous les services résolus et accessibles"
prereq "Sauvegarde FINALE complète du xpenology effectuée et vérifiée"
prereq "Je comprends que le xpenology sera WIPÉ (données détruites)"
echo
read -r -p "Saisir 'WIPE XPENOLOGY' pour confirmer la destruction : " CONF
[[ "$CONF" == "WIPE XPENOLOGY" ]] || die "Confirmation non saisie. Abandon."
log "Arrêt des conteneurs + arrêt du système xpenology"
# L'arrêt propre se fait de préférence via la GUI DSM (arrêt système).
# En SSH, on arrête les conteneurs puis le système :
ssh -p 222 Master@"$XP_HOST" "sudo /var/packages/ContainerManager/target/usr/bin/docker stop \$(/var/packages/ContainerManager/target/usr/bin/docker ps -q) 2>/dev/null || true"
journal "Conteneurs xpenology arrêtés" "Arrêt propre avant wipe (G3)" "docker stop sur tous les conteneurs" "redémarrage du xpenology"
echo
echo "⚠️ ÉTAPE MANUELLE OBLIGATOIRE :"
echo " 1. Dans la GUI DSM : arrêter proprement le système."
echo " 2. Débrancher le xpenology."
echo " 3. (Optionnel) Wipe physique des disques si réutilisation/vente."
echo
read -r -p "Une fois le xpenology éteint et débranché, saisir 'DONE' pour journaliser : " FINAL
[[ "$FINAL" == "DONE" ]] || die "Non confirmé."
journal "xpenology décommissionné" "G9 = seul cœur famille (G4/G5)" "éteint + débranché, G9 sur .25" "aucun (irréversible) — backup 3-2-1 testé au préalable"
log "Terminé. Le G9 est maintenant le seul cœur famille."
+42
View File
@@ -0,0 +1,42 @@
#!/usr/bin/env bash
# Alerte ops (Matrix en direct via Synapse ; fallback webhook n8n).
# Secrets lus dans /etc/restic/matrix.env (600 root), jamais affichés (G9).
set -uo pipefail
[[ -f /etc/restic/matrix.env ]] && { set -a; . /etc/restic/matrix.env; set +a; }
[[ -f /etc/restic.env ]] && { set -a; . /etc/restic.env; set +a; }
MSG="${1:?message}"
TXN="restic-g9-$(date +%s%N)"
CLEAN=$(printf '%s' "$MSG" | tr -d '"\\')
have_jq=0
command -v jq >/dev/null 2>&1 && have_jq=1
json_text() {
if [[ $have_jq -eq 1 ]]; then jq -cn --arg b "$1" '{msgtype:"m.text", body:$b}'
else printf '{"msgtype":"m.text","body":"%s"}' "$1"; fi
}
json_webhook() {
if [[ $have_jq -eq 1 ]]; then jq -cn --arg m "$1" '{event:"restic-g9", host:"g9", message:$m}'
else printf '{"event":"restic-g9","host":"g9","message":"%s"}' "$1"; fi
}
if [[ -n "${MATRIX_TOKEN:-}" && -n "${MATRIX_ROOM:-}" ]]; then
BODY=$(json_text "[G9 backup] ${CLEAN}")
ROOM_ENC=$(printf '%s' "$MATRIX_ROOM" | sed 's/ /%20/g')
if curl -fsS -m 20 -X PUT \
"${MATRIX_SERVER}/_matrix/client/v3/rooms/${ROOM_ENC}/send/m.room.message/${TXN}" \
-H "Authorization: Bearer ${MATRIX_TOKEN}" \
-H 'Content-Type: application/json' \
-d "$BODY" >/dev/null; then
exit 0
fi
fi
if [[ -n "${RESTIC_NOTIFY_WEBHOOK:-}" ]]; then
BODY=$(json_webhook "${CLEAN}")
if curl -fsS -m 20 -X POST "$RESTIC_NOTIFY_WEBHOOK" \
-H 'Content-Type: application/json' -d "$BODY" >/dev/null; then
exit 0
fi
fi
echo "notify : ni Matrix ni webhook joignable" >&2
exit 1