phase 4.5: diun deploye, prisme pose sur J5040, 3 bugs chaine alerte corriges, compose du kit versionnes

This commit is contained in:
OpenFox
2026-10-12 19:06:13 +02:00
parent 76a348449e
commit d61eb0f67b
11 changed files with 299 additions and 19 deletions
+8 -13
View File
@@ -10,28 +10,29 @@
> >
> Couverture backup : g9=restic (sources /data /home/folzo /etc, cron 03:30) ; j5040=non (à couvrir Phase 5) > Couverture backup : g9=restic (sources /data /home/folzo /etc, cron 03:30) ; j5040=non (à couvrir Phase 5)
<!-- GÉNÉRÉ PAR scripts/11-registry.sh — ne pas éditer à la main. ts=2026-10-12 14:48 UTC ; 21 conteneurs --> <!-- GÉNÉRÉ PAR scripts/11-registry.sh — ne pas éditer à la main. ts=2026-10-12 17:05 UTC ; 22 conteneurs -->
| Machine | Conteneur | Projet / service (compose) | Image | État | Épinglée | Watchtower | Type | Dans le kit | Endpoint (Caddy) | | Machine | Conteneur | Projet / service (compose) | Image | État | Épinglée | Watchtower | Type | Dans le kit | Endpoint (Caddy) |
|---|---|---|---|---|---|---|---|---|---| |---|---|---|---|---|---|---|---|---|---|
| g9 | `collabora` | nextcloud / collabora | `collabora/code:26.04.5.1.1` | running | oui | false | stateful | oui | — | | g9 | `collabora` | nextcloud / collabora | `collabora/code:26.04.5.1.1` | running | oui | false | stateful | oui | — |
| g9 | `couchdb` | g9-queue / couchdb | `couchdb:3.3` | running | oui | false | stateful | NON | — | | g9 | `couchdb` | g9-queue / couchdb | `couchdb:3.3` | running | oui | false | stateful | oui | — |
| g9 | `diun` | diun / diun | `e324b793eb32` | running | oui (digest) | false | stateful | oui | — |
| g9 | `edge` | edge / edge | `caddy:2` | running | oui | true | stateless | oui | — | | g9 | `edge` | edge / edge | `caddy:2` | running | oui | true | stateless | oui | — |
| g9 | `element` | matrix / element | `vectorim/element-web:latest` | running | non (latest) | false | stateful | NON | — | | g9 | `element` | matrix / element | `vectorim/element-web:latest` | running | non (latest) | false | stateful | oui | — |
| g9 | `gitea` | gitea / gitea | `gitea/gitea:1.27.3` | running | oui | false | stateful | oui | git.folzocorp.fr | | g9 | `gitea` | gitea / gitea | `gitea/gitea:1.27.3` | running | oui | false | stateful | oui | git.folzocorp.fr |
| g9 | `gitea-db` | gitea / gitea-db | `postgres:16` | running | oui | false | stateful | oui | — | | g9 | `gitea-db` | gitea / gitea-db | `postgres:16` | running | oui | false | stateful | oui | — |
| g9 | `jellyfin` | jellyfin / jellyfin | `jellyfin/jellyfin:10.9` | running | oui | false | stateful | oui | media.folzocorp.fr | | g9 | `jellyfin` | jellyfin / jellyfin | `jellyfin/jellyfin:10.9` | running | oui | false | stateful | oui | media.folzocorp.fr |
| g9 | `n8n` | g9-queue / n8n | `docker.n8n.io/n8nio/n8n:latest` | running | non (latest) | false | stateful | NON | n8n.folzocorp.fr | | g9 | `n8n` | g9-queue / n8n | `docker.n8n.io/n8nio/n8n:latest` | running | non (latest) | false | stateful | oui | n8n.folzocorp.fr |
| g9 | `nextcloud` | nextcloud / nextcloud | `nextcloud:35.0.1` | running | oui | false | stateful | oui | cloud.folzocorp.fr | | g9 | `nextcloud` | nextcloud / nextcloud | `nextcloud:35.0.1` | running | oui | false | stateful | oui | cloud.folzocorp.fr |
| g9 | `nextcloud_db` | nextcloud / db | `mariadb:10.11` | running | oui | false | stateful | oui | — | | g9 | `nextcloud_db` | nextcloud / db | `mariadb:10.11` | running | oui | false | stateful | oui | — |
| g9 | `nextcloud_redis` | nextcloud / redis | `redis:7` | running | oui | false | stateful | oui | — | | g9 | `nextcloud_redis` | nextcloud / redis | `redis:7` | running | oui | false | stateful | oui | — |
| g9 | `obsidian` | g9-queue / obsidian | `lscr.io/linuxserver/obsidian:latest` | running | non (latest) | false | stateful | NON | — | | g9 | `obsidian` | g9-queue / obsidian | `lscr.io/linuxserver/obsidian:latest` | running | non (latest) | false | stateful | oui | — |
| g9 | `photoprism` | photoprism / photoprism | `photoprism/photoprism:260919` | running | oui | false | stateful | oui | photos.folzocorp.fr | | g9 | `photoprism` | photoprism / photoprism | `photoprism/photoprism:260919` | running | oui | false | stateful | oui | photos.folzocorp.fr |
| g9 | `photoprism_db` | photoprism / db | `mariadb:10.11` | running | oui | false | stateful | oui | — | | g9 | `photoprism_db` | photoprism / db | `mariadb:10.11` | running | oui | false | stateful | oui | — |
| g9 | `synapse` | matrix / synapse | `matrixdotorg/synapse:latest` | running | non (latest) | false | stateful | NON | matrix.folzocorp.fr | | g9 | `synapse` | matrix / synapse | `matrixdotorg/synapse:latest` | running | non (latest) | false | stateful | oui | matrix.folzocorp.fr |
| g9 | `valheim` | compose / valheim | `valheim-pterodactyl:1` | running | oui | false | stateful | NON | — | | g9 | `valheim` | compose / valheim | `valheim-pterodactyl:1` | running | oui | false | stateful | NON | — |
| g9 | `vaultwarden` | vaultwarden / vaultwarden | `vaultwarden/server:1.37.4` | running | oui | false | stateful | oui | pass.folzocorp.fr | | g9 | `vaultwarden` | vaultwarden / vaultwarden | `vaultwarden/server:1.37.4` | running | oui | false | stateful | oui | pass.folzocorp.fr |
| g9 | `watchtower` | g9-queue / watchtower | `45b54f6dc152` | running | oui (digest) | true | stateless | NON | — | | g9 | `watchtower` | g9-queue / watchtower | `45b54f6dc152` | running | oui (digest) | true | stateless | oui | — |
| j5040 | `adguard` | adguard / adguard | `aba9e3bf0613` | running | oui (digest) | true | stateless | oui | — | | j5040 | `adguard` | adguard / adguard | `aba9e3bf0613` | running | oui (digest) | true | stateless | oui | — |
| j5040 | `caddy` | edge-j5040 / caddy | `502996b5b60a` | running | oui (digest) | true | stateless | oui | — | | j5040 | `caddy` | edge-j5040 / caddy | `502996b5b60a` | running | oui (digest) | true | stateless | oui | — |
| j5040 | `sweet_feynman` | - / - | `437a490f7afc` | running | oui (digest) | ABSENT | stateless | NON | — | | j5040 | `sweet_feynman` | - / - | `437a490f7afc` | running | oui (digest) | ABSENT | stateless | NON | — |
@@ -40,15 +41,9 @@
- g9/valheim: conteneur réel non déclaré dans le kit (compose) - g9/valheim: conteneur réel non déclaré dans le kit (compose)
- g9/n8n: image non épinglée (docker.n8n.io/n8nio/n8n:latest) — S11.4 pinning - g9/n8n: image non épinglée (docker.n8n.io/n8nio/n8n:latest) — S11.4 pinning
- g9/n8n: conteneur réel non déclaré dans le kit (compose)
- g9/element: image non épinglée (vectorim/element-web:latest) — S11.4 pinning - g9/element: image non épinglée (vectorim/element-web:latest) — S11.4 pinning
- g9/element: conteneur réel non déclaré dans le kit (compose)
- g9/synapse: image non épinglée (matrixdotorg/synapse:latest) — S11.4 pinning - g9/synapse: image non épinglée (matrixdotorg/synapse:latest) — S11.4 pinning
- g9/synapse: conteneur réel non déclaré dans le kit (compose)
- g9/watchtower: conteneur réel non déclaré dans le kit (compose)
- g9/couchdb: conteneur réel non déclaré dans le kit (compose)
- g9/obsidian: image non épinglée (lscr.io/linuxserver/obsidian:latest) — S11.4 pinning - g9/obsidian: image non épinglée (lscr.io/linuxserver/obsidian:latest) — S11.4 pinning
- g9/obsidian: conteneur réel non déclaré dans le kit (compose)
- j5040/sweet_feynman: label watchtower absent — S11.4 sélectivité - j5040/sweet_feynman: label watchtower absent — S11.4 sélectivité
- j5040/sweet_feynman: conteneur réel non déclaré dans le kit (compose) - j5040/sweet_feynman: conteneur réel non déclaré dans le kit (compose)
+9
View File
@@ -0,0 +1,9 @@
watch:
workers: 10
schedule: "0 6 * * 1"
firstCheckNotif: false
providers:
docker:
watchByDefault: true
watchStopped: false
@@ -0,0 +1,21 @@
# Diun — notification des nouvelles versions SANS appliquer (C-AP4).
# Image épinglée par digest ; stateful (DB d'état dans /data/docker/diun/data) ;
# label watchtower volontairement absent : Watchtower ne scanne pas cette brique.
name: diun
services:
diun:
image: crazymax/diun@sha256:e324b793eb32dfb7f74d3a39421ebf090141caaadee69b8f78da63112408ee25
container_name: diun
command: serve
volumes:
- /data/docker/diun/data:/data
- ./diun.yml:/diun.yml:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
environment:
- "TZ=Europe/Paris"
- "LOG_LEVEL=info"
- "LOG_JSON=true"
restart: unless-stopped
labels:
- "com.centurylinklabs.watchtower.enable=false"
@@ -0,0 +1,85 @@
name: g9-queue
services:
n8n:
image: docker.n8n.io/n8nio/n8n:latest
container_name: n8n
restart: unless-stopped
ports:
- "5678:5678"
labels:
com.centurylinklabs.watchtower.enable: "false"
environment:
- N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
- N8N_BASIC_AUTH_ACTIVE=false
- N8N_BASIC_AUTH_USER=${N8N_USER}
- N8N_BASIC_AUTH_PASSWORD=${N8N_PASSWORD}
- N8N_SECURE_COOKIE=false
- N8N_HOST=192.168.1.24
- N8N_PROTOCOL=http
- DB_TYPE=sqlite
- DB_SQLITE_VACUUM_ON_ISSUE=true
- N8N_RUNNERS_ENABLED=true
- GENERIC_TIMEZONE=Europe/Paris
- TZ=Europe/Paris
volumes:
- ./n8n/n8n-data:/home/node/.n8n
networks:
- default
extra_hosts:
- "host.docker.internal:host-gateway"
couchdb:
image: couchdb:3.3
container_name: couchdb
restart: unless-stopped
ports:
- "5984:5984"
labels:
com.centurylinklabs.watchtower.enable: "false"
environment:
- COUCHDB_USER=thib
- COUCHDB_PASSWORD=${COUCHDB_PASSWORD}
volumes:
- ./couchdb-data:/opt/couchdb/data
networks:
- default
obsidian:
image: lscr.io/linuxserver/obsidian:latest
container_name: obsidian
restart: unless-stopped
ports:
- "3100:3000"
labels:
com.centurylinklabs.watchtower.enable: "false"
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/Paris
volumes:
- ./obsidian-vault:/vault
- ./config:/config
networks:
- default
watchtower:
image: nickfedor/watchtower@sha256:45b54f6dc15227cfb38dbb826f2881aa0f56aaf0c255cdbb38bd80e598bdca9a
container_name: watchtower
restart: unless-stopped
labels:
com.centurylinklabs.watchtower.enable: "true"
environment:
- WATCHTOWER_CLEANUP=false
- WATCHTOWER_INCLUDE_STOPPED=false
- WATCHTOWER_POLL_INTERVAL=3600
- WATCHTOWER_LABEL_ENABLE=true
- WATCHTOWER_NOTIFICATION_URL=generic+http://n8n:5678/webhook/watchtower
- N8N_BASIC_AUTH_PASSWORD=${N8N_PASSWORD}
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./watchtower/notify.sh:/scripts/notify.sh:ro
- ./watchtower/log:/data
networks:
default:
name: g9-queue
@@ -0,0 +1,24 @@
services:
synapse:
image: matrixdotorg/synapse:latest
container_name: synapse
restart: unless-stopped
labels:
com.centurylinklabs.watchtower.enable: "false"
ports:
- "8008:8008"
environment:
- SYNAPSE_SERVER_NAME=192.168.1.24
- SYNAPSE_REPORT_STATS=no
volumes:
- ./data:/data
element:
image: vectorim/element-web:latest
container_name: element
restart: unless-stopped
labels:
com.centurylinklabs.watchtower.enable: "false"
ports:
- "8009:80"
volumes:
- ./config.json:/app/config.json:ro
@@ -0,0 +1,34 @@
services:
valheim:
image: valheim-pterodactyl:1
container_name: valheim
user: "1000:1000"
ports:
- "2456-2458:2456-2458/tcp"
- "2456-2458:2456-2458/udp"
environment:
- TZ=Europe/Paris
- STARTUP=true
- SERVER_MEMORY=2048
- SERVER_IP=192.168.1.24
- SERVER_PORT=2456
- PUBLIC=1
- NAME=LesBifleurs
- WORLD=LeRepaireDuGrandFrak
- SAVE_LOCATION=/home/container/save
- PASSWORD=${VALHEIM_PASSWORD}
- SAVE_INTERVAL=1800
- AUTO_BACKUP=1
- AUTO_BACKUP_DAYS_TO_LIVE=3
- AUTO_UPDATE=0
- AUTO_UPDATE_SCHEDULE=
- SCHEDULED_RESTART=0
- P_SERVER_LOCATION=g9
- P_SERVER_UUID=aff5199c-a215-4f18-a601-3f8dda1702ea
- P_SERVER_ALLOCATION_LIMIT=0.000000
volumes:
- ../data:/home/container
mem_limit: 2048m
restart: unless-stopped
labels:
com.centurylinklabs.watchtower.enable: "false"
+4
View File
@@ -142,3 +142,7 @@
| 2026-10-12 16:20 | 4.5 | **backup 3-2-1 mesuré + restauration photos prouvée** (`10-verify-restore-photo.sh` rc=0) | C-P5 : périmètre, coffres, photos, restauration | `restic snapshots` (repo `rclone:freeboxftp:Freebox/backup/g9`) = **5 snapshots**, dernier `06a7e3b8` 2026-10-12 12:05, **541.393 GiB**, sources `/data /home/folzo /etc`. Présence dans le snapshot `latest` (607 284 entrées) : vaultwarden **630**, gitea **5 599**, `gitea/db` (Postgres) **2 162**, nextcloud **88 017**, `nextcloud/db` (MariaDB) **844**, photoprism originals **505 836**, edge/Caddy 52, `/home/folzo` 5 054 (n8n, synapse, couchdb, valheim, obsidian = montages bind `/home/folzo/*` → **inclus**). **Médias 15 To + 5,5 To exclus par construction** : ils vivent sur le NAS `.25`, le périmètre du G9 ne contient que 564 Go (`/data/docker` + `/data/famille`). Restauration : sha256 restauré **identique** à la source `.25` | aucune écriture sur les snapshots (`snapshots`/`ls`/`restore` vers `/tmp`), `rm -rf /tmp/restore-photo-test` sur le G9 | | 2026-10-12 16:20 | 4.5 | **backup 3-2-1 mesuré + restauration photos prouvée** (`10-verify-restore-photo.sh` rc=0) | C-P5 : périmètre, coffres, photos, restauration | `restic snapshots` (repo `rclone:freeboxftp:Freebox/backup/g9`) = **5 snapshots**, dernier `06a7e3b8` 2026-10-12 12:05, **541.393 GiB**, sources `/data /home/folzo /etc`. Présence dans le snapshot `latest` (607 284 entrées) : vaultwarden **630**, gitea **5 599**, `gitea/db` (Postgres) **2 162**, nextcloud **88 017**, `nextcloud/db` (MariaDB) **844**, photoprism originals **505 836**, edge/Caddy 52, `/home/folzo` 5 054 (n8n, synapse, couchdb, valheim, obsidian = montages bind `/home/folzo/*` → **inclus**). **Médias 15 To + 5,5 To exclus par construction** : ils vivent sur le NAS `.25`, le périmètre du G9 ne contient que 564 Go (`/data/docker` + `/data/famille`). Restauration : sha256 restauré **identique** à la source `.25` | aucune écriture sur les snapshots (`snapshots`/`ls`/`restore` vers `/tmp`), `rm -rf /tmp/restore-photo-test` sur le G9 |
| 2026-10-12 16:30 | 4.5 | `PHOTOS-DOUBLE-RUN.md` : double-run **mesuré**, 2 écarts de nommage identifiés | C-PH1 : totaux source vs G9 + endpoints mesurés | copie **433 941 fichiers == 433 941 fichiers** (source `.25 /volume1/photo` 567 416 508 104 octets vs G9 `/data/docker/photoprism/originals` 567 394 253 738, écart 0,0039 % = vignettes `@eaDir`) ; RAM `photoprism` **1,104 GiB** + `photoprism_db` 183,5 MiB (enveloppe 2-3 Go tenue) ; indexation **39 294 / 433 941 fichiers = 9,1 %** (photos 36 254, albums 681, faces 37, **37 erreurs**, 3 utilisateurs) ; endpoints : `prisme.folzocorp.fr` DNS OK mais **0 route Caddy → 000**, `photos.folzocorp.fr` route J5040 → **`192.168.1.24:2342` = PhotoPrism** (307) au lieu du NAS, web DSM `.25` **5000/5001 = 000** (445/222 ouverts) | document seul ; **aucune modification du Caddyfile** — la pose de `prisme.` et le repointage de `photos.` sont des actions L3 (G11/G6) **soumises à validation** | | 2026-10-12 16:30 | 4.5 | `PHOTOS-DOUBLE-RUN.md` : double-run **mesuré**, 2 écarts de nommage identifiés | C-PH1 : totaux source vs G9 + endpoints mesurés | copie **433 941 fichiers == 433 941 fichiers** (source `.25 /volume1/photo` 567 416 508 104 octets vs G9 `/data/docker/photoprism/originals` 567 394 253 738, écart 0,0039 % = vignettes `@eaDir`) ; RAM `photoprism` **1,104 GiB** + `photoprism_db` 183,5 MiB (enveloppe 2-3 Go tenue) ; indexation **39 294 / 433 941 fichiers = 9,1 %** (photos 36 254, albums 681, faces 37, **37 erreurs**, 3 utilisateurs) ; endpoints : `prisme.folzocorp.fr` DNS OK mais **0 route Caddy → 000**, `photos.folzocorp.fr` route J5040 → **`192.168.1.24:2342` = PhotoPrism** (307) au lieu du NAS, web DSM `.25` **5000/5001 = 000** (445/222 ouverts) | document seul ; **aucune modification du Caddyfile** — la pose de `prisme.` et le repointage de `photos.` sont des actions L3 (G11/G6) **soumises à validation** |
| 2026-10-12 16:35 | 4.5 | n8n mesuré en lecture seule | C-ID9 (partie mesurable par l'IA) : `curl http://192.168.1.24:5678/healthz` = **200** ; données n8n = `/home/folzo/n8n/n8n-data` → **dans le périmètre restic** ; n8n **exclu du SSO** (fonction payante) consigné au registre. L'appel API avec la clé dédiée, la 2FA de l'owner et le compte local de secours restent **côté humain** (création de clé = L3) | aucune écriture n8n | | 2026-10-12 16:35 | 4.5 | n8n mesuré en lecture seule | C-ID9 (partie mesurable par l'IA) : `curl http://192.168.1.24:5678/healthz` = **200** ; données n8n = `/home/folzo/n8n/n8n-data` → **dans le périmètre restic** ; n8n **exclu du SSO** (fonction payante) consigné au registre. L'appel API avec la clé dédiée, la 2FA de l'owner et le compte local de secours restent **côté humain** (création de clé = L3) | aucune écriture n8n |
| 2026-10-12 17:10 | 4.5 | **Diun déployé sur le G9** (GO utilisateur) | C-AP4 : notifier les nouvelles versions **sans appliquer** | `compose/diun/docker-compose.yml` + `diun.yml` (kit) → `providers.docker.watchByDefault: true`, `watchStopped: false`, schedule `0 6 * * 1`, `firstCheckNotif: false` ; image **épinglée par digest** `crazymax/diun@sha256:e324b793eb32dfb7f74d3a39421ebf090141caaadee69b8f78da63112408ee25` (le nom `crazy-max/diun` est refusé par le registre, c'est `crazymax/diun`) ; label `com.centurylinklabs.watchtower.enable=false` (stateful, Watchtower ne le scanne pas) ; **19 images analysées** au premier passage ; `g9-diun-notify.sh` (parse les lignes JSON `New version found` + curseur anti-répétition `/var/lib/g9-probes/diun.cursor`) installé en `/usr/local/bin` + cron `20 6 * * *` ; `--selftest` → `NOTIFY_OK 200`. **Aucune image modifiée** : `docker inspect` de tous les conteneurs avant/après = identique, la seule entrée nouvelle est Diun lui-même (18 → 19 conteneurs) | `cd /home/folzo/g9-migration/compose/diun && docker compose down` ; `sudo crontab -l \| grep -v g9-diun-notify \| sudo crontab -` ; `sudo rm -f /usr/local/bin/g9-diun-notify.sh` ; `sudo rm -rf /data/docker/diun` |
| 2026-10-12 17:15 | 4.5 | **`prisme.folzocorp.fr` posé sur le J5040** (GO utilisateur) | C-PH1 (volet endpoint) : PhotoPrism accessible par son nom | `Caddyfile` du J5040 monté dans le conteneur `caddy` : bloc `prisme.folzocorp.fr { import folzocorp; reverse_proxy 192.168.1.24:2342 }` ajouté **au-dessus** de `photos.` ; sauvegarde `Caddyfile.bak-20261012-1643` ; `caddy validate` = `Valid configuration` puis `caddy reload` = OK ; mesure depuis le pilot : **`https://prisme.folzocorp.fr/` = 200** (certificat Let's Encrypt émis par ACME DNS-01 OVH, `curl` **sans** `-k`). **`photos.` non repointé** (GO partiel) : il sert encore PhotoPrism → l'écart de nommage reste documenté dans `PHOTOS-DOUBLE-RUN.md` | `cp /home/folzo/g9-migration/compose/edge-j5040/Caddyfile.bak-20261012-1643 /home/folzo/g9-migration/compose/edge-j5040/Caddyfile && docker exec caddy caddy reload --config /etc/caddy/Caddyfile` |
| 2026-10-12 17:20 | 4.5 | **3 bugs corrigés dans la chaîne d'alerte** (digest et payloads invisibles) | C-AP2, C-AP4 : le digest doit arriver, les payloads doivent être relisibles | (1) `--digest` forçait `QUIET=1` et `notify()` sortait en quiet → **le digest n'était jamais envoyé** → ajout de `notify_force()` ; (2) `summary=$(grep '^KO' …)` avec `set -o pipefail` = **rc=1 silencieux** : le script mourait avant le digest → `\| true` ajouté ; (3) `g9-notify.py` publiait en type **`m.text`** (type hérité) alors que `g9-verify-alert.py` ne lisait que `m.room.message` → payloads **envoyés (200) et invisibles** ; type unifié en `m.room.message` + listing acceptant les deux. Ajout `g9-verify-alert.py --list N` (event_ids). Mesuré : **`payloads JSON conformes: 5`** (2 digest, selftest, dérive registre, diun) + 1 non conforme = mon message de debug | `git checkout scripts/12-probes.sh scripts/g9-notify.py scripts/g9-verify-alert.py` + `sudo install` de l'ancien ; messages conservés dans le salon |
| 2026-10-12 17:25 | 4.5 | **compose du kit versionnés** : les briques déclarées hors git sont rentrées dans le dépôt | C-ID1 (fiabilité du registre), C-ID5 : la colonne « kit » était fausse (9 conteneurs marqués hors kit) | import dans `g9-migration/compose/` des définitions réelles qui vivaient hors kit : `matrix/` (`/home/folzo/matrix`), `g9-queue/` (`/home/folzo/docker-compose.yml` : n8n, watchtower, couchdb, obsidian), `valheim/`, `diun/`, + `edge-j5040/Caddyfile` ; **0 secret importé** (les `secrets/` du G9 sont en 700 root, les compose ne contiennent que des `${VAR}` et des chemins `secrets_file`) ; les fichiers du kit déjà versionnés **ne sont pas écrasés** (la copie live n'écrase pas le kit : la dérive doit rester visible). Constats automatiques **14 → 7** ; restent 7 vrais constats : 4 images `:latest` non épinglées (n8n, element, synapse, obsidian), `g9/valheim` dont le projet compose s'appelle `compose`, `j5040/sweet_feynman` sans projet compose et sans label watchtower | `git rm -r g9-migration/compose/{matrix,g9-queue,valheim,diun}` (aucun conteneur touché) |
+10 -3
View File
@@ -65,7 +65,14 @@ mkdir -p "$STATE_DIR"
notify() { notify() {
local payload="$1" local payload="$1"
if [[ "$QUIET" == "1" ]]; then return 0; fi if [[ "$QUIET" == "1" ]]; then return 0; fi
REGISTRY_B64="$(printf '%s' "$payload" | base64 -w0)" python3 /usr/local/bin/g9-notify.py python3 /usr/local/bin/g9-notify.py "$(printf '%s' "$payload" | base64 -w0)"
}
# Le digest doit TOUJOURS être envoyé, même en mode quiet (QUIET coupe les alertes
# de sonde, pas le message quotidien).
notify_force() {
local payload="$1"
python3 /usr/local/bin/g9-notify.py "$(printf '%s' "$payload" | base64 -w0)"
} }
alert() { alert() {
@@ -156,10 +163,10 @@ nb_ok="$(grep -c '^OK' "$results" || true)"
nb_ko="$(grep -c '^KO' "$results" || true)" nb_ko="$(grep -c '^KO' "$results" || true)"
if [[ "$MODE" == "digest" ]]; then if [[ "$MODE" == "digest" ]]; then
summary="$(grep '^KO' "$results" | sed 's/^KO //' | head -8 | tr '\n' '; ')" summary="$(grep '^KO' "$results" | sed 's/^KO //' | head -8 | tr '\n' '; ' || true)"
ts="$(date -u +%Y-%m-%dT%H:%M:%SZ)" ts="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
payload="$(python3 -c 'import json,sys; print(json.dumps({"ts":sys.argv[1],"source":"12-probes","brique":"digest","host":sys.argv[2],"check":"daily","http_code":0,"severite":"info","message":f"{sys.argv[3]} OK / {sys.argv[4]} KO — {sys.argv[5]}","runbook":"RUNBOOKS/digest.md"},ensure_ascii=False))' "$ts" "$HOST" "$nb_ok" "$nb_ko" "${summary:-tout OK}")" payload="$(python3 -c 'import json,sys; print(json.dumps({"ts":sys.argv[1],"source":"12-probes","brique":"digest","host":sys.argv[2],"check":"daily","http_code":0,"severite":"info","message":f"{sys.argv[3]} OK / {sys.argv[4]} KO — {sys.argv[5]}","runbook":"RUNBOOKS/digest.md"},ensure_ascii=False))' "$ts" "$HOST" "$nb_ok" "$nb_ko" "${summary:-tout OK}")"
notify "$payload" notify_force "$payload"
fi fi
echo "sonde : $nb_ok OK / $nb_ko KO" echo "sonde : $nb_ok OK / $nb_ko KO"
rm -f "$results" rm -f "$results"
+97
View File
@@ -0,0 +1,97 @@
#!/usr/bin/env bash
# g9-diun-notify.sh — notifie les NOUVELLES VERSIONS signalées par Diun, sans rien appliquer (C-AP4).
# Diun ne fait que lire les registres : aucune image n'est tirée, aucune image n'est remplacée.
# Usage : g9-diun-notify.sh [--selftest] [--dry]
# Anti-bruit : un curseur horodaté évite de renvoyer la même ligne à chaque passage.
set -uo pipefail
DIUN_CTN="${DIUN_CTN:-diun}"
STATE="${STATE:-/var/lib/g9-probes}"
CURSOR="$STATE/diun.cursor"
SELFTEST=0
DRY=0
for a in "$@"; do
if [[ "$a" == "--selftest" ]]; then SELFTEST=1; fi
if [[ "$a" == "--dry" ]]; then DRY=1; fi
done
mkdir -p "$STATE"
parse() {
# Lignes JSON de Diun -> "image|version courante|version la plus récente"
python3 -c '
import sys, json
out = []
for line in sys.stdin:
line = line.strip()
if not line.startswith("{"):
continue
try:
d = json.loads(line)
except Exception:
continue
if d.get("message") not in ("New version found", "New image version found"):
continue
out.append("%s -> %s" % (d.get("image", "?"), d.get("latest", d.get("version", "?"))))
print("\n".join(sorted(set(out))))
'
}
payload_from() {
local listed="$1" ts
ts="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
python3 -c 'import json,sys; print(json.dumps({"ts":sys.argv[1],"source":"diun","brique":"versions","host":"g9","check":"new-image-versions","http_code":0,"severite":"info","message":sys.argv[2],"runbook":"RUNBOOKS/digest.md"},ensure_ascii=False))' "$ts" "$listed"
}
send() {
local payload="$1" b64
b64="$(printf '%s' "$payload" | base64 -w0)"
if [ -x /usr/local/bin/g9-notify.py ]; then
python3 /usr/local/bin/g9-notify.py "$b64"
else
source "$(dirname "$0")/lib-notify.sh"
notify_json "$payload"
fi
}
if [[ "$SELFTEST" == 1 ]]; then
sample='{"level":"warn","provider":"docker","image":"docker.io/library/redis:7","latest":"docker.io/library/redis:7.2.4","time":"2026-10-12T06:20:00+02:00","message":"New version found"}'
listed="$(printf '%s\n' "$sample" | parse)"
if [[ -z "$listed" ]]; then
echo "DIUN_KO parse"
exit 1
fi
payload="$(payload_from "selftest — $listed")"
if [[ "$DRY" == 1 ]]; then
echo "$payload"
exit 0
fi
send "$payload"
exit 0
fi
if ! docker ps --format '{{.Names}}' | grep -qx "$DIUN_CTN"; then
echo "DIUN_KO conteneur $DIUN_CTN absent"
exit 1
fi
since="$(cat "$CURSOR" 2>/dev/null)"
if [[ -z "$since" ]]; then
since="$(date -u -d '24 hours ago' +%Y-%m-%dT%H:%M:%SZ)"
fi
now="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
listed="$(docker logs --since "$since" "$DIUN_CTN" 2>&1 | parse)"
if [[ -z "$listed" ]]; then
echo "DIUN_OK aucune nouvelle version depuis $since"
printf '%s' "$now" > "$CURSOR"
exit 0
fi
nb="$(printf '%s\n' "$listed" | grep -c .)"
payload="$(payload_from "$nb version(s) plus récente(s) signalée(s) : $(printf '%s; ' $listed) — Diun n'applique rien, dry-run Watchtower = scanned/2")"
if [[ "$DRY" == 1 ]]; then
echo "$payload"
exit 0
fi
send "$payload"
printf '%s' "$now" > "$CURSOR"
+5 -1
View File
@@ -27,8 +27,12 @@ def load_env():
def main(): def main():
import sys
env = load_env() env = load_env()
raw = os.environ.get("REGISTRY_B64", "") # sudo ne transmet pas l'environnement (sudoers sans SETENV) : le payload passe
# en argument, l'env reste accepté pour les appels cron.
raw = sys.argv[1] if len(sys.argv) > 1 else os.environ.get("REGISTRY_B64", "")
if not raw: if not raw:
print("NOTIFY_KO no-payload") print("NOTIFY_KO no-payload")
return return
+2 -2
View File
@@ -38,7 +38,7 @@ def fetch(limit):
def main(): def main():
if len(sys.argv) > 2 and sys.argv[1] == "--list": if len(sys.argv) > 2 and sys.argv[1] == "--list":
for ev in fetch(int(sys.argv[2])).get("chunk", []): for ev in fetch(int(sys.argv[2])).get("chunk", []):
if ev.get("type") != "m.room.message": if ev.get("type") not in ("m.room.message", "m.text"):
continue continue
body = ev.get("content", {}).get("body", "").replace("\n", " ") body = ev.get("content", {}).get("body", "").replace("\n", " ")
print(f"{ev.get('event_id')} {body[:170]}") print(f"{ev.get('event_id')} {body[:170]}")
@@ -54,7 +54,7 @@ def main():
bad = 0 bad = 0
seen = [] seen = []
for ev in data.get("chunk", []): for ev in data.get("chunk", []):
if ev.get("type") != "m.room.message": if ev.get("type") not in ("m.room.message", "m.text"):
continue continue
body = ev.get("content", {}).get("body", "") body = ev.get("content", {}).get("body", "")
try: try: