Le livre de Claude

Pages de scripts et d'études de Claude Code sur le PC Elio+Jux

0_Claude et le Jux_VPS

0_Claude et le Jux_VPS

A_liens entre Claude et le KDRIVE


kDrive API REST — Infomaniak

Paramètre Valeur
Drive ID 591617
Token Bearer cnAZannhRpecgAfsQM7QiO4Vqh37g6Ht_hm4f0E6eDihMRaP2ZckKCmW959T5ST2Nv3qhGlqs0ltpNAb
Base URL https://api.infomaniak.com/3/drive/591617

Endpoints fonctionnels

Méthode Endpoint Usage
GET /files/{dir_id}/files Lister le contenu d'un dossier
GET /files/{file_id} Métadonnées d'un fichier
POST /upload?directory_id={id}&file_name={name}&conflict=replace&total_size={bytes} Uploader un fichier. conflict=replace écrase le fichier existant de même nom. conflict=version crée une nouvelle version.


Recovery automatique FUSE — monitor-rclone.sh (2026-06-24)

Le script /home/debian/monitor-rclone.sh (cron */5 * * * *) surveille les 5 mounts rclone et effectue une recovery complète si l'un d'eux est mort.

Fonctionnement

Pour chaque mount (/music, /komga, /livres, /audiobookshelf/audiobooks, /photo) :

timeout 10 ls — si inaccessible : sudo systemctl restart (kdrive-music, kdrive-komga, kdrive-livres, rclone-audiobooks, rclone-photo) Attente 5s + vérification que le mount répond Restart des containers Docker associés : /music → navidrome-navidrome-1 /komga → komga /livres → Kavita /audiobookshelf/audiobooks → audiobookshelf /photo → Immich-SERVER Immich-MICROSERVICES

Le script est silencieux quand tout va bien — il ne logue que les pannes et recoveries. Log : /home/debian/logs/rclone-monitor.log

Avant cette refactorisation, le script lançait un rclone mount direct (en parallèle des services systemd) et ne redémarrait pas les containers — le FUSE était remonté mais Navidrome gardait l'ancienne référence corrompue.

Suppression de fichiers — non fonctionnelle (testé 2026-06-09)

Tous les endpoints suivants retournent {"result":"error","error":{"code":"method_not_found"}} :

La doc officielle (developer.infomaniak.com) n'est pas accessible publiquement. L'endpoint de suppression reste inconnu.

Stratégie de rétention — rotation par jour de la semaine

Puisque la suppression via API ne fonctionne pas, le script de backup utilise une rotation par jour de la semaine (date +%u → 1=lundi … 7=dimanche) avec conflict=replace. Résultat : exactement 7 fichiers max par service, les anciens sont écrasés automatiquement chaque semaine.

Script : /opt/backups/vps_backup.sh — cron : 0 2 * * *

Dossiers kDrive (TOOJUX/jux_vps)

Service Dossier ID Exemple de fichier
BookStack 1427916 bookstack_db_day1.7z
Immich 1427919 immich_db_day1.7z
Joplin 1427920 joplin_db_day1.7z (~630 Mo)
Mealie 1427921 mealie_db_day1.7z
Readeck 1427922 readeck_db_day1.7z
0_Claude et le Jux_VPS

B_les procédures de Claude

1/ la sauvegarde quotidienne de la base de données Joplin sur Kdrive (sans images)   

5/ 260611 - Komga-PDF 

6/ 260614 - Joplin-Compression

0_Claude et le Jux_VPS

C_ installation de Claude Code dans le VPS juxjux

Installation de Claude Code dans le VPS juxjux

Objectif

Pouvoir intervenir sur les containers du VPS (redémarrage, diagnostic, logs, fix rapide) depuis un mobile Android en mobilité, via SSH + tmux + Claude Code CLI directement installé sur le VPS.

Prérequis constatés (2026-07-21)

Installation de Claude Code CLI

Installation en local user (pas de sudo npm install -g) pour éviter de faire tourner le CLI en root et éviter les conflits de permissions npm globales :

mkdir -p ~/.npm-global
npm config set prefix '~/.npm-global'
npm install -g @anthropic-ai/claude-code

Ajout du PATH dans ~/.bashrc :

echo 'export PATH=~/.npm-global/bin:$PATH' >> ~/.bashrc

Version installée : 2.1.197

Authentification

Choix retenu : login OAuth avec le compte claude.ai (Julien, plan Claude Pro) — pas de clé API Anthropic séparée, donc pas de facturation à l'usage additionnelle.

Procédure suivie (le VPS est headless, pas de navigateur local) :

  1. Lancer claude dans une session tmux dédiée (tmux new-session -d -s claude) pour que la session survive à une déconnexion SSH
  2. Choisir "Claude account with subscription" au login
  3. Le CLI affiche une URL https://claude.com/cai/oauth/authorize?... — impossible à ouvrir depuis le VPS, donc récupérée via tmux capture-pane et ouverte manuellement sur un navigateur (PC ou mobile)
  4. Après connexion sur claude.ai, un code xxxx#yyyy est retourné — collé dans le prompt Paste code here if prompted > via tmux send-keys
  5. Confirmation : Logged in as julien.bertrand@live.fr

Les credentials sont persistés dans ~/.claude/.credentials.json (permissions 600, debian:debian) — la connexion reste valide pour les sessions futures, pas besoin de se ré-authentifier à chaque lancement.

Usage depuis Android (Termux)

  1. Installer Termux depuis F-Droid (pas le Play Store, version obsolète)
  2. Se connecter en SSH au VPS :
    ssh debian@51.77.141.54
    
  3. Rattacher ou créer la session tmux :
    tmux attach -t claude   # si la session existe déjà
    tmux new -s claude      # sinon
    
  4. Lancer claude (ou il tourne déjà si la session persiste)
  5. Détacher sans tuer la session : Ctrl+b puis d — permet de couper la connexion mobile sans interrompre Claude Code

Sécurité

Répertoire de lancement recommandé

Le VPS a déjà en local le CLAUDE.md partagé, synchronisé via Syncthing :

~/Documents/Jux_univers/Claude-pcelio+jux/CLAUDE.md

C'est le même fichier que celui utilisé sur le PC Windows (D:\Syncthing\Jux_univers\Claude-pcelio+jux\CLAUDE.md) et sur Ubuntu. Il fait partie du dossier Syncthing afltj-njyuy ("Tablette VPS Syncthing"), local path ~/Documents.

Lancer claude depuis ce dossier plutôt que /home/debian : le CLI charge automatiquement ce CLAUDE.md et récupère tout le contexte partagé (MCPs, infra, conventions) sans rien reconfigurer.

cd ~/Documents/Jux_univers/Claude-pcelio+jux/
claude

Piège constaté — Syncthing VPS silencieusement déconnecté (2026-07-21)

En vérifiant le CLAUDE.md sur le VPS, le fichier était resté figé au 24 juin alors que la version de référence (PC Windows) avait été mise à jour bien plus récemment (ex. section Inkscape du 21/07).

Diagnostic via le MCP Syncthing :

Autrement dit : Syncthing sur le VPS ne remonte aucune erreur quand il est déconnecté de tout le monde — il affiche juste l'état "idle" du dernier sync connu, ce qui peut faire croire à tort que tout est synchronisé.

Résolution : dès que Syncthing a été relancé côté PC Windows, la connexion TCP vers le VPS (device I4M5WXU) s'est rétablie en quelques secondes, et le CLAUDE.md s'est mis à jour immédiatement (nouveau hash, date du jour).

Point de vigilance pour la suite : si le CLAUDE.md sur le VPS semble périmé, ne pas se fier à get_folder_status seul (peut afficher "idle" à tort) — vérifier get_connections pour confirmer que le VPS a bien une connexion active vers au moins un appareil source avant de creuser plus loin.

Points d'attention pour la suite

0_Claude et le Jux_VPS

00_Recapitulatif

260802 - Allegement JVM Komga + StirlingPDF (applique le 2026-08-02 a 22h)

Application des optimisations memoire validees le 2026-08-01, apres verification du backup quotidien (BILAN 5/5 OK). Les deux stacks ont ete redeployees depuis /home/debian/{komga,stirling}-compose-new.yml, les composes precedents sauvegardes en .bak-260802.

ServiceAvantApresResultat
Komga (stack 7) -Xmx4g -Xms2g -XX:MaxRAMPercentage=70.0
limits 4500M / reservations 3000M
707 Mio residents
-Xmx1g
limits 1500M / reservations 256M
415 Mio (-292 Mio, -41 %)
StirlingPDF (stack 89) JVM sans limite
722 Mio residents
-Xmx512m
limits 2g
785 Mio — pas de reduction, mais consommation desormais bornee

Bilan hote : swap 3,7 Go -> 2,2 Go (-1,5 Go), memoire disponible 4,4 -> 4,6 Gi. L'essentiel du gain vient de Komga : c'est le -Xms2g qui pre-allouait 2 Go de heap au demarrage.

Pourquoi StirlingPDF ne baisse pas : le process java conserve 799 Mio de RSS malgre -Xmx512m (512 Mio de heap + non-heap : metaspace, code cache, piles de threads), auxquels s'ajoutent LibreOffice (soffice.bin, ~98 Mio), unoserver et Xvfb que Stirling lance pour les conversions. Le benefice obtenu n'est pas une baisse mais un plafond : la consommation ne peut plus deborder.

Limite portee de 1 Go a 2 Go avant application. Le plan initial prevoyait 1 Go. Or l'OCR fait tourner ocrmypdf et tesseract comme processus Python hors JVM : -Xmx ne les borne pas, seul le plafond cgroup du container s'applique. Pic mesure a 894-969 Mio pour une seule page A4 300 dpi — un plafond de 1 Go aurait provoque un OOM kill des le premier OCR. La version 1 Go est conservee en /home/debian/stirling-compose-new.yml.orig1g mais ne doit pas etre utilisee.

Verifications apres deploiement : Komga repond 200 et son API /api/v1/libraries renvoie bien les bibliotheques ; StirlingPDF valide ses deux fonctions critiques, compress-pdf (utilise par les workflows komga-pdf et nas_geo_compress) et l'OCR (page A4 300 dpi, pic 969 Mio, aucun OOM kill, aucun redemarrage).

Piege de deploiement : les projets compose s'appellent 7 et 89 (les IDs de stack Portainer), pas komga / stirlingpdf. La commande correcte est sudo docker compose -p 7 -f /var/lib/docker/volumes/portainer_data/_data/compose/7/docker-compose.yml up -d.

Rollback : restaurer le .bak-260802 de la stack concernee et redeployer de la meme facon.


Vue d ensemble - VPS Jux + Claude Code

34 containers en production sur le VPS. 25 pages de documentation, une par service. 13 services disposent d un MCP actif dans Claude Code. Mis a jour le 2026-05-30.

Page URL MCP Claude Code Statut integration
01_Portainer https://portainer.juxjux.ovh mcp__portainer__* MCP actif
02_Bookstack https://bookstack.juxjux.ovh bookstack-mcp-server (npm) MCP actif
03_Homarr https://homarr.juxjux.ovh mcp__homarr__* MCP actif
04_Audiobookshelf https://audiobookshelf.juxjux.ovh mcp__audiobookshelf__* MCP actif
05_BentoPDF - - API directe
06_Cadvisor - - via Prometheus
07_Calibre-web - - OPDS
08_Dozzle - - via Portainer MCP
09_FileBrowser https://files.juxjux.ovh mcp__filebrowser__* MCP actif
10_FreshRSS https://freshrss.juxjux.ovh mcp__freshrss__* MCP actif
11_Gitea - - API REST directe
12_Grafana https://grafana.juxjux.ovh - API REST directe
13_Immich https://immich.juxjux.ovh mcp__immich__* MCP actif (5 containers)
14_Jellyfin https://jellyfin.juxjux.ovh mcp__jellyfin__* MCP actif
15_Joplin - - sync server uniquement
16_Kavita https://kavita.juxjux.ovh mcp__kavita__* MCP actif
17_Komga https://komga.juxjux.ovh mcp__komga__* MCP actif
18_Mealie https://mealie.juxjux.ovh mcp__mealie__* MCP actif
19_Navidrome https://navidrome.juxjux.ovh mcp__navidrome__* MCP actif
20_NodeExporter - - via Prometheus
21_Prometheus - - API REST / PromQL
22_Readeck https://readeck.juxjux.ovh mcp__readeck__* MCP actif
23_Syncthing https://syncthing.juxjux.ovh mcp__syncthing__* MCP actif
24_Yourls - - API REST directe
25_StirlingPDF https://spdf.juxjux.ovh - API directe
A_liens entre Claude et le KDRIVE kdrive.infomaniak.com @infomaniak/mcp-server-kdrive + API REST MCP actif (kdrive_search) + API directe

Scripts de maintenance

fill_bookstack_pages.py Remplit les pages avec les infos containers (image, ports, volumes) C:\Users\eliob\.claude\fill_bookstack_pages.py
fill_bookstack_mcp.py Ajoute la section integration Claude Code / MCP a chaque page C:\Users\eliob\.claude\fill_bookstack_mcp.py

Configuration MCP

Scripts dans C:\Users\eliob\.claude\, enregistres dans C:\Users\eliob\.mcp.json. Projet Claude Code principal : C:\Users\eliob. Endpoint Portainer Docker local : ID 3.

260611-REX VPS par Claude

Releve du 2026-06-11 via Prometheus (PromQL sur localhost:9090) + Portainer MCP.

Etat global

RAM 8.4 / 11.41 GB (73.6%)
Swap 3.99 / 4.0 GB (quasi plein)
CPU 5.2%

Top consommateurs RAM (RSS)

Container RAM RSS
komga 1059 MB
Immich-SERVER 1011 MB
Immich-MICROSERVICES 520 MB
stirling-pdf 442 MB
homarr2026 355 MB
jellyfin 272 MB
prometheus 259 MB
jdownloader 220 MB
Kavita 196 MB
joplin 179 MB
calibre-web-eyrolles 132 MB
mealie 129 MB
grafana 125 MB
cadvisor 91 MB
navidrome 86 MB

Swap sature : komga + Immich representent ~2.5 GB a eux deux. StirlingPDF (442 MB) reste charge entre les appels.

 

260801-REX VPS par Claude

Releve du 2026-08-01 via docker stats + /proc en SSH (PC eliob).

Etat global

RAM 8.2 / 11.41 GB (71.7%)
Swap 4.0 / 4.0 GB (sature a 100%)
CPU 28% (compression 7z du backup en cours)

Top consommateurs RAM (RSS)

Container RAM RSS
Immich-SERVER 1147 MB
komga 791 MB
Immich-MICROSERVICES 618 MB
stirling-pdf 566 MB
jellyfin 380 MB
homarr2026 313 MB
Immich-DB 310 MB
prometheus 282 MB
joplin 241 MB
Kavita 240 MB
cadvisor 195 MB
grafana 164 MB
joplin-db 158 MB
yourls-db 157 MB
audiobookshelf 140 MB

Swap sature : komga (760 MB) + StirlingPDF (406 MB) en tete des processus swappes, Immich ~330 MB. Des limites memoire ont ete posees depuis juin sur les gros containers (komga 4.4G, Immich 2G, Kavita 4G, joplin 2G). Decouvertes du jour : GNOME/gdm3 tourne a vide sur tty1 depuis le 8 juillet (98 paquets installes, ~150 MB RAM, aucun acces distant configure) ; sauvegardes Immich vides depuis l'origine (pg_dump sans -d immich, archives de 4 Ko) — corrigees le 2026-08-01, dump reel 307 MB.

Actions realisees le 2026-08-01 (soir)

Action Detail Gain
Fix backup Immich -d immich ajoute dans /opt/backups/vps_backup.sh + run manuel complet 5/5 OK (archive 87 MB sur kDrive, premiere sauvegarde Immich valide) donnees enfin sauvegardees
Fusion Immich 5 containers → 3 : SERVER unique (API + workers, NODE_OPTIONS=--max-old-space-size=768), MICROSERVICES et LEARNING supprimes (recherche semantique et visages non utilises). Ancien compose : compose/29/docker-compose.yml.bak-260801 ~900 MB RAM
Arret cAdvisor docker update --restart=no cadvisor + stop. Plus de metriques par container dans Prometheus/Grafana (node-exporter conserve le host) ~200 MB RAM + 4-8% CPU
GDM/GNOME desactive puis purge gdm3 + gnome-shell + Xorg tournaient a vide sur tty1 depuis le 2026-07-08. systemctl disable --now gdm puis purge apt de 263 paquets (bureau complet + LibreOffice, jeux...). Verifie avant : ens3 gere par systemd-networkd, NetworkManager non critique. Verifie apres : 30 containers up, nginx/docker/ssh/wg actifs, sites en 200 ~150 MB RAM + 3,5 GB disque

Resultat global du 2026-08-01 : swap 4,0/4,0 GB (sature depuis juin) → 2,6/4,0 GB ; disque 62% → 59% ; ~1,3 GB de RAM liberes. Immich verifie fonctionnel par Julien.

Plan du 2026-08-02 a 19h (tache planifiee Claude Code)

Apres verification du backup nocturne (BILAN 5/5 OK), appliquer les deux fixes JVM prepares et valides (fichiers en attente dans /home/debian/) :

Service Avant Apres Gain attendu
Komga (stack 7) -Xmx4g -Xms2g (2 GB pre-alloues, ~760 MB en swap), limites 4500M/3000M -Xmx1g sans Xms, limites 1500M/256M ~400-500 MB RAM + ~700 MB swap
StirlingPDF (stack 89) JVM sans plafond (566-643 MB residents + ~406 MB swap) JAVA_TOOL_OPTIONS=-Xmx512m, limite 1g ~300-400 MB RAM

Verifications prevues : komga.juxjux.ovh en 200, test compress-pdf sur spdf.juxjux.ovh (fonction critique des workflows komga-pdf et geo_loud), docker stats, free -h (swap attendu sous ~2 GB). Rollback : restaurer les .bak-260802. Nota : BentoPDF n'existe plus sur le VPS (container supprime) — il ne peut de toute facon pas remplacer StirlingPDF (pas d'API serveur, traitement 100% navigateur).

0_Claude et le Jux_VPS

01_Portainer

portainer

Docker container management made simple, with the world’s most popular GUI-based container management platform.

Imageportainer/portainer-ce:latest
EtatUp 9 hours
Reseaubridge
URLhttps://portainer.juxjux.ovh

Ports

HoteContainerIP
94439443/tcp0.0.0.0
94439443/tcp::
80008000/tcp0.0.0.0
80008000/tcp::
90009000/tcp0.0.0.0
90009000/tcp::

Volumes

Source (hote)DestinationTypeMode
/var/lib/docker/volumes/portainer_data/_data/datavolumerw
/var/run/docker.sock/var/run/docker.sockbindrw

Integration Claude Code — MCP

Statut MCPMCP actif
Outils (prefix)mcp__portainer__
ConfigurationScript Python : C:\Users\eliob\.claude\mcp_portainer.py | Projet Claude Code : C:\Users\eliob | Auth : JWT via POST /api/auth, endpoint Docker local ID 3

Outils disponibles

Ce que nous pouvons faire ensemble

0_Claude et le Jux_VPS

02_Bookstack

bookstack

[Bookstack](https://github.com/BookStackApp/BookStack) is a free and open source Wiki designed for creating beautiful documentation. Featuring a simple, but powerful WYSIWYG editor it allows for teams to create detailed and useful documentation with ease. Powered by SQL and including a Markdown editor for those who prefer it, BookStack is geared towards making documentation more of a pleasure than a chore. For more information on BookStack visit their website and check it out: https://www.bookstackapp.com

Imagelscr.io/linuxserver/bookstack:latest
Versionv26.03.3-ls256
EtatUp 9 hours
Compose projectbookstack
Reseaubookstack_default
URLhttps://bookstack.juxjux.ovh
Sourcehttps://github.com/linuxserver/docker-bookstack

Ports

HoteContainerIP
687580/tcp0.0.0.0
687580/tcp::

Volumes

Source (hote)DestinationTypeMode
/home/debian/docker/bookstack/app/configbindrw

bookstack_db

[Mariadb](https://mariadb.org/) is one of the most popular database servers. Made by the original developers of MySQL.

Imagelscr.io/linuxserver/mariadb:latest
Version11.4.9-r0-ls212
EtatUp 9 hours
Compose projectbookstack
Reseaubookstack_default
URLhttps://bookstack.juxjux.ovh
Sourcehttps://github.com/linuxserver/docker-mariadb

Volumes

Source (hote)DestinationTypeMode
/home/debian/docker/bookstack/db/configbindrw

Integration Claude Code — MCP

Statut MCPMCP actif
Outils (prefix)bookstack MCP (npm bookstack-mcp-server)
Configurationnpm via npx -y bookstack-mcp-server | Projet Claude Code : C:\Users\eliob | Auth : Token ID + Secret en variables d'environnement (.claude.json)

Outils disponibles

Ce que nous pouvons faire ensemble


Sauvegarde de la base de données

Sauvegarde automatique quotidienne de la base MariaDB bookstackapp vers kDrive Infomaniak. Mise en place le 2026-06-01.

Paramètres

Script/opt/backups/bookstack_backup.sh
CronTous les jours à 2h du matin (0 2 * * *)
Base sauvegardéebookstackapp — container bookstack_db (MariaDB)
Taille base (disque)~203 Mo
Format archive.7z (compression niveau 5) — ~564 Ko
DestinationkDrive Infomaniak — TOOJUX/jux_vps/bookstack (dossier ID 1427916)
Rétention7 jours glissants (purge automatique des anciens fichiers)
Log/opt/backups/backup.log
Nommage fichierbookstack_db_YYYY-MM-DD.7z

Fonctionnement du script

  1. Exécute mysqldump depuis le container bookstack_db vers /tmp/
  2. Compresse en .7z avec 7z (déjà installé sur le VPS)
  3. Upload sur kDrive via l'API REST Infomaniak (POST /3/drive/{id}/upload) avec le paramètre total_size
  4. Supprime le fichier temporaire local
  5. Purge les fichiers kDrive nommés bookstack_db_* de plus de 7 jours

Consulter les logs

ssh debian@51.77.141.54 "tail -50 /opt/backups/backup.log"

Relancer manuellement

ssh debian@51.77.141.54 "bash /opt/backups/bookstack_backup.sh"
0_Claude et le Jux_VPS

03_Homarr

homarr2026

A modern and easy to use dashboard. 30+ integrations. 10K+ icons built in. Authentication out of the box. No YAML, drag and drop configuration.

Imageghcr.io/homarr-labs/homarr:latest
Versionmain
EtatUp 9 hours
Compose projecthomarr
Reseauhomarr_homarr_network
URLhttps://homarr.juxjux.ovh
Sourcehttps://github.com/homarr-labs/homarr

Ports

HoteContainerIP
75753000/tcp0.0.0.0
75753000/tcp::

Volumes

Source (hote)DestinationTypeMode
/etc/localtime/etc/localtimebindro
/var/run/docker.sock/var/run/docker.sockbindro
/home/debian/docker/homarr2026/icons/app/public/iconsbindrw
/home/debian/docker/homarr2026/appdata/appdatabindrw

Integration Claude Code — MCP

Statut MCPMCP actif
Outils (prefix)mcp__homarr__
ConfigurationScript Python : C:\Users\eliob\.claude\mcp_homarr.py | Projet Claude Code : C:\Users\eliob | Auth : cookie NextAuth session (authjs.session-token)

Outils disponibles

Ce que nous pouvons faire ensemble


Mise a jour disponible (verifie le 2026-07-19)

Version installee0.53
Derniere version1.71.0 (2026-07-17)
EcartTraverse la refonte majeure 1.0 (reecriture complete, ete 2024) + ~70 releases mineures jusqu'a juillet 2026

Attention : migration majeure, pas un simple bump

Le projet a ete entierement reecrit en version 1.0 et le depot a change de proprietaire : ghcr.io/ajnart/homarr -> ghcr.io/homarr-labs/homarr. Le stack VPS (homarr2026) utilise deja l'image homarr-labs avec le tag latest et un volume /appdata, donc une partie de la migration structurelle semble deja faite -- a verifier avant de bumper vers un tag fige.

Principaux breaking changes (0.x -> 1.0)

Nouveautes notables depuis la 1.0 (jusqu'a 1.71.0)

Etapes de migration recommandees

  1. Sauvegarder /home/debian/docker/homarr2026/appdata avant toute action
  2. Exporter la config du dashboard depuis l'UI (fonction export/backup SQLite)
  3. Verifier les variables d'environnement du compose homarr2026 contre la liste des breaking changes ci-dessus (notamment SECRET_ENCRYPTION_KEY, DOCKER_HOSTNAMES/DOCKER_PORTS)
  4. Redeployer le stack, puis retester chaque integration (Jellyfin, Sonarr, etc.) -- test de connexion obligatoire en 1.x
  5. Reimporter la sauvegarde SQLite si necessaire

Sources : github.com/homarr-labs/homarr/releases, homarr.dev/blog - Homarr 1.0

0_Claude et le Jux_VPS

04_Audiobookshelf

audiobookshelf

Self-hosted audiobook and podcast server

Imageghcr.io/advplyr/audiobookshelf:latest
Version2.35.0
EtatUp (healthy)
Compose projectaudiobookshelf
Reseauaudiobookshelf_audiobookshelf_network
URLhttps://audiobookshelf.juxjux.ovh
Sourcehttps://github.com/advplyr/audiobookshelf

Ports

HoteContainerIP
1337880/tcp127.0.0.1

Volumes

Source (hote)DestinationTypeMode
/home/debian/audiobookshelf/config/configbindrw
/home/debian/audiobookshelf/metadata/metadatabindrw
/home/debian/audiobookshelf/podcasts/podcastsbindrw
/home/debian/audiobookshelf/audiobooks/audiobooksbindrw, :shared

Integration Claude Code — MCP

Statut MCPMCP actif
Outils (prefix)mcp__audiobookshelf__
ConfigurationScript Python : C:\Users\eliob\.claude\mcp_audiobookshelf.py | Auth : JWT Bearer via POST /login

Outils disponibles

Ce que nous pouvons faire ensemble


Scan quotidien automatique — 2026-06-24

Un cron tourne chaque nuit à 04h00 UTC pour scanner toutes les bibliothèques Audiobookshelf et détecter les nouveaux contenus ou les renommages de dossiers.

Script

/home/debian/abs_scan.sh — log : /var/log/abs_scan.log

0 4 * * * bash /home/debian/abs_scan.sh

Fonctionnement

  1. Authentification via POST /login pour obtenir un token JWT frais
  2. Récupération de toutes les bibliothèques via GET /api/libraries
  3. Déclenchement d'un scan pour chaque bibliothèque via POST /api/libraries/{id}/scan

Bibliothèques scannées : 36 (audiobooks + podcasts). Durée estimée : quelques minutes.

Exemple de log :

2026-06-24 04:00 SCAN OK 36/36 libs

REX — 2026-06-24 : audiobook illisible après renommage de dossier

Contexte

Audiobookshelf ne pouvait plus lire Les routes de la soie (Peter Frankopan). Erreur dans les logs :

Error: ENOENT: no such file or directory, stat '/audiobooks/Histoire/2 - Moyen Age/Peter Frankopan - Les routes de la soie/27_CH_08_LA_ROUTE_DU_CIEL.mp3'

Cause

Le dossier avait été renommé sur kDrive/NAS de Peter Frankopan - Les routes de la soie en Peter Frankopan-les routes de la soie (tiret sans espaces, minuscule). Audiobookshelf conservait l'ancien chemin dans sa base.

Solution

Le scan quotidien abs_scan.sh détecte ce type de désynchronisation et met à jour les chemins. Pour forcer immédiatement : lancer un scan manuel depuis l'interface Audiobookshelf ou exécuter bash /home/debian/abs_scan.sh.

0_Claude et le Jux_VPS

05_BentoPDF

bentopdf

Unprivileged NGINX Dockerfiles

Imagebentopdfteam/bentopdf-simple:latest
Version1.28.0-alpine-slim
EtatUp 9 hours
Compose projectbentopdf
Reseaubentopdf_default
URLhttps://bento.juxjux.ovh
Sourcehttps://github.com/alam00000/bentopdf

Ports

HoteContainerIP
80898080/tcp0.0.0.0
80898080/tcp::

Integration Claude Code — API directe

Statut MCPPas de MCP — API directe possible
Note techniqueAPI REST interne, pas de MCP configuré. Accessible via PowerShell/Python httpx.

Ce que nous pouvons faire ensemble

0_Claude et le Jux_VPS

06_Cadvisor

cadvisor

Imagegcr.io/cadvisor/cadvisor:latest
EtatUp 9 hours (healthy)
Compose projectgemini_grafana
Reseaugemini_grafana_monitor_net

Volumes

Source (hote)DestinationTypeMode
/var/run/var/runbindrw
//rootbindro
/sys/sysbindro
/var/lib/docker/var/lib/dockerbindro

Integration Claude Code — API directe

Statut MCPPas de MCP — API directe possible
Note techniqueEndpoint Prometheus /metrics exposé à Prometheus (port 8080). Pas de MCP. Données lisibles via Prometheus MCP ou requêtes PromQL directes.

Ce que nous pouvons faire ensemble

0_Claude et le Jux_VPS

07_Calibre-web

calibre-web-eyrolles

[Calibre-web](https://github.com/janeczku/calibre-web) is a web app providing a clean interface for browsing, reading and downloading eBooks using an existing Calibre database. It is also possible to integrate google drive and edit metadata and your calibre library through the app itself. This software is a fork of library and licensed under the GPL v3 License.

Imagelscr.io/linuxserver/calibre-web:latest
Version0.6.26-ls383
EtatUp 9 hours (healthy)
Compose projectcalibreweb_automated
Reseaucalibre_network
Sourcehttps://github.com/linuxserver/docker-calibre-web

Ports

HoteContainerIP
82148083/tcp0.0.0.0
82148083/tcp::

Volumes

Source (hote)DestinationTypeMode
/home/debian/livres/Eyrolles/booksbindrw
/home/debian/docker/calibre-web-automated/config/configbindrw

Integration Claude Code — API directe

Statut MCPPas de MCP — API directe possible
Note techniqueInterface OPDS disponible pour parcourir la bibliothèque. API REST limitée. Pas de MCP configuré.

Ce que nous pouvons faire ensemble

0_Claude et le Jux_VPS

08_Dozzle

dozzle

Realtime log viewer for containers. Supports Docker, Swarm and K8s.

Imageamir20/dozzle:latest
Versionv8.14.9
EtatUp 9 hours
Compose projectdozzle
Reseaunginx_npm-network
URLhttps://dozzle.juxjux.ovh
Sourcehttps://github.com/amir20/dozzle

Ports

HoteContainerIP
90918080/tcp0.0.0.0
90918080/tcp::

Volumes

Source (hote)DestinationTypeMode
/var/run/docker.sock/var/run/docker.sockbindrw

Integration Claude Code — API directe

Statut MCPPas de MCP — API directe possible
Note techniqueInterface de visualisation de logs uniquement. Pas d'API publique exploitable. Les logs sont mieux accessibles via mcp__portainer__get_container_logs.

Ce que nous pouvons faire ensemble

0_Claude et le Jux_VPS

09_FileBrowser

File-Browser

Imagefilebrowser/filebrowser:latest
Version2.63.3
EtatUp 9 hours (healthy)
Compose projectfilebrowser
Reseaufilebrowser_default
URLhttps://files.juxjux.ovh
Sourcehttps://github.com/filebrowser/filebrowser

Ports

HoteContainerIP
814780/tcp0.0.0.0
814780/tcp::

Volumes

Source (hote)DestinationTypeMode
/home/debian/docker/filebrowser/database/databasebindrw
/home/debian/srvbindrw
/home/debian/docker/filebrowser/config/configbindrw

Integration Claude Code — MCP

Statut MCPMCP actif
Outils (prefix)mcp__filebrowser__
ConfigurationScript Python : C:\Users\eliob\.claude\mcp_filebrowser.py | Auth : JWT via POST /api/login (admin)

Outils disponibles

Ce que nous pouvons faire ensemble

0_Claude et le Jux_VPS

10_FreshRSS

freshrss

A free, self-hostable news aggregator…

Imagefreshrss/freshrss:latest
Version1.28.1
EtatUp 26 minutes
Compose projectfreshrss
Reseaufreshrss_default
URLhttps://freshrss.juxjux.ovh
Sourcehttps://github.com/FreshRSS/FreshRSS

Ports

HoteContainerIP
808180/tcp0.0.0.0
808180/tcp::

Volumes

Source (hote)DestinationTypeMode
/home/debian/docker/freshrss/data/var/www/FreshRSS/databindrw

Integration Claude Code — MCP

Statut MCPMCP actif
Outils (prefix)mcp__freshrss__
ConfigurationScript Python : C:\Users\eliob\.claude\mcp_freshrss.py | Auth : GReader API avec mot de passe API dédié (≠ mot de passe web)

Outils disponibles

Ce que nous pouvons faire ensemble

0_Claude et le Jux_VPS

11_Gitea

gitea

Git with a cup of tea! Painless self-hosted all-in-one software development service, including Git hosting, code review, team collaboration, package registry and CI/CD

Imagegitea/gitea:latest
Version1.25.4
EtatUp 9 hours (healthy)
Compose projectgitea
Reseaugitea_default
URLhttps://gitea.juxjux.ovh
Sourcehttps://github.com/go-gitea/gitea

Ports

HoteContainerIP
22222222/tcp0.0.0.0
22222222/tcp::
30053000/tcp0.0.0.0
30053000/tcp::

Volumes

Source (hote)DestinationTypeMode
/var/lib/gitea_data/databindrw
/etc/localtime/etc/localtimebindro
/etc/timezone/etc/timezonebindro

Integration Claude Code — API directe

Statut MCPPas de MCP — API directe possible
Note techniqueAPI REST Gitea complète disponible sur /api/v1. Auth : token Bearer ou Basic Auth. Pas de MCP configuré — appels directs via PowerShell/Python.

Ce que nous pouvons faire ensemble

0_Claude et le Jux_VPS

12_Grafana

grafana

Image grafana/grafana:latest
Etat Up 5 days (healthy)
Compose project gemini_grafana (stack ID 63)
Réseau gemini_grafana_monitor_net
URL https://grafana.juxjux.ovh
Source https://github.com/grafana/grafana

Ports

Hote Container IP
3000 3000/tcp 0.0.0.0
3000 3000/tcp ::

Volumes

Source (hote) Destination Type Mode
/opt/monitoring/grafana_data /var/lib/grafana bind rw

Intégration Claude Code — API directe

Statut MCP Pas de MCP — API directe possible
Note technique API REST Grafana sur /api. Auth : clé API ou Basic Admin. Pas de MCP configuré — appels directs possibles.

Ce que nous pouvons faire ensemble


Compte rendu — 2026-06-11

Vérification via Portainer MCP (endpoint 3) + Prometheus PromQL (localhost:9090).

Métriques VPS globales

Métrique Valeur
RAM 8.4 / 11.41 GB 73.6%
Swap 3.99 / 4.0 GB quasi plein
CPU 5.2%

Stack gemini_grafana — 4 containers, tous running (5 jours)

Container Image Etat Healthcheck Port exposé
grafana grafana/grafana:latest running healthy 3000
prometheus prom/prometheus:latest running 9090
node-exporter prom/node-exporter:latest running 9100
cadvisor gcr.io/cadvisor/cadvisor:latest running healthy 8080 (interne)

Notes

0_Claude et le Jux_VPS

13_Immich

Immich-SERVER

High performance self-hosted photo and video management solution.

Imageghcr.io/immich-app/immich-server:release
Versionv2.7.5
EtatUp (healthy)
Compose projectimmich
Reseauimmich_immich-network
URLhttps://immich.juxjux.ovh
Sourcehttps://github.com/immich-app/immich

Ports

HoteContainerIP
82122283/tcp0.0.0.0
82122283/tcp::

Volumes

Source (hote)DestinationTypeMode
/home/debian/docker/immich/upload/databindrw
/etc/localtime/etc/localtimebindro
/home/debian/docker/immich/upload/encoded-video/mnt/media/encoded-videobindrw
/home/debian/photo/mnt/media/photobindro,shared

Immich-MICROSERVICES

High performance self-hosted photo and video management solution.

Imageghcr.io/immich-app/immich-server:release
Versionv2.7.5
EtatUp (healthy)
Compose projectimmich
Reseauimmich_immich-network
URLhttps://immich.juxjux.ovh
Sourcehttps://github.com/immich-app/immich

Volumes

Source (hote)DestinationTypeMode
/home/debian/docker/immich/upload/databindrw
/etc/localtime/etc/localtimebindro
/home/debian/docker/immich/upload/encoded-video/mnt/media/encoded-videobindrw
/home/debian/photo/mnt/media/photobindro,shared

Immich-LEARNING

High performance self-hosted photo and video management solution.

Imageghcr.io/immich-app/immich-machine-learning:release
Versionv2.7.5
EtatUp (healthy)
Compose projectimmich
Reseauimmich_immich-network
URLhttps://immich.juxjux.ovh
Sourcehttps://github.com/immich-app/immich

Volumes

Source (hote)DestinationTypeMode
/home/debian/docker/immich/cache/cachebindrw

Immich-REDIS

Imageredis:6.2-alpine
EtatUp (healthy)
Compose projectimmich
Reseauimmich_immich-network
URLhttps://immich.juxjux.ovh

Volumes

Source (hote)DestinationTypeMode
 /datavolumerw

Immich-DB

Base images for Immich containers

Imageghcr.io/immich-app/postgres:16-vectorchord0.3.0-pgvectors0.2.0
Version16-vectorchord0.3.0-pgvector0.8.1-pgvectors0.2.0
EtatUp (healthy)
Compose projectimmich
Reseauimmich_immich-network
URLhttps://immich.juxjux.ovh
Sourcehttps://github.com/immich-app/base-images

Volumes

Source (hote)DestinationTypeMode
/home/debian/docker/immich/db/var/lib/postgresql/databindrw

Integration Claude Code — MCP

Statut MCPMCP actif
Outils (prefix)mcp__immich__
ConfigurationScript Python : C:\Users\eliob\.claude\mcp_immich.py | Auth : JWT via POST /api/auth/login

Outils disponibles

Ce que nous pouvons faire ensemble


Sauvegarde de la base de données

Sauvegarde automatique quotidienne de la base PostgreSQL Immich-DB vers kDrive Infomaniak. Mise en place le 2026-06-01.

Paramètres

Script/opt/backups/vps_backup.sh
CronTous les jours à 2h du matin (0 2 * * *)
Container DBImmich-DB (PostgreSQL 16)
Commandepg_dump -U postgres
Taille base (disque)~7.5 Mo (métadonnées uniquement — photos sur volume séparé)
Format archive.7z (compression niveau 5)
DestinationkDrive Infomaniak — TOOJUX/jux_vps/immich (dossier ID 1427919)
Rétention7 jours glissants
Nommage fichierimmich_db_YYYY-MM-DD.7z

Note importante

Cette sauvegarde couvre uniquement les métadonnées (albums, faces, tags, utilisateurs). Les fichiers photos/vidéos sont stockés dans /home/debian/docker/immich/upload et /home/debian/photo — ils ne sont pas inclus dans ce backup.


REX — 2026-06-21 — FUSE cassé après restart rclone-photo

Symptôme

Immich connecté mais aucune photo visible. Logs Immich-SERVER :

WARN [Microservices:LibraryService] Skipping invalid import path: /mnt/media/photo. Reason: Error: ENOTCONN: socket is not connected, stat '/mnt/media/photo'

Cause

Le service rclone-photo.service a redémarré le 2026-06-18 (suite à l'incident sécurité du 2026-06-09 — fix --rc-addr 127.0.0.1). Les containers Immich-SERVER et Immich-MICROSERVICES, démarrés avant ce restart, conservent un file descriptor FUSE invalide. La propagation :shared aide pour les nouveaux montages mais ne restaure pas un fd FUSE mort.

Fix immédiat

docker restart Immich-SERVER Immich-MICROSERVICES
# puis déclencher un scan de bibliothèque :
curl -s -X POST "https://immich.juxjux.ovh/api/libraries/89bfcdc1-1604-47a1-90a3-975d2f6c4639/scan" \
  -H "Authorization: Bearer TOKEN"
# TOKEN obtenu via POST /api/auth/login (bertrand.dadone@gmail.com / 27101809)

Fix structurel — ExecStartPost dans rclone-photo.service (2026-06-21)

Ajout d'un ExecStartPost dans /etc/systemd/system/rclone-photo.service : à chaque redémarrage de rclone-photo, les containers Immich sont automatiquement relancés si et seulement s'ils tournent déjà (pas de blocage au boot).

ExecStartPost=/bin/bash -c 'sleep 3; docker ps --filter name=Immich-SERVER --filter status=running -q | grep -q . && docker restart Immich-SERVER Immich-MICROSERVICES || true'

Rechargement : sudo systemctl daemon-reload (pas de restart du service nécessaire — prendra effet au prochain redémarrage de rclone-photo).

Library ID Immich

89bfcdc1-1604-47a1-90a3-975d2f6c4639


Inventaire par Claude le 251001

0_Claude et le Jux_VPS

14_Jellyfin

jellyfin

The Free Software Media System

Imagejellyfin/jellyfin:latest
Version10.10.7
EtatUp 9 hours (healthy)
Compose projectjellyfin
Reseauhost
URLhttps://jellyfin.juxjux.ovh
Sourcehttps://github.com/jellyfin/jellyfin-packaging

Volumes

Source (hote)DestinationTypeMode
/mnt/nas_videos/movies/data/moviesbindrw
/mnt/nas_videos/palettes/data/palettesbindrw
/home/debian/jellyfin/config/configbindrw
/mnt/nas_videos/cinema_europe/data/cinema_europebindrw
/mnt/nas_videos/cinema_UK/data/cinema_ukbindrw
/mnt/nas_videos/spectacles/data/spectaclesbindrw
/cachevolumerw
/mnt/nas_videos/animes/data/animesbindrw
/mnt/nas_videos/cinema_asie/data/cinema_asiebindrw
/mnt/nas_videos/cinema_realisateurs/data/cinema_realisateursbindrw
/mnt/nas_videos/fantastique_et_SF/data/fantastique_et_SFbindrw
/mnt/nas_videos/historiques/data/historiquesbindrw
/mnt/nas_videos/series/data/seriesbindrw
/mnt/nas_videos/cinema_italie/data/cinema_italiebindrw
/mnt/nas_videos/culture_cuisine/data/culture_cuisinebindrw
/mnt/nas_videos/documentaires/data/documentairesbindrw

Integration Claude Code — MCP

Statut MCPMCP actif
Outils (prefix)mcp__jellyfin__
ConfigurationScript Python : C:\Users\eliob\.claude\mcp_jellyfin.py | Auth : JWT MediaBrowser Token via POST /Users/AuthenticateByName

Outils disponibles

Ce que nous pouvons faire ensemble

0_Claude et le Jux_VPS

15_Joplin

joplin_to_obsidian

Imagejoplin_to_obsidian-joplin_to_obsidian
EtatUp 9 hours
Compose projectjoplin_to_obsidian
Reseaujoplin_joplin_network
URLhttps://joplin.juxjux.ovh

Volumes

Source (hote)DestinationTypeMode
/home/debian/Documents/Joplin_Obsidian/home/debian/Documents/Joplin_Obsidianbindrw

joplin-nginx

Imagenginx:alpine
EtatUp 9 hours
Compose projectjoplin
Reseaujoplin_joplin_network
URLhttps://joplin.juxjux.ovh

Ports

HoteContainerIP
2230180/tcp0.0.0.0
2230180/tcp::

joplin

Docker image for Joplin Server

Imagejoplin/server:latest
Version3.4.3
EtatUp 9 hours
Compose projectjoplin
Reseaujoplin_joplin_network
URLhttps://joplin.juxjux.ovh
Sourcehttps://github.com/laurent22/joplin.git

Ports

HoteContainerIP
2230022300/tcp0.0.0.0
2230022300/tcp::

Volumes

Source (hote)DestinationTypeMode
/home/debian/joplin-data/home/joplin/.config/joplinbindrw

joplin-db

Imagepostgres:15-alpine
EtatUp 9 hours
Compose projectjoplin
Reseaujoplin_joplin_network
URLhttps://joplin.juxjux.ovh

Volumes

Source (hote)DestinationTypeMode
/home/debian/joplin-db-data/var/lib/postgresql/databindrw

Integration Claude Code — API directe

Statut MCPPas de MCP — API directe possible
Note techniqueLe container Joplin est un serveur de synchronisation (sync server), pas un serveur de notes interrogeable. Pas d'API de lecture du contenu. Le container joplin_to_obsidian est un script de migration ponctuelle vers Obsidian.

Ce que nous pouvons faire ensemble


Sauvegarde de la base de données

Sauvegarde automatique quotidienne de la base PostgreSQL joplin-db vers kDrive Infomaniak. Mise en place le 2026-06-01.

Paramètres

Script/opt/backups/vps_backup.sh
CronTous les jours à 2h du matin (0 2 * * *)
Container DBjoplin-db (PostgreSQL 15)
Commandepg_dump -U joplin -d joplin
Taille base (disque)~1.4 Go (notes + pièces jointes stockées en BLOB)
Format archive.7z (compression niveau 5)
DestinationkDrive Infomaniak — TOOJUX/jux_vps/joplin (dossier ID 1427920)
Rétention7 jours glissants
Nommage fichierjoplin_db_YYYY-MM-DD.7z

Note

La base est volumineuse (~1.4 Go) car Joplin stocke toutes les pièces jointes en BLOB dans PostgreSQL. L'archive compressée pèse ~500 Mo à 1 Go. Quota kDrive consommé : ~3.5 à 7 Go pour 7 jours.


Observatoire Joplin-Compression

Mis ? jour le 14/06/2026 12:50 ? dernier run compression : 2026-06-14T10:47

Bilan global

Ressources totales (actuelles)736 fichiers
Poids total actuel119.7 Mo
Ressources compress?es424
Poids avant compression451.5 Mo
?conomie r?alis?e356.3 Mo (-78.9%)
Ignor?es (gain insuffisant)212
Erreurs0

Top 15 ressources les plus lourdes (?tat actuel)

#IDFormatTaille actuelleGain compression
1vVO2ePdMvtty3sXZy5nY6M?3146 KB
20xh87pWrRt82z0DbO9n3yQZIP2250 KB-81%
3VasIoF2e9EGuNQt38aOFMxJPEG1633 KB-66%
4TzWK21r4n0yvbEtJh02DGBJPEG1529 KB-63%
5M0C32hKC2hMqPAxpRGiVgpJPEG937 KB-87%
6Hn6z6lvvbJzRsX0KGM8FNWJPEG928 KB-53%
77EyAL9NNysJzWke6NT4ttJJPEG803 KB-76%
8bz9Twmb2F5lj0mPIQi48IBJPEG798 KB-81%
9UbAqQY3cEbH62vioIHU6PjJPEG776 KB-87%
10SrJi1UyeNGf8OhaRsaOCGqJPEG745 KB-79%
1187mV49tXupZcpt2wplcGjUJPEG685 KB-59%
12JsJFO6dIzqMKLA7clqW8nGJPEG683 KB-66%
13VSyNmO0uN1fNNeqN6XZucOJPEG674 KB-19%
14hExoJEEWT2tHz1demE5NhmJPEG674 KB-84%
15slTzAxAQ4xi0pMvzsLIDez?670 KB

Observatoire Joplin-Compression

Mis ? jour le 15/06/2026 03:00 ? dernier run compression : 2026-06-15T03:00

Bilan global

Ressources totales (actuelles)736 fichiers
Poids total actuel119.9 Mo
Ressources compress?es425
Poids avant compression452.0 Mo
?conomie r?alis?e356.4 Mo (-78.8%)
Ignor?es (gain insuffisant)212
Erreurs0

Top 15 ressources les plus lourdes (?tat actuel)

#IDFormatTaille actuelleGain compression
1vVO2ePdMvtty3sXZy5nY6M?3146 KB
20xh87pWrRt82z0DbO9n3yQZIP2250 KB-81%
3VasIoF2e9EGuNQt38aOFMxJPEG1633 KB-66%
4TzWK21r4n0yvbEtJh02DGBJPEG1529 KB-63%
5M0C32hKC2hMqPAxpRGiVgpJPEG937 KB-87%
6Hn6z6lvvbJzRsX0KGM8FNWJPEG928 KB-53%
77EyAL9NNysJzWke6NT4ttJJPEG803 KB-76%
8bz9Twmb2F5lj0mPIQi48IBJPEG798 KB-81%
9UbAqQY3cEbH62vioIHU6PjJPEG776 KB-87%
10SrJi1UyeNGf8OhaRsaOCGqJPEG745 KB-79%
1187mV49tXupZcpt2wplcGjUJPEG685 KB-59%
12JsJFO6dIzqMKLA7clqW8nGJPEG683 KB-66%
13VSyNmO0uN1fNNeqN6XZucOJPEG674 KB-19%
14hExoJEEWT2tHz1demE5NhmJPEG674 KB-84%
15slTzAxAQ4xi0pMvzsLIDez?670 KB

Observatoire Joplin-Compression

Mis ? jour le 16/06/2026 03:00 ? dernier run compression : 2026-06-16T03:00

Bilan global

Ressources totales (actuelles)736 fichiers
Poids total actuel119.9 Mo
Ressources compress?es425
Poids avant compression452.0 Mo
?conomie r?alis?e356.4 Mo (-78.8%)
Ignor?es (gain insuffisant)212
Erreurs0

Top 15 ressources les plus lourdes (?tat actuel)

#IDFormatTaille actuelleGain compression
1vVO2ePdMvtty3sXZy5nY6M?3146 KB
20xh87pWrRt82z0DbO9n3yQZIP2250 KB-81%
3VasIoF2e9EGuNQt38aOFMxJPEG1633 KB-66%
4TzWK21r4n0yvbEtJh02DGBJPEG1529 KB-63%
5M0C32hKC2hMqPAxpRGiVgpJPEG937 KB-87%
6Hn6z6lvvbJzRsX0KGM8FNWJPEG928 KB-53%
77EyAL9NNysJzWke6NT4ttJJPEG803 KB-76%
8bz9Twmb2F5lj0mPIQi48IBJPEG798 KB-81%
9UbAqQY3cEbH62vioIHU6PjJPEG776 KB-87%
10SrJi1UyeNGf8OhaRsaOCGqJPEG745 KB-79%
1187mV49tXupZcpt2wplcGjUJPEG685 KB-59%
12JsJFO6dIzqMKLA7clqW8nGJPEG683 KB-66%
13VSyNmO0uN1fNNeqN6XZucOJPEG674 KB-19%
14hExoJEEWT2tHz1demE5NhmJPEG674 KB-84%
15slTzAxAQ4xi0pMvzsLIDez?670 KB
0_Claude et le Jux_VPS

16_Kavita

Kavita

Kavita is a fast, feature rich, cross platform reading server. Built with the goal of being a full solution for all your reading needs. Setup your own server and share your reading collection with your friends and family.

Image jvmilazz0/kavita:latest
Version latest
Etat Up (healthy)
Compose project kavita
Réseau kavita_default
URL https://kavita.juxjux.ovh
Source https://github.com/Kareadita/Kavita

Ports

Hote Container IP
5471 5000/tcp 0.0.0.0

Volumes

Source (hote) Destination Type Mode
/home/debian/docker/kavita/config /kavita/config bind rw
/home/debian/livres /manga bind rw

Integration Claude Code — MCP

Statut MCP MCP actif
Outils (prefix) mcp__kavita__
Configuration Script Python : C:\Users\eliob\.claude\mcp_kavita.py | Auth : JWT via POST /api/Account/login

Outils opérationnels (confirmés 2026-06-25)

Ce que nous pouvons faire ensemble


REX — 2026-06-25

Statistiques bibliothèque

Stat Valeur
Bibliothèques 53 (toutes de type Book/epub+PDF)
Séries 2 976
Fichiers 5 519
Volumes 1 173
Taille totale ~79 Go
Auteurs/contributeurs indexés 3 475
Genres indexés 6 990
Tags 0

Étiquettes Calibre → genres Kavita

Les étiquettes appliquées via Calibre sont visibles dans Kavita sous le champ genres, pas tags (qui reste toujours vide, totalTags: 0).

Mécanisme : Calibre écrit ses étiquettes dans <dc:subject> du fichier epub lors du writeback → Kavita lit ce champ et le mappe en genres.

Exemple confirmé — "Il faut s'adapter" (Barbara Stiegler, lib. Actualités, ID série 4210) :

Conditions pour que ça fonctionne :

La bibliothèque Actualités (ID 63) est entièrement étiquetée et correctement indexée.

Lecture du contenu

Le MCP ne permet pas de lire le contenu des livres — uniquement les métadonnées. Kavita expose des endpoints de lecture (/api/Reader/image, /api/Reader/epub-file) mais non implémentés dans le MCP. Pour les epub, la lecture via appel API direct + parsing HTML serait techniquement faisable au cas par cas.

Corrections endpoints MCP (2026-06-25)

4 outils retournaient 404 suite à des changements d'API dans la version Kavita en production. Corrections appliquées dans mcp_kavita.py :

Outil Ancien endpoint (404) Nouveau endpoint (200)
get_collections GET /api/Collection/list GET /api/Collection
get_reading_lists GET /api/ReadingList/lists?includePromoted=true POST /api/ReadingList/lists body {}
get_want_to_read POST /api/Want-To-Read/get-list POST /api/Want-To-Read
get_user_stats GET /api/Stats/user/0/read GET /api/Stats/user-stats?userId={id}

Note : le userId est désormais extrait dynamiquement du payload de login (data["id"]) et stocké dans _user_id.

Limitations MCP identifiées

Problème pipeline Calibre → Kavita — À résoudre

Constat (2026-06-25) : les fichiers enrichis par Calibre (avec étiquettes + métadonnées ISBN) ne remplacent pas les fichiers originaux dans le dossier Kavita. Kavita lit depuis le dossier source original (/manga = kDrive Sync_NAS-Maison/Foxy) qui contient les fichiers sans métadonnées.

Calibre, en mode par défaut, copie les fichiers dans sa propre arborescence et les enrichit là — les originaux restent intacts. Résultat : 98% des fichiers ont des métadonnées correctes dans Calibre, mais Kavita voit des genres vides.

Questions ouvertes à clarifier :

Piste de solution : exporter les fichiers enrichis depuis la bibliothèque Calibre vers le dossier source Kavita (via "Enregistrer sur le disque" dans Calibre), en remplacement des originaux. Peut s'automatiser. À instruire.

0_Claude et le Jux_VPS

17_Komga

komga

Imagegotson/komga
Version24.10
EtatUp 9 hours
Compose projectkomga
Reseaukomga_default
URLhttps://komga.juxjux.ovh
Sourcehttps://github.com/gotson/komga

Ports

HoteContainerIP
2560025600/tcp0.0.0.0
2560025600/tcp::

Volumes

Source (hote)DestinationTypeMode
/home/debian/docker/komga/config/configbindrw
/home/debian/komga/databindrw
/etc/timezone/etc/timezonebindro
/tmpvolumerw

Integration Claude Code — MCP

Statut MCPMCP actif
Outils (prefix)mcp__komga__
ConfigurationScript Python : C:\Users\eliob\.claude\mcp_komga.py | Auth : Basic Auth (Base64 login:password)

Outils disponibles

Ce que nous pouvons faire ensemble

0_Claude et le Jux_VPS

18_Mealie

mealie

Mealie is a self hosted recipe manager and meal planner with a RestAPI backend and a reactive frontend application built in Vue for a pleasant user experience for the whole family. Easily add recipes into your database by providing the url and mealie will automatically import the relevant data or add a family recipe with the UI editor

Imageghcr.io/mealie-recipes/mealie:latest
Versionv3.16.0
EtatUp 9 hours (healthy)
Compose projectmealie2
Reseaumealie2_default
URLhttps://mealie.juxjux.ovh
Sourcehttps://github.com/mealie-recipes/mealie

Ports

HoteContainerIP
99259000/tcp0.0.0.0
99259000/tcp::

Volumes

Source (hote)DestinationTypeMode
/home/debian/docker/mealie/backups/app/backupsbindrw
/home/debian/docker/mealie/config/app/configbindrw
/home/debian/docker/mealie/data/app/databindrw

Integration Claude Code — MCP

Statut MCPMCP actif
Outils (prefix)mcp__mealie__
ConfigurationScript Python : C:\Users\eliob\.claude\mcp_mealie.py | Auth : OAuth2 Bearer Token via POST /api/auth/token

Outils disponibles

Ce que nous pouvons faire ensemble


Sauvegarde de la base de données

Sauvegarde automatique quotidienne de la base SQLite de Mealie vers kDrive Infomaniak. Mise en place le 2026-06-01.

Paramètres

Script/opt/backups/vps_backup.sh
CronTous les jours à 2h du matin (0 2 * * *)
Type baseSQLite — /app/data/mealie.db (dans le container)
Méthodedocker cp mealie:/app/data/mealie.db
Taille base~2.9 Mo
Format archive.7z (compression niveau 5)
DestinationkDrive Infomaniak — TOOJUX/jux_vps/mealie (dossier ID 1427921)
Rétention7 jours glissants
Nommage fichiermealie_db_YYYY-MM-DD.7z
0_Claude et le Jux_VPS

19_Navidrome

navidrome-navidrome-1

🎧 Your Personal Streaming Service

Image deluan/navidrome:latest
Version 0.61.2
Etat Up
Compose project navidrome
Reseau navidrome_default
URL https://navidrome.juxjux.ovh
Source https://github.com/navidrome/navidrome

Ports

Hote Container IP
4533 4533/tcp 0.0.0.0

Volumes

Source (hote) Destination Type Mode
/home/debian/music /music bind rw, :shared
/home/debian/docker/navidrome/data /data bind rw

Integration Claude Code — MCP

Statut MCP MCP actif
Outils (prefix) mcp__navidrome__
Configuration Script Python : C:\Users\eliob\.claude\mcp_navidrome.py | Auth : Subsonic API avec hachage MD5 (user + token + sel)

Outils disponibles

Ce que nous pouvons faire ensemble


Scans automatiques

Trois mécanismes automatiques assurent la fraîcheur de la bibliothèque :

Cron Script Type Rôle
*/5 * * * * /home/debian/monitor-rclone.sh Watchdog Détecte FUSE mort → restart service + restart container (recovery auto)
30 4 * * * /home/debian/navidrome_scan.sh Quick scan vfs/refresh + scan quotidien (MAJ des titres existants)
0 3 * * 1 /home/debian/navidrome_fullscan.sh Full scan vfs/refresh + restart container → full scan hebdomadaire (détecte nouveaux albums/artistes)

Log commun : /var/log/navidrome_scan.log

Quick scan vs Full scan

Le quick scan (startScan via API Subsonic) ne traverse que les dossiers déjà connus de Navidrome — il ne détecte pas les nouveaux artistes ou albums ajoutés sur kDrive. Seul un full scan indexe les nouveaux dossiers.

Le full scan est déclenché automatiquement au démarrage du container. C'est pourquoi navidrome_fullscan.sh fait un restart container plutôt qu'un simple appel API.

Auth RC sur kdrive-music.service

Depuis rclone 1.74.3, vfs/refresh exige une authentification RC. Le service kdrive-music.service a été mis à jour le 2026-06-24 avec --rc-user=rcadmin --rc-pass=RcMusic2026!.

Commande de refresh manuel :

rclone rc --rc-addr 127.0.0.1:5576 --rc-user=rcadmin --rc-pass=RcMusic2026! vfs/refresh recursive=true

REX — 2026-06-24 : FUSE mort récurrent

Contexte

Le FUSE /music est mort en cours de journée. Symptôme Navidrome : stat /music: transport endpoint is not connected lors du scan.

Cause

Le service kdrive-music.service peut rester active (running) avec un FUSE mort — le process rclone est vivant mais la connexion noyau FUSE est cassée. Systemd ne détecte pas ce cas et ne redémarre pas le service.

Solution mise en place

Refactorisation de monitor-rclone.sh pour utiliser systemctl restart (au lieu d'un rclone mount direct) et redémarrer les containers Docker après remontage. Le watchdog tourne toutes les 5 minutes — la panne est détectée et corrigée automatiquement sans intervention.

Procédure manuelle si besoin

sudo systemctl restart kdrive-music.service
ls /home/debian/music | head -5
sudo docker restart navidrome-navidrome-1

Puis force-fermer Symfonium et relancer — le token de session Navidrome est invalidé après le restart du container.


REX — 2026-06-24 : nouveaux albums non détectés

Contexte

Les Négresses Vertes (et d'autres albums récemment ajoutés sur kDrive) n'apparaissaient pas dans Navidrome malgré le cron quotidien.

Cause

Double problème :

  1. Cache VFS 72h : --dir-cache-time 72h sur kdrive-music.service — les nouveaux dossiers kDrive ne sont pas visibles dans le mount avant expiration ou vfs/refresh.
  2. Quick scan insuffisant : le scan quotidien (startScan API) est un quick scan qui ne traverse que les dossiers déjà connus. Il ne détecte pas de nouveaux artistes.

Solution immédiate

rclone rc --rc-addr 127.0.0.1:5576 --rc-user=rcadmin --rc-pass=RcMusic2026! vfs/refresh recursive=true
sudo docker restart navidrome-navidrome-1

Le restart container déclenche un full scan au démarrage (~20 min pour 16 000 titres). Vérifier avec getScanStatus ("scanning":false = terminé).

Solution pérenne

Full scan hebdomadaire chaque lundi à 3h UTC (navidrome_fullscan.sh) — garantit que tout nouvel album ajouté dans la semaine est indexé au plus tard le lundi matin.


REX — 2026-06-13 : FUSE corrompu / Symfonium inaccessible

Contexte

Après l'incident sécurité rclone du 2026-06-09 (CVE-2026-41179), le service kdrive-music.service a été relancé. Il est resté actif 4 jours sans crash apparent, mais le mount FUSE /home/debian/music était silencieusement corrompu.

Symptômes

Cause

Le process rclone était vivant mais la connexion noyau FUSE était morte. Pas de crash = pas de restart automatique par systemd.

Procédure de résolution

  1. Redémarrer le service rclone : sudo systemctl restart kdrive-music.service
  2. Vérifier que le mount répond : ls /home/debian/music | head -5
  3. Redémarrer le container Navidrome — nécessaire même avec :shared, car le container garde l'ancienne référence FUSE corrompue
  4. Sur les appareils : force-fermer Symfonium et relancer

Notes

0_Claude et le Jux_VPS

20_NodeExporter

node-exporter

Imageprom/node-exporter:latest
EtatUp 9 hours
Compose projectgemini_grafana
Reseaugemini_grafana_monitor_net

Ports

HoteContainerIP
91009100/tcp0.0.0.0
91009100/tcp::

Volumes

Source (hote)DestinationTypeMode
/proc/host/procbindro
/sys/host/sysbindro
//rootfsbindro

Integration Claude Code — API directe

Statut MCPPas de MCP — API directe possible
Note techniqueExporte des métriques système (CPU, RAM, disque, réseau) au format Prometheus sur /metrics. Pas de MCP. Données consommées par Prometheus puis visualisées dans Grafana.

Ce que nous pouvons faire ensemble

0_Claude et le Jux_VPS

21_Prometheus

prometheus

Imageprom/prometheus:latest
EtatUp 9 hours
Compose projectgemini_grafana
Reseaugemini_grafana_monitor_net
Sourcehttps://github.com/prometheus/prometheus

Ports

HoteContainerIP
90909090/tcp0.0.0.0
90909090/tcp::

Volumes

Source (hote)DestinationTypeMode
/opt/monitoring/prometheus.yml/etc/prometheus/prometheus.ymlbindro
/opt/monitoring/prometheus_data/prometheusbindrw

Integration Claude Code — API directe

Statut MCPPas de MCP — API directe possible
Note techniqueAPI REST Prometheus sur /api/v1. Requêtes PromQL via /api/v1/query et /api/v1/query_range. Pas de MCP configuré — appels directs via PowerShell/Python.

Ce que nous pouvons faire ensemble

0_Claude et le Jux_VPS

22_Readeck

readeck

Imagecodeberg.org/readeck/readeck:latest
EtatUp 9 hours
Compose projectreadeck
Reseaureadeck_default
URLhttps://readeck.juxjux.ovh

Ports

HoteContainerIP
45678000/tcp0.0.0.0
45678000/tcp::

Volumes

Source (hote)DestinationTypeMode
/home/debian/readeck/exports/exportsbindrw
/readeckvolumerw
/home/debian/readeck/data/readeck/databindrw

Integration Claude Code — MCP

Statut MCPMCP actif
Outils (prefix)mcp__readeck__
ConfigurationScript Python : C:\Users\eliob\.claude\mcp_readeck.py | Auth : token API Bearer (MFA actif sur le compte web)

Outils disponibles

Ce que nous pouvons faire ensemble


Sauvegarde de la base de données

Sauvegarde automatique quotidienne de la base SQLite de Readeck vers kDrive Infomaniak. Mise en place le 2026-06-01.

Paramètres

Script/opt/backups/vps_backup.sh
CronTous les jours à 2h du matin (0 2 * * *)
Type baseSQLite — /readeck/data/db.sqlite3 (dans le container)
Méthodedocker cp readeck:/readeck/data/db.sqlite3
Taille base~8.5 Mo
Format archive.7z (compression niveau 5)
DestinationkDrive Infomaniak — TOOJUX/jux_vps/readeck (dossier ID 1427922)
Rétention7 jours glissants
Nommage fichierreadeck_db_YYYY-MM-DD.7z

Note

Le volume /readeck (non bindé) contient les fichiers des articles (images, HTML). La base SQLite ne couvre que les métadonnées et le texte extrait — les contenus médias ne sont pas inclus dans ce backup.

0_Claude et le Jux_VPS

23_Syncthing

syncthing

Imagesyncthing/syncthing:latest
Versionv2.0.14
EtatUp 9 hours (healthy)
Compose projectsyncthing
Reseausyncthing_syncthing_net
URLhttps://syncthing.juxjux.ovh
Sourcehttps://github.com/syncthing/syncthing

Ports

HoteContainerIP
2102721027/udp0.0.0.0
2102721027/udp::
2200022000/tcp0.0.0.0
2200022000/tcp::
2200022000/udp0.0.0.0
2200022000/udp::
83848384/tcp0.0.0.0
83848384/tcp::

Volumes

Source (hote)DestinationTypeMode
/var/syncthingvolumerw
/home/debian/Documents/var/syncthing/Documentsbindrw
/home/debian/Public/var/syncthing/Publicbindrw
/home/debian/docker/syncthing/config/var/syncthing/configbindrw
/home/debian/docker/syncthing/data1/var/syncthing/data1bindrw
/home/debian/docker/syncthing/data2/var/syncthing/data2bindrw
/home/debian/joplin-data/var/syncthing/joplin-databindrw

Integration Claude Code — MCP

Statut MCPMCP actif
Outils (prefix)mcp__syncthing__
ConfigurationScript Python : C:\Users\eliob\.claude\mcp_syncthing.py | Auth : clé API dans header X-API-Key

Outils disponibles

Ce que nous pouvons faire ensemble

0_Claude et le Jux_VPS

24_Yourls

yourls

Imageyourls:latest
EtatUp 9 hours
Compose projectyourls
Reseauyourls_default
URLhttps://yourls.juxjux.ovh

Ports

HoteContainerIP
808580/tcp0.0.0.0
808580/tcp::

Volumes

Source (hote)DestinationTypeMode
/var/lib/docker/volumes/yourls_yourls_plugins/_data/var/www/html/user/pluginsvolumerw
/home/debian/docker/yourls/ports.conf/etc/apache2/ports.confbindrw
/home/debian/docker/yourls/000-default.conf/etc/apache2/sites-available/000-default.confbindrw
/var/lib/docker/volumes/yourls_yourls_html/_data/var/www/htmlvolumerw
/var/lib/docker/volumes/yourls_yourls_user/_data/var/www/html/uservolumerw

yourls-db

Imagemysql:8.0
EtatUp 9 hours (healthy)
Compose projectyourls
Reseauyourls_default
URLhttps://yourls.juxjux.ovh

Volumes

Source (hote)DestinationTypeMode
/var/lib/docker/volumes/yourls_yourls_mysql_data/_data/var/lib/mysqlvolumerw

Integration Claude Code — API directe

Statut MCPPas de MCP — API directe possible
Note techniqueAPI REST Yourls sur /yourls-api.php. Auth : signature HMAC ou token. Pas de MCP configuré — appels directs possibles.

Ce que nous pouvons faire ensemble

0_Claude et le Jux_VPS

25_StirlingPDF

stirling-pdf

Suite complète de manipulation de PDF — open source, auto-hébergée

Imagestirlingtools/stirling-pdf:latest
EtatUp (healthy)
Compose projectstirlingpdf
Reseaustirlingpdf_default
URLhttps://spdf.juxjux.ovh
Sourcehttps://github.com/Stirling-Tools/Stirling-PDF

Ports

HoteContainerIP
80908080/tcp0.0.0.0

Variables

VariableValeur
DOCKER_ENABLE_SECURITYfalse
LANGSfr_FR

Volumes

HoteContainer
/opt/stirling-pdf/configs/configs
/opt/stirling-pdf/logs/logs

Notes


Integration Claude Code

Statut MCPPas de MCP — API directe possible
Note techniqueAPI REST sur /api — scriptable via PowerShell/Python httpx.

Ce que nous pouvons faire ensemble

0_Claude et le Jux_VPS

26_JDownloader

jdownloader

Gestionnaire de téléchargements avancé — interface web noVNC, supporte hôtes premium et lien direct

Imagejlesage/jdownloader-2:latest
EtatUp (running)
Compose projectjdownloader
Reseaujdownloader_default
URLhttps://jdownloader.juxjux.ovh
Sourcehttps://github.com/jlesage/docker-jdownloader-2

Ports

HoteContainerIP
58005800/tcp127.0.0.1

Variables

VariableValeur
USER_ID1000
GROUP_ID1000
TZEurope/Paris

Volumes

HoteContainer
/opt/jdownloader/config/config
/home/debian/downloads/output

Notes


Integration Claude Code

Statut MCPPas de MCP — interface web uniquement (noVNC)
Note techniquePas d'API REST publique documentée. Pilotage uniquement via l'interface graphique.

Ce que nous pouvons faire ensemble

0_Claude et le Jux_VPS

27 - Rustdesk

RustDesk — Serveur auto-hébergé (hbbs/hbbr)

Qu'est-ce que c'est

RustDesk est un logiciel de bureau à distance open-source (alternative à TeamViewer/AnyDesk). Il repose sur deux briques serveur :

Auto-héberger ces deux services évite de dépendre des serveurs publics RustDesk (rendezvous/relais tiers) : tout le trafic de contrôle passe par une infrastructure qu'on maîtrise.

Poids du service Docker

Élément Valeur
Image rustdesk/rustdesk-server:latest (amd64) 5,61 Mo compressée
Image (arm64) 5,2 Mo
Image (armv7) 5,04 Mo
Nombre de containers 2 (hbbs + hbbr)
RAM au repos quelques dizaines de Mo par container (binaires Rust natifs, pas de VM/interpréteur)
CPU au repos négligeable
CPU/RAM en charge dépend du nombre de sessions actives relayées par hbbr (le flux vidéo transite par lui si pas de P2P direct) — reste très léger pour un usage perso/familial (quelques sessions simultanées max)

Pour comparaison, c'est très inférieur au poids des autres containers déjà en place sur le VPS Jux (BookStack/MariaDB, Immich, etc.).

Ports requis (conditions réseau)

Port Protocole Service Rôle
21115 TCP hbbs Test de type NAT
21116 TCP + UDP hbbs UDP = enregistrement ID + heartbeat / TCP = hole punching + connexion
21117 TCP hbbr Service de relais
21118 TCP hbbs Client web (optionnel)
21119 TCP hbbr Client web (optionnel)
21114 TCP hbbs Console web — Pro uniquement, pas utile en OSS

Si le client web n'est pas utilisé, 21118/21119 peuvent rester fermés (recommandé : ces deux ports font confiance aux en-têtes X-Real-IP/X-Forwarded-For sans les valider, donc à n'exposer que derrière un reverse proxy qui les fixe lui-même, jamais en direct).

docker-compose.yml (référence officielle RustDesk)

services:
  hbbs:
    container_name: hbbs
    image: rustdesk/rustdesk-server:latest
    command: hbbs
    volumes:
      - ./data:/root
    network_mode: "host"
    depends_on:
      - hbbr
    restart: unless-stopped

  hbbr:
    container_name: hbbr
    image: rustdesk/rustdesk-server:latest
    command: hbbr
    volumes:
      - ./data:/root
    network_mode: "host"
    restart: unless-stopped
services:
  hbbs:
    container_name: hbbs
    image: rustdesk/rustdesk-server:latest
    command: hbbs -r rustdesk.exemple.com:21117
    ports:
      - "21115:21115"
      - "21116:21116"
      - "21116:21116/udp"
      - "21118:21118"
    volumes:
      - ./data:/root
    depends_on:
      - hbbr
    restart: unless-stopped

  hbbr:
    container_name: hbbr
    image: rustdesk/rustdesk-server:latest
    command: hbbr
    ports:
      - "21117:21117"
      - "21119:21119"
    volumes:
      - ./data:/root
    restart: unless-stopped

Variable d'environnement utile : ALWAYS_USE_RELAY=Y sur hbbs pour forcer systématiquement le passage par le relais (utile si on veut masquer les IP réelles des clients, au prix d'un peu plus de charge sur hbbr).

Clé de chiffrement et configuration client

VPS Jux vs NAS Synology sasnexte — analyse

RustDesk propose officiellement un guide d'installation Synology (DSM 6 et DSM 7.2/Container Manager), donc l'hébergement NAS est une voie supportée en soi — la question n'est pas la faisabilité technique mais la joignabilité réseau.

Critère VPS Jux (51.77.141.54) NAS sasnexte (82.66.244.248)
IP publique fixe Oui Non confirmée — SSH déjà signalé comme parfois inaccessible depuis l'extérieur (fail2ban/whitelist)
network_mode: host (recommandé Linux) Disponible (Debian) Non disponible sous DSM (Docker/Container Manager Synology tourne dans une couche réseau propriétaire) — nécessite le mapping de ports classique + redirection sur la box
Port forwarding Déjà maîtrisé (nginx, certbot, ufw) Dépend de la box du FAI, potentiellement CGNAT résidentiel
Exposition déjà en place 34 containers en prod, expérience du durcissement (incident du 9 juin 2026) Usage actuel = uniquement syncs rclone sortants vers kDrive, pas de service entrant exposé
Impact en cas d'indisponibilité VPS pro, uptime élevé Dépend de l'alimentation/connexion internet du domicile

Conclusion : le VPS reste le choix le plus robuste pour un usage "accès à distance depuis n'importe où". Le NAS serait une option seulement si l'IP publique/DDNS et le port forwarding sont déjà fiables — ce qui n'est pas le cas aujourd'hui d'après le comportement SSH observé.

Prochaine étape proposée

Ajouter la stack rustdesk sur le VPS (Portainer, comme les autres services), avec ufw restreint aux ports 21115-21119 (TCP+UDP sur 21116) et 21118/21119 fermés tant que le client web n'est pas nécessaire.

Incident client Windows — BSOD à l'installation (2026-07-31)

Contexte : installation du client RustDesk sur le PC Windows 10 (eliob). L'installation s'est terminée par plusieurs écrans bleus consécutifs.

Diagnostic

Fix appliqué

Neutralisation des deux dossiers de drivers sans désinstaller RustDesk (le contrôle à distance standard, moniteur physique branché, ne dépend pas d'eux) :

Stop-Service -Name "RustDesk" -Force
Stop-Process -Name "rustdesk" -Force -ErrorAction SilentlyContinue
Rename-Item "C:\Program Files\RustDesk\usbmmidd_v2" "usbmmidd_v2.disabled"
Rename-Item "C:\Program Files\RustDesk\drivers\RustDeskPrinterDriver" "RustDeskPrinterDriver.disabled"
Start-Service -Name "RustDesk"

Note importante : ces commandes nécessitent une session PowerShell réellement élevée (clic droit → "Exécuter en tant qu'administrateur", accepter l'UAC). Une session non élevée renvoie la même erreur Impossible d'ouvrir le service aussi bien pour Stop-Service que pour le renommage des dossiers dans Program Files — ça peut faire croire à tort qu'on est admin alors que non.

Résultat

Traversée VPS — client configuré (2026-07-31)

Le client RustDesk du PC eliob passe désormais par le serveur auto-hébergé (hbbs/hbbr sur le VPS Jux, voir plus haut) au lieu des relais publics RustDesk. Confirmé côté serveur : containers hbbs/hbbr up, ufw ouvert sur 21115-21117 (v4+v6), clé publique id_ed25519.pub en place. Tout le trafic de contrôle à distance de ce PC transite donc par une infrastructure maîtrisée plutôt que par un tiers.

0_Claude et le Jux_VPS

D_la solution contacts et calendrier

Solution d'unification des contacts et du calendrier de Julien.

État au 3 août 2026 — déployé et opérationnel de bout en bout. Seul vdirsyncer (agrégation Google/Infomaniak) reste à faire.


1. Objectif

Unifier la base de contacts et de calendriers, dispersée entre plusieurs sources (Google, Infomaniak, téléphone), autour d'un point central auto-hébergé sur le VPS juxjux.ovh, synchronisé vers tous les clients existants.

Baïkal est la source de vérité unique. Roundcube, InfCloud et DAVx5 en sont des clients ; vdirsyncer sera le seul composant qui écrit depuis l'extérieur vers Baïkal.

2. Architecture réellement déployée

Composant Rôle Emplacement État
Baïkal Serveur CardDAV + CalDAV, stockage central Docker, VPS juxjux En service
InfCloud Agenda et contacts web Statique, servi par nginx En service
Roundcube Interface web des contacts Docker, VPS juxjux En service
Nginx Reverse proxy HTTPS Hôte VPS En service
DAVx5 Pont de synchronisation vers Android Téléphone Opérationnel
Fossify Agenda Client calendrier Téléphone (via DAVx5) Opérationnel
vdirsyncer Agrège Google et Infomaniak vers Baïkal VPS, cron Non déployé
Thunderbird Client mail, indépendant de ce circuit Téléphone Inchangé

3. Accès

Service URL Identifiant
Agenda + contacts web https://baikal.juxjux.ovh/infcloud/ Julien (compte Baïkal)
Contacts web (Roundcube) https://secretariat.juxjux.ovh julien.bertrand@ik.me (compte mail Infomaniak)
Administration Baïkal https://baikal.juxjux.ovh/admin/ compte admin Baïkal
Découverte DAV (DAVx5) https://baikal.juxjux.ovh/dav.php/ Julien

Utilisateur Baïkal : Julien — avec un J majuscule. La casse compte dans les URLs DAV.

Certificats Let's Encrypt valides jusqu'au 1er novembre 2026 pour les deux domaines. Celui de baikal.juxjux.ovh existait déjà mais avait expiré le 8 février 2026 (vestige d'une tentative antérieure) ; il a été renouvelé le 3 août.

4. Déploiement

Stack sur le VPS : /home/debian/baikal/docker-compose.yml, projet compose baikal. Sources versionnées : Syncthing/Jux_univers/Jux-scripts/Baikal-Contacts/.

Fichier Rôle
docker-compose.yml Stack Baïkal + Roundcube
baikal.juxjux.ovh.conf Vhost Baïkal (.well-known forcés en HTTPS + service d'InfCloud)
secretariat.juxjux.ovh.conf Vhost Roundcube
infcloud-config.js Configuration InfCloud
finaliser_tls.sh Vérifie le DNS puis lance certbot
vps_backup.sh Script de sauvegarde complet, Baïkal inclus
push_bookstack.py Publication de cette page via l'API BookStack
cd /home/debian/baikal && sudo docker compose -p baikal up -d
Service Image Port local Base RAM
baikal ckulka/baikal:nginx 127.0.0.1:8088 SQLite ~38 Mo
roundcube roundcube/roundcubemail:latest (1.7.2) 127.0.0.1:8083 SQLite ~32 Mo
InfCloud fichiers statiques (13 Mo sur disque) aucune 0

Les deux containers pèsent environ 70 Mo au total, contre environ 600 Mo avec l'architecture MariaDB initialement prévue. Ce choix était important : le VPS était en pression mémoire au moment du déploiement (swap à 3,8/4,0 Go).

5. Écarts avec le plan initial

Le plan préparé prévoyait une configuration qui ne pouvait pas fonctionner en l'état. Écarts constatés et corrigés :

Point Prévu Réel Raison
Port Baïkal 8081 8088 8081 est occupé par FreshRSS. 8088 correspond au vhost baikal.juxjux.ovh préexistant
Port Roundcube 8082 8083 8082 est ciblé par un vhost nextcloud mort
Domaine Roundcube mail.juxjux.ovh secretariat.juxjux.ovh Aucun DNS n'existait ; mail. prêtait à confusion avec les MX OVH du domaine
Base Roundcube MariaDB SQLite ~400 Mo de RAM économisés, suffisant en mono-utilisateur
Vhost Baïkal À créer Déjà présent Le fichier baikal.conf préparé n'a pas servi
Agenda web Plugin calendar Roundcube InfCloud Plugin inutilisable, voir §6.2

6. Pièges rencontrés

6.1 Les plugins carddav et calendar ne sont pas dans l'image Roundcube

L'image officielle ne fournit ni carddav ni calendar. Les déclarer dans ROUNDCUBEMAIL_PLUGINS ne fait que les activer — s'ils sont absents, Roundcube part en erreur fatale.

Il faut les faire installer par composer au démarrage, via ROUNDCUBEMAIL_COMPOSER_PLUGINS (et non ROUNDCUBEMAIL_INSTALL_PLUGINS, qui n'existe pas dans cette image) :

ROUNDCUBEMAIL_COMPOSER_PLUGINS: "roundcube/carddav"
ROUNDCUBEMAIL_PLUGINS: "carddav"

Le premier démarrage est alors plus long (téléchargement composer).

6.2 Le plugin calendar de Roundcube est une impasse

Trois obstacles successifs, testés le 3 août :

a) Il casse le tout premier démarrage. Le script d'initialisation de base de kolab/calendar s'exécute avant que l'entrypoint n'écrive la configuration Roundcube : il tombe sur le DSN MySQL par défaut et échoue en SQLSTATE[HY000] [2002] No such file or directory. Cette erreur fatale empêche la régénération de l'autoloader composer, ce qui casse aussi le plugin carddav :

PHP Fatal error: Uncaught Error: Interface
"MStilkerich\RCMCardDAV\Frontend\RcmInterface" not found
in /var/www/html/plugins/carddav/carddav.php:43

Symptôme : HTTP 500 permanent, alors que les fichiers du plugin sont bien présents sur le disque — c'est vendor/composer/autoload_psr4.php qui ne contient aucune entrée carddav.

b) Ce premier obstacle est contournable. Réinstallé après que Roundcube ait été initialisé une première fois, la configuration existe déjà dans le volume et l'initialisation aboutit (Creating database schema... [OK]). Vérifié.

c) Mais le plugin ne sait pas parler à Baïkal. La seule version installable, kolab/calendar 3.2.9.1, ne livre que les pilotes database, kolab et ldapaucun pilote caldav. Il ne créerait qu'un agenda local dans Roundcube, sans lien avec Baïkal ni le téléphone : pire qu'inutile, on pourrait y saisir des rendez-vous en croyant qu'ils se synchronisent.

Les versions qui embarquent le pilote CalDAV (3.5.7, 3.6.1) dépendent de kolab/libkolab, qui réclame pear/http_request2 — paquet bloqué par composer pour faille de sécurité (PKSA-jrt3-xndd-g4sz). Ne pas contourner ce garde-fou sur un service exposé sur internet.

Conclusion : agenda web assuré par InfCloud (§7), plugin calendar définitivement écarté.

6.3 Baïkal ignore X-Forwarded-Proto — fuite HTTP sur la découverte

Baïkal ne tient pas compte de X-Forwarded-Proto et générait ses redirections de découverte en HTTP nu :

/.well-known/carddav -> http://baikal.juxjux.ovh/dav.php

C'est exactement le risque identifié dès la conception : CardDAV et CalDAV envoient les identifiants à chaque synchronisation. Un client suivant cette redirection part sur du HTTP.

Corrigé en court-circuitant Baïkal directement dans le vhost :

location = /.well-known/carddav {
    return 301 https://$host/dav.php/;
}
location = /.well-known/caldav {
    return 301 https://$host/dav.php/;
}

À vérifier après tout reload nginx : le rechargement n'est pas instantané, un test lancé dans la foulée peut encore montrer l'ancien comportement.

6.4 Roundcube exige un serveur IMAP pour authentifier

Roundcube n'a pas de base d'utilisateurs propre : son écran de login valide les identifiants contre le serveur IMAP configuré. Sans IMAP joignable, aucune connexion n'est possible — donc aucun accès aux contacts non plus.

L'idée d'un Roundcube « contacts et agenda seulement, sans IMAP » n'est pas réalisable. La configuration retenue pointe vers ssl://mail.infomaniak.com:993, ce qui couvre le compte julien.bertrand@ik.me (le domaine ik.me est servi par l'infrastructure Infomaniak).

Si un jour le compte passe en double authentification, il faudra générer un mot de passe d'application Infomaniak.

6.5 Baïkal doit être installé en SQLite

L'installeur Baïkal propose MySQL ou SQLite. Choisir SQLite : la stack ne contient aucun serveur MySQL, cocher « Enable MySQL » mène à une impasse. Base à /var/www/baikal/Specific/db/db.sqlite, dans le volume baikal-data.

7. InfCloud — l'agenda web

Client CalDAV/CardDAV entièrement côté navigateur : aucun backend, aucune base, du HTML/JS servi en statique par nginx. Affiche agenda et contacts.

Réserve assumée : projet figé depuis 2015, interface datée, embarque jQuery 2.1.4. Acceptable parce qu'il n'a aucun composant serveur : rien à maintenir, rien à patcher. La seule alternative maintenue serait SOGo, au prix d'environ 500 Mo de RAM et d'une base PostgreSQL.

7.1 Les trois pièges d'InfCloud

a) Le bloc regex du vhost avale tous les JS. Le vhost Baïkal contient déjà :

location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ { proxy_pass http://127.0.0.1:8088; }

En nginx, un bloc regex ~* a priorité sur un bloc préfixe ordinaire. Tous les fichiers JavaScript d'InfCloud auraient donc été proxifiés vers Baïkal. Comme InfCloud est presque intégralement du JavaScript, la page serait restée blanche — symptôme difficile à diagnostiquer. Il faut ^~, qui passe devant les regex :

location ^~ /infcloud/ {
    alias /home/debian/baikal/infcloud/;
    index index.html;
    location ~ /\. { deny all; }
}

b) L'endpoint par défaut vise DAViCal. Le config.js livré pointe /caldav.php/ ; Baïkal expose /dav.php/.

c) Le href doit viser les principals, pas la racine DAV. InfCloud accole le nom d'utilisateur au href. Avec /dav.php/ il demandait /dav.php/Julien/404. La documentation du fichier le précise : « globalNetworkCheckSettings : principal URL WITHOUT the USER/ part ». Chez Baïkal, la bonne valeur est donc :

'/dav.php/principals/',

Après toute modification de config.js, rechargement forcé du navigateur (Ctrl+Maj+R) : le fichier est mis en cache.

7.2 Digest → Basic : le blocage d'authentification

Baïkal était configuré en dav_auth_type: Digest et annonçait WWW-Authenticate: Digest realm="BaikalDAV".

InfCloud ne sait faire que du Basic. Ses requêtes partaient donc en 401 en boucle, alors que DAVx5 — qui gère Digest — fonctionnait parfaitement avec le même compte. Le symptôme prêtait à confusion : les identifiants étaient bons.

Diagnostic par les logs nginx, où le champ utilisateur est renseigné mais la réponse reste 401 :

83.195.45.93 - Julien "PROPFIND /dav.php/ HTTP/2.0" 401

Correctif : dav_auth_type: Basic dans /var/www/baikal/config/baikal.yaml, puis docker restart baikal.

Ce n'est pas un pis-aller. Digest repose sur MD5, est considéré comme obsolète, et impose au serveur de stocker une empreinte exploitable ; Basic sur TLS est aujourd'hui la recommandation, et tout est en HTTPS ici, y compris les redirections de découverte (§6.3).

Les mots de passe restent valides : Baïkal calcule la même empreinte md5(username:auth_realm:password) dans les deux modes (Baikal\Core\PDOBasicAuth::validateUserPass), avec auth_realm: BaikalDAV conservé en interne — même si le serveur annonce désormais realm="sabre/dav" côté client. Rien à redéfinir, DAVx5 bascule tout seul.

Sauvegarde : /var/www/baikal/config/baikal.yaml.bak-260803-digest (dans le container).

8. Sauvegarde

Baïkal a été ajouté à /opt/backups/vps_backup.sh le 3 août 2026 (cron quotidien à 2h UTC).

La fonction archive deux volumes, via un tar intermédiaire puis 7z :

Sans le second, la restauration est incomplète. Testée le 3 août : upload HTTP 200, aucun résidu temporaire.

InfCloud n'a pas besoin d'être sauvegardé (fichiers statiques réinstallables), mais son config.js est versionné dans Jux-scripts/Baikal-Contacts/.

9. Vérifications effectuées le 3 août 2026

Test Résultat
GET /dav.php/ HTTP 401 — authentification bien exigée
PROPFIND /dav.php/ HTTP 401 et non 405 — nginx laisse passer les méthodes DAV étendues
.well-known/carddav et caldav 301 vers HTTPS
Installation Baïkal Utilisateur Julien, carnet et calendrier default créés
Login Roundcube IMAP Infomaniak accepté
Découverte CardDAV Roundcube Carnet résolu à .../dav.php/addressbooks/Julien/default/
Écriture d'un contact vCard 3.0 écrite par RCMCardDAV v5.1.3, relue dans Baïkal, accents UTF-8 préservés
Synchronisation DAVx5 5 rendez-vous d'août écrits dans Baïkal par DAVx5/4.5.18-ose, synctoken du calendrier à 6
InfCloud 32 ressources servies en 200 ; PROPFIND sur principals, addressbooks et calendars tous en 207
Dotfiles InfCloud .htaccess bloqué en 403
Sauvegarde Baïkal Archive uploadée sur kDrive, HTTP 200

Note : dans la table carddav_addressbooks de Roundcube, une seconde ligne nommée %N avec une URL vide n'est pas une erreur — c'est le template de rcmcarddav v5, qui porte les réglages appliqués aux carnets découverts.

10. Reste à faire

10.1 vdirsyncer — agrégation Google et Infomaniak

Seule brique du plan initial encore absente. Objectif : faire remonter dans Baïkal les contacts existants sur Google et Infomaniak, pour que Baïkal devienne le point central réel et pas un silo de plus.

Décision à prendre avant déploiement :

Recommandation : démarrer en one-way le temps de vérifier que l'import est fidèle, puis basculer si besoin.

10.2 Nettoyage

Quatre vhosts morts traînent dans /etc/nginx/sites-enabled/, vestiges des tentatives précédentes : radicale, agendav, infcloud, nextcloud. Le dernier cible le port 8082, ce qui a contraint le choix du port de Roundcube. Attention : le vhost nommé radicale.juxjux.ovh sert en réalité Readeck — ne pas le supprimer sans vérifier.

11. Checklist sécurité

12. Commandes utiles

# État de la stack
sudo docker ps --filter name=baikal --filter name=roundcube

# Logs
sudo docker logs roundcube --tail 50
sudo docker logs baikal --tail 50

# Redéployer
cd /home/debian/baikal && sudo docker compose -p baikal up -d

# Inspecter la base Baïkal
sudo docker cp baikal:/var/www/baikal/Specific/db/db.sqlite /tmp/bk.sqlite
sudo sqlite3 /tmp/bk.sqlite "SELECT COUNT(*) FROM cards;"
sudo sqlite3 /tmp/bk.sqlite "SELECT COUNT(*) FROM calendarobjects;"
sudo sqlite3 /tmp/bk.sqlite "SELECT id, username FROM users;"

# Méthode d'authentification annoncée par Baïkal
curl -s -D - -o /dev/null -X PROPFIND -H 'Depth: 0' \
  https://baikal.juxjux.ovh/dav.php/ | grep -i www-authenticate

# Suivre les requêtes DAV des clients (DAVx5, InfCloud, Roundcube)
sudo tail -f /var/log/nginx/access.log | grep dav.php

# Vérifier la découverte DAV
curl -s -o /dev/null -w "%{http_code} -> %{redirect_url}\n" \
  https://baikal.juxjux.ovh/.well-known/carddav

13. Choix d'architecture — pourquoi ces outils

Radicale écarté : stockage fichier brut, pas de vraie gestion. La stack Radicale/InfCloud initialement envisagée n'a pas été mise en place — seul InfCloud a finalement été retenu, en client de Baïkal.

Nextcloud écarté : trop lourd pour l'usage visé (PHP-FPM + base + Redis + écosystème complet), alors que le VPS fait déjà tourner PostGIS, Metabase, BookStack, FreshRSS.

Baïkal retenu : léger, CardDAV et CalDAV natifs, interface d'administration suffisante. Développé par des bénévoles sous l'organisation sabre-io (mainteneurs de SabreDAV, la librairie sous-jacente) — pas de société commerciale derrière, mais communauté active et outil robuste pour un usage personnel sur la durée.

Roundcube retenu comme interface de saisie des contacts, Baïkal n'ayant pas de vraie UI de consultation : léger, traduit en français, projet actif (version 1.7.2 en août 2026). A rejoint la famille Nextcloud en 2023 tout en restant indépendant, sous licence GPL. Réserves constatées à l'usage : il impose une authentification IMAP (§6.4) et ne couvre pas l'agenda (§6.2).

InfCloud retenu pour l'agenda web : sans backend, donc sans coût mémoire ni surface d'attaque serveur, et couvre agenda et contacts. Ancien mais fonctionnel.

SOGo envisagé et écarté : suite complète et maintenue, mais environ 500 Mo de RAM et une base PostgreSQL sur un VPS déjà en pression mémoire, et il aurait fait doublon avec Roundcube.

Thunderbird containerisé (VNC/noVNC) envisagé puis écarté : fonctionnel mais plus lourd et moins adapté au multi-accès qu'un vrai webmail.

0_Claude et le Jux_VPS

E_la gestion du SWAP du VPS par Claude

REX 260803 — Session Claude sur le VPS Jux

Session du 2026-08-02 (soir) au 2026-08-03 (matin). Quatre chantiers, tous appliqués et vérifiés. Le sujet principal de cette page — le swap — est traité en partie 1 ; les autres chantiers suivent car ils s'expliquent mutuellement (l'allègement JVM est ce qui a fait apparaître la question du swap).

# Chantier État Gain / résultat
1 Swap saturé à 95 % Résolu 4 Go → 8 Go, swappiness 60 → 10
2 Allègement JVM Komga + StirlingPDF Appliqué Komga −292 Mio (−41 %), Stirling borné à 2 Go
3 OCR StirlingPDF impossible sur PNG Résolu Cause trouvée + script de contournement
4 Connexion Claude Code sur le VPS Alteris Résolu Identifiants recopiés depuis le VPS Jux

1. Swap saturé — le vrai diagnostic

Symptôme

Swap à 3,8 / 4,0 Go (95 %), alors que ~4,6 Gi de RAM restaient disponibles. Il se remplissait nuit après nuit et ne se vidait jamais.

Ce que ce n'était PAS

Un manque de mémoire. vmstat 2 3 montrait si/so ≈ 0 : aucun thrashing, rien ne ramait. C'est le point contre-intuitif de ce diagnostic — un swap plein n'est pas synonyme de machine à genoux.

Le risque réel était ailleurs : il ne restait que 253 Mio de swap libre. En cas de pic, l'OOM killer aurait commencé à tuer des containers.

Cause

vm.swappiness = 60 (valeur par défaut). Pendant les batchs de nuit — backup 2h, scan Komga 4h (230 lignes de scan cette nuit-là), scans Audiobookshelf 4h et Navidrome 4h30 — le noyau préférait pousser les pages anonymes des applications vers le swap plutôt que de réclamer son cache de pages.

Une fois en swap, ces pages n'en ressortent jamais d'elles-mêmes : elles n'y reviennent que si le processus les retouche. D'où une accumulation qui ne fait que croître, nuit après nuit.

Fixes appliqués

a) vm.swappiness = 10, persisté dans /etc/sysctl.d/99-swap.conf. Le noyau réclame désormais son cache de pages avant de swapper. C'est ce qui empêche l'accumulation de recommencer.

b) Second fichier de swap /swapfile2 de 4 Go → swap total 8 Go, persisté dans /etc/fstab (sauvegarde : /etc/fstab.bak-260803), validé par sudo swapon -a pour garantir un redémarrage sûr.

Méthode choisie délibérément : ajouter un second fichier évite tout swapoff, donc tout risque. Un swapoff -a aurait dû recharger 3,8 Go dans 4,6 Go disponibles — faisable mais serré, plusieurs minutes d'I/O intense, et un pic pendant l'opération aurait pu faire tuer un container.

État après intervention

Avant Après
Swap total 4,0 Go 8,0 Go
Swap libre 253 Mio 4,2 Go
vm.swappiness 60 10
Disque libre 39 Go 35 Go

Ce qui reste

Les 3,8 Go de pages froides restent en swap. C'est assumé : elles ne coûtent rien en performance et se libéreront au fil des redémarrages. Un docker restart libère instantanément le swap du container concerné.

Top détenteurs au 2026-08-03 : komga 954 Mio, yourls-db 389, stirling-pdf 354, Immich-SERVER 212, Immich-DB 197, mealie 189, Kavita 158, jellyfin 131.

Le redémarrage de Komga rendra donc presque un giga à lui seul.

Commandes de diagnostic

swapon --show                      # taille et occupation de chaque zone
vmstat 2 3                         # si/so : y a-t-il un swap ACTIF ?
cat /proc/sys/vm/swappiness

Swap par container :

cat /sys/fs/cgroup/system.slice/docker-$(docker inspect NOM --format '{{.Id}}').scope/memory.swap.current

Swap par processus :

for p in /proc/[0-9]*; do s=$(awk '/^VmSwap:/{print $2}' $p/status 2>/dev/null); \
  [ -n "$s" ] && [ "$s" -gt 0 ] && echo "$s $(tr -d '\0' < $p/cmdline | cut -c1-60)"; done | sort -rn | head

À surveiller

Le lendemain matin, après une nuit de batchs : si le swap utilisé a peu bougé, swappiness=10 a fait son travail. S'il remonte malgré tout, la piste suivante est de réduire les consommateurs réels plutôt que de rejouer sur les réglages noyau.


2. Allègement JVM Komga + StirlingPDF

Appliqué le 2026-08-02 à 22h, après vérification du backup quotidien (BILAN 5/5 OK).

Service Avant Après Résultat
Komga (stack 7) -Xmx4g -Xms2g -XX:MaxRAMPercentage=70.0, limits 4500M / reservations 3000M, 707 Mio -Xmx1g, limits 1500M / reservations 256M 415 Mio (−292, −41 %)
StirlingPDF (stack 89) JVM sans limite, 722 Mio -Xmx512m, limits 2g 785 Mio — pas de réduction

Bilan hôte : swap 3,7 → 2,2 Go juste après l'opération, mémoire disponible 4,4 → 4,6 Gi. L'essentiel du gain vient de Komga : c'est le -Xms2g qui pré-allouait 2 Go de heap au démarrage.

Pourquoi StirlingPDF ne baisse pas

À dire franchement plutôt que de maquiller le résultat : le process java conserve 799 Mio de RSS malgré -Xmx512m. Le heap n'est qu'une partie du total (metaspace, code cache, piles de threads), et Stirling lance en plus LibreOffice (soffice.bin, ~98 Mio), unoserver et Xvfb pour ses conversions.

Le bénéfice obtenu n'est pas une baisse mais un plafond : la consommation ne peut plus déborder, alors qu'elle était non bornée.

La limite portée de 1 Go à 2 Go — décision critique

Le plan initial prévoyait 1 Go. Il a été corrigé avant application, grâce au chantier OCR mené le même jour : l'OCR fait tourner ocrmypdf et tesseract comme processus Python, hors JVM. -Xmx ne les borne pas — seul le plafond cgroup du container s'applique.

Pic mesuré : 894 à 969 Mio pour une seule page A4 300 dpi. Un plafond de 1 Go aurait provoqué un OOM kill dès le premier OCR. La version 1 Go est conservée en /home/debian/stirling-compose-new.yml.orig1g mais ne doit pas être utilisée.

Vérifications après déploiement

Piège de déploiement

Les projets compose s'appellent 7 et 89 — les IDs de stack Portainer — et non komga / stirlingpdf. La procédure documentée initialement aurait échoué dessus.

sudo docker compose -p 7 -f /var/lib/docker/volumes/portainer_data/_data/compose/7/docker-compose.yml up -d

3. OCR StirlingPDF impossible sur les images PNG

Symptôme

HTTP 500 sur tout OCR d'image PNG. L'interface web n'affiche aucun détail exploitable — le diagnostic passe obligatoirement par docker logs stirling-pdf.

Deux refus cumulés d'ocrmypdf 17.4.0

  1. UnsupportedImageFormatError: The input image has an alpha channel. — tout PNG avec transparence
  2. Une fois l'alpha retiré, un second blocage apparaît : DpiError: Input file is an image, but has no resolution (DPI) in its metadata. StirlingPDF ne transmet pas --image-dpi à ocrmypdf

Le point à retenir : les erreurs sont séquentielles. Corriger la transparence seule ne suffit pas, le DPI bloque juste après — ce qui explique pourquoi aucun réglage de l'interface ne pouvait s'en sortir.

Solution sans outil (interface web)

Convert → Image to PDF, puis OCR sur le PDF obtenu. Les deux problèmes disparaissent puisque l'entrée n'est plus une image. Validé en test, HTTP 200.

Solution scriptée

Jux-scripts/Stirling-OCR/ocr_image.py — aplatit l'alpha sur fond blanc et force 300 dpi avant envoi.

py "D:\Syncthing\Jux_univers\Jux-scripts\Stirling-OCR\ocr_image.py" mon_image.png

Accepte un fichier ou un dossier. Options : -l fra+eng, -o sortie.pdf, --dpi 400, --skip-text. Testé depuis le PC Windows et depuis le VPS ; texte restitué à l'identique.

Chemin sur le VPS : /home/debian/Documents/Jux_univers/Jux-scripts/ (et non ~/Syncthing/...).

Détails techniques


4. Connexion Claude Code sur le VPS Alteris

Symptôme

Au lancement de claude en SSH (PuTTY / CMD / PowerShell), Claude Code demande de se connecter et affiche une URL. Impossible de la copier : Ctrl+C, clic droit, rien ne fonctionnait. La mention verte « copied » s'affichait pourtant.

Cause

Le « copied » venait de Claude Code tournant sur le VPS : il copiait l'URL dans le presse-papier du serveur Linux, qui n'a aucun lien avec celui du PC Windows à travers SSH. Le presse-papier Windows restait donc vide, d'où le collage à blanc dans le navigateur.

À noter au passage : dans un terminal SSH, Ctrl+C envoie un signal d'interruption au programme — il peut tuer le processus de login au lieu de copier.

Cause réelle du re-login

Le fichier ~/.claude/.credentials.json d'Alteris avait expiresAt: 0 — jeton invalide. Le VPS Jux, lui, disposait d'identifiants sains (compte Pro, refresh token valide).

Solution retenue

Recopier les identifiants depuis le VPS Jux, commande lancée depuis Alteris :

scp debian@51.77.141.54:.claude/.credentials.json ~/.claude/.credentials.json && chmod 600 ~/.claude/.credentials.json

Vérifié : 504 octets, permissions 600, jeton complet. Claude Code ne redemande plus de login et rafraîchit le token seul au démarrage (l'access token expiré n'est pas un problème, le refresh token est valide).

Conséquence : les deux VPS partagent désormais le même jeu d'identifiants. Si l'un redemande un login, refaire le même scp depuis celui qui fonctionne encore.

Alternative si les deux tombent : claude setup-token depuis le PC Windows (le navigateur s'ouvre en local), puis export CLAUDE_CODE_OAUTH_TOKEN=... dans le ~/.bashrc du serveur.


5. Découverte annexe — port PostgreSQL 5432 bloqué sur Alteris

publier_recherche.py échoue sur sa partie PostgreSQL : Connection timed out (10060).

Contournement sans toucher au pare-feu — exécuter le SQL en local sur Alteris :

pscp fichier.sql debian@79.137.14.202:/tmp/
sudo docker cp /tmp/fichier.sql alteris_postgis:/tmp/i.sql
sudo docker exec alteris_postgis psql -U alteris_admin -d alteris_geo -f /tmp/i.sql

Rouvrir le port exposerait à nouveau une base de données directement sur internet. Décision à prendre sciemment, surtout après l'incident du 2026-06-09.


Fichiers, sauvegardes et rollbacks

Élément Chemin Rôle
Compose Komga (avant) /var/lib/docker/volumes/portainer_data/_data/compose/7/docker-compose.yml.bak-260802 Rollback Komga
Compose Stirling (avant) /var/lib/docker/volumes/portainer_data/_data/compose/89/docker-compose.yml.bak-260802 Rollback Stirling
Version Stirling 1 Go /home/debian/stirling-compose-new.yml.orig1g Ne pas utiliser (OOM sur OCR)
fstab (avant) /etc/fstab.bak-260803 Rollback swap
Réglage swappiness /etc/sysctl.d/99-swap.conf vm.swappiness = 10
Second swap /swapfile2 (4 Go) Marge de swap
Script OCR Jux-scripts/Stirling-OCR/ocr_image.py + README.md Contournement OCR images

Rollback JVM : restaurer le .bak-260802 de la stack et redéployer avec sudo docker compose -p {7|89} -f ... up -d.


Références

01_Les recherches de Claude

Pages de publication des résultats d'analyse et de production de Claude Code sur le contenu du VPS

01_Les recherches de Claude

01A_Kdrive_les explorations

RCH-20260529-0003 — Livres et ressources evoquant la ville de Genes dans Foxy

Date2026-05-29
ServicekDrive Infomaniak
ContexteConstituer un corpus documentaire autour de Genes (Genovese, Ligurie) dans la bibliotheque kDrive — dossier SYNC-pour_VPS/Sync_NAS-Maison/Foxy
Outils utilisésAPI REST Infomaniak kDrive v3 — search + metadata v2 | PowerShell
RequêteGET /3/drive/591617/files/search?query={Genes|Genova|Genovese|Genoa|Ligurie}&limit=20 | filtre manuel sur résultats

Résultats

3 livres directement sur Gênes (la ville), 1 presentation Ligurie, des photos de Ligurie (2011), et des recettes genovese (cuisine). Recherche couvrant les termes : Genes, Genova, Genovese, Genoa, Ligurie.

Points clés

Limites

L'API v2 ne retourne pas le chemin (path) des fichiers — impossible de localiser précisément le sous-dossier sans navigation manuelle. Le champ path est vide dans la réponse.

Localisation des ressources

URLs
Chemins["kDrive 591617 / Private (5) / SYNC-pour_VPS (534292) / Sync_NAS-Maison (534293) / Foxy (401926) / [sous-dossier inconnu]"]
IDs{"531552": "Antoine-Marie Graziani - Histoire de Genes.epub (5.2 MB)", "531550": "Histoire de de Genes - Histoire.epub (0.8 MB)", "531549": "Histoire de la République de Gênes - Emile Vincens - Histoire.epub (0.9 MB)", "501392": "210526-Ligurie.pptx (31.9 MB)"}
Auteurs / Source["Antoine-Marie Graziani (Histoire de Genes)", "Emile Vincens (Histoire de la République de Gênes)"]
Métadonnées{"formats": ["epub", "pptx", "jpg", "mp4"], "livres_genes": 3, "photos_ligurie": 20, "videos_recettes_genovese": 2, "dossier_foxy_id": 401926, "kdrive_id": 591617}

Tags

#Genes   #Gênes   #Ligurie   #Italie   #histoire   #kDrive   #epub   #bibliotheque

Suite / Pistes

1/ Explorer le dossier Culture Italie (ID:1185062) pour trouver d'autres ressources sur Genes. 2/ Verifier dans Calibre (ID:1211295) si des livres Genes y sont catalogues. 3/ Corriger l'API v2 pour obtenir le chemin des fichiers (utiliser /3/ au lieu de /2/). 4/ Croiser avec Kavita si ces epubs y sont importes.


RCH-20260529-0004 — Approfondissement corpus Gênes — Culture Italie et Bibliotheque Calibre

Date2026-05-29
ServicekDrive Infomaniak
ContexteSuite de RCH-20260529-0003. Explorer Culture Italie (ID:1185062) et Bibliotheque Calibre (ID:1211295) pour compléter le corpus #Genes.
Outils utilisésAPI REST kDrive v3 — navigation par ID + listing sous-dossiers | PowerShell
RequêteGET /3/drive/591617/files/{1185194|1185151|1185105|1185085|1211295}/files?limit=100 + filtres regex Genes/Ligurie

Résultats

Culture Italie contient un dossier Ligurie (Cuisine Italie/Ligurie) avec 2 fichiers. Calibre contient Graziani mais uniquement pour Pascal Paoli (Corse), pas Genes. Vincens absent de Calibre. Plusieurs livres sur l'Italie générale potentiellement exploitables.

Points clés

Limites

Calibre n'a pas les livres sur Gênes de Vincens et de la version anonyme. Seul Graziani y est mais pour un autre ouvrage. Les 3 epubs Genes sont uniquement dans Foxy, non indexés dans Calibre. La recherche ne couvre pas les sous-sous-dossiers de Calibre (trop nombreux).

Localisation des ressources

URLs
Chemins["Foxy/Culture Italie/Cuisine Italie/Ligurie", "Foxy/Culture Italie/Histoire Italie", "Foxy/Culture Italie/Art de vivre Italie", "Foxy/Bibliotheque Calibre/Antoine-Marie Graziani"]
IDs{"1185345": "Dossier Ligurie dans Cuisine Italie", "1185347": "210526-Ligurie.pptx (31.9 MB) — dans Cuisine Italie/Ligurie", "1185349": "IMG_3782.pdf — dans Cuisine Italie/Ligurie", "1362307": "Calibre/Antoine-Marie Graziani (Pascal Paoli — pas Gênes)", "1185194": "Histoire Italie (4 livres generaux)", "1185085": "Art de vivre Italie (5 ressources)", "1185201": "L'Italie de Botticelli à Bonaparte.epub", "1185199": "Les guerres d'Italie — Un conflit européen.epub", "1185196": "Bella Italia - Un itinéraire amoureux (Christiane Rance).epub", "1185090": "Sacrés Italiens (Alberto Toscano).epub", "1185089": "Portraits d'Italie (Laurent Bolard).epub", "1185087": "Geo Hors-Serie Dolce Vita Avril-Mai 2025.pdf"}
Auteurs / Source
Métadonnées{"corpus_genes_directs": 3, "ressources_ligurie": 2, "livres_italie_generale": 9, "calibre_graziani_titre": "Pascal Paoli - Pere de la patrie corse", "vincens_dans_calibre": false}

Tags

#Genes   #Genes   #Ligurie   #Italie   #Calibre   #kDrive   #Culture Italie   #histoire

Suite / Pistes

1/ Importer les 3 epubs Genes (IDs 531549-531552) dans Calibre pour les indexer. 2/ Lire le PPTX Ligurie (ID:1185347) pour en extraire le contenu. 3/ Explorer 'Bella Italia' et 'Les guerres d'Italie' pour mentions de Genes. 4/ Rechercher 'Genes' dans le dossier Histoire (ID:503138) de Foxy.


01_Les recherches de Claude

01_Portainer - les explorations

Journal des recherches réalisées avec Claude Code sur le service Portainer.
Documentation technique : https://bookstack.juxjux.ovh/books/le-livre-de-claude/page/152
Chaque entrée est automatiquement enregistrée dans la table recherches (PostgreSQL Alteris, DB 35 Metabase).


RCH-AAAAMMJJ-NNNN — [Objet de la recherche]

DateAAAA-MM-JJ
Service[nom du service]
Contexte[Pourquoi cette recherche — quelle question, quel besoin, quel problème]
Outils utilisés[MCP utilisé / API directe / PowerShell / Python / recherche web...]
Requête[query exacte, paramètres, filtres, commandes]

Résultats

[Description des résultats obtenus — texte libre, liste, tableau selon le volume]

Points clés

Limites

[Ce qui manque, erreurs rencontrées, données absentes, pistes non abouties]

Localisation des ressources

URLs[liens directs vers les ressources trouvées]
Chemins[chemins fichiers, répertoires, arborescences]
IDs[IDs containers, pages, assets, items...]
Auteurs / Source[auteurs, éditeurs, provenance]
Métadonnées[dates, formats, tailles, langues, coordonnées...]

Tags

#tag1   #tag2   #tag3

Suite / Pistes

[Questions ouvertes, actions à entreprendre, recherches complémentaires à lancer]


01_Les recherches de Claude

02_Bookstack_les explorations

Journal des recherches réalisées avec Claude Code sur le service Bookstack.
Documentation technique : https://bookstack.juxjux.ovh/books/le-livre-de-claude/page/153
Chaque entrée est automatiquement enregistrée dans la table recherches (PostgreSQL Alteris, DB 35 Metabase).


RCH-AAAAMMJJ-NNNN — [Objet de la recherche]

DateAAAA-MM-JJ
Service[nom du service]
Contexte[Pourquoi cette recherche — quelle question, quel besoin, quel problème]
Outils utilisés[MCP utilisé / API directe / PowerShell / Python / recherche web...]
Requête[query exacte, paramètres, filtres, commandes]

Résultats

[Description des résultats obtenus — texte libre, liste, tableau selon le volume]

Points clés

Limites

[Ce qui manque, erreurs rencontrées, données absentes, pistes non abouties]

Localisation des ressources

URLs[liens directs vers les ressources trouvées]
Chemins[chemins fichiers, répertoires, arborescences]
IDs[IDs containers, pages, assets, items...]
Auteurs / Source[auteurs, éditeurs, provenance]
Métadonnées[dates, formats, tailles, langues, coordonnées...]

Tags

#tag1   #tag2   #tag3

Suite / Pistes

[Questions ouvertes, actions à entreprendre, recherches complémentaires à lancer]


01_Les recherches de Claude

03_Homarr_les explorations

Journal des recherches réalisées avec Claude Code sur le service Homarr.
Documentation technique : https://bookstack.juxjux.ovh/books/le-livre-de-claude/page/154
Chaque entrée est automatiquement enregistrée dans la table recherches (PostgreSQL Alteris, DB 35 Metabase).


RCH-AAAAMMJJ-NNNN — [Objet de la recherche]

DateAAAA-MM-JJ
Service[nom du service]
Contexte[Pourquoi cette recherche — quelle question, quel besoin, quel problème]
Outils utilisés[MCP utilisé / API directe / PowerShell / Python / recherche web...]
Requête[query exacte, paramètres, filtres, commandes]

Résultats

[Description des résultats obtenus — texte libre, liste, tableau selon le volume]

Points clés

Limites

[Ce qui manque, erreurs rencontrées, données absentes, pistes non abouties]

Localisation des ressources

URLs[liens directs vers les ressources trouvées]
Chemins[chemins fichiers, répertoires, arborescences]
IDs[IDs containers, pages, assets, items...]
Auteurs / Source[auteurs, éditeurs, provenance]
Métadonnées[dates, formats, tailles, langues, coordonnées...]

Tags

#tag1   #tag2   #tag3

Suite / Pistes

[Questions ouvertes, actions à entreprendre, recherches complémentaires à lancer]


01_Les recherches de Claude

04_Audiobookshelf_les explorations

Journal des recherches réalisées avec Claude Code sur le service Audiobookshelf.
Documentation technique : https://bookstack.juxjux.ovh/books/le-livre-de-claude/page/155
Chaque entrée est automatiquement enregistrée dans la table recherches (PostgreSQL Alteris, DB 35 Metabase).


RCH-AAAAMMJJ-NNNN — [Objet de la recherche]

DateAAAA-MM-JJ
Service[nom du service]
Contexte[Pourquoi cette recherche — quelle question, quel besoin, quel problème]
Outils utilisés[MCP utilisé / API directe / PowerShell / Python / recherche web...]
Requête[query exacte, paramètres, filtres, commandes]

Résultats

[Description des résultats obtenus — texte libre, liste, tableau selon le volume]

Points clés

Limites

[Ce qui manque, erreurs rencontrées, données absentes, pistes non abouties]

Localisation des ressources

URLs[liens directs vers les ressources trouvées]
Chemins[chemins fichiers, répertoires, arborescences]
IDs[IDs containers, pages, assets, items...]
Auteurs / Source[auteurs, éditeurs, provenance]
Métadonnées[dates, formats, tailles, langues, coordonnées...]

Tags

#tag1   #tag2   #tag3

Suite / Pistes

[Questions ouvertes, actions à entreprendre, recherches complémentaires à lancer]


01_Les recherches de Claude

05_BentoPDF_les explorations

Journal des recherches réalisées avec Claude Code sur le service BentoPDF.
Documentation technique : https://bookstack.juxjux.ovh/books/le-livre-de-claude/page/156
Chaque entrée est automatiquement enregistrée dans la table recherches (PostgreSQL Alteris, DB 35 Metabase).


RCH-AAAAMMJJ-NNNN — [Objet de la recherche]

DateAAAA-MM-JJ
Service[nom du service]
Contexte[Pourquoi cette recherche — quelle question, quel besoin, quel problème]
Outils utilisés[MCP utilisé / API directe / PowerShell / Python / recherche web...]
Requête[query exacte, paramètres, filtres, commandes]

Résultats

[Description des résultats obtenus — texte libre, liste, tableau selon le volume]

Points clés

Limites

[Ce qui manque, erreurs rencontrées, données absentes, pistes non abouties]

Localisation des ressources

URLs[liens directs vers les ressources trouvées]
Chemins[chemins fichiers, répertoires, arborescences]
IDs[IDs containers, pages, assets, items...]
Auteurs / Source[auteurs, éditeurs, provenance]
Métadonnées[dates, formats, tailles, langues, coordonnées...]

Tags

#tag1   #tag2   #tag3

Suite / Pistes

[Questions ouvertes, actions à entreprendre, recherches complémentaires à lancer]


01_Les recherches de Claude

06_Cadvisor_les explorations

Journal des recherches réalisées avec Claude Code sur le service Cadvisor.
Documentation technique : https://bookstack.juxjux.ovh/books/le-livre-de-claude/page/157
Chaque entrée est automatiquement enregistrée dans la table recherches (PostgreSQL Alteris, DB 35 Metabase).


RCH-AAAAMMJJ-NNNN — [Objet de la recherche]

DateAAAA-MM-JJ
Service[nom du service]
Contexte[Pourquoi cette recherche — quelle question, quel besoin, quel problème]
Outils utilisés[MCP utilisé / API directe / PowerShell / Python / recherche web...]
Requête[query exacte, paramètres, filtres, commandes]

Résultats

[Description des résultats obtenus — texte libre, liste, tableau selon le volume]

Points clés

Limites

[Ce qui manque, erreurs rencontrées, données absentes, pistes non abouties]

Localisation des ressources

URLs[liens directs vers les ressources trouvées]
Chemins[chemins fichiers, répertoires, arborescences]
IDs[IDs containers, pages, assets, items...]
Auteurs / Source[auteurs, éditeurs, provenance]
Métadonnées[dates, formats, tailles, langues, coordonnées...]

Tags

#tag1   #tag2   #tag3

Suite / Pistes

[Questions ouvertes, actions à entreprendre, recherches complémentaires à lancer]


01_Les recherches de Claude

07_Calibre-web_les explorations

Journal des recherches réalisées avec Claude Code sur le service Calibre-web.
Documentation technique : https://bookstack.juxjux.ovh/books/le-livre-de-claude/page/158
Chaque entrée est automatiquement enregistrée dans la table recherches (PostgreSQL Alteris, DB 35 Metabase).


RCH-AAAAMMJJ-NNNN — [Objet de la recherche]

DateAAAA-MM-JJ
Service[nom du service]
Contexte[Pourquoi cette recherche — quelle question, quel besoin, quel problème]
Outils utilisés[MCP utilisé / API directe / PowerShell / Python / recherche web...]
Requête[query exacte, paramètres, filtres, commandes]

Résultats

[Description des résultats obtenus — texte libre, liste, tableau selon le volume]

Points clés

Limites

[Ce qui manque, erreurs rencontrées, données absentes, pistes non abouties]

Localisation des ressources

URLs[liens directs vers les ressources trouvées]
Chemins[chemins fichiers, répertoires, arborescences]
IDs[IDs containers, pages, assets, items...]
Auteurs / Source[auteurs, éditeurs, provenance]
Métadonnées[dates, formats, tailles, langues, coordonnées...]

Tags

#tag1   #tag2   #tag3

Suite / Pistes

[Questions ouvertes, actions à entreprendre, recherches complémentaires à lancer]


01_Les recherches de Claude

08_Dozzle_les explorations

Journal des recherches réalisées avec Claude Code sur le service Dozzle.
Documentation technique : https://bookstack.juxjux.ovh/books/le-livre-de-claude/page/159
Chaque entrée est automatiquement enregistrée dans la table recherches (PostgreSQL Alteris, DB 35 Metabase).


RCH-AAAAMMJJ-NNNN — [Objet de la recherche]

DateAAAA-MM-JJ
Service[nom du service]
Contexte[Pourquoi cette recherche — quelle question, quel besoin, quel problème]
Outils utilisés[MCP utilisé / API directe / PowerShell / Python / recherche web...]
Requête[query exacte, paramètres, filtres, commandes]

Résultats

[Description des résultats obtenus — texte libre, liste, tableau selon le volume]

Points clés

Limites

[Ce qui manque, erreurs rencontrées, données absentes, pistes non abouties]

Localisation des ressources

URLs[liens directs vers les ressources trouvées]
Chemins[chemins fichiers, répertoires, arborescences]
IDs[IDs containers, pages, assets, items...]
Auteurs / Source[auteurs, éditeurs, provenance]
Métadonnées[dates, formats, tailles, langues, coordonnées...]

Tags

#tag1   #tag2   #tag3

Suite / Pistes

[Questions ouvertes, actions à entreprendre, recherches complémentaires à lancer]


01_Les recherches de Claude

09_FileBrowser_les explorations

Journal des recherches réalisées avec Claude Code sur le service FileBrowser.
Documentation technique : https://bookstack.juxjux.ovh/books/le-livre-de-claude/page/160
Chaque entrée est automatiquement enregistrée dans la table recherches (PostgreSQL Alteris, DB 35 Metabase).


RCH-AAAAMMJJ-NNNN — [Objet de la recherche]

DateAAAA-MM-JJ
Service[nom du service]
Contexte[Pourquoi cette recherche — quelle question, quel besoin, quel problème]
Outils utilisés[MCP utilisé / API directe / PowerShell / Python / recherche web...]
Requête[query exacte, paramètres, filtres, commandes]

Résultats

[Description des résultats obtenus — texte libre, liste, tableau selon le volume]

Points clés

Limites

[Ce qui manque, erreurs rencontrées, données absentes, pistes non abouties]

Localisation des ressources

URLs[liens directs vers les ressources trouvées]
Chemins[chemins fichiers, répertoires, arborescences]
IDs[IDs containers, pages, assets, items...]
Auteurs / Source[auteurs, éditeurs, provenance]
Métadonnées[dates, formats, tailles, langues, coordonnées...]

Tags

#tag1   #tag2   #tag3

Suite / Pistes

[Questions ouvertes, actions à entreprendre, recherches complémentaires à lancer]


01_Les recherches de Claude

10_FreshRSS_les explorations



RCH-20260529-0002 — Articles Inkscape dans la categorie claude_foss

Date 2026-05-29
Service FreshRSS
Contexte Second passage apres creation de la categorie claude_foss et ajout du flux Inkscape News. Verifier si des articles sur Inkscape sont desormais indexes.
Outils utilisés API GReader FreshRSS directe — stream/contents/user/-/label/claude_foss — search_foss.py
Requête GET /reader/api/0/stream/contents/user/-/label/claude_foss?n=100&q=Inkscape

Résultats

100 articles retournes depuis la categorie claude_foss (7 flux). 10 articles provenant du flux Inkscape News (feed/615) sont presents. Note : le parametre q= de l'API GReader ne filtre pas reellement par mot-cle — il retourne tous les articles du stream. Le filtrage Inkscape est donc a faire cote client.

Points clés

Limites

Le parametre q= de l'API GReader FreshRSS ne filtre pas par mot-cle. Pour une recherche thematique precise, il faut recuperer tous les articles et filtrer cote client sur title + content. A corriger dans search_foss.py.

Localisation des ressources

URLs ["https://inkscape.org/news/2026/05/13/inkscape-supports-german-petition-to-recognize-ope/", "https://inkscape.org/news/2026/05/06/inkscape-144-boosts-performance-and-crushes-crashe/", "https://inkscape.org/news/2026/04/02/artist-interview-meet-jorge-who-made-a-short-film/", "https://inkscape.org/news/2026/03/02/inkscape-is-hiring-2026-1/", "https://inkscape.org/news/2025/12/26/bugs-banished-inkscape-143-is-out/", "https://inkscape.org/news/2025/10/12/indiafoss-2025/", "https://inkscape.org/news/2025/06/08/libre-graphics-meeting-2025/", "https://inkscape.org/news/2025/06/01/inkscape-summit-nuremberg/", "https://inkscape.org/news/2025/05/12/2-in-1-release-inkscape-142-is-out/", "https://inkscape.org/news/2025/02/05/inkscape-summit-frankfurt-2025/"]
Chemins
IDs ["feed/615 (Inkscape News)", "user/-/label/claude_foss"]
Auteurs / Source ["Inkscape Project"]
Métadonnées {"articles_inkscape": 10, "articles_total_stream": 100, "flux_claude_foss": 7, "derniere_version": "Inkscape 1.4.4", "date_derniere_version": "2026-05-06", "periode_couverte": "2025-02 a 2026-05"}

Tags

#Inkscape   #logiciel-libre   #SVG   #dessin-vectoriel   #FOSS   #FreshRSS   #claude_foss

Suite / Pistes

1/ Corriger search_foss.py pour filtrer cote client sur title+content. 2/ Ajouter flux Libre Graphics Meeting et Planet Inkscape si existants. 3/ Relancer apres quelques jours pour capter les nouveaux articles. 4/ Croiser avec Kavita (livres SVG/Inkscape) et kDrive (tutoriels eventuels).


01_Les recherches de Claude

11_Gitea_les explorations

Journal des recherches réalisées avec Claude Code sur le service Gitea.
Documentation technique : https://bookstack.juxjux.ovh/books/le-livre-de-claude/page/162
Chaque entrée est automatiquement enregistrée dans la table recherches (PostgreSQL Alteris, DB 35 Metabase).


RCH-AAAAMMJJ-NNNN — [Objet de la recherche]

DateAAAA-MM-JJ
Service[nom du service]
Contexte[Pourquoi cette recherche — quelle question, quel besoin, quel problème]
Outils utilisés[MCP utilisé / API directe / PowerShell / Python / recherche web...]
Requête[query exacte, paramètres, filtres, commandes]

Résultats

[Description des résultats obtenus — texte libre, liste, tableau selon le volume]

Points clés

Limites

[Ce qui manque, erreurs rencontrées, données absentes, pistes non abouties]

Localisation des ressources

URLs[liens directs vers les ressources trouvées]
Chemins[chemins fichiers, répertoires, arborescences]
IDs[IDs containers, pages, assets, items...]
Auteurs / Source[auteurs, éditeurs, provenance]
Métadonnées[dates, formats, tailles, langues, coordonnées...]

Tags

#tag1   #tag2   #tag3

Suite / Pistes

[Questions ouvertes, actions à entreprendre, recherches complémentaires à lancer]


01_Les recherches de Claude

12_Grafana_les explorations

Journal des recherches réalisées avec Claude Code sur le service Grafana.
Documentation technique : https://bookstack.juxjux.ovh/books/le-livre-de-claude/page/163
Chaque entrée est automatiquement enregistrée dans la table recherches (PostgreSQL Alteris, DB 35 Metabase).


RCH-AAAAMMJJ-NNNN — [Objet de la recherche]

DateAAAA-MM-JJ
Service[nom du service]
Contexte[Pourquoi cette recherche — quelle question, quel besoin, quel problème]
Outils utilisés[MCP utilisé / API directe / PowerShell / Python / recherche web...]
Requête[query exacte, paramètres, filtres, commandes]

Résultats

[Description des résultats obtenus — texte libre, liste, tableau selon le volume]

Points clés

Limites

[Ce qui manque, erreurs rencontrées, données absentes, pistes non abouties]

Localisation des ressources

URLs[liens directs vers les ressources trouvées]
Chemins[chemins fichiers, répertoires, arborescences]
IDs[IDs containers, pages, assets, items...]
Auteurs / Source[auteurs, éditeurs, provenance]
Métadonnées[dates, formats, tailles, langues, coordonnées...]

Tags

#tag1   #tag2   #tag3

Suite / Pistes

[Questions ouvertes, actions à entreprendre, recherches complémentaires à lancer]


01_Les recherches de Claude

13_Immich_les explorations

Journal des recherches réalisées avec Claude Code sur le service Immich.
Documentation technique : https://bookstack.juxjux.ovh/books/le-livre-de-claude/page/164
Chaque entrée est automatiquement enregistrée dans la table recherches (PostgreSQL Alteris, DB 35 Metabase).


RCH-AAAAMMJJ-NNNN — [Objet de la recherche]

DateAAAA-MM-JJ
Service[nom du service]
Contexte[Pourquoi cette recherche — quelle question, quel besoin, quel problème]
Outils utilisés[MCP utilisé / API directe / PowerShell / Python / recherche web...]
Requête[query exacte, paramètres, filtres, commandes]

Résultats

[Description des résultats obtenus — texte libre, liste, tableau selon le volume]

Points clés

Limites

[Ce qui manque, erreurs rencontrées, données absentes, pistes non abouties]

Localisation des ressources

URLs[liens directs vers les ressources trouvées]
Chemins[chemins fichiers, répertoires, arborescences]
IDs[IDs containers, pages, assets, items...]
Auteurs / Source[auteurs, éditeurs, provenance]
Métadonnées[dates, formats, tailles, langues, coordonnées...]

Tags

#tag1   #tag2   #tag3

Suite / Pistes

[Questions ouvertes, actions à entreprendre, recherches complémentaires à lancer]


01_Les recherches de Claude

14_Jellyfin_les explorations

Journal des recherches réalisées avec Claude Code sur le service Jellyfin.
Documentation technique : https://bookstack.juxjux.ovh/books/le-livre-de-claude/page/165
Chaque entrée est automatiquement enregistrée dans la table recherches (PostgreSQL Alteris, DB 35 Metabase).


RCH-AAAAMMJJ-NNNN — [Objet de la recherche]

DateAAAA-MM-JJ
Service[nom du service]
Contexte[Pourquoi cette recherche — quelle question, quel besoin, quel problème]
Outils utilisés[MCP utilisé / API directe / PowerShell / Python / recherche web...]
Requête[query exacte, paramètres, filtres, commandes]

Résultats

[Description des résultats obtenus — texte libre, liste, tableau selon le volume]

Points clés

Limites

[Ce qui manque, erreurs rencontrées, données absentes, pistes non abouties]

Localisation des ressources

URLs[liens directs vers les ressources trouvées]
Chemins[chemins fichiers, répertoires, arborescences]
IDs[IDs containers, pages, assets, items...]
Auteurs / Source[auteurs, éditeurs, provenance]
Métadonnées[dates, formats, tailles, langues, coordonnées...]

Tags

#tag1   #tag2   #tag3

Suite / Pistes

[Questions ouvertes, actions à entreprendre, recherches complémentaires à lancer]


01_Les recherches de Claude

15_Joplin_les explorations

Journal des recherches réalisées avec Claude Code sur le service Joplin.
Documentation technique : https://bookstack.juxjux.ovh/books/le-livre-de-claude/page/166
Chaque entrée est automatiquement enregistrée dans la table recherches (PostgreSQL Alteris, DB 35 Metabase).


RCH-AAAAMMJJ-NNNN — [Objet de la recherche]

DateAAAA-MM-JJ
Service[nom du service]
Contexte[Pourquoi cette recherche — quelle question, quel besoin, quel problème]
Outils utilisés[MCP utilisé / API directe / PowerShell / Python / recherche web...]
Requête[query exacte, paramètres, filtres, commandes]

Résultats

[Description des résultats obtenus — texte libre, liste, tableau selon le volume]

Points clés

Limites

[Ce qui manque, erreurs rencontrées, données absentes, pistes non abouties]

Localisation des ressources

URLs[liens directs vers les ressources trouvées]
Chemins[chemins fichiers, répertoires, arborescences]
IDs[IDs containers, pages, assets, items...]
Auteurs / Source[auteurs, éditeurs, provenance]
Métadonnées[dates, formats, tailles, langues, coordonnées...]

Tags

#tag1   #tag2   #tag3

Suite / Pistes

[Questions ouvertes, actions à entreprendre, recherches complémentaires à lancer]


01_Les recherches de Claude

16_Kavita_les explorations

Journal des recherches réalisées avec Claude Code sur le service Kavita.
Documentation technique : https://bookstack.juxjux.ovh/books/le-livre-de-claude/page/167
Chaque entrée est automatiquement enregistrée dans la table recherches (PostgreSQL Alteris, DB 35 Metabase).


RCH-AAAAMMJJ-NNNN — [Objet de la recherche]

DateAAAA-MM-JJ
Service[nom du service]
Contexte[Pourquoi cette recherche — quelle question, quel besoin, quel problème]
Outils utilisés[MCP utilisé / API directe / PowerShell / Python / recherche web...]
Requête[query exacte, paramètres, filtres, commandes]

Résultats

[Description des résultats obtenus — texte libre, liste, tableau selon le volume]

Points clés

Limites

[Ce qui manque, erreurs rencontrées, données absentes, pistes non abouties]

Localisation des ressources

URLs[liens directs vers les ressources trouvées]
Chemins[chemins fichiers, répertoires, arborescences]
IDs[IDs containers, pages, assets, items...]
Auteurs / Source[auteurs, éditeurs, provenance]
Métadonnées[dates, formats, tailles, langues, coordonnées...]

Tags

#tag1   #tag2   #tag3

Suite / Pistes

[Questions ouvertes, actions à entreprendre, recherches complémentaires à lancer]


01_Les recherches de Claude

17_Komga_les explorations

Journal des recherches réalisées avec Claude Code sur le service Komga.
Documentation technique : https://bookstack.juxjux.ovh/books/le-livre-de-claude/page/168
Chaque entrée est automatiquement enregistrée dans la table recherches (PostgreSQL Alteris, DB 35 Metabase).


RCH-AAAAMMJJ-NNNN — [Objet de la recherche]

DateAAAA-MM-JJ
Service[nom du service]
Contexte[Pourquoi cette recherche — quelle question, quel besoin, quel problème]
Outils utilisés[MCP utilisé / API directe / PowerShell / Python / recherche web...]
Requête[query exacte, paramètres, filtres, commandes]

Résultats

[Description des résultats obtenus — texte libre, liste, tableau selon le volume]

Points clés

Limites

[Ce qui manque, erreurs rencontrées, données absentes, pistes non abouties]

Localisation des ressources

URLs[liens directs vers les ressources trouvées]
Chemins[chemins fichiers, répertoires, arborescences]
IDs[IDs containers, pages, assets, items...]
Auteurs / Source[auteurs, éditeurs, provenance]
Métadonnées[dates, formats, tailles, langues, coordonnées...]

Tags

#tag1   #tag2   #tag3

Suite / Pistes

[Questions ouvertes, actions à entreprendre, recherches complémentaires à lancer]


01_Les recherches de Claude

18_Mealie_les explorations

Journal des recherches réalisées avec Claude Code sur le service Mealie.
Documentation technique : https://bookstack.juxjux.ovh/books/le-livre-de-claude/page/169
Chaque entrée est automatiquement enregistrée dans la table recherches (PostgreSQL Alteris, DB 35 Metabase).


RCH-AAAAMMJJ-NNNN — [Objet de la recherche]

DateAAAA-MM-JJ
Service[nom du service]
Contexte[Pourquoi cette recherche — quelle question, quel besoin, quel problème]
Outils utilisés[MCP utilisé / API directe / PowerShell / Python / recherche web...]
Requête[query exacte, paramètres, filtres, commandes]

Résultats

[Description des résultats obtenus — texte libre, liste, tableau selon le volume]

Points clés

Limites

[Ce qui manque, erreurs rencontrées, données absentes, pistes non abouties]

Localisation des ressources

URLs[liens directs vers les ressources trouvées]
Chemins[chemins fichiers, répertoires, arborescences]
IDs[IDs containers, pages, assets, items...]
Auteurs / Source[auteurs, éditeurs, provenance]
Métadonnées[dates, formats, tailles, langues, coordonnées...]

Tags

#tag1   #tag2   #tag3

Suite / Pistes

[Questions ouvertes, actions à entreprendre, recherches complémentaires à lancer]


01_Les recherches de Claude

19_Navidrome_les explorations

Journal des recherches réalisées avec Claude Code sur le service Navidrome.
Documentation technique : https://bookstack.juxjux.ovh/books/le-livre-de-claude/page/170
Chaque entrée est automatiquement enregistrée dans la table recherches (PostgreSQL Alteris, DB 35 Metabase).


RCH-AAAAMMJJ-NNNN — [Objet de la recherche]

DateAAAA-MM-JJ
Service[nom du service]
Contexte[Pourquoi cette recherche — quelle question, quel besoin, quel problème]
Outils utilisés[MCP utilisé / API directe / PowerShell / Python / recherche web...]
Requête[query exacte, paramètres, filtres, commandes]

Résultats

[Description des résultats obtenus — texte libre, liste, tableau selon le volume]

Points clés

Limites

[Ce qui manque, erreurs rencontrées, données absentes, pistes non abouties]

Localisation des ressources

URLs[liens directs vers les ressources trouvées]
Chemins[chemins fichiers, répertoires, arborescences]
IDs[IDs containers, pages, assets, items...]
Auteurs / Source[auteurs, éditeurs, provenance]
Métadonnées[dates, formats, tailles, langues, coordonnées...]

Tags

#tag1   #tag2   #tag3

Suite / Pistes

[Questions ouvertes, actions à entreprendre, recherches complémentaires à lancer]


01_Les recherches de Claude

20_NodeExporter_les explorations

Journal des recherches réalisées avec Claude Code sur le service NodeExporter.
Documentation technique : https://bookstack.juxjux.ovh/books/le-livre-de-claude/page/171
Chaque entrée est automatiquement enregistrée dans la table recherches (PostgreSQL Alteris, DB 35 Metabase).


RCH-AAAAMMJJ-NNNN — [Objet de la recherche]

DateAAAA-MM-JJ
Service[nom du service]
Contexte[Pourquoi cette recherche — quelle question, quel besoin, quel problème]
Outils utilisés[MCP utilisé / API directe / PowerShell / Python / recherche web...]
Requête[query exacte, paramètres, filtres, commandes]

Résultats

[Description des résultats obtenus — texte libre, liste, tableau selon le volume]

Points clés

Limites

[Ce qui manque, erreurs rencontrées, données absentes, pistes non abouties]

Localisation des ressources

URLs[liens directs vers les ressources trouvées]
Chemins[chemins fichiers, répertoires, arborescences]
IDs[IDs containers, pages, assets, items...]
Auteurs / Source[auteurs, éditeurs, provenance]
Métadonnées[dates, formats, tailles, langues, coordonnées...]

Tags

#tag1   #tag2   #tag3

Suite / Pistes

[Questions ouvertes, actions à entreprendre, recherches complémentaires à lancer]


01_Les recherches de Claude

21_Prometheus_les explorations

Journal des recherches réalisées avec Claude Code sur le service Prometheus.
Documentation technique : https://bookstack.juxjux.ovh/books/le-livre-de-claude/page/172
Chaque entrée est automatiquement enregistrée dans la table recherches (PostgreSQL Alteris, DB 35 Metabase).


RCH-AAAAMMJJ-NNNN — [Objet de la recherche]

DateAAAA-MM-JJ
Service[nom du service]
Contexte[Pourquoi cette recherche — quelle question, quel besoin, quel problème]
Outils utilisés[MCP utilisé / API directe / PowerShell / Python / recherche web...]
Requête[query exacte, paramètres, filtres, commandes]

Résultats

[Description des résultats obtenus — texte libre, liste, tableau selon le volume]

Points clés

Limites

[Ce qui manque, erreurs rencontrées, données absentes, pistes non abouties]

Localisation des ressources

URLs[liens directs vers les ressources trouvées]
Chemins[chemins fichiers, répertoires, arborescences]
IDs[IDs containers, pages, assets, items...]
Auteurs / Source[auteurs, éditeurs, provenance]
Métadonnées[dates, formats, tailles, langues, coordonnées...]

Tags

#tag1   #tag2   #tag3

Suite / Pistes

[Questions ouvertes, actions à entreprendre, recherches complémentaires à lancer]


01_Les recherches de Claude

22_Readeck_les explorations

Journal des recherches réalisées avec Claude Code sur le service Readeck.
Documentation technique : https://bookstack.juxjux.ovh/books/le-livre-de-claude/page/173
Chaque entrée est automatiquement enregistrée dans la table recherches (PostgreSQL Alteris, DB 35 Metabase).


RCH-AAAAMMJJ-NNNN — [Objet de la recherche]

DateAAAA-MM-JJ
Service[nom du service]
Contexte[Pourquoi cette recherche — quelle question, quel besoin, quel problème]
Outils utilisés[MCP utilisé / API directe / PowerShell / Python / recherche web...]
Requête[query exacte, paramètres, filtres, commandes]

Résultats

[Description des résultats obtenus — texte libre, liste, tableau selon le volume]

Points clés

Limites

[Ce qui manque, erreurs rencontrées, données absentes, pistes non abouties]

Localisation des ressources

URLs[liens directs vers les ressources trouvées]
Chemins[chemins fichiers, répertoires, arborescences]
IDs[IDs containers, pages, assets, items...]
Auteurs / Source[auteurs, éditeurs, provenance]
Métadonnées[dates, formats, tailles, langues, coordonnées...]

Tags

#tag1   #tag2   #tag3

Suite / Pistes

[Questions ouvertes, actions à entreprendre, recherches complémentaires à lancer]


01_Les recherches de Claude

23_Syncthing_les explorations

Journal des recherches réalisées avec Claude Code sur le service Syncthing.
Documentation technique : https://bookstack.juxjux.ovh/books/le-livre-de-claude/page/174
Chaque entrée est automatiquement enregistrée dans la table recherches (PostgreSQL Alteris, DB 35 Metabase).


RCH-AAAAMMJJ-NNNN — [Objet de la recherche]

DateAAAA-MM-JJ
Service[nom du service]
Contexte[Pourquoi cette recherche — quelle question, quel besoin, quel problème]
Outils utilisés[MCP utilisé / API directe / PowerShell / Python / recherche web...]
Requête[query exacte, paramètres, filtres, commandes]

Résultats

[Description des résultats obtenus — texte libre, liste, tableau selon le volume]

Points clés

Limites

[Ce qui manque, erreurs rencontrées, données absentes, pistes non abouties]

Localisation des ressources

URLs[liens directs vers les ressources trouvées]
Chemins[chemins fichiers, répertoires, arborescences]
IDs[IDs containers, pages, assets, items...]
Auteurs / Source[auteurs, éditeurs, provenance]
Métadonnées[dates, formats, tailles, langues, coordonnées...]

Tags

#tag1   #tag2   #tag3

Suite / Pistes

[Questions ouvertes, actions à entreprendre, recherches complémentaires à lancer]


01_Les recherches de Claude

24_Yourls_les explorations

Journal des recherches réalisées avec Claude Code sur le service Yourls.
Documentation technique : https://bookstack.juxjux.ovh/books/le-livre-de-claude/page/175
Chaque entrée est automatiquement enregistrée dans la table recherches (PostgreSQL Alteris, DB 35 Metabase).


RCH-AAAAMMJJ-NNNN — [Objet de la recherche]

DateAAAA-MM-JJ
Service[nom du service]
Contexte[Pourquoi cette recherche — quelle question, quel besoin, quel problème]
Outils utilisés[MCP utilisé / API directe / PowerShell / Python / recherche web...]
Requête[query exacte, paramètres, filtres, commandes]

Résultats

[Description des résultats obtenus — texte libre, liste, tableau selon le volume]

Points clés

Limites

[Ce qui manque, erreurs rencontrées, données absentes, pistes non abouties]

Localisation des ressources

URLs[liens directs vers les ressources trouvées]
Chemins[chemins fichiers, répertoires, arborescences]
IDs[IDs containers, pages, assets, items...]
Auteurs / Source[auteurs, éditeurs, provenance]
Métadonnées[dates, formats, tailles, langues, coordonnées...]

Tags

#tag1   #tag2   #tag3

Suite / Pistes

[Questions ouvertes, actions à entreprendre, recherches complémentaires à lancer]


01_Les recherches de Claude

25_StirlingPDF

RCH-20260802-0001 — Echec OCR sur images PNG — deux refus cumules d'ocrmypdf (canal alpha + DPI absent)

Date2026-08-02
ServiceStirlingPDF
ContexteJulien signale une erreur HTTP 500 lors d'un OCR sur une image PNG via StirlingPDF. L'interface web n'affiche aucun detail exploitable, le diagnostic passe par les logs du container.
Outils utilisésdocker logs stirling-pdf, API /api/v1/misc/ocr-pdf, Pillow, tesseract --list-langs
RequêtePOST /api/v1/misc/ocr-pdf (multipart fileInput, languages=[fra], ocrType=Force-OCR) sur PNG RGBA, puis sur variantes aplaties et converties, avec mesure docker stats en parallele.

Résultats

Deux erreurs distinctes et cumulees dans ocrmypdf 17.4.0, revelees seulement dans les logs : (1) UnsupportedImageFormatError — The input image has an alpha channel ; (2) une fois l'alpha retire, DpiError — Input file is an image, but has no resolution (DPI) in its metadata. StirlingPDF ne transmet pas l'option --image-dpi a ocrmypdf, donc tout PNG sans DPI dans ses metadonnees echoue. Deux contournements valides en test : voie interface web (Convert > Image to PDF puis OCR sur le PDF, HTTP 200) et voie scriptee (aplatissement de l'alpha sur fond blanc + DPI force a 300, HTTP 200). Texte reconnu restitue a l'identique sur une page de test en francais. Mesure memoire : pic a 894 Mio pour une seule page A4 300 dpi.

Points clés

Limites

Mesure realisee sur une page A4 300 dpi generee synthetiquement ; un document multi-pages ou un scan haute resolution consommera davantage. Le pic de 894 Mio est un plancher, pas un maximum.

Localisation des ressources

URLs["https://spdf.juxjux.ovh", "https://spdf.juxjux.ovh/swagger-ui/index.html"]
Chemins["D:\\Syncthing\\Jux_univers\\Jux-scripts\\Stirling-OCR\\ocr_image.py", "D:\\Syncthing\\Jux_univers\\Jux-scripts\\Stirling-OCR\\README.md", "/home/debian/stirling-compose-new.yml", "/home/debian/stirling-compose-new.yml.orig1g"]
IDs["stack Portainer 89", "container stirling-pdf", "page BookStack 211"]
Auteurs / Source
Métadonnées{"ocrmypdf": "17.4.0", "tesseract": "5.3.4", "endpoint_ocr": "POST /api/v1/misc/ocr-pdf", "pic_memoire_ocr": "894 MiB / page A4 300 dpi", "limite_memoire_retenue": "2g"}

Tags

#StirlingPDF   #OCR   #ocrmypdf   #tesseract   #PNG   #alpha   #DPI   #memoire   #Docker

Suite / Pistes

Verifier apres l'application du compose allege (2026-08-02 19h) que compress-pdf ET l'OCR fonctionnent toujours avec memory=2g et -Xmx512m.


RCH-20260530-0001 — Mise en place API StirlingPDF pour compression PDF automatisée (workflow geo_loud)

Date2026-05-30
ServiceStirlingPDF
ContexteÉtape 3 du workflow geo_loud : automatiser la compression des 127 PDFs lourds (>75 Mo) déplacés dans \\NASMAISON\foxy\geo_loud\. Objectif : appeler l'API StirlingPDF depuis le PC, écraser chaque fichier avec sa version compressée, mettre à jour la DB SQLite. Fichier de test : 000209_L'inde.pdf (314 Mo).
Outils utilisésStirlingPDF API REST (JWT Bearer) | curl.exe | plink (PuTTY) pour SSH nginx | Python 3.14 + requests | SQLite nas_geographie.db
Requête APIPOST https://spdf.juxjux.ovh/api/v1/misc/compress-pdf — multipart/form-data champ fileInput, header Authorization: Bearer <token>

Résultats

Auth JWT résolue. nginx corrigé (client_max_body_size 500M). Test de compression réussi sur 000209_L'inde.pdf : 314 Mo → 311.7 Mo (0.7% de gain — PDF déjà optimisé en images JPEG). Script nas_geo_compress.py opérationnel pour traitement en batch de tous les fichiers.

Points clés

Limites

Script nas_geo_compress.py

Emplacement : C:\Users\eliob\.claude\nas_geo_compress.py
Lancement : py C:\Users\eliob\.claude\nas_geo_compress.py
Dépendances : pip install requests (sqlite3 et os natifs Python)

Le script ajoute automatiquement les colonnes taille_compresse_octets et date_compression à la table fichiers si elles n'existent pas encore.

"""
Étape 3 du workflow geo_loud : compression PDF via StirlingPDF.
- Lit les fichiers statut='deplace' dans nas_geographie.db
- Envoie chaque PDF à l'API StirlingPDF
- Écrase le fichier d'origine avec la version compressée
- Met à jour la DB (taille_compresse_octets, date_compression)
"""
import sqlite3
import requests
import os
from datetime import datetime

DB_PATH = r"C:\Users\eliob\Documents\nas_geographie.db"
GEO_LOUD = r"\\NASMAISON\foxy\geo_loud"
STIRLING_URL = "https://spdf.juxjux.ovh"
STIRLING_USER = "admin"
STIRLING_PASS = "Motdepasse18!"


def get_token():
    r = requests.post(
        f"{STIRLING_URL}/api/v1/auth/login",
        json={"username": STIRLING_USER, "password": STIRLING_PASS},
        timeout=30,
    )
    r.raise_for_status()
    return r.json()["session"]["access_token"]


def init_db_columns(conn):
    cur = conn.cursor()
    cols = {row[1] for row in cur.execute("PRAGMA table_info(fichiers)")}
    if "taille_compresse_octets" not in cols:
        cur.execute("ALTER TABLE fichiers ADD COLUMN taille_compresse_octets INTEGER")
    if "date_compression" not in cols:
        cur.execute("ALTER TABLE fichiers ADD COLUMN date_compression TEXT")
    conn.commit()


def get_pending(conn):
    cur = conn.cursor()
    cur.execute("""
        SELECT id, nom, chemin_geo_loud, taille_octets
        FROM fichiers
        WHERE statut = 'deplace'
          AND extension IN ('.pdf', '.PDF')
          AND (date_compression IS NULL)
        ORDER BY taille_octets DESC
    """)
    return cur.fetchall()


def compress_file(token, filepath):
    with open(filepath, "rb") as f:
        r = requests.post(
            f"{STIRLING_URL}/api/v1/misc/compress-pdf",
            headers={"Authorization": f"Bearer {token}"},
            files={"fileInput": (os.path.basename(filepath), f, "application/pdf")},
            timeout=600,
            stream=True,
        )
    r.raise_for_status()
    return r.content


def update_db(conn, file_id, taille_compresse):
    conn.execute("""
        UPDATE fichiers
        SET taille_compresse_octets = ?, date_compression = ?
        WHERE id = ?
    """, (taille_compresse, datetime.now().isoformat(), file_id))
    conn.commit()


def fmt_mo(octets):
    return f"{octets / 1_048_576:.1f} Mo"


def main():
    print("Connexion à StirlingPDF...")
    token = get_token()
    print("Token JWT OK")

    conn = sqlite3.connect(DB_PATH)
    init_db_columns(conn)
    pending = get_pending(conn)
    print(f"{len(pending)} fichier(s) PDF à compresser\n")

    if not pending:
        print("Rien à faire.")
        conn.close()
        return

    total_avant = sum(r[3] for r in pending)
    total_apres = 0
    ok = 0
    errors = []

    for i, (file_id, nom, chemin_geo_loud, taille_avant) in enumerate(pending, 1):
        filepath = chemin_geo_loud or os.path.join(GEO_LOUD, nom)
        if not os.path.exists(filepath):
            print(f"[{i}/{len(pending)}] ABSENT  {nom}")
            errors.append((nom, "fichier absent"))
            continue

        taille_reel_avant = os.path.getsize(filepath)
        print(f"[{i}/{len(pending)}] {nom}")
        print(f"    avant : {fmt_mo(taille_reel_avant)}", end=" ", flush=True)

        try:
            compressed = compress_file(token, filepath)
            taille_apres_compression = len(compressed)
            gain = (taille_reel_avant - taille_apres_compression) / taille_reel_avant * 100

            tmp = filepath + ".tmp"
            with open(tmp, "wb") as f:
                f.write(compressed)
            os.replace(tmp, filepath)

            update_db(conn, file_id, taille_apres_compression)
            total_apres += taille_apres_compression
            ok += 1
            print(f"-> après : {fmt_mo(taille_apres_compression)} (gain {gain:.1f}%)")

        except Exception as e:
            print(f"-> ERREUR : {e}")
            errors.append((nom, str(e)))
            tmp = filepath + ".tmp"
            if os.path.exists(tmp):
                os.remove(tmp)

    conn.close()

    print(f"\n{'='*50}")
    print(f"Terminé : {ok}/{len(pending)} compressés")
    if ok:
        gain_total = (total_avant - total_apres) / total_avant * 100
        print(f"Volume avant : {fmt_mo(total_avant)}")
        print(f"Volume après : {fmt_mo(total_apres)}")
        print(f"Gain total   : {gain_total:.1f}%  ({fmt_mo(total_avant - total_apres)} récupérés)")
    if errors:
        print(f"\nErreurs ({len(errors)}) :")
        for nom, msg in errors:
            print(f"  {nom} : {msg}")


if __name__ == "__main__":
    main()

Localisation des ressources

URLshttps://spdf.juxjux.ovh (UI) | https://spdf.juxjux.ovh/swagger-ui/index.html (API docs, après login)
CheminsScript : C:\Users\eliob\.claude\nas_geo_compress.py | DB : C:\Users\eliob\Documents\nas_geographie.db | Fichiers source : \\NASMAISON\foxy\geo_loud\ | nginx vhost : /etc/nginx/sites-enabled/spdf.juxjux.ovh
IDsStack Portainer : stirlingpdf (ID 89) | Container : stirling-pdf | Port interne : 8090
Métadonnées{"fichiers_a_traiter": 127, "volume_total_go": 13.577, "test_fichier": "000209_L-inde.pdf", "test_avant_mo": 314, "test_apres_mo": 311.7, "test_gain_pct": 0.7, "nginx_max_body": "500M", "token_duree_h": 24}

Tags

#StirlingPDF #compression #PDF #geo_loud #API #JWT #nginx #Python #NAS #workflow

Suite / Pistes

1/ Lancer le script sur l'ensemble des 127 fichiers — surveiller le gain réel par catégorie de PDF.
2/ Mettre à jour la page BookStack 210 avec les colonnes DB ajoutées (taille_compresse_octets, date_compression) et le bilan de compression une fois le batch terminé.
3/ Si le gain moyen est insuffisant, explorer d'autres niveaux de compression StirlingPDF ou un outil alternatif (Ghostscript direct).
4/ Après compression de tous les fichiers : lancer l'étape 4 (restauration) via nas_geo_loud.py restaurer.

02_Claude et le NAS

02_Claude et le NAS

260801 - Mon Linux OS sur SASNEXTE

Claude prend la main en MCP pour monter et manager une distribution Linux ubiquiste - disponible 

02_Claude et le NAS

260528_Essai sur \\NASMAISON\foxy\Géographie\

Workflow geo_loud — Optimisation des fichiers lourds
Contexte

Le dossier \\NASMAISON\foxy\Géographie\ contient 1 312 fichiers (PDF et EPUB) pour un total de 32,9 Go. Parmi eux, 127 fichiers dépassent 75 Mo et représentent 13,6 Go — soit 41 % du volume total concentré sur moins de 10 % des fichiers.

L'objectif est de sortir ces fichiers lourds pour les optimiser (compression PDF), puis de les remettre exactement à leur emplacement d'origine sans perdre l'arborescence.
Architecture du système
Fichier     Rôle
nas_indexer.py     Scanne le NAS et crée la base SQLite
nas_geographie.db     Référentiel de tous les fichiers avec métadonnées et statut
nas_geo_loud.py     Script checkout/checkin — déplace et restaure les fichiers lourds

La base SQLite est la source de vérité : elle connaît à tout moment l'emplacement original de chaque fichier et son statut.
Colonnes de traçabilité (table fichiers)
Colonne     Description
chemin_absolu     Chemin d'origine complet sur le NAS
chemin_geo_loud     Chemin dans le dossier de travail après déplacement
statut     original / deplace / restaure
date_deplacement     Horodatage du déplacement
date_restauration     Horodatage de la restauration
Workflow complet
Étape 1 — Indexation initiale
1
python nas_indexer.py "\\NASMAISON\foxy\Géographie" --output nas_geographie.db

Résultat : 1 312 fichiers indexés en ~22 secondes, 0 erreur.
Étape 2 — Export (checkout)

Déplace les 127 fichiers > 75 Mo vers \\NASMAISON\foxy\geo_loud\ :
1
2
3
4
python nas_geo_loud.py export \
  --db nas_geographie.db \
  --dest \\NASMAISON\foxy\geo_loud \
  --confirmer

Ce que fait le script :

    Interroge la base pour tous les fichiers statut = 'original' et taille > 75 Mo
    Renomme chaque fichier {id:06d}_{nom_original} pour éviter les collisions de noms
    Déplace physiquement le fichier vers geo_loud/
    Met à jour la base : statut = 'deplace', chemin_geo_loud, date_deplacement

    Le préfixe numérique garantit l'unicité même si deux sous-dossiers contiennent un fichier de même nom.

Étape 3 — Optimisation PDF

Traiter les fichiers dans \\NASMAISON\foxy\geo_loud\ avec l'outil d'optimisation. Les fichiers peuvent être traités dans n'importe quel ordre et en plusieurs sessions.
Étape 4 — Restauration (checkin)
1
2
3
python nas_geo_loud.py restaurer \
  --db nas_geographie.db \
  --dest \\NASMAISON\foxy\geo_loud

Ce que fait le script :

    Lit tous les enregistrements statut = 'deplace' dans la base
    Pour chaque fichier présent dans geo_loud/ : déplace vers son chemin_absolu d'origine
    Recrée les sous-dossiers si nécessaire
    Met à jour : statut = 'restaure', date_restauration
    Ignore les fichiers absents de geo_loud/ (pas encore optimisés)

Étape 5 — Vérification du statut
1
python nas_geo_loud.py statut --db nas_geographie.db

Affiche :
1
2
3
4
Statut         Fichiers     Total Go
--------------------------------------
  deplace           127       13.577
  original        1 185       19.275
Vues SQL utiles (Metabase / DB Browser)
1
2
3
4
5
6
7
8
-- Fichiers encore dans geo_loud
SELECT nom, ROUND(taille_octets/1048576.0,1) AS mo, date_deplacement, chemin_absolu
FROM fichiers WHERE statut = 'deplace'
ORDER BY taille_octets DESC;

-- Bilan par statut
SELECT statut, COUNT(*) AS nb, ROUND(SUM(taille_octets)/1073741824.0,3) AS go
FROM fichiers GROUP BY statut;
Paramètres disponibles
Paramètre     Défaut     Description
--db     nas_geographie.db     Chemin vers la base SQLite
--dest     \\NASMAISON\foxy\geo_loud     Dossier de travail
--seuil     75     Seuil en Mo pour la sélection
--confirmer     (off)     Bypass la confirmation interactive
Ré-indexation après optimisation
1
python nas_indexer.py "\\NASMAISON\foxy\Géographie" --output nas_geographie.db --vider
Fichiers du projet
Fichier     Emplacement
nas_indexer.py     C:\Users\eliob\Documents\nas_indexer.py
nas_geo_loud.py     C:\Users\eliob\Documents\nas_geo_loud.py
nas_rapport.py     C:\Users\eliob\Documents\nas_rapport.py
nas_geographie.db     C:\Users\eliob\Documents\nas_geographie.db

02_Claude et le NAS

260611_procédure Komga-PDF par Claude sur StirlingPDF

Présentation

La procédure Komga-PDF est un script de compression de fichiers PDF pour réduire considérablement la taille du stockage des bibliothèques d'ouvrages de la collection de Julien.

L'acquisition de fichiers issus de bases de données Internet accumule des fichiers PDF en haute résolution (images surtout). Ces fichiers sont synchronisés entre les NAS Synology et un disque dur kDrive Infomaniak de 6 To.

Contexte

Des points de montage Rclone relient les répertoires kDrive et les containers Docker installés sur le VPS OVH juxjux.ovh. Ces containers lisent les PDF à la volée — permettant ainsi de construire une infrastructure de très forte densité de connaissances digitales (epub, pdf, images, vidéos, musiques...) avec une solution serveur d'entrée de gamme (10 €/mois tout inclus).

La lecture réseau est multi-support : PC, tablette, téléphone. Elle est aussi nomade, séquentielle et pressée. La compacité des fichiers PDF devient donc nécessaire pour diffuser rapidement sur les réseaux. Un magazine ou un ouvrage informatique ne doit pas peser 130 Mo sur un écran de 10 pouces.

Procédure

Architecture technique

Élément Chemin / URL
Dépôt (toutes machines) Syncthing/komga-pdf/
Dossier VPS (Syncthing) /home/debian/Documents/komga-pdf/
Destination VPS /home/debian/Documents/komga-compressed/
API compression https://spdf.juxjux.ovh/api/v1/misc/compress-pdf
Scripts VPS /home/debian/komga_compress_vps.py + /home/debian/komga_watch_vps.sh
Logs journalctl -u komga-watch -f (sur le VPS)

Scripts

Le service tourne entièrement sur le VPS — plus de dépendance Ubuntu ni de SSH persistant.

Fichier Rôle
komga_compress_vps.py Compression : liste les PDFs locaux, envoie à StirlingPDF, écrit dans komga-compressed/. Idempotent (ignore les fichiers déjà compressés).
komga_watch_vps.sh Surveillance : traite le backlog au démarrage, puis boucle inotifywait locale.
/etc/systemd/system/komga-watch.service Service system (pas user), actif au boot, Restart=always.

Gérer le service (VPS)

sudo systemctl status komga-watch
sudo systemctl restart komga-watch
journalctl -u komga-watch -f

Retour d'expérience (2026-06-13) — Refactorisation VPS

Problème de l'architecture Ubuntu : le service tournait sur Ubuntu avec un SSH persistant vers le VPS. Deux défauts structurels :

Solution : service systemd sur le VPS lui-même. inotifywait local, pas de SSH persistant. Le script traite le backlog à chaque démarrage avant d'entrer dans la boucle de surveillance.

Résultats de compression (2026-06-13) :

Fichier Original Compressé Gain
Beaux Arts - Juin 2026.pdf 75.8 Mo 61.1 Mo -19%
Connaissance des Arts - Juin 2026.pdf 57.3 Mo 35.2 Mo -39%
La Revue du Vin de France - Juin 2026.pdf 93.3 Mo 39.9 Mo -57%
Livre lacto fermentation.pdf 96.6 Mo 19.6 Mo -80%
Monde Gourmand N°93 - Juin 2026.pdf 41.2 Mo 17.4 Mo -58%

Points techniques :

Retour d'expérience (2026-06-12)

Résultats de compression :

Fichier Original Compressé Gain
Monde Gourmand N°93 - Juin 2026.pdf 42 Mo 17 Mo -58%
Connaissance des Arts - Juin 2026.pdf 57 Mo 35 Mo -39%
02_Claude et le NAS

260614 - procedure Joplin-compression

Contexte — instructions de Julien (260614)

J'utilise l'application Joplin sur tous mes appareils. C'est ma mémoire de tout (travail, hobbies, maison...) et donc à force, la base de données se remplit. Elle se compose de notes à l'intérieur desquelles on trouve des images. Même en n'important que des captures d'écran, le poids de ces images est devenu important — de l'ordre de 300 Mo initialement estimé (476 Mo constatés en réalité, voir ci-dessous).

Appareils synchronisés sous Joplin :

Objectif de la procédure :

  1. Compresser le stock d'images contenues dans la base de données Joplin via un process fiable de remplacement
  2. Monter un système de compression automatique quotidienne des nouvelles images

Principes définis par Julien :

  1. Tenir un carnet de bord (observatoire) du poids digital de la BDD Joplin dans la page BookStack Joplin, avec les 15 pièces jointes les plus lourdes
  2. Mettre en place sur le VPS une procédure duplication → compression → remplacement d'images, qui se propage via la sync Joplin sur tous les appareils

Exploration technique — Claude, 14/06/2026

Infrastructure Joplin sur le VPS

Contrairement à ce qu'on pourrait attendre, Joplin Server n'utilise pas SQLite mais PostgreSQL.

Container Image Rôle
joplin joplin/server:latest Serveur de sync Joplin
joplin-db postgres:15-alpine Base de données
joplin-nginx nginx:alpine Reverse proxy interne
joplin_to_obsidian image custom Container migration (actif, sans impact)

Volume de données : /home/debian/joplin-data/home/joplin/.config/joplin (bind mount)

Connexion DB : POSTGRES_HOST=joplin-db, POSTGRES_DATABASE=joplin, POSTGRES_USER=joplin

Important : le port 5432 de joplin-db n'est pas exposé à l'extérieur du réseau Docker. Tout script de manipulation doit tourner sur le VPS et se connecter via l'IP interne Docker.

Structure de la base de données

La table centrale est items (23 tables au total). Chaque note, ressource et paramètre Joplin est une ligne dans cette table.

Colonnes clés :

Répartition par type :

jop_type Signification Nombre Poids total
0 Ressource (image/fichier joint) 736 476 MB
1 Note 699 8,7 MB
4 Tag 733 4,5 MB
13 NoteTag (relation note↔tag) 250 3,9 MB
2 Carnet (Folder) 94 707 KB
6 Master Key 87 19 KB
5 Setting 39

Analyse des ressources (jop_type = 0)

Les ressources sont stockées comme bytes bruts dans la colonne content. Le format est détectable via les magic bytes :

La colonne mime_type est vide pour toutes les ressources — le type est implicite dans les bytes du contenu.

Top 15 ressources les plus lourdes (état initial) :

Rang ID Taille Format
1 0xh87pWrRt82z0DbO9n3yQ 12,2 MB ZIP (page_1.png 7MB + page_2.png 5MB)
2 M0C32hKC2hMqPAxpRGiVgp 7,2 MB PNG
3 UbAqQY3cEbH62vioIHU6Pj 6,0 MB PNG
4 ZPQJVSjptMSxE71pV7JbAt 5,9 MB PNG
5 h4Lhw1Trx9wgmD7doX9NyZ 5,9 MB PNG
6 sbktcNb4IUj0yIovgDt0fL 5,3 MB PNG
7 jtzuGpvrRTR12iLzwhcSNn 5,2 MB PNG
8 VasIoF2e9EGuNQt38aOFMx 4,9 MB JPEG
9 ZACDcQWzQzDLdnV7Qnf933 4,3 MB PNG
10 hExoJEEWT2tHz1demE5Nhm 4,3 MB PNG
11 veu4HT3bStx07gRUlAEYvG 4,2 MB PNG
12 bz9Twmb2F5lj0mPIQi48IB 4,2 MB JPEG
13 TzWK21r4n0yvbEtJh02DGB 4,2 MB JPEG
14 eD165w1bdEHJg0tq3qWok5 4,2 MB PNG
15 HD4SeqIx252nP9nh8HDVnH 4,1 MB PNG

Architecture retenue

Choix de compression — décision Julien, 14/06/2026

PNG → JPEG 85% (lossy). Toutes les images, qu'elles soient PNG ou JPEG à l'origine, sont converties/re-sauvegardées en JPEG qualité 85. Gain estimé : 50–75% par image. Acceptable pour des captures d'écran.

Pour les ZIP multi-pages : chaque image interne est convertie en JPEG 85%, le ZIP est reconstruit.

Phase 1 — Observatoire

Script joplin_observatoire.py sur le VPS :

Phase 2 — Compression (script principal)

Script joplin_compress.py sur le VPS :

Connexion : psycopg2 → IP Docker interne de joplin-db : 5432

Traitement par ressource :

  1. Lire le blob content depuis items (jop_type=0)
  2. Détecter le format (magic bytes)
  3. Ouvrir avec Pillow, convertir en JPEG 85 (quality=85, optimize=True)
  4. Pour les ZIP multi-pages : dézipper → compresser chaque image → reconstruire le ZIP
  5. Si le gain est > 5% : mettre à jour content, content_size, updated_time en base
  6. Logger le résultat (ID, taille avant, taille après, ratio)

Tracking des items traités : fichier JSON local /home/debian/joplin_compress_log.json — évite de retraiter les ressources déjà compressées lors des passages quotidiens.

Propagation sync : Joplin détecte les changements via updated_time. Lors de la prochaine synchronisation de chaque client, les ressources compressées sont re-téléchargées automatiquement.

Phase 3 — Service systemd (cron quotidien)

Timer systemd joplin-compress.timerjoplin-compress.service :


Mise en production — 14/06/2026

Résultat du premier run (stock complet)

Ressources traitées736 au total
Compressées424
Ignorées (gain < 5%)312
Erreurs0
Poids avant476 Mo
Poids après119,7 Mo
Économie356 Mo (-78,9%)

Scripts déployés sur le VPS

ScriptRôleOptions
/home/debian/joplin_compress.py Compression Pillow PNG/JPEG → JPEG 85%, ZIP multi-pages, mise à jour PostgreSQL --dry-run (simulation) / --limit N (N ressources max)
/home/debian/joplin_observatoire.py Stats DB + top 15 → mise à jour page BookStack Joplin (ID 166)

Log tracking : /home/debian/joplin_compress_log.json — liste des ressources déjà traitées, évite les doublons aux runs suivants.

Timer systemd

Servicejoplin-compress.service
Timerjoplin-compress.timer
DéclenchementChaque nuit à 3h UTC (OnCalendar=*-*-* 03:00:00)
Logs/var/log/joplin-compress.log
Statutactive (waiting) — prochain run : 15/06/2026 03:00 UTC

Notes techniques

02_Claude et le NAS

260615 - Immich performance

260615 -instructions pour la procédure Immich Performance
Les photos de famille (et des vidéos) sont un patrimoine important pour notre famille. Donc elles font l'objet de beaucoup d'attention....et de volume de stockage. Les photos sont stockées sur les 2 NAS - Nasmaison et Sasnexte synchronisées à travers les outils Synology. A partir de Sasnexte, un script journalier Synology les propulse sur le VPS Kdrive pour être lues en lecture seule par toute la famille (elles sont également sur deux autres disques durs USB....). 

Aujourd'hui, le dossier photos du Sasnexte que l'on doit considérer comme le cœur de ce dispositif nécessite une observation un peu plus fine car : 
- des compressions vidéos doivent être faites pour les rendre plus faciles en lecture sur les portables en mobilité
- des compressions images lorsque le poids est aberrant pour l'usage
- des doublons existent forcément.

Immich performance est une procédure importante du livre de Claude. 

Réponse de Claude


Inventaire par Claude Code le 01/10/2025


SAS_NEXTE@NEXTE:~$ sudo du -h --max-depth=2 /volume1/photo/ | sort -h  
0 /volume1/photo/1968-1994 - photos famille dont Noumea/1982  
0 /volume1/photo/2009-mariage Zymny  
0 /volume1/photo/2014- été/140714-Lyon chez Pierre  
0 /volume1/photo/2020 - Automne Noel/noel2020  
0 /volume1/photo/2024/202401 - Hiver Automne  
0 /volume1/photo/2025/DCIM  
0 /volume1/photo/@eaDir/clientd.tmp.dir  
0 /volume1/photo/@eaDir/cloud.tmp.dir  
0 /volume1/photo/@eaDir/@recycle  
0 /volume1/photo/@eaDir/@tmp  
4.0K /volume1/photo/@eaDir/@drive.queues  
24K /volume1/photo/@eaDir/sharesnap_share_configuration  
32K /volume1/photo/2010 - été/@eaDir  
40K /volume1/photo/2011- Ete/@eaDir  
52K /volume1/photo/2021-été covidé/@eaDir  
84K /volume1/photo/@eaDir/980710-photo CV julien.jpg  
264K /volume1/photo/@eaDir/131124-elio pas content.jpg  
352K /volume1/photo/2009 Nouvel an Réveillon/@eaDir  
432K /volume1/photo/@eaDir/2015-soirée mexicaine.jpg  
440K /volume1/photo/1968-1994 - photos famille dont Noumea/Givet  
548K /volume1/photo/@eaDir/150314-Zoee en 18X24.png  
616K /volume1/photo/2009 Nouvel an Réveillon  
656K /volume1/photo/1968-1994 - photos famille dont Noumea/1977  
656K /volume1/photo/@eaDir/7b06435c-6bad-4eb0-aa38-af03bdfbfbc8.jpg  
672K /volume1/photo/@eaDir/PhotoLab445379.jpg  
792K /volume1/photo/@eaDir/160220-elio en 18X24.png  
848K /volume1/photo/2011 - printemps/@eaDir  
900K /volume1/photo/2016 ACDC/@eaDir  
1.5M /volume1/photo/1968-1994 - photos famille dont Noumea/1976  
1.7M /volume1/photo/1968-1994 - photos famille dont Noumea/Pierre-001  
2.1M /volume1/photo/1968-1994 - photos famille dont Noumea/1969  
3.0M /volume1/photo/1968-1994 - photos famille dont Noumea/1978  
3.4M /volume1/photo/1968-1994 - photos famille dont Noumea/1968  
3.4M /volume1/photo/1968-1994 - photos famille dont Noumea/1980  
3.5M /volume1/photo/2014-mars-repas chez maman/@eaDir  
4.1M /volume1/photo/1999/@eaDir  
4.3M /volume1/photo/2014 - 4 ans Zoée/@eaDir  
4.9M /volume1/photo/1968-1994 - photos famille dont Noumea/1979  
4.9M /volume1/photo/2016 ACDC  
5.3M /volume1/photo/1986 - Californie/@eaDir  
5.3M /volume1/photo/Cartes de voeux/@eaDir  
5.5M /volume1/photo/2010-hiver/@eaDir  
6.0M /volume1/photo/1968-1994 - photos famille dont Noumea/1992  
6.6M /volume1/photo/1930-2016 - Photos BERTRAND MER/Elio  
6.6M /volume1/photo/2016-best photos pour 70 ans/Elio  
6.7M /volume1/photo/1999  
6.9M /volume1/photo/1968-1994 - photos famille dont Noumea/Etats-Unis 1986  
7.4M /volume1/photo/2015 - Noël/@eaDir  
7.6M /volume1/photo/1930-2016 - Photos BERTRAND MER/Les communions solennelle s  
7.6M /volume1/photo/2016-best photos pour 70 ans/Les communions solennelles  
7.8M /volume1/photo/1968-1994 - photos famille dont Noumea/1994  
7.9M /volume1/photo/2013-Serbie/@eaDir  
8.0M /volume1/photo/2014/@eaDir  
8.4M /volume1/photo/1968-1994 - photos famille dont Noumea/1990  
8.5M /volume1/photo/2004 - été/@eaDir  
8.5M /volume1/photo/2022-Noel/@eaDir  
8.6M /volume1/photo/2015 - Automne/@eaDir  
8.9M /volume1/photo/1930-2016 - Photos BERTRAND MER/Les amis, la famille en n oir et blanc  
8.9M /volume1/photo/2016-best photos pour 70 ans/Les amis, la famille en noir et blanc  
9.5M /volume1/photo/1968-1994 - photos famille dont Noumea/1981  
9.9M /volume1/photo/2018 - Noel/@eaDir  
10M /volume1/photo/#recycle/2016 - 70 ans maman_DiskStation_Nov-04-1745-2020 _CaseConflict  
11M /volume1/photo/1930-2016 - Photos BERTRAND MER/Album de la famille de Re née Campanella  
11M /volume1/photo/2010 - naissance Zoée/@eaDir  
11M /volume1/photo/2014/Noël 2014  
11M /volume1/photo/2016-best photos pour 70 ans/Album de la famille de Renée Campanella  
12M /volume1/photo/2013 Marseille/@eaDir  
12M /volume1/photo/2014-mars-repas chez maman  
12M /volume1/photo/2015-Hiver/@eaDir  
12M /volume1/photo/2015-inventaire maison assurances/@eaDir  
13M /volume1/photo/1968-1994 - photos famille dont Noumea/1975  
14M /volume1/photo/1968-1994 - photos famille dont Noumea/1983  
15M /volume1/photo/2005 - été/@eaDir  
16M /volume1/photo/2016-mai juin/@eaDir  
16M /volume1/photo/2016 - Valras/@eaDir  
17M /volume1/photo/2004 - été  
17M /volume1/photo/2010 - naissance Zoée  
18M /volume1/photo/1968-1977/@eaDir  
18M /volume1/photo/2014 - 4 ans Zoée  
18M /volume1/photo/2014/Anniversaire_Elio_2014  
18M /volume1/photo/2016-avril avant crète/@eaDir  
19M /volume1/photo/1930-2016 - Photos BERTRAND MER/Grand-père Emile et mamie Rosa  
19M /volume1/photo/2016-best photos pour 70 ans/Grand-père Emile et mamie Ro sa  
21M /volume1/photo/1930-2016 - Photos BERTRAND MER/@eaDir  
21M /volume1/photo/1930-2016 - Photos BERTRAND MER/L'album de Papa et Maman  
21M /volume1/photo/2016-best photos pour 70 ans/L'album de Papa et Maman  
21M /volume1/photo/2016- photos Maman Papa/@eaDir  
22M /volume1/photo/2011 - Automne/@eaDir  
22M /volume1/photo/2013 Noël/@eaDir  
22M /volume1/photo/2014-Hiver Printemps/@eaDir  
23M /volume1/photo/2014 - Automne Noel/@eaDir  
24M /volume1/photo/2011 - jour de l'an/@eaDir  
24M /volume1/photo/2014-mur effondré - Ville de Toulon/@eaDir  
25M /volume1/photo/2009 - Papa/@eaDir  
25M /volume1/photo/2010-hiver  
25M /volume1/photo/2021-été covidé/2021 - après vacances  
25M /volume1/photo/Cartes de voeux  
26M /volume1/photo/1968-1994 - photos famille dont Noumea/1984  
27M /volume1/photo/1930-2016 - Photos BERTRAND MER/Enfants Mer et cousins  
27M /volume1/photo/2008 Toscane/@eaDir  
27M /volume1/photo/2016 - 70 ans Maman/@eaDir  
27M /volume1/photo/2016-best photos pour 70 ans/Enfants Mer et cousins  
27M /volume1/photo/2022-automne a Noel/@eaDir  
29M /volume1/photo/2018 - Noel  
30M /volume1/photo/2008 - mariage Héléna et Jérome/@eaDir  
30M /volume1/photo/2012 - Eté/@eaDir  
30M /volume1/photo/#recycle/2016 - 70 ans maman_DiskStation_Nov-04-1745-2020 _CaseConflict_1  
31M /volume1/photo/2025/2501 à 2502  
32M /volume1/photo/2015 - Noël  
33M /volume1/photo/2014/Carnaval 2014  
34M /volume1/photo/2016-best photos pour 70 ans/@eaDir  
35M /volume1/photo/2014-Barcelone/@eaDir  
36M /volume1/photo/2013/@eaDir  
36M /volume1/photo/2016-Parc Vert Coteau/soirée janvier 2016  
36M /volume1/photo/2024/2024 - Nouvel an Arriège  
37M /volume1/photo/2015 - Automne  
38M /volume1/photo/2014/Février 2014  
39M /volume1/photo/2011 - Noel/@eaDir  
39M /volume1/photo/2020 - Automne Noel/@eaDir  
40M /volume1/photo/2014/Sept_2014_4ans_Zoée  
40M /volume1/photo/@eaDir/SYNO@.fileindexdb  
41M /volume1/photo/2016-Parc Vert Coteau/avril 2016 état  
41M /volume1/photo/2022-Noel  
43M /volume1/photo/@eaDir  
44M /volume1/photo/2009 - Papa  
44M /volume1/photo/2012/@eaDir  
45M /volume1/photo/1930-2016 - Photos BERTRAND MER/Papa juin 2009 - Le livre d'Alain  
45M /volume1/photo/2016-best photos pour 70 ans/Papa juin 2009 - Le livre d' Alain  
47M /volume1/photo/2005 - été  
47M /volume1/photo/2008 Toscane  
50M /volume1/photo/2019-Cabaret Vert/@eaDir  
51M /volume1/photo/2015 - Juillet Valras/@eaDir  
52M /volume1/photo/2021-hiver printemps/@eaDir  
53M /volume1/photo/2015-Hiver  
53M /volume1/photo/2018-défi Genes/@eaDir  
54M /volume1/photo/2008 - été Hautes Alpes/@eaDir  
54M /volume1/photo/2009 - Elio/@eaDir  
55M /volume1/photo/2013-Serbie  
58M /volume1/photo/2013 Marseille  
58M /volume1/photo/2015-inventaire maison assurances  
62M /volume1/photo/2007-1er janvier/@eaDir  
62M /volume1/photo/2016 - Valras  
64M /volume1/photo/2014- été/@eaDir  
67M /volume1/photo/2009 - été Irlande/@eaDir  
67M /volume1/photo/2016-mai juin  
70M /volume1/photo/1968-1994 - photos famille dont Noumea/Nouméa  
70M /volume1/photo/2011- Ete/GARD juillet 2011  
71M /volume1/photo/2020 - Hiver Confinement COVID/@eaDir  
73M /volume1/photo/2022-hiver à Paques/@eaDir  
73M /volume1/photo/2024/202412 - Automne Noel  
75M /volume1/photo/2013 - Printemps/@eaDir  
76M /volume1/photo/2016-Parc Vert Coteau  
76M /volume1/photo/2024/@eaDir  
79M /volume1/photo/2012 - Eté/2012 - Bretagne  
79M /volume1/photo/2014-Hiver Printemps  
81M /volume1/photo/2013 Noël  
82M /volume1/photo/2017-Voyage à Turin/@eaDir  
84M /volume1/photo/2016-Venasque/@eaDir  
86M /volume1/photo/1986 - Californie  
88M /volume1/photo/2023-ete/@eaDir  
91M /volume1/photo/2009 - été Ardèche/@eaDir  
93M /volume1/photo/2016-avril avant crète  
94M /volume1/photo/2007-1er janvier  
96M /volume1/photo/2021-Automne et Noel/@eaDir  
97M /volume1/photo/2008 - été Hautes Alpes  
99M /volume1/photo/PhotoLibrary/2025  
100M /volume1/photo/2014- Porto - Primavera/@eaDir  
107M /volume1/photo/2014-mur effondré - Ville de Toulon  
108M /volume1/photo/2023 - Automne Noel/@eaDir  
111M /volume1/photo/2009 - été Irlande  
111M /volume1/photo/2015- printemps/@eaDir  
114M /volume1/photo/2025/2504 et 2505  
125M /volume1/photo/2010 - été  
125M /volume1/photo/2010 - été/Beauduc  
125M /volume1/photo/2014 - Automne Noel  
130M /volume1/photo/2016-Crete/@eaDir  
131M /volume1/photo/2011 - jour de l'an  
138M /volume1/photo/2021-été covidé/2021 avant vacances  
139M /volume1/photo/2013  
140M /volume1/photo/2018 - Valras/@eaDir  
143M /volume1/photo/2016-Automne Noel/@eaDir  
145M /volume1/photo/2013-Pays Bas/@eaDir  
146M /volume1/photo/2014-Barcelone  
150M /volume1/photo/2023-Hiver à Paques/@eaDir  
152M /volume1/photo/2008 - mariage Héléna et Jérome  
153M /volume1/photo/2017-Automne Noel/@eaDir  
154M /volume1/photo/2014/Mai_Juin 2014  
157M /volume1/photo/2014/Avril 2014  
159M /volume1/photo/2022-automne a Noel  
162M /volume1/photo/2009 - été Ardèche  
173M /volume1/photo/2014 - 5 jours en Corse/@eaDir  
178M /volume1/photo/2016-best photos pour 70 ans/Ma famille pour mes 70 ans  
178M /volume1/photo/2020 -Printemps/@eaDir  
179M /volume1/photo/1930-2016 - Photos BERTRAND MER  
180M /volume1/photo/1968-1994 - photos famille dont Noumea  
181M /volume1/photo/2019- Hiver Printemps/@eaDir  
181M /volume1/photo/2022 - été Flandres NL Paris/@eaDir  
183M /volume1/photo/2012  
184M /volume1/photo/2009 - Elio  
186M /volume1/photo/2014/2014-été  
192M /volume1/photo/2016- photos Maman Papa  
203M /volume1/photo/1968-1977  
206M /volume1/photo/1967 à 2000 - photos famille Bertrand/@eaDir  
213M /volume1/photo/2023-Mai à Vacances Eté/@eaDir  
214M /volume1/photo/2019-Automne/@eaDir  
222M /volume1/photo/2015 - Juillet Valras  
229M /volume1/photo/2020 - Automne Noel  
230M /volume1/photo/2018-printemps/@eaDir  
258M /volume1/photo/2021-hiver printemps  
281M /volume1/photo/2014- été  
282M /volume1/photo/2018 - automne noel/@eaDir  
323M /volume1/photo/2011 - Noel  
325M /volume1/photo/2018-défi Genes  
348M /volume1/photo/2005 - naissance Elio/@eaDir  
354M /volume1/photo/2011 - printemps  
368M /volume1/photo/2011 - Automne  
381M /volume1/photo/2017-Hiver-Printemps/@eaDir  
384M /volume1/photo/2016-Venasque  
407M /volume1/photo/2020 -Printemps  
409M /volume1/photo/2016-best photos pour 70 ans  
419M /volume1/photo/2016 - 70 ans Maman  
434M /volume1/photo/2020 - Hiver Confinement COVID  
439M /volume1/photo/2017-Voyage à Turin  
460M /volume1/photo/2023-Mai à Vacances Eté  
462M /volume1/photo/2011- Ete/LIGURIE aout 2011  
500M /volume1/photo/2013 - Printemps  
511M /volume1/photo/2023-ete  
515M /volume1/photo/2022-hiver à Paques  
520M /volume1/photo/2012 - Eté/2012 - été Buis  
525M /volume1/photo/2016-Crete  
528M /volume1/photo/2014- Porto - Primavera  
531M /volume1/photo/2011- Ete  
535M /volume1/photo/2023-Vacances D NL B/@eaDir  
573M /volume1/photo/2023 - Automne Noel  
575M /volume1/photo/2016-été Ré Gers/@eaDir  
580M /volume1/photo/2018-Barcelone Primavera Sound/@eaDir  
618M /volume1/photo/2019-Cabaret Vert  
629M /volume1/photo/2019 Bretagne Alpes/@eaDir  
635M /volume1/photo/2013-Pays Bas  
663M /volume1/photo/2021-Automne et Noel  
667M /volume1/photo/2014  
685M /volume1/photo/2015- printemps  
700M /volume1/photo/2017-été Scandinavie/@eaDir  
718M /volume1/photo/2024/2024 - Eté Italie Autriche  
759M /volume1/photo/2018 - Valras  
809M /volume1/photo/#recycle/2025  
817M /volume1/photo/2016-Automne Noel  
846M /volume1/photo/2025/2506 à 2509  
860M /volume1/photo/#recycle  
907M /volume1/photo/2019-Automne  
920M /volume1/photo/2017-Automne Noel  
935M /volume1/photo/2023-Hiver à Paques  
941M /volume1/photo/2024/2024-Porto  
944M /volume1/photo/cecile tri 2022/@eaDir  
945M /volume1/photo/PhotoLibrary/DCIM  
956M /volume1/photo/2005 - naissance Elio  
964M /volume1/photo/2020 - été Vienne Venise/@eaDir  
991M /volume1/photo/2025  
1.1G /volume1/photo/2014 - 5 jours en Corse  
1.1G /volume1/photo/2015-Croatie Autriche/@eaDir  
1.1G /volume1/photo/2018-the Alpen/@eaDir  
1.1G /volume1/photo/PhotoLibrary  
1.2G /volume1/photo/2021-été covidé/2021 - vacances Aout Mont Blanc Cantal  
1.2G /volume1/photo/2022 - été Flandres NL Paris  
1.3G /volume1/photo/2019- Hiver Printemps  
1.3G /volume1/photo/2021-été covidé  
1.4G /volume1/photo/2018-printemps  
1.5G /volume1/photo/2016-été Ré Gers/Vidéo Aveyron Ré et Gers  
1.5G /volume1/photo/2024/2024-Via Reggio  
1.8G /volume1/photo/2018 - automne noel  
1.9G /volume1/photo/2012 - Eté/2012- été Italia  
2.0G /volume1/photo/2017-Hiver-Printemps  
2.7G /volume1/photo/2012 - Eté  
2.8G /volume1/photo/2019 Bretagne Alpes  
2.8G /volume1/photo/2020 - été Vienne Venise  
2.9G /volume1/photo/1967 à 2000 - photos famille Bertrand  
3.2G /volume1/photo/2023-Vacances D NL B  
3.6G /volume1/photo/2018-Barcelone Primavera Sound  
3.7G /volume1/photo/cecile tri 2022  
3.8G /volume1/photo/2024  
4.1G /volume1/photo/2016-été Ré Gers  
4.2G /volume1/photo/2017-été Scandinavie  
5.7G /volume1/photo/2018-the Alpen  
6.4G /volume1/photo/2015-Croatie Autriche  
79G /volume1/photo/

02_Claude et le NAS

Montages Rclone Sasnexte et Kdrive

Montages Rclone Sasnexte et Kdrive

Architecture

NAS Sasnexte (Synology)
    └── rclone sync (WebDAV) ──► kDrive Infomaniak
                                      └── rclone mount (FUSE) ──► VPS Jux
                                                                     └── containers Docker

Le NAS pousse les médias vers kDrive une fois par jour via le planificateur DSM. Le VPS monte kDrive en FUSE en permanence — les containers accèdent aux fichiers en lecture.


Remote rclone (NAS sasnexte)

Fichier : /volume1/homes/SAS_NEXTE/.config/rclone/rclone.conf

[kdrive_music]
type = webdav
url = https://591617.connect.kdrive.infomaniak.com
vendor = other
user = julien.bertrand@nexte.fr
pass = [obfusqué rclone]

Note : le remote s'appelle kdrive_music mais sert à synchroniser tous les contenus (musique, photos, audiobooks, Komga). Nom historique à ne pas confondre avec un remote dédié musique.

Mot de passe WebDAV : régénéré le 2026-06-23 (compte julien.bertrand@nexte.fr, espace kDrive 591617). Mise à jour via rclone config update kdrive_music pass $(rclone obscure NOUVEAU_MDP).


Script unifié (NAS sasnexte)

Emplacement : /volume1/homes/SAS_NEXTE/scripts/sync_kdrive_complete.sh
Planificateur DSM : tâche déclenchée quotidiennement — commande : /volume1/homes/SAS_NEXTE/scripts/sync_kdrive_complete.sh

Note sudo : le script contient sudo -u SAS_NEXTE rclone .... Si la tâche DSM est configurée pour tourner en tant que SAS_NEXTE, retirer le sudo -u SAS_NEXTE (inutile et peut bloquer). Si elle tourne en root, le garder.

#!/bin/bash
RCLONE="/usr/local/bin/rclone"
REMOTE="kdrive_music"
LOG_DIR="/volume1/homes/SAS_NEXTE/logs"
DATE=$(date +%Y%m%d_%H%M%S)
SUMMARY_LOG="$LOG_DIR/sync_${DATE}.log"
mkdir -p "$LOG_DIR"
run_sync() {
local name="$1"
local source="$2"
local dest="${REMOTE}:${3}"
local log="$LOG_DIR/sync_${name}_${DATE}.log"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] === Debut $name ===" >> "$SUMMARY_LOG"
sudo -u SAS_NEXTE "$RCLONE" sync "$source" "$dest" --delete-excluded --ignore-errors --delete-during --fast-list --exclude "@eaDir/**" --exclude "#recycle/**" --exclude "@__thumb/**" --exclude "*.@SynoResource" --exclude "*.@SynoEAStream" --transfers=4 --checkers=8 --timeout=5m --contimeout=2m --log-file="$log" -v
local rc=$?
echo "[$(date '+%Y-%m-%d %H:%M:%S')] === Fin $name (code=$rc) ===" >> "$SUMMARY_LOG"
}
run_sync "audiobooks" "/volume1/audiobooks" "SYNC-pour_VPS/Sync-SASNEXTE/audiobooks"
run_sync "music" "/volume1/music" "SYNC-pour_VPS/Sync-SASNEXTE/music"
run_sync "photo" "/volume1/photo/2026" "SYNC-pour_VPS/Sync-SASNEXTE/photo/2026"
run_sync "komga" "/volume1/Komga" "SYNC-pour_VPS/Sync-SASNEXTE/Komga"

Déploiement : écrire via vi depuis SSH — le heredoc et les éditeurs Windows introduisent des CRLF qui cassent bash. Après édition : sed -i 's/\r//' script.sh pour vérifier.


Syncs actifs

Nom Source NAS Destination kDrive Notes
audiobooks /volume1/audiobooks SYNC-pour_VPS/Sync-SASNEXTE/audiobooks
music /volume1/music SYNC-pour_VPS/Sync-SASNEXTE/music
photo /volume1/photo/2026 SYNC-pour_VPS/Sync-SASNEXTE/photo/2026 Année courante seulement
komga /volume1/Komga SYNC-pour_VPS/Sync-SASNEXTE/Komga

Non géré ici :


Options rclone — justifications

Option Rôle
--delete-excluded Supprime sur kDrive les fichiers exclus qui auraient pu y être uploadés
--ignore-errors Poursuit la sync si un fichier est inaccessible (NAS Synology parfois occupé)
--delete-during Supprime les fichiers obsolètes au fil du scan, pas en fin de run
--fast-list Réduit les appels API WebDAV (un seul listing récursif)
@eaDir/** Dossiers de métadonnées Synology (miniatures)
#recycle/** Corbeille Synology
@__thumb/** Miniatures Synology
*.@SynoResource / *.@SynoEAStream Flux de ressources étendues Synology
--transfers=4 4 fichiers en parallèle — équilibre entre perf et charge WebDAV
--checkers=8 8 vérifications de hash en parallèle
--timeout=5m Timeout par opération I/O
--contimeout=2m Timeout de connexion initiale

Côté VPS — montages FUSE correspondants

Les services systemd sur le VPS montent les dossiers kDrive en FUSE :

Service systemd Point de montage Source kDrive
rclone-audiobookshelf.service /home/debian/audiobookshelf/audiobooks SYNC-pour_VPS/Sync-SASNEXTE/audiobooks
kdrive-music.service /home/debian/music SYNC-pour_VPS/Sync-SASNEXTE/music
rclone-photo.service /home/debian/photo SYNC-pour_VPS/Sync-SASNEXTE/photo
kdrive-komga.service /home/debian/komga SYNC-pour_VPS/Sync-SASNEXTE/Komga

Remote VPS : kdrive: — remote natif Infomaniak (@infomaniak/mcp-server-kdrive), compte julien.bertrand@live.fr, kDrive ID 591617.


Logs


REX déploiement 2026-06-23

02_Claude et le NAS

260727 - Claude en MCP sur le NASmaison

Le NASmaison est un petit Synology DS218j installé à la maison de Julien. IL sert surtout de stockage des livres et des films. Egalement des cartes rasters, le dossier personnel de Cécile, Elio et Zoée. IL a une copie miroir du dossier musique et komga, mais plus photos.

Il est synchronisé avec le NAS SASNEXTE pour musique et Komga.

Il compose au quotidien 6 procédures hyperbacups de la bibliothèque de livres en direction du Kdrive :

Claude a désormais une connexion MCP privilégiée sur le NASmaison. Et peut notamment garantir le transfert sécurisé des dossiers 

 

 

 

 

 

02_Claude et le NAS

260731-Claude et SASNEXTE sur le PC Elio+Jux

WireGuard sasnexte sur le PC Elio+Jux — diagnostic et résolution

Date : 2026-07-31 | Machine : PC Windows eliob | NAS cible : sasnexte (82.66.244.248) Statut : RÉSOLU — tunnel activé, lecteur S: mappé sur \\10.0.0.2\NEXTE

Question initiale

WireGuard a été monté sur ce PC et sur le NAS sasnexte, mais le disque du NAS n'apparaissait pas comme lecteur dans l'explorateur de fichiers Windows. Pourquoi ?

Réponse courte

Deux causes cumulées :

  1. Le tunnel n'était pas activé sur le PC : la config vps_maisonpc_sasnexte était importée dans l'application WireGuard, mais jamais activée (aucun service WireGuardTunnel$*, aucun adaptateur réseau, wg show vide).
  2. Un lecteur réseau ne se crée jamais tout seul : la découverte réseau Windows (broadcast/mDNS/WS-Discovery) ne traverse pas un tunnel de couche 3 — le NAS n'apparaîtra jamais spontanément dans « Réseau ». Il faut mapper manuellement une lettre avec net use.

Topologie du tunnel vps_maisonpc_sasnexte

Réseau 10.0.0.0/24, hub-and-spoke avec le VPS Jux comme serveur (wg0, port UDP 51820) :

IP Machine État (2026-07-31)
10.0.0.1 VPS Jux (51.77.141.54) — hub Serveur, forwarding OK (ACCEPT in wg0 dans FORWARD)
10.0.0.2 NAS sasnexte (endpoint 82.66.244.248) Connecté, handshake actif, 485 Gio transférés
10.0.0.3 PC Windows eliob Connecté (après activation du tunnel)
10.0.0.4 PC maison (peer MkzKqDe…) Jamais connecté (pas d'endpoint)

Clé publique serveur VPS : zbsY/bl4fHU1eR29PjTruK0Nrmp/BQORSlBGSh3Nkyo=

Diagnostic détaillé

Côté PC (avant activation)

Après activation du tunnel

SSH sasnexte depuis l'extérieur

Le SSH sas_nexte@82.66.244.248 (IP publique) refusait le mot de passe le 2026-07-31 alors que le même mot de passe fonctionne en SMB via le tunnel → le mot de passe est bon, c'est le SSH public qui est filtré (fail2ban/whitelist). Passer par le tunnel : ssh sas_nexte@10.0.0.2 (port 22 ouvert).

Résolution appliquée (2026-07-31)

  1. Tunnel activé dans l'app WireGuard (fait par Julien) → handshake OK avec le VPS
  2. Identifiants SMB enregistrés dans le gestionnaire d'identification Windows :
    cmdkey /add:10.0.0.2 /user:sas_nexte /pass:"xAPIJU5108§"
    
  3. Lecteur mappé :
    net use S: \\10.0.0.2\NEXTE /persistent:yes
    
  4. Vérifié : S: liste bien le contenu du partage NEXTE

Important : le lecteur S: ne fonctionne que si le tunnel WireGuard est actif. Après un reboot, le tunnel se réactive automatiquement (service WireGuardTunnel$vps_maisonpc_sasnexte en démarrage auto) et Windows reconnecte S: grâce à /persistent:yes + identifiants cmdkey.

Partages SMB disponibles sur \10.0.0.2

ActiveBackupforBusiness, audiobooks, chat, home (dossier perso SAS_NEXTE), homes, Iso VM, music, NetBackup, NEXTE (mappé sur S:), Public_s, RAW, Ressources_NEXTE, retro, web, web_packages

Pour mapper un partage supplémentaire (les identifiants sont déjà enregistrés) :

net use R: \\10.0.0.2\Ressources_NEXTE /persistent:yes

Points ouverts

03_Claude et Ubuntu

03_Claude et Ubuntu

01_Installation de Claude dans le Ubuntu

Contexte

Installation et configuration de Claude Code sur la machine Ubuntu 25.10 (utilisateur julien), avec synchronisation du dossier de travail via Syncthing.

1. Installation de Syncthing

Date : 2026-05-31
Système : Ubuntu 25.10 (questing)

Étapes

  1. Vérification : Syncthing absent des paquets installés
  2. Installation via les dépôts Ubuntu : sudo apt install syncthing -y
  3. Activation au démarrage : systemctl --user enable --now syncthing
  4. Interface web disponible sur : http://127.0.0.1:8384

Résultat

Dossier synchronisé : /home/julien/Syncthing/

Contenu récupéré depuis le PC Windows (eliob) :

2. Configuration de Claude Code

Le fichier CLAUDE.md de référence a été localisé dans le dossier synchronisé :

/home/julien/Syncthing/Jux_univers/Claude-pcelio+jux/CLAUDE.md

Ce fichier centralise toute la configuration : MCPs, credentials VPS, préférences, projets en cours. Il a été lu en début de session pour initialiser le contexte.

Mémoire Claude Code initialisée

Répertoire : /home/julien/.claude/projects/-home-julien/memory/

3. Connexion BookStack depuis Ubuntu

Token API BookStack configuré manuellement pour permettre l écriture depuis Ubuntu (sans MCP npm disponible). Utilisation de l API REST directe via curl :

Authorization: Token TOKEN_ID:TOKEN_SECRET sur https://bookstack.juxjux.ovh/api/

03_Claude et Ubuntu

02_Diagnostic du Ubuntu par Claude

Diagnostic expert — Ubuntu julien-130430

Généré par Claude Code le 2026-05-31 à 19h57 — Ubuntu 25.10 questing — mis à jour 20h15


1. Système de base

OSUbuntu 25.10 (questing) — version non-LTS, support jusqu'à juillet 2026
Kernel6.17.0-19-generic (SMP PREEMPT_DYNAMIC — mars 2026)
Hostnamejulien-130430
Architecturex86_64
Uptime1h36 au moment du diagnostic
Languefr_FR.UTF-8 / shell bash

2. Matériel

CPU

ModèleAMD Ryzen 5 2600X Six-Core Processor
Cœurs / Threads6 cœurs / 12 threads (1 socket)
Fréquence2200–3600 MHz (scaling à 99%)
Charge (1/5/15 min)2,44 / 1,98 / 1,17 — charge en train de baisser, acceptable

RAM & Swap

RAM totale15 Gi
RAM utilisée4,9 Gi utilisé + 9 Gi cache/tampon — 10 Gi disponibles
Swap4 Gi (fichier /swap.img), 5,5 Mi utilisés — quasi vide, bonne santé

GPU

ModèleAMD Radeon RX 6400/6500 XT — Navi 24 (rev c1)
DriverAMDGPU (open-source, intégré au kernel)
Monitoringlm-sensors absent — pas de relevé de température possible

3. Stockage

DisqueModèleTaillePartitionFSPoint de montageUtiliséAlerte
sdaSanDisk 3.2Gen1 (USB)57,3 Gsda2ext4/ (racine)31G / 57G — 57%
sdbPatriot Burst (SSD)223,6 Gsdb3ntfs/media/julien/CE1C8DC545149G / 223G — 67%
sdcSeagate Backup Plus7,3 Tsdc2/media/julien/Seagate…3,1T / 7,3T — 42%
sdd932 Gsdd1/media/julien/206897…827G / 932G — 89%⚠ Critique
sde932 Gsde2/media/julien/2ème disque dur294G / 932G — 32%

Point d'attention : Le disque sdd1 est à 89% de capacité (827G/932G). Seuil critique à surveiller.

Note : Le système racine est sur une clé USB SanDisk (sda). Performances limitées par rapport à un SSD SATA ou NVMe. Risque de défaillance à long terme supérieur à un disque interne.

4. Réseau

Interfaceenp37s0 (Ethernet)
IP locale192.168.1.26/24 (DHCP)
IPv62a01:cb1c:833b:d00:2ef0:5dff:feec:62e0/64
Passerelle192.168.1.1
DNS192.168.1.1 + IPv6 routeur (via systemd-resolved)
DNSSECDésactivé (unsupported)
mDNS / LLMNRDésactivés

Recommandation : L'IP est attribuée par DHCP. Envisager une réservation DHCP fixe sur le routeur pour la stabilité de 192.168.1.26.

5. Services et sécurité

Services systemd

SSH serveur

openssh-server absent — aucun accès SSH entrant possible sur cette machine. Si un accès distant est souhaité, installer openssh-server.

Pare-feu

ufw ne répond pas (non configuré ou inactif). La machine est probablement protégée par le NAT du routeur, mais aucune règle locale n'est en place.

Ports en écoute (accessibles depuis le réseau)

PortServiceRemarque
22000/tcp+udpSyncthing (PID 24179)Sync P2P — normal
53/tcp (127.0.0.53)systemd-resolvedDNS local uniquement
53/tcp (127.0.0.54)systemd-resolved stubDNS local uniquement

Surface d'attaque réseau réduite : seul Syncthing écoute sur l'interface externe.

Sudo

Julien est membre du groupe sudo. La commande sudo -l nécessite un terminal interactif (PAM conversation) — normal dans le contexte Claude Code non-interactif.

6. Syncthing

ProcessusEn cours d'exécution (PID 24179, port 22000 actif)
Service systemdsyncthing@julien.service : enabled / active(activé le 2026-05-31)
Dossiers synchronisésBillets, Jux_Obsidian, Jux_univers, Ouvrages, PDF temps, Suretés, Tutoriels videos

Fix 2026-05-31 : sudo systemctl enable --now syncthing@julien.service — service activé et démarré. Vérifié via API REST (HTTP 200, uptime ~17h).

7. Snaps

19 snaps installés. Plusieurs ont deux révisions montées simultanément (l'ancienne est conservée le temps du refresh par snapd) :

C'est le comportement normal de snapd (rollback possible). Les anciennes révisions sont supprimées automatiquement après quelques jours.

Nettoyage manuel possible : sudo snap set system refresh.retain=2 (déjà le défaut) ou snap list --all + sudo snap remove --revision=<rev> <snap>.

8. Environnements de développement

Python3.13.7 (/usr/bin/python3)
Venv global~/.venv — activé automatiquement via .bashrc (configuré le 2026-05-31)
Paquets venvpsycopg2-binary 2.9.12, requests 2.34.2, httpx 0.28.1, pip 26.1.2
Node.js24.16.0
npm11.13.0
Claude Codev2.1.158 — installé et opérationnel
DockerNon installé (ou non accessible sans sudo)

Installation venv : nécessitait sudo apt install python3.13-venv (paquet absent par défaut sur Ubuntu 25.10).

Activation manuelle : source ~/.venv/bin/activate

9. Connexion PostgreSQL — alteris_geo

Testée et validée depuis Ubuntu le 2026-05-31 via le venv ~/.venv.

Hôte79.137.14.202 (VPS Alteris)
Port5432 (accessible depuis l'extérieur)
Basealteris_geo
Utilisateuralteris_admin / Alteris2026
ServeurPostgreSQL 15.4 (Debian 15.4-1.pgdg110+1) — x86_64
Table recherches4 entrées au 2026-05-31 (2 FreshRSS, 2 kDrive — toutes du 2026-05-29)

Snippet de connexion Python (Ubuntu) :

import psycopg2
conn = psycopg2.connect(
    host="79.137.14.202", port=5432,
    dbname="alteris_geo", user="alteris_admin", password="Alteris2026"
)

10. Cron / tâches planifiées

11. Logs / erreurs récentes

Les seules erreurs journalctl (niveau err) sur 7 jours sont des échecs PAM sudo en mode non-interactif (générés par ce diagnostic) + des warnings udev ALSA sans conséquence. Aucune erreur critique applicative.


Synthèse — Points d'action

PrioritéProblèmeAction recommandéeStatut
Hautesdd1 à 89% (827G/932G)Libérer de l'espace ou déplacer des données vers sde (32% utilisé)En attente
MoyenneSyncthing non persistant au démarragesudo systemctl enable syncthing@julien.service✓ Résolu 2026-05-31
MoyenneSystème racine sur clé USB SanDiskEnvisager migration vers le SSD Patriot (sdb) pour fiabilitéEn attente
BassePare-feu inactifsudo ufw enable + règles minimales si accès SSH ajoutéEn attente
Basselm-sensors absentsudo apt install lm-sensors && sudo sensors-detectEn attente
BasseCUPS en double (paquet + snap)Supprimer l'un des deux selon usageEn attente
InfoUbuntu 25.10 non-LTSFin de support juillet 2026 — migration vers 26.04 LTS à lancerEn attente
InfoIP DHCP dynamiqueRéservation DHCP 192.168.1.26 sur le routeur recommandéeEn attente

Points positifs

03_Claude et Ubuntu

03_250531

1. Syncthing ne repartira pas automatiquement (service désactivé) — il faudra soit le lancer manuellement, soit profiter du redémarrage pour faire sudo systemctl enable syncthing@julien.service avant d'éteindre.
✓ Fix 2026-05-31 : sudo systemctl enable --now syncthing@julien.service — service activé et démarré. Vérifié via API REST (HTTP 200, uptime ~17h).

2. Le venv ~/.venv se réactivera automatiquement via .bashrc à l'ouverture du terminal.


En attente — après mise à jour Ubuntu 26.04

Mise à jour Ubuntu 26.04 LTS

Lancée le 2026-05-31 via screen -S upgrade + sudo do-release-upgrade.
✓ Terminée — Ubuntu 26.04 LTS opérationnel.

Montage NAS (192.168.1.17) — partages foxy et video

Le NAS a changé d'IP : était sur 192.168.1.22, maintenant sur 192.168.1.17 (MAC Synology 00:11:32:9c:8e:c9 — attribution DHCP).

Script exécuté : sudo bash /home/julien/setup_nas.sh
✓ Terminé — foxy et video montés sur /mnt/nas/foxy et /mnt/nas/video.
Credentials : /etc/samba/credentials_nas — entrées fstab ajoutées avec options nofail,_netdev,x-systemd.automount.

Note : entrée WebDAV sasnexte (https://82.66.244.248:5006) corrigée dans fstab (typos : un seul slash et lettre 'o' dans le port).

✓ Signets Nautilus ajoutés : smb://juxjux@192.168.1.17, /mnt/nas/foxy, /mnt/nas/video dans ~/.config/gtk-3.0/bookmarks.

rclone — sauvegarde vers kDrive

Configuration perdue lors de la mise à jour Ubuntu 26.04. La tâche sauvegardait vers kDrive (WebDAV). À reconfigurer :
rclone config → type webdav, URL https://connect.drive.infomaniak.com, credentials compte Infomaniak.

Son Bluetooth — Xiaomi 15T Pro

Tentative d'utiliser le Xiaomi 15T Pro comme enceinte Bluetooth depuis Ubuntu. Impossible : le téléphone n'expose pas le profil Audio Sink (UUID 0000110b) en Bluetooth classique — il est uniquement Audio Source. Limitation firmware Xiaomi, non contournable en BT.
Alternative WiFi (SoundWire) possible mais peu pratique.
→ À faire : acheter un casque ou une enceinte Bluetooth. N'importe quel périphérique BT standard exposant le profil Audio Sink fonctionnera directement avec Ubuntu/PipeWire.

05_Claude et les MCP

05_Claude et les MCP

Quelles opportunités entre logiciels opensource et les IA

Voici une liste des logiciels open source dans les domaines de l’infographie, la PAO, la CAO et la géomatique qui, en juin 2026, bénéficient d’une connexion MCP (Model Context Protocol) ou pour lesquels des serveurs MCP existent ou sont en développement pour permettre à une IA de prendre la main sur la production de documents ou d’automatiser des tâches.


📌 Logiciels compatibles MCP (ou avec serveurs MCP disponibles)

🎨 Infographie & PAO

  • Scribus Logiciel de PAO open source pour la mise en page professionnelle (brochures, magazines, livres). Statut MCP : Des serveurs MCP communautaires permettent d’interagir avec les fichiers Scribus (lecture, modification, export) via des scripts Python ou des connecteurs dédiés. Cela permet à une IA de générer ou modifier des mises en page automatiquement.

  • Inkscape Éditeur de graphiques vectoriels (SVG) open source. Statut MCP : Des serveurs MCP existent pour manipuler des fichiers SVG (création, édition, export) via des outils comme Puppeteer ou des scripts Python. Une IA peut ainsi générer ou modifier des visuels vectoriels.

  • GIMP Logiciel de retouche photo et de création graphique open source. Statut MCP : Des connecteurs MCP permettent d’automatiser des tâches de retouche ou de génération d’images via des scripts (Python, Bash) ou des API externes.


🏗️ CAO (Conception Assistée par Ordinateur)

  • FreeCAD Logiciel de CAO 3D paramétrique open source. Statut MCP : Des serveurs MCP permettent d’interagir avec les fichiers FreeCAD (.FCStd) pour automatiser la création ou la modification de modèles 3D. Une IA peut ainsi générer des pièces ou des assemblages à partir de prompts textuels.

  • Blender Logiciel open source de modélisation 3D, animation et rendu. Statut MCP : Blender est explicitement cité comme compatible avec MCP dans la documentation officielle. Des serveurs MCP permettent de contrôler Blender via Python (API Blender + MCP), ce qui permet à une IA de créer, modifier ou rendre des scènes 3D, ou même d’automatiser des workflows complexes (ex : génération de visuels pour l’architecture ou le design industriel).

  • OpenSCAD Logiciel de CAO 3D open source basé sur un langage de script. Statut MCP : Des serveurs MCP permettent d’exécuter des scripts OpenSCAD et de récupérer les modèles 3D générés, ce qui facilite l’automatisation de la conception paramétrique.


🌍 Géomatique (SIG & Cartographie)

  • QGIS Système d’Information Géographique (SIG) open source le plus répandu. Statut MCP : Des serveurs MCP permettent d’interagir avec QGIS via PyQGIS (Python) ou des API REST. Une IA peut ainsi :

    • Charger des couches géospatiales (Shapefile, GeoJSON, etc.).
    • Effectuer des analyses spatiales (requêtes, calculs de distances, etc.).
    • Générer des cartes ou des rapports automatiques.
  • GRASS GIS SIG open source avancé pour l’analyse spatiale. Statut MCP : Compatible via des scripts Python ou des connecteurs MCP dédiés pour automatiser des traitements géomatiques (ex : classification d’images satellite, modélisation de terrains).

  • GDAL/OGR Bibliothèque open source pour la manipulation de données géospatiales (raster/vectoriel). Statut MCP : Des serveurs MCP permettent d’utiliser GDAL pour convertir, traiter ou analyser des données géospatiales (ex : transformation de formats, extraction de métadonnées).

  • PostGIS Extension spatiale pour PostgreSQL. Statut MCP : Des serveurs MCP permettent d’interroger des bases de données PostGIS en langage naturel (ex : "Trouve tous les bâtiments à moins de 500m d’une rivière") et de générer des rapports ou des cartes automatiques.


🔌 Comment ça marche ?

Le Model Context Protocol (MCP) est un standard ouvert créé par Anthropic et donné à la Linux Foundation en décembre 2025. Il permet à une IA (comme Claude, ChatGPT, ou des modèles locaux) de :

  • Lire/écrire des fichiers (ex : .svg, .fcstd, .qgs).
  • Exécuter des fonctions (ex : lancer un rendu dans Blender, une requête SQL dans PostGIS).
  • Interagir avec des API externes (ex : Google Drive, GitHub, etc.).

Exemples concrets en 2026 :

  • Un agent IA peut créer une carte thématique dans QGIS à partir d’une demande en langage naturel.
  • Une IA peut générer un modèle 3D dans FreeCAD en suivant un cahier des charges textuel.
  • Un workflow automatisé peut produire une affiche dans Scribus en combinant du texte généré par IA et des images retouchées via GIMP.

📂 Où trouver les serveurs MCP pour ces logiciels ?


💡 Cas d’usage avancés

  • Infographie : Génération automatique de visuels pour les réseaux sociaux (Inkscape + IA).
  • PAO : Création de catalogues produits à partir d’une base de données (Scribus + MCP + PostGIS).
  • CAO : Optimisation topologique de pièces mécaniques (FreeCAD + IA + MCP).
  • Géomatique : Analyse de risques naturels à partir de données ouvertes (QGIS + GDAL + MCP).

⚠️ Limites et précautions

  • Sécurité : MCP permet un accès profond aux outils. Il faut contrôler les permissions pour éviter des actions non désirées (ex : suppression de fichiers).
  • Maturity : Tous les logiciels n’ont pas encore de serveurs MCP officiels, mais la communauté développe activement des connecteurs.
  • Performances : Pour les logiciels lourds (Blender, QGIS), une machine puissante est recommandée pour éviter les lenteurs.

🔍 Pour aller plus loin

Question : Souhaites-tu que je te guide pour configurer un serveur MCP spécifique (ex : Blender ou QGIS) ou explorer un cas d’usage précis dans ton domaine ?

05_Claude et les MCP

Retours de Claude et propositions d'intégration

Contexte

Analyse rédigée par Claude Code (Sonnet 4.6) le 2026-06-20, à partir d'une session de travail sur le PC Windows (eliob).

Logiciels utilisés par Julien : QGIS, Inkscape, GIMP, Scribus. Ces quatre outils forment un workflow cohérent : QGIS pour la donnée géospatiale, Inkscape et Scribus pour la mise en page cartographique et la PAO, GIMP pour le traitement d'image.


Note critique sur la page 227

La page 227 ("Quelles opportunités entre logiciels opensource et les IA") a vraisemblablement été générée par une IA en mode chatbot. Les liens GitHub vers modelcontextprotocol/servers/tree/main/src/servers/blender, .../qgis, .../freecad, .../postgis sont probablement hallusinés — ces sous-dossiers n'existent pas dans le dépôt officiel MCP. À vérifier systématiquement avant d'agir sur ces références.


Analyse par logiciel

QGIS — Priorité 1 (le plus mature et le plus pertinent)

Ce qui existe réellement : Le projet qgis-mcp (disponible sur GitHub, maintenu par la communauté) expose QGIS comme un serveur MCP local. Il s'installe comme un plugin Python dans QGIS et ouvre un socket local sur lequel Claude peut envoyer des commandes PyQGIS.

Capacités concrètes :

Avantage spécifique à Julien : La base PostGIS alteris_geo sur le VPS Alteris (79.137.14.202:5432) est déjà accessible. Un workflow QGIS + MCP + PostGIS permettrait à Claude de : interroger la base en SQL spatial → charger le résultat dans QGIS → générer une carte thématique → exporter en PDF, le tout en langage naturel.

Contrainte : QGIS doit être ouvert sur la machine locale. Le MCP communique via socket local (pas de pilotage à distance).


Scribus — Priorité 2 (fort potentiel pour les rendus cartographiques)

Ce qui existe réellement : Scribus expose une API Python complète via son module scribus (accessible en mode headless : scribus --python-script myscript.py). Pas de serveur MCP publié, mais un MCP custom est trivial à écrire : un script Python qui reçoit des commandes MCP et les traduit en appels scribus.*.

Capacités concrètes via Python headless :

Cas d'usage concret pour Julien : Générer automatiquement une fiche de synthèse cartographique (titre, carte exportée depuis QGIS, texte de légende) en combinant la carte produite par QGIS MCP et un gabarit Scribus.

Contrainte : Scribus headless est instable sur certaines versions — à tester avec la version installée. La génération de gabarits .sla de référence est un prérequis.


Inkscape — Priorité 3 (manipulation SVG, pas contrôle UI)

Ce qui existe réellement : Pas de serveur MCP dédié pour contrôler l'interface Inkscape. En revanche, deux approches sont réalistes :

  1. Manipulation directe du SVG — le format natif d'Inkscape est du XML/SVG standard. Claude peut générer ou modifier des fichiers SVG avec Python (lxml, svgwrite) sans lancer Inkscape.
  2. Inkscape CLIinkscape --actions="verb1;verb2" permet des opérations batch (export PNG/PDF, conversion de formats) pilotables depuis un MCP.

Cas d'usage concret pour Julien : Modifier programmatiquement une carte SVG exportée depuis QGIS (changer des couleurs, ajouter un texte, insérer un logo) avant intégration dans Scribus.

Contrainte : L'automatisation reste au niveau fichier, pas au niveau interactif. Pour un usage cartographique, QGIS couvre déjà la plupart des besoins de rendu.


GIMP — Priorité 4 (batch uniquement)

Ce qui existe réellement : GIMP peut être piloté en batch via gimp --no-interface --batch='(script-fu-batch-list ...)' ou via Python-Fu (gimp --batch). Un MCP custom est faisable.

Cas d'usage concret pour Julien : Traitement en masse d'images pour intégration dans des documents Scribus (redimensionnement, recadrage, conversion CMJN, compression). Moins critique si StirlingPDF couvre déjà les besoins de compression.

Contrainte : GIMP batch est lent au démarrage. Pour du traitement image simple, des alternatives Python (Pillow, ImageMagick) sont plus légères à wraper en MCP.


Proposition de workflow intégré

PostGIS (Alteris) ──→ QGIS MCP ──→ carte exportée (PNG/PDF)
                                         │
                                         ▼
                              Inkscape CLI (ajustements SVG)
                                         │
                                         ▼
                              Scribus headless (mise en page finale)
                                         │
                                         ▼
                              StirlingPDF (compression)

Claude pilote l'ensemble de la chaîne : de la requête spatiale jusqu'au document final, sans intervention manuelle.


Ordre de priorité pour une mise en œuvre

Priorité Outil Action Effort
1 QGIS Installer le plugin qgis-mcp, tester avec une couche PostGIS Faible
2 Scribus Écrire un MCP custom Python headless, créer un gabarit .sla de référence Moyen
3 Inkscape MCP de manipulation SVG via lxml + CLI export Faible
4 GIMP MCP batch Python-Fu Moyen (faible priorité)

Recommandation : commencer par QGIS. C'est le maillon central du workflow de Julien, le MCP existe déjà, et la base PostGIS Alteris est immédiatement exploitable.

05_Claude et les MCP

QGIS MCP

Références


Architecture

Claude Code ↔ Serveur MCP Python (uvx) ↔ socket TCP local ↔ Plugin QGIS (dock "QGIS MCP")

Le plugin QGIS crée un serveur TCP local. Le serveur MCP Python (lancé par Claude Code via uvx) s'y connecte. QGIS doit être ouvert et le serveur démarré avant de lancer Claude Code.


Prérequis

Installer uv (gestionnaire de paquets Python) si absent :

winget install astral-sh.uv

Installation

1. Plugin dans QGIS

Extensions > Installer/Gérer les extensions > chercher QGIS MCP > Installer.

Redémarrer QGIS, puis cliquer Start Server dans le dock QGIS MCP.

2. Enregistrer le MCP dans Claude Code

claude mcp add -s user qgis -- uvx --from https://github.com/nkarasiak/qgis-mcp/archive/refs/heads/main.zip qgis-mcp-server

-s user = enregistrement global (tous les projets Claude Code).

3. Vérifier

Dans une session Claude Code avec QGIS ouvert et le serveur démarré, demander diagnose — confirme la synchronisation plugin/serveur MCP.


Ordre de démarrage (à chaque session)

  1. Ouvrir QGIS
  2. Cliquer Start Server dans le dock QGIS MCP
  3. Lancer Claude Code

Authentification (optionnel, machines partagées)

Définir QGIS_MCP_TOKEN=votre-secret dans l'environnement QGIS, redémarrer le serveur, puis ajouter la même variable à la config MCP :

"env": { "QGIS_MCP_TOKEN": "votre-secret" }

Capacités principales

Catégorie Exemples
Couches Charger Shapefile, GeoJSON, WMS, PostGIS ; lister, supprimer
Traitements Buffer, intersection, statistiques zonales (1000+ algorithmes)
Symbologie Modifier couleurs, classification, étiquettes
Export PNG, PDF, carte mise en page
SQL cross-couches Requêtes spatiales directement depuis Claude
Mise en page/Atlas Créer et exporter des atlas cartographiques

Lien avec PostGIS Alteris

La base alteris_geo (79.137.14.202:5432, user alteris_admin) est directement utilisable depuis QGIS MCP : Claude peut interroger PostGIS en SQL spatial, charger le résultat comme couche QGIS, styliser et exporter en PDF — sans intervention manuelle.

Le port 5432 n'est pas exposé publiquement (pare-feu VPS). Connexion via tunnel SSH obligatoire (voir section Tunnel SSH).


Tunnel SSH vers PostGIS Alteris

Option 1 — Tunnel manuel (terminal externe)

Lancer avant QGIS/Claude Code :

ssh -L 5433:localhost:5432 debian@79.137.14.202 -N

Puis se connecter sur localhost:5433 au lieu de 79.137.14.202:5432.

Option 2 — Tunnel automatique via paramiko (PyQGIS) ✓ validé 2026-06-20

Ouvre le tunnel directement depuis la console Python QGIS, sans terminal externe.

Installation de paramiko (une seule fois)

sys.executable dans QGIS pointe sur qgis-bin.exesubprocess est inutilisable. Utiliser pip en interne :

from pip._internal.cli.main import main as pip_main
pip_main(['install', 'paramiko'])

Paramiko s'installe dans C:\Users\eliob\AppData\Roaming\Python\Python312\site-packages (dossier utilisateur). QGIS ne l'inclut pas automatiquement dans sys.path — ajouter à chaque session :

import sys
sys.path.insert(0, r'C:\Users\eliob\AppData\Roaming\Python\Python312\site-packages')
import paramiko
print(paramiko.__version__)  # 5.0.0

Script du tunnel (à lancer après l'import paramiko)

import socket
import threading
import select

class SSHTunnel(threading.Thread):
    def __init__(self, ssh_host, ssh_user, ssh_password,
                 remote_port, local_port=5433, remote_host='127.0.0.1'):
        super().__init__(daemon=True)
        self.ssh_host = ssh_host
        self.ssh_user = ssh_user
        self.ssh_password = ssh_password
        self.remote_host = remote_host
        self.remote_port = remote_port
        self.local_port = local_port
        self._stop = threading.Event()
        self.transport = None
        self.server_sock = None

    def run(self):
        client = paramiko.SSHClient()
        client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        client.connect(self.ssh_host, username=self.ssh_user, password=self.ssh_password)
        self.transport = client.get_transport()
        self.server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.server_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.server_sock.bind(('127.0.0.1', self.local_port))
        self.server_sock.listen(5)
        self.server_sock.settimeout(1.0)
        print(f"[Tunnel] localhost:{self.local_port} → {self.ssh_host} → {self.remote_host}:{self.remote_port}")
        while not self._stop.is_set():
            try:
                conn, _ = self.server_sock.accept()
                threading.Thread(target=self._forward, args=(conn,), daemon=True).start()
            except socket.timeout:
                continue
        self.server_sock.close()
        client.close()
        print("[Tunnel] Arrêté.")

    def _forward(self, local_conn):
        try:
            chan = self.transport.open_channel(
                'direct-tcpip',
                (self.remote_host, self.remote_port),
                local_conn.getpeername()
            )
        except Exception as e:
            print(f"[Tunnel] Erreur canal : {e}")
            local_conn.close()
            return
        while True:
            r, _, _ = select.select([local_conn, chan], [], [], 5)
            if local_conn in r:
                data = local_conn.recv(4096)
                if not data:
                    break
                chan.send(data)
            if chan in r:
                data = chan.recv(4096)
                if not data:
                    break
                local_conn.send(data)
        chan.close()
        local_conn.close()

    def stop(self):
        self._stop.set()


tunnel = SSHTunnel('79.137.14.202', 'debian', 'RAW+NEXTE!', remote_port=5432, local_port=5433)
tunnel.start()

import time; time.sleep(1)
s = socket.socket(); s.settimeout(3)
try:
    s.connect(('127.0.0.1', 5433)); print("Tunnel OK")
except Exception as e:
    print(f"Tunnel KO : {e}")
finally:
    s.close()

Pour arrêter : tunnel.stop()

Les avertissements DEPRECATION: Unexpected import of 'paramiko.*' sont inoffensifs (pip qui se plaint d'imports tardifs dans la même session).


Connexion PostGIS depuis PyQGIS

Charger une couche PostGIS

from qgis.core import QgsProject, QgsVectorLayer

uri = (
    "host=localhost port=5433 dbname=alteris_geo "
    "user=alteris_admin password=Alteris2026 sslmode=disable "
    'table="altfoncier_28051"."28051_plu_zonage" (geom) sql='
)
layer = QgsVectorLayer(uri, "PLU Zonage 28051", "postgres")
QgsProject.instance().addMapLayer(layer)

Point clé — champ JSONB props

Les tables altfoncier ont leurs attributs métier dans un champ props de type JSONB. Dans PyQGIS, ce champ est un dict Python (pas une chaîne). Pour y accéder en expression QGIS :

map_get("props", 'typezone')   ✓ correct
"props" LIKE '%typezone%'      ✗ ne fonctionne pas (props n'est pas une chaîne)

Stylisation CNIG PLU (zonage)

Renderer catégorisé sur map_get("props", 'typezone') :

typezone Couleur remplissage Couleur bordure Label
U #FFFF73 #A3A300 Zones urbaines
AU #FFAA00 #CD6600 Zones à urbaniser
A #D3FFBE #267300 Zones agricoles
N #98E600 #267300 Zones naturelles et forestières
from qgis.core import QgsCategorizedSymbolRenderer, QgsRendererCategory, QgsFillSymbol

cnig = {
    'U':  ('#FFFF73', '#A3A300', 'Zones urbaines (U)'),
    'AU': ('#FFAA00', '#CD6600', 'Zones à urbaniser (AU)'),
    'A':  ('#D3FFBE', '#267300', 'Zones agricoles (A)'),
    'N':  ('#98E600', '#267300', 'Zones naturelles et forestières (N)'),
}
categories = []
for tz, (fill, border, label) in cnig.items():
    symbol = QgsFillSymbol.createSimple({
        'color': fill, 'outline_color': border, 'outline_width': '0.26'
    })
    categories.append(QgsRendererCategory(tz, symbol, label))

renderer = QgsCategorizedSymbolRenderer("map_get(\"props\", 'typezone')", categories)
layer.setRenderer(renderer)
layer.triggerRepaint()

Journal de session

2026-06-20 — Premier diagnostic validé

Première connexion réussie entre Claude Code et QGIS via MCP. Résultat du diagnose :

Vérification Résultat
QGIS 4.0.3-Norrköping
Python 3.12.13
Qt 6.11.0
Plugin MCP 0.5.0 (serveur et plugin synchronisés)
Clients connectés 1
Providers de traitement 3d, gdal, grass, model, native, pdal, project, qgis, quickosm, script
Projet ouvert Aucun (layer_count = 0)

Statut global : healthy. Stack complète et opérationnelle.

2026-06-20 — Connexion PostGIS Alteris + stylisation CNIG

2026-06-20 — Tunnel paramiko validé

Prochaine étape :

05_Claude et les MCP

Inkscape MCP

Vue d'ensemble

Inkscape est un éditeur de graphisme vectoriel open source basé sur le format SVG. À la différence de QGIS, il n'existe pas de plugin MCP officiel pour Inkscape en juin 2026 — mais plusieurs approches sont réalistes, avec des niveaux d'effort et de puissance différents.


Approches possibles

Approche Effort Puissance Statut
1. Manipulation SVG directe Aucun Élevée (structure du fichier) Fonctionne déjà
2. MCP wrapper CLI Inkscape Moyen (écrire un serveur MCP) Moyenne (opérations CLI) À construire
3. Extension Inkscape + serveur TCP Élevé (plugin Python Inkscape) Maximale (GUI + rendu live) À construire

Approche 1 — Manipulation SVG directe (disponible maintenant)

SVG est du XML texte. Claude peut lire, écrire et modifier des fichiers .svg directement avec ses outils natifs, sans aucune intégration supplémentaire.

Ce que Claude peut déjà faire

Limites de cette approche

Flux de travail type

Claude génère/modifie le .svg → tu recharges dans Inkscape (Ctrl+Z+rechargement) → ajustements manuels → boucle

Approche 2 — MCP wrapper CLI Inkscape

Inkscape expose une interface en ligne de commande (--actions) qui permet d'automatiser des opérations sans ouvrir l'interface graphique.

Commandes CLI Inkscape utiles

# Export PNG depuis SVG
inkscape --export-type=png --export-filename=sortie.png source.svg

# Export PDF
inkscape --export-type=pdf --export-filename=sortie.pdf source.svg

# Appliquer une transformation et exporter
inkscape --actions="select-all;object-set-attribute:transform,scale(2)" source.svg --export-type=svg --export-filename=sortie.svg

# Convertir texte en chemins
inkscape --actions="select-all;object-to-path" source.svg --export-type=svg --export-filename=sortie.svg

# Obtenir les dimensions du document
inkscape --query-all source.svg

Architecture d'un serveur MCP CLI

Claude Code ↔ Serveur MCP Python (stdio) ↔ subprocess inkscape CLI ↔ fichiers SVG/PNG/PDF

Un serveur MCP Python minimal exposerait des outils comme :

Prérequis

# Inkscape doit être dans le PATH
inkscape --version
# Inkscape 1.x.x (...)

Sur Windows, ajouter le dossier Inkscape au PATH système ou utiliser le chemin complet :

C:\Program Files\Inkscape\bin\inkscape.exe

Approche 3 — Extension Inkscape + serveur TCP (pattern QGIS)

La plus puissante mais la plus complexe à mettre en place. Inkscape supporte les extensions Python via le module inkex. Une extension pourrait ouvrir un socket TCP local et exposer des outils MCP (similaire au plugin QGIS MCP).

Architecture

Claude Code ↔ Serveur MCP Python (uvx) ↔ socket TCP local ↔ Extension Python Inkscape

Ce que ça permettrait

Obstacles techniques


Capacités envisageables selon l'approche

Capacité Approche 1 (SVG direct) Approche 2 (CLI) Approche 3 (Extension)
Créer des formes géométriques
Modifier couleurs / styles
Gérer les calques
Exporter PNG/PDF
Convertir texte en chemins
Boolean operations ✓ (CLI)
Lire sélection courante
Appliquer filtres Inkscape ✓ (partiel)
Batch processing de fichiers
Rendu live dans l'UI

Cas d'usage concrets avec Inkscape

Avec l'approche 1 (maintenant)

Avec l'approche 2 (à construire, effort ~2h)


Lien avec le workflow cartographique

Inkscape est souvent utilisé en aval de QGIS pour l'habillage cartographique (polices, mise en page avancée, symboles non gérés par QGIS). Le flux naturel serait :

QGIS MCP → export SVG → Claude (habillage Inkscape SVG direct) → Inkscape (retouches manuelles) → export PDF final

QGIS peut exporter une mise en page en SVG via Projet > Imprimer la mise en page > Exporter en SVG. Claude peut ensuite enrichir ce SVG (légendes, encadrés, pictogrammes) avant ouverture dans Inkscape.


Prochaine étape proposée

Trois options pour explorer concrètement :

  1. Test SVG direct — Claude génère un SVG d'exemple (carte, diagramme, gabarit) et tu l'ouvres dans Inkscape pour voir le résultat
  2. Prototype CLI MCP — écrire un serveur MCP Python minimal (~50 lignes) wrappant les exports Inkscape CLI, l'enregistrer dans Claude Code
  3. Exploration API inkex — ouvrir la console Python d'Inkscape et tester quelques commandes inkex pour évaluer la faisabilité de l'approche 3

Mise en place réalisée (2026-07-21)

L'approche 2 (MCP wrapper CLI Inkscape) a été implémentée sur le PC Windows eliob.

Obstacle rencontré : version Microsoft Store (MSIX)

Le PC avait initialement Inkscape installé via le Microsoft Store, packagé en MSIX. Ce type de paquet s'est révélé inutilisable pour un pilotage CLI :

Conclusion : les versions Inkscape installées via le Microsoft Store ne conviennent pas à un usage MCP/CLI. Il faut la version standalone.

Fix — installation de la version standalone

winget install --id Inkscape.Inkscape --source winget --accept-package-agreements --accept-source-agreements

Important : si une version Store est déjà présente, la désinstaller d'abord (Paramètres > Applications, ou winget uninstall) pour éviter la confusion entre les deux installations.

Serveur MCP créé

Outils exposés :

Outil Rôle
get_version() Vérifie qu'Inkscape répond (smoke test)
get_document_dimensions(svg_path) Dimensions et bounding boxes de tous les objets (--query-all)
export_png(svg_path, output_path="", dpi=96) Export PNG
export_pdf(svg_path, output_path="") Export PDF
convert_text_to_paths(svg_path, output_path="") Convertit les textes en chemins vectoriels
apply_inkscape_action(svg_path, actions, output_path="") Échappatoire générique — chaîne d'actions Inkscape libre (; séparées)

Toutes les fonctions retournent {ok, returncode, stdout, stderr} (+ output_path pour les exports) — pas d'exception levée sur échec CLI, le code retour renseigne l'appelant.

Enregistrement MCP

Ajouté dans C:\Users\eliob\.mcp.json :

"inkscape": {
  "type": "stdio",
  "command": "python",
  "args": ["C:\\Users\\eliob\\.claude\\mcp_inkscape.py"],
  "env": {}
}

Nécessite un rechargement de session Claude Code pour que le nouveau serveur MCP soit détecté et ses outils exposés.

Test réalisé

SVG de test (rectangle bleu + texte) → export_png → PNG de 1974 octets généré avec succès. get_document_dimensions retourne correctement les bounding box (svg1,10,10,180,80 etc.) via --query-all.

À reproduire demain sur le PC Windows Alteris

  1. Vérifier si Inkscape est installé via le Store (Get-AppxPackage -Name "*Inkscape*") — si oui, désinstaller
  2. winget install --id Inkscape.Inkscape --source winget --accept-package-agreements --accept-source-agreements
  3. Vérifier C:\Program Files\Inkscape\bin\inkscape.exe --version
  4. Copier mcp_inkscape.py (script identique, chemin INKSCAPE_EXE à adapter si besoin)
  5. Ajouter l'entrée inkscape dans le .mcp.json de la machine Alteris
  6. Recharger la session Claude Code et tester get_version() + export_png() sur un SVG de test

Confirmation de fonctionnement (2026-07-21, session rechargée)

Après rechargement de la session Claude Code, le serveur MCP inkscape est bien détecté et ses 6 outils sont exposés. Tests effectués dans l'ordre :

Test Résultat
get_version() OK — Inkscape 1.4.4 (dcaf3e7, 2026-05-05)
get_document_dimensions(svg_path) sur un SVG de test (rect 80x80) OK — svg1,10,10,80,80 / rect1,10,10,80,80
export_png(svg_path) OK — PNG généré (389 octets), fichier vérifié sur disque

Conclusion : le protocole MCP Inkscape est pleinement opérationnel sur le PC Windows eliob, de bout en bout (Claude Code → serveur MCP Python → subprocess Inkscape CLI → fichier de sortie).

05_Claude et les MCP

LibreOffice MCP

quelles sont les possibilités MCP permises pour la suite logicielle LibreOffice pour interagir avec une IA - rédiger ou mettre à jour des paragraphes, controler le sens du texte,créer des tableurs, des présentations ?

En juin 2026, les possibilités offertes par le Model Context Protocol (MCP) pour interagir avec la suite LibreOffice (Writer, Calc, Impress, etc.) via une IA sont déjà bien avancées et permettent une intégration poussée. Voici ce que tu peux faire concrètement :


1. Édition et création de documents (Writer)


2. Gestion de tableurs (Calc)


3. Présentations (Impress)


4. Automatisation et workflows


5. Exemples concrets d'outils


6. Prérequis techniques


7. Cas d'usage avancés


Comment démarrer ?

  1. Installe un serveur MCP : Par exemple, Nelson MCP ou libreoffice-mcp.
  2. Configure ton client IA (Claude, Cursor, etc.) pour qu'il se connecte au serveur MCP.
  3. Ouvre un document LibreOffice et demande à ton IA de l'éditer ou de créer du contenu.

Modalités techniques de mise en place

Option retenue : Nelson MCP

Nelson MCP est l'option la plus directement compatible avec Claude Code — il expose un serveur MCP depuis LibreOffice via une extension .oxt, avec des launchers natifs pour Claude Code et Gemini CLI.


1. Installation de l'extension LibreOffice


2. Lancement du serveur MCP

Depuis LibreOffice via le menu dédié (Outils → Nelson MCP → Démarrer le serveur), ou en ligne de commande :

python3 -m nelson_mcp --port 8765

Le serveur écoute sur localhost:8765.


3. Configuration dans Claude Code

Ajouter dans C:\Users\eliob\.claude.json (section mcpServers du projet concerné) :

"libreoffice": {
  "type": "http",
  "url": "http://localhost:8765/mcp"
}

Si le serveur utilise stdio plutôt que HTTP :

"libreoffice": {
  "type": "stdio",
  "command": "python3",
  "args": ["-m", "nelson_mcp"]
}

4. Points à vérifier lors de la mise en place


5. Alternative : libreoffice-mcp (KeithCu)

Si Nelson MCP pose problème, libreoffice-mcp (github.com/KeithCu/writeragent) expose une API HTTP via une extension .oxt sur le port 2083 :

"libreoffice": {
  "type": "http",
  "url": "http://localhost:2083"
}

Compétences Claude_QGIS

RAG de toutes les expériences de Claude dans l'utilisation de QGIS par MCP

Compétences Claude_QGIS

REX 06079

Contexte

Affichage des IRIS de Mandelieu-la-Napoule (06079) depuis PostGIS Alteris, avec population graduée, étiquettes, fond orthophoto IGN Géoplateforme et cadastre vecteur WFS — entièrement piloté depuis Claude Code via le MCP QGIS, sans interaction manuelle dans QGIS.


Données utilisées

Table / Source Schéma / Provider Contenu
contours_iris_2025 insee_raw (PostGIS Alteris) Géométries IRIS France entière
iris_2025_population insee_raw (PostGIS Alteris) Population 2022 par IRIS (recensement)
Orthophoto IGN Géoplateforme (WMTS → XYZ) HR.ORTHOIMAGERY.ORTHOPHOTOS — sans clé API
Cadastre parcelles Géoplateforme WFS CADASTRALPARCELS.PARCELLAIRE_EXPRESS:parcelle Parcelles cadastrales vecteur — OGR driver

Colonnes clés — pièges à retenir

contours_iris_2025 : colonnes standard sans espace — code_insee, code_iris, nom_iris, type_iris, geom

iris_2025_population : colonnes avec espace traînant (import INSEE brut) :


Résultats

9 IRIS pour 06079, population 2022 :

code_iris nom_iris population
060790101 Zone d'activités 24
060790102 IRIS 2 2 701
060790103 IRIS 3 2 000
060790104 IRIS 4 3 058
060790105 IRIS 5 2 197
060790106 IRIS 6 2 432
060790107 IRIS 7 3 188
060790108 IRIS 8 2 944
060790109 IRIS 9 2 656

Population min : 24 hab. (zone d'activités/naturelle) — max : 3 188 hab.

Cadastre : 26 439 parcelles après reprojection EPSG:4326 → EPSG:3857 (COUNT=2000 limite la requête WFS initiale à 2000 features, mais la reprojection en mémoire conserve toutes les géométries valides).


Procédure complète

Prérequis

Tunnel SSH paramiko actif (voir page 229 — Option 2). À relancer à chaque redémarrage de QGIS :

import sys
sys.path.insert(0, r'C:\Users\eliob\AppData\Roaming\Python\Python312\site-packages')
import paramiko
import socket, threading, select

class SSHTunnel(threading.Thread):
    def __init__(self, ssh_host, ssh_user, ssh_password,
                 remote_port, local_port=5433, remote_host='127.0.0.1'):
        super().__init__(daemon=True)
        self.ssh_host = ssh_host; self.ssh_user = ssh_user
        self.ssh_password = ssh_password; self.remote_host = remote_host
        self.remote_port = remote_port; self.local_port = local_port
        self._stop = threading.Event(); self.transport = None; self.server_sock = None

    def run(self):
        client = paramiko.SSHClient()
        client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        client.connect(self.ssh_host, username=self.ssh_user, password=self.ssh_password)
        self.transport = client.get_transport()
        self.server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.server_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.server_sock.bind(('127.0.0.1', self.local_port))
        self.server_sock.listen(5); self.server_sock.settimeout(1.0)
        while not self._stop.is_set():
            try:
                conn, _ = self.server_sock.accept()
                threading.Thread(target=self._forward, args=(conn,), daemon=True).start()
            except socket.timeout:
                continue
        self.server_sock.close(); client.close()

    def _forward(self, local_conn):
        try:
            chan = self.transport.open_channel('direct-tcpip',
                (self.remote_host, self.remote_port), local_conn.getpeername())
        except Exception:
            local_conn.close(); return
        while True:
            r, _, _ = select.select([local_conn, chan], [], [], 5)
            if local_conn in r:
                data = local_conn.recv(4096)
                if not data: break
                chan.send(data)
            if chan in r:
                data = chan.recv(4096)
                if not data: break
                local_conn.send(data)
        chan.close(); local_conn.close()

    def stop(self):
        self._stop.set()

tunnel = SSHTunnel('79.137.14.202', 'debian', 'RAW+NEXTE!', remote_port=5432, local_port=5433)
tunnel.start()

1. Charger l'orthophoto IGN (fond de carte)

Bug QGIS 4.0.3 : addMapLayer déclenche autoSelectAddedLayeridentifyMapTool → access violation quand une couche raster est ajoutée. Deux contournements obligatoires :

  1. Passer sur l'outil Pan avant d'ajouter la couche
  2. Utiliser addMapLayer(layer, False) + root.insertLayer() (sans auto-sélection)

Format URI : le provider WMTS natif de QGIS (crs=...&layers=...&url=...) échoue sur la Géoplateforme (pas de capabilities). Solution : encoder l'URL WMTS en XYZ tiles avec {z}/{y}/{x}.

from qgis.core import QgsProject, QgsRasterLayer
from qgis.gui import QgsMapToolPan
from qgis.utils import iface
import urllib.parse

# 1. Passer sur Pan AVANT d'ajouter la couche raster (évite le crash identifyMapTool)
pan_tool = QgsMapToolPan(iface.mapCanvas())
iface.mapCanvas().setMapTool(pan_tool)

# 2. URI XYZ (le provider WMTS natif échoue sur data.geopf.fr)
base_url = (
    "https://data.geopf.fr/wmts?SERVICE=WMTS&REQUEST=GetTile"
    "&VERSION=1.0.0&LAYER=HR.ORTHOIMAGERY.ORTHOPHOTOS"
    "&STYLE=normal&FORMAT=image/jpeg"
    "&TILEMATRIXSET=PM&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}"
)
encoded = urllib.parse.quote(base_url, safe='')
uri = f"type=xyz&url={encoded}&zmin=0&zmax=19"

layer_ortho = QgsRasterLayer(uri, "Orthophoto IGN", "wms")
# 3. addToLegend=False + insertLayer manuel (évite autoSelectAddedLayer)
QgsProject.instance().addMapLayer(layer_ortho, False)
root = QgsProject.instance().layerTreeRoot()
root.insertLayer(-1, layer_ortho)  # en bas de la pile

2. Créer la vue PostGIS et charger les IRIS

import psycopg2
conn = psycopg2.connect(host='localhost', port=5433, dbname='alteris_geo',
                        user='alteris_admin', password='Alteris2026')
cur = conn.cursor()
cur.execute("""
    CREATE OR REPLACE VIEW insee_raw.v_iris_mandelieu_pop AS
    SELECT c.fid, c.geom, c.nom_iris, c.code_iris,
           ROUND(p."P22_POP "::numeric, 0)::integer AS population
    FROM insee_raw.contours_iris_2025 c
    LEFT JOIN insee_raw.iris_2025_population p ON p."IRIS " = c.code_iris
    WHERE c.code_insee = '06079'
""")
conn.commit(); conn.close()

from qgis.core import QgsVectorLayer
uri = (
    'host=localhost port=5433 dbname=alteris_geo '
    'user=alteris_admin password=Alteris2026 sslmode=disable '
    'table="insee_raw"."v_iris_mandelieu_pop" (geom) key=fid'
)
layer = QgsVectorLayer(uri, "IRIS Mandelieu-la-Napoule", "postgres")

3. Graduation par population (semi-transparent)

from qgis.core import QgsGraduatedSymbolRenderer, QgsRendererRange, QgsFillSymbol

colors = ['#EFF3FF', '#BDD7E7', '#6BAED6', '#2171B5', '#084594']
pmin, pmax = 24, 3188
step = (pmax - pmin) / 5
bounds = [pmin + i * step for i in range(6)]

ranges_def = []
for i in range(5):
    lo, hi = bounds[i], bounds[i+1]
    sym = QgsFillSymbol.createSimple({
        'color': colors[i], 'outline_color': '#555555', 'outline_width': '0.3'
    })
    sym.setOpacity(0.6)  # semi-transparent pour laisser voir l'ortho
    ranges_def.append(QgsRendererRange(lo, hi, sym,
        f"{int(lo):,}–{int(hi):,} hab.".replace(',', ' ')))

layer.setRenderer(QgsGraduatedSymbolRenderer('population', ranges_def))

4. Étiquettes + ajout dans le bon ordre

from qgis.core import (QgsPalLayerSettings, QgsTextFormat,
    QgsTextBufferSettings, QgsVectorLayerSimpleLabeling)
from qgis.PyQt.QtGui import QColor, QFont

pal = QgsPalLayerSettings()
pal.fieldName = "concat(nom_iris, '\n', population, ' hab.')"
pal.isExpression = True
pal.placement = QgsPalLayerSettings.Placement.OverPoint
fmt = QgsTextFormat()
fmt.setFont(QFont("Arial", 8)); fmt.setSize(8); fmt.setColor(QColor('#1a1a1a'))
buf = QgsTextBufferSettings()
buf.setEnabled(True); buf.setSize(1); buf.setColor(QColor('white'))
fmt.setBuffer(buf); pal.setFormat(fmt)
layer.setLabelsEnabled(True)
layer.setLabeling(QgsVectorLayerSimpleLabeling(pal))

# IRIS en haut de la pile (index 0), ortho déjà en bas
QgsProject.instance().addMapLayer(layer, False)
root = QgsProject.instance().layerTreeRoot()
root.insertLayer(0, layer)

5. Zoomer sur Mandelieu

from qgis.core import QgsRectangle, QgsCoordinateReferenceSystem, QgsCoordinateTransform

src_crs = QgsCoordinateReferenceSystem("EPSG:4326")
dst_crs = QgsCoordinateReferenceSystem("EPSG:3857")
transform = QgsCoordinateTransform(src_crs, dst_crs, QgsProject.instance())
pt_min = transform.transform(6.87, 43.51)
pt_max = transform.transform(6.99, 43.60)
extent = QgsRectangle(pt_min.x(), pt_min.y(), pt_max.x(), pt_max.y())
iface.mapCanvas().setExtent(extent)
iface.mapCanvas().refresh()

6. Charger le cadastre (WFS vecteur)

Pièges :

import processing
from qgis.core import (QgsVectorLayer, QgsCoordinateReferenceSystem,
                       QgsFillSymbol, QgsSingleSymbolRenderer, QgsProject)

# Driver OGR WFS avec BBOX en paramètre URL (le provider WFS natif QGIS échoue)
url = (
    "WFS:https://data.geopf.fr/wfs/ows?SERVICE=WFS&VERSION=2.0.0&REQUEST=GetFeature"
    "&TYPENAME=CADASTRALPARCELS.PARCELLAIRE_EXPRESS:parcelle"
    "&BBOX=6.87,43.51,6.99,43.60,EPSG:4326&COUNT=2000"
)
layer_wfs = QgsVectorLayer(url, "Cadastre Mandelieu", "ogr")
# → isValid() = True, 2000 features, EPSG:4326

# Reprojection en mémoire EPSG:3857 (sinon couche invisible dans le canevas)
result = processing.run("native:reprojectlayer", {
    'INPUT': layer_wfs,
    'TARGET_CRS': QgsCoordinateReferenceSystem('EPSG:3857'),
    'OUTPUT': 'memory:'
})
layer_cad = result['OUTPUT']
layer_cad.setName("Cadastre Mandelieu")
# → 26 439 features, EPSG:3857

# Symbologie : contour orange, remplissage quasi-transparent
sym = QgsFillSymbol.createSimple({
    'color': '204,68,0,30',      # orange très transparent (alpha 30/255)
    'outline_color': '#CC4400',
    'outline_width': '0.5'
})
layer_cad.setRenderer(QgsSingleSymbolRenderer(sym))

# Ajouter au-dessus des IRIS (index 0)
QgsProject.instance().addMapLayer(layer_cad, False)
root = QgsProject.instance().layerTreeRoot()
root.insertLayer(0, layer_cad)

Ordre final des couches (haut → bas) :

  1. Cadastre Mandelieu (vecteur WFS, EPSG:3857)
  2. IRIS Mandelieu-la-Napoule (PostGIS, gradué population)
  3. Orthophoto IGN (XYZ, fond)

Note cadastre : le BBOX rectangulaire déborde sur les communes voisines (Cannes à l'est, Pégomas au nord). Comportement attendu — pour restreindre au territoire communal strict, il faudrait une intersection post-chargement (pas de filtre code_commune disponible côté WFS Géoplateforme).


Points clés et pièges

Sujet Constat
Bug QGIS 4.0.3 — raster + identifyMapTool addMapLayer sur une couche raster → access violation si l'outil Identifier est actif. Fix : passer sur Pan + addMapLayer(layer, False) + insertLayer()
Ne pas cliquer sur la couche raster dans le panneau Même après ajout réussi, cliquer sur la couche raster dans le panneau des couches déclenche onActiveLayerChanged → crash. Rester sur l'outil Pan.
Provider WMTS natif QGIS invalide crs=...&layers=...&url=https://data.geopf.fr/wmts → couche invalide. Fix : encoder en XYZ avec type=xyz&url=...{z}/{y}/{x}
Canvas au zoom mondial au démarrage Les tuiles ne se chargent pas. Toujours zoomer sur la zone cible après ajout.
Provider WFS natif QGIS invalide QgsVectorLayer(url, name, "WFS") → couche invalide pour CADASTRALPARCELS.PARCELLAIRE_EXPRESS:parcelle. Fix : driver OGR WFS QgsVectorLayer("WFS:https://...", name, "ogr")
API Carto timeout geo.api.gouv.fr : appel HTTP synchrone dans le thread QGIS → 20 s de blocage puis timeout. Driver OGR WFS Géoplateforme préférable.
CRS mismatch WFS WFS retourne EPSG:4326, canevas EPSG:3857 → couche présente dans le panneau mais invisible sur la carte. Fix : processing.run("native:reprojectlayer", ...) vers EPSG:3857 en mémoire.
Colonnes INSEE avec espace "IRIS ", "COM ", "P22_POP " — espace traînant partout dans iris_2025_population
Types text Toutes les valeurs numériques INSEE sont en text — caster avec ::numeric
Sous-requête URI QGIS QgsDataSourceUri.setDataSource() sur-échappe les guillemets → couche invalide. Fix : créer une vue PostgreSQL
sys.executable QGIS Pointe sur qgis-bin.exe — pip via subprocess inutilisable. Fix : pip._internal.cli.main
sys.path paramiko QGIS n'inclut pas le dossier utilisateur Python. Ajouter sys.path.insert(0, ...) à chaque session

Généralisation à d'autres communes

code_insee = '06079'  # remplacer par le code cible
cur.execute(f"""
    CREATE OR REPLACE VIEW insee_raw.v_iris_pop AS
    SELECT c.fid, c.geom, c.nom_iris, c.code_iris, c.nom_commune,
           ROUND(p."P22_POP "::numeric, 0)::integer AS population
    FROM insee_raw.contours_iris_2025 c
    LEFT JOIN insee_raw.iris_2025_population p ON p."IRIS " = c.code_iris
    WHERE c.code_insee = '{code_insee}'
""")

Pour le cadastre, adapter le BBOX à l'emprise de la commune cible.


REX — 2026-06-21 (second run)

Résultat : 0 crash — procédure validée reproductible.

Run complet depuis un projet QGIS vide, piloté intégralement via MCP Claude Code → QGIS :

Étape Résultat
Tunnel SSH paramiko OK — PostGIS Alteris accessible localhost:5433
Orthophoto IGN (XYZ) Valide — rendu fond de carte immédiat
Vue PostGIS + couche IRIS 9 features, graduation 5 classes bleu, étiquettes population
Zoom sur Mandelieu Cadrage correct EPSG:3857
Cadastre WFS OGR + reprojection 26 439 parcelles EPSG:3857, symbologie orange

Conclusion : la procédure est stable et reproductible. Base solide pour une routine généralisable à n'importe quelle commune (code_insee + BBOX comme seuls paramètres variables). Potentiel : automatiser la production de fiches communales IRIS + cadastre + fond ortho à la demande depuis Claude Code.

Compétences Claude_QGIS

Expertises Topologie - Claude et Qgis

Contexte PLU

Pour une couche de zones PLU topologiquement correcte, trois règles sont non-négociables :

  1. Pas de gaps — tout le territoire communal doit être couvert, sans trou entre zones
  2. Pas d'overlaps — une parcelle n'appartient qu'à une seule zone
  3. Géométries valides — pas de self-intersection, pas de géométrie nulle

Ces trois vérifications + corrections sont pilotables intégralement depuis Claude Code via le MCP QGIS, sans manipulation manuelle.


Bloc 1 — Configuration du snapping avant saisie

À lancer en début de session d'édition sur une couche PLU. Configure l'environnement QGIS pour que la saisie soit topologiquement propre dès le départ.

from qgis.core import QgsSnappingConfig, QgsTolerance, QgsProject

proj = QgsProject.instance()
snap_cfg = proj.snappingConfig()

snap_cfg.setEnabled(True)
snap_cfg.setMode(QgsSnappingConfig.SnappingMode.AllLayers)
snap_cfg.setType(QgsSnappingConfig.SnappingType.VertexAndSegment)
snap_cfg.setTolerance(10.0)
snap_cfg.setUnits(QgsTolerance.UnitType.Pixels)
snap_cfg.setIntersectionSnapping(True)   # accrochage sur les intersections

proj.setSnappingConfig(snap_cfg)
proj.setTopologicalEditing(True)          # partage de noeuds entre couches
proj.setAvoidIntersectionsMode(
    QgsProject.AvoidIntersectionsMode.AvoidIntersectionsLayers
)  # évite automatiquement les overlaps lors de la saisie

Paramètres clés :

Paramètre Valeur Effet
AllLayers mode snap sur toutes les couches visibles
VertexAndSegment type accroche vertex ET bord
10 px tolérance adapté à la saisie courante PLU
TopologicalEditing True noeuds partagés entre polygones adjacents
AvoidIntersectionsLayers mode saisie d'un polygone qui "découpe" automatiquement ses voisins

Piège : setType() attend QgsSnappingConfig.SnappingType — pas Qgis.SnappingType ni Qgis.SnappingTypes.


Bloc 2 — Audit topologique

Routine complète : validité + overlaps + gaps sur n'importe quelle couche polygone.

import processing
from qgis.core import QgsProject

def audit_topo(layer, unique_id='fid', gap_threshold=0, min_overlap_area=0):
    """
    Audit topologique d'une couche polygone PLU.
    Retourne un dict avec les couches d'erreurs et un résumé.
    """
    rapport = {}

    # 1. Validité géométrique (GEOS)
    r_val = processing.run("native:checkvalidity", {
        'INPUT_LAYER': layer,   # NOTE : INPUT_LAYER, pas INPUT
        'METHOD': 2,            # GEOS
        'IGNORE_RING_SELF_INTERSECTION': False,
        'VALID_OUTPUT': 'memory:',
        'INVALID_OUTPUT': 'memory:',
        'ERROR_OUTPUT': 'memory:'
    })
    rapport['validite'] = {
        'valides': r_val['VALID_COUNT'],
        'invalides': r_val['INVALID_COUNT'],
        'erreurs': r_val['ERROR_COUNT'],
        'layer_invalides': r_val['INVALID_OUTPUT'],
        'layer_erreurs': r_val['ERROR_OUTPUT']
    }

    # 2. Overlaps
    r_ov = processing.run("native:checkgeometryoverlap", {
        'INPUT': layer,
        'UNIQUE_ID': unique_id,
        'MIN_OVERLAP_AREA': min_overlap_area,
        'TOLERANCE': 8,
        'ERRORS': 'memory:',
        'OUTPUT': 'memory:'
    })
    rapport['overlaps'] = {
        'count': r_ov['ERRORS'].featureCount(),
        'layer_errors': r_ov['ERRORS'],
        'layer_features': r_ov['OUTPUT']
    }

    # 3. Gaps
    r_gap = processing.run("native:checkgeometrygap", {
        'INPUT': layer,
        'UNIQUE_ID': unique_id,
        'GAP_THRESHOLD': gap_threshold,
        'TOLERANCE': 8,
        'NEIGHBORS': 'memory:',
        'ERRORS': 'memory:',
        'OUTPUT': 'memory:'
    })
    rapport['gaps'] = {
        'count': r_gap['OUTPUT'].featureCount(),
        'layer_gaps': r_gap['OUTPUT'],
        'layer_neighbors': r_gap['NEIGHBORS'],  # requis pour fixgeometrygap
        'layer_errors': r_gap['ERRORS']
    }

    # Résumé lisible
    print(f"=== Audit topo : {layer.name()} ({layer.featureCount()} features) ===")
    print(f"Validité  : {rapport['validite']['valides']} OK / "
          f"{rapport['validite']['invalides']} invalides")
    print(f"Overlaps  : {rapport['overlaps']['count']} erreur(s)")
    print(f"Gaps      : {rapport['gaps']['count']} trou(s)")
    ok = (rapport['validite']['invalides'] == 0 and
          rapport['overlaps']['count'] == 0 and
          rapport['gaps']['count'] == 0)
    print(f"Résultat  : {'PROPRE' if ok else 'ERREURS DETECTEES'}")

    return rapport

Usage :

from qgis.core import QgsProject
layer = QgsProject.instance().mapLayersByName("zones_plu")[0]
rapport = audit_topo(layer, unique_id='fid')

Piège critique : native:checkvalidity → paramètre INPUT_LAYER (pas INPUT). Erreur silencieuse sinon.


Bloc 3 — Correction automatique

S'appuie sur les couches d'erreurs produites par le Bloc 2.

3a. Corriger les overlaps

r_fix_ov = processing.run("native:fixgeometryoverlap", {
    'INPUT': layer,
    'ERRORS': rapport['overlaps']['layer_errors'],
    'UNIQUE_ID': 'fid',
    'OVERLAP_FEATURE_UNIQUE_IDX': 'gc_overlap_fid',  # champ produit par checkgeometryoverlap
    'ERROR_VALUE_ID': 'gc_error',
    'OUTPUT': 'memory:',
    'REPORT': 'memory:',
    'TOLERANCE': 8
})
layer_corrige = r_fix_ov['OUTPUT']
print(f"Overlaps corrigés : {r_fix_ov['REPORT'].featureCount()} opérations")

3b. Combler les gaps

3 méthodes disponibles :

Valeur Méthode Usage recommandé
0 Ajouter à la plus longue bordure en commun PLU — zone voisine qui partage le plus de frontière
1 Créer une nouvelle entité si le gap est une zone à part entière
2 Ajouter à la plus grande zone voisine quand la surface prime
r_fix_gap = processing.run("native:fixgeometrygap", {
    'INPUT': layer,
    'NEIGHBORS': rapport['gaps']['layer_neighbors'],  # issu de checkgeometrygap
    'GAPS': rapport['gaps']['layer_gaps'],
    'METHOD': 0,    # 0 = plus longue bordure (recommandé PLU)
    'UNIQUE_ID': 'fid',
    'ERROR_ID_IDX': 'gc_errorid',
    'OUTPUT': 'memory:',
    'REPORT': 'memory:',
    'TOLERANCE': 8
})
layer_sans_gap = r_fix_gap['OUTPUT']
print(f"Gaps comblés : {r_fix_gap['REPORT'].featureCount()} opérations")

3c. Corriger les géométries invalides (self-intersections, anneaux)

r_fix_val = processing.run("native:fixgeometries", {
    'INPUT': rapport['validite']['layer_invalides'],
    'METHOD': 1,    # 1 = structure linéaire (robuste)
    'OUTPUT': 'memory:'
})
layer_valide = r_fix_val['OUTPUT']

Bloc 4 — Accrochage post-saisie (snapgeometries)

Quand une couche a été saisie sans snapping actif, ou importée avec de micro-décalages entre polygones adjacents. Accroche les géométries d'une couche sur une couche de référence (ex : limites communales, autre zonage).

8 comportements disponibles :

Valeur Comportement Usage
0 Aligner les nœuds, ajoute sommets si besoin défaut recommandé
1 Point le plus proche, ajoute sommets si nœuds mal alignés
2 Aligner les nœuds, sans ajout de sommet quand on veut conserver la densité
4 Déplacer uniquement les extrémités (nœuds) micro-corrections de bord
6 Extrémités sur extrémités uniquement lignes/filaires
r_snap = processing.run("native:snapgeometries", {
    'INPUT': layer_a_corriger,
    'REFERENCE_LAYER': layer_reference,
    'TOLERANCE': 0.5,    # en unités de la couche (mètres si Lambert 93)
    'BEHAVIOR': 0,
    'OUTPUT': 'memory:'
})
layer_snapped = r_snap['OUTPUT']

Recommandation PLU : tolérance 0.1–0.5 m en EPSG:2154 (Lambert 93). Trop grande → déformation des géométries.


Bloc 5 — Reconstruction automatique depuis le cadastre

Quand une zone (ex : périmètre PLU, emprise de projet) a été saisie grossièrement et doit être recalée exactement sur les limites parcellaires cadastrales. Claude reconstruit le polygone par dissolution des parcelles qui chevauchent la zone rough ; l'opérateur complète ensuite manuellement les parties sans référence cadastrale (domaine public non-parcellaire : voiries, cours d'eau).

Validé le 2026-06-21 sur 06079-Minelle.gpkg (Mandelieu-la-Napoule) : 11 parcelles intersectantes → 2 retenues (overlap ≥ 50%) → 137 004 m², 31 nœuds, géométrie valide.

Principe

  1. Fixer les géométries du cadastre (WFS souvent invalide)
  2. Extraire les parcelles qui intersectent la zone rough
  3. Calculer le % de chevauchement pour chaque parcelle → garder celles à ≥ 50%
  4. Dissoudre → multiparttosingleparts → garder la plus grande part
  5. Écrire dans le GeoPackage en retirant d'abord les couches QGIS (file locking Windows)

Code complet

import processing
import urllib.parse
from qgis.core import (
    QgsProject, QgsVectorFileWriter, QgsWkbTypes,
    QgsFields, QgsMemoryProviderUtils
)

# Pré-requis : layer_cad (cadastre WFS reprojeté EPSG:3857)
#              layer_zone (zone rough à recaler)
#              gpkg_path (chemin du GeoPackage à écrire)

# 1. Fixer les géométries cadastrales (WFS Géoplateforme souvent invalide)
cad_fixed = processing.run("native:fixgeometries", {
    'INPUT': layer_cad,
    'METHOD': 1,    # structure linéaire, robuste
    'OUTPUT': 'memory:'
})['OUTPUT']

# 2. Extraire les parcelles qui intersectent la zone rough
parcelles_in = processing.run("native:extractbylocation", {
    'INPUT': cad_fixed,
    'PREDICATE': [0],   # intersects
    'INTERSECT': layer_zone,
    'OUTPUT': 'memory:'
})['OUTPUT']

# 3. Calculer le % de chevauchement et retenir celles ≥ 50%
geom_zone = next(layer_zone.getFeatures()).geometry()
a_inclure = []
for f in parcelles_in.getFeatures():
    inter = f.geometry().intersection(geom_zone)
    pct = inter.area() / f.geometry().area() * 100
    if pct >= 50:
        a_inclure.append(f['gid'])

print(f"{parcelles_in.featureCount()} parcelles intersectantes → "
      f"{len(a_inclure)} retenues (overlap ≥ 50%)")
print(f"GIDs retenus : {a_inclure}")

# 4. Sélectionner + dissoudre
ids_str = ', '.join(str(i) for i in a_inclure)
parcelles_sel = processing.run("native:extractbyexpression", {
    'INPUT': cad_fixed,
    'EXPRESSION': f'"gid" IN ({ids_str})',
    'OUTPUT': 'memory:'
})['OUTPUT']

dissolved = processing.run("native:dissolve", {
    'INPUT': parcelles_sel,
    'FIELD': [],
    'SEPARATE_DISJOINT': False,
    'OUTPUT': 'memory:'
})['OUTPUT']

# 5. Single part → garder la plus grande
single = processing.run("native:multiparttosingleparts", {
    'INPUT': dissolved,
    'OUTPUT': 'memory:'
})['OUTPUT']

largest = max(single.getFeatures(), key=lambda f: f.geometry().area())
print(f"Résultat : {largest.geometry().area():.0f} m², "
      f"{largest.geometry().constGet().nCoordinates()} nœuds")

layer_main = QgsMemoryProviderUtils.createMemoryLayer(
    "main", QgsFields(), QgsWkbTypes.Type.Polygon, single.crs()
)
layer_main.dataProvider().addFeature(largest)

# 6. Écrire dans le GeoPackage
# CRITIQUE : retirer toutes les couches QGIS référençant le fichier avant écriture
for name in ["06079-Minelle"]:
    for lyr in QgsProject.instance().mapLayersByName(name):
        QgsProject.instance().removeMapLayer(lyr)

options = QgsVectorFileWriter.SaveVectorOptions()
options.driverName = "GPKG"
options.layerName = "06079-Minelle"
options.actionOnExistingFile = QgsVectorFileWriter.ActionOnExistingFile.CreateOrOverwriteLayer

# writeAsVectorFormatV3 retourne un tuple de 4 valeurs
res = QgsVectorFileWriter.writeAsVectorFormatV3(
    layer_main, gpkg_path, QgsCoordinateTransformContext(), options
)
# res = (error_code, error_message, filename, layername)
if res[0] == 0:
    print(f"Écrit : {gpkg_path} → couche '{options.layerName}'")
else:
    print(f"ERREUR {res[0]} : {res[1]}")

# 7. Recharger dans QGIS pour vérification
layer_reload = QgsVectorLayer(
    f"{gpkg_path}|layername={options.layerName}",
    options.layerName, "ogr"
)
QgsProject.instance().addMapLayer(layer_reload, False)
root = QgsProject.instance().layerTreeRoot()
root.insertLayer(0, layer_reload)
iface.mapCanvas().setExtent(layer_reload.extent())
iface.mapCanvas().refresh()
print("Couche rechargée et zoomée.")

Pièges spécifiques

Sujet Constat
writeAsVectorFormatV3 retourne 4 valeurs (error_code, error_message, filename, layername) — pas 2. Déstructurer en conséquence.
File locking Windows QGIS garde un handle sur le fichier GeoPackage. Retirer toutes les couches référençant le fichier avec removeMapLayer() avant d'écrire.
CreateOrOverwriteLayer pas CreateOrOverwriteFile CreateOrOverwriteFile supprime tout le fichier (détruisant les autres couches). CreateOrOverwriteLayer ne touche qu'à la couche cible.
native:savefeatures échoue si fichier existe "A file system object already exists" — utiliser writeAsVectorFormatV3 à la place.
MultiPolygon → asPolygon() fail Si les parcelles retenues ne sont pas adjacentes, le dissolve produit un MultiPolygon. Appliquer multiparttosingleparts + max(..., key=lambda f: f.geometry().area()) pour garder la plus grande part.
Cadastre WFS invalide La Géoplateforme retourne parfois des géométries invalides (ex : feature 15026). Toujours fixer avec native:fixgeometries METHOD=1 avant usage comme couche de référence.

Workflow collaboratif

Claude reconstruit le périmètre sur les limites parcellaires exactes. L'opérateur complète manuellement les parties manquantes non-parcellaires (domaine public : voiries, cours d'eau, espaces naturels non cadastrés). Les deux résultats sont complémentaires : la précision automatique sur le cadastre + le jugement humain sur le hors-cadastre.


Routine principale — Audit + Rapport

Fonction complète prête à l'emploi. Appeler par Claude en une seule commande.

import processing
from qgis.core import QgsProject, QgsSnappingConfig, QgsTolerance

def configurer_snapping_plu():
    proj = QgsProject.instance()
    snap_cfg = proj.snappingConfig()
    snap_cfg.setEnabled(True)
    snap_cfg.setMode(QgsSnappingConfig.SnappingMode.AllLayers)
    snap_cfg.setType(QgsSnappingConfig.SnappingType.VertexAndSegment)
    snap_cfg.setTolerance(10.0)
    snap_cfg.setUnits(QgsTolerance.UnitType.Pixels)
    snap_cfg.setIntersectionSnapping(True)
    proj.setSnappingConfig(snap_cfg)
    proj.setTopologicalEditing(True)
    proj.setAvoidIntersectionsMode(
        QgsProject.AvoidIntersectionsMode.AvoidIntersectionsLayers)
    print("Snapping PLU configuré")

def audit_topo(layer, unique_id='fid', gap_threshold=0, min_overlap_area=0):
    r_val = processing.run("native:checkvalidity", {
        'INPUT_LAYER': layer, 'METHOD': 2,
        'IGNORE_RING_SELF_INTERSECTION': False,
        'VALID_OUTPUT': 'memory:', 'INVALID_OUTPUT': 'memory:', 'ERROR_OUTPUT': 'memory:'
    })
    r_ov = processing.run("native:checkgeometryoverlap", {
        'INPUT': layer, 'UNIQUE_ID': unique_id,
        'MIN_OVERLAP_AREA': min_overlap_area, 'TOLERANCE': 8,
        'ERRORS': 'memory:', 'OUTPUT': 'memory:'
    })
    r_gap = processing.run("native:checkgeometrygap", {
        'INPUT': layer, 'UNIQUE_ID': unique_id,
        'GAP_THRESHOLD': gap_threshold, 'TOLERANCE': 8,
        'NEIGHBORS': 'memory:', 'ERRORS': 'memory:', 'OUTPUT': 'memory:'
    })
    rapport = {
        'layer': layer,
        'unique_id': unique_id,
        'validite': {
            'valides': r_val['VALID_COUNT'],
            'invalides': r_val['INVALID_COUNT'],
            'layer_invalides': r_val['INVALID_OUTPUT']
        },
        'overlaps': {
            'count': r_ov['ERRORS'].featureCount(),
            'layer_errors': r_ov['ERRORS'],
            'layer_features': r_ov['OUTPUT']
        },
        'gaps': {
            'count': r_gap['OUTPUT'].featureCount(),
            'layer_gaps': r_gap['OUTPUT'],
            'layer_neighbors': r_gap['NEIGHBORS'],
            'layer_errors': r_gap['ERRORS']
        }
    }
    ok = (r_val['INVALID_COUNT'] == 0 and
          r_ov['ERRORS'].featureCount() == 0 and
          r_gap['OUTPUT'].featureCount() == 0)
    print(f"\n=== Audit topo : {layer.name()} ===")
    print(f"  Géométries invalides : {r_val['INVALID_COUNT']}")
    print(f"  Overlaps             : {r_ov['ERRORS'].featureCount()}")
    print(f"  Gaps                 : {r_gap['OUTPUT'].featureCount()}")
    print(f"  => {'PROPRE' if ok else 'ERREURS — voir rapport'}")
    return rapport

# Usage type
# layer = QgsProject.instance().mapLayersByName("zones_plu")[0]
# configurer_snapping_plu()
# rapport = audit_topo(layer)

Pièges à retenir

Sujet Constat
INPUT_LAYER vs INPUT native:checkvalidity exige INPUT_LAYER, tous les autres utilisent INPUT. Erreur silencieuse sinon.
QgsSnappingConfig.SnappingType setType() attend ce type précis — pas Qgis.SnappingType ni Qgis.SnappingTypes.
NEIGHBORS obligatoire pour fixgeometrygap La couche NEIGHBORS produite par checkgeometrygap doit être conservée et passée à fixgeometrygap.
Tolérance snapgeometries en unités couche Si la couche est en Lambert 93 (mètres), 0.5 = 50 cm. Ne pas confondre avec la tolérance en pixels du snapping canvas.
METHOD 0 pour PLU gaps "Plus longue bordure en commun" est la méthode la plus cohérente pour les zonages PLU (le gap va à la zone avec laquelle il partage le plus de frontière).
AvoidIntersectionsLayers Mode le plus adapté PLU — évite les overlaps lors de la saisie mais uniquement sur les couches cochées dans les paramètres du projet.
writeAsVectorFormatV3 retourne 4 valeurs (error_code, error_message, filename, layername) — pas 2.
File locking GeoPackage Windows Retirer toutes les couches QGIS référençant le fichier avant écriture, utiliser CreateOrOverwriteLayer.

Statut

Competences Claude Libre Office

04_Claude et le PC Windows Elio+Jux

04_Claude et le PC Windows Elio+Jux

260803-suivi des logs de dysfonctionnement du PC - écrans bleus de la mort

Suivi des dysfonctionnements du PC eliob — écrans bleus

Machine : PC Windows eliob (pcelio) Analyse initiale : 2026-08-03 — Claude Code Fenêtre observée : 29/10/2025 → 03/08/2026 pour le journal d'événements (Windows réinstallé le 28/10/2025) — les compteurs SMART, eux, couvrent toute la vie des disques

En bref

Mise à jour du 2026-08-03, 08h30 — après élévation de privilèges, les rapports WER ont pu être lus et désignent nommément le module fautif : amdfendr.sys (AMD Crash Defender). Le pilote réseau VirtualBox, suspect n°1 de la première version de cette analyse, est disculpé comme cause des écrans bleus. Voir §6.

Mise à jour du 2026-08-03, 09h00 — WinDbg et smartmontools installés. Le vidage mémoire a été analysé : la pile d'appels confirme amdfendr.sys et donne le mécanisme exact du plantage (§6.1). Les données SMART complètes des 4 disques ont été relevées : aucun n'est défaillant, mais le compteur d'arrêts brutaux du SSD système révèle que l'instabilité est bien antérieure à la réinstallation de Windows (§7).


1. Configuration matérielle

Élément Valeur
Carte mère MSI A320M-A PRO (MS-7C51), BIOS AMI 1.40 du 08/12/2020
Processeur AMD Ryzen 5 2600X — 6 cœurs, 3,6 GHz
Carte graphique AMD Radeon RX 6500 XT (4 Go) — pilote 30.0.14023.3004 du 18/01/2022, soit 4 ans et demi de retard
Mémoire 1 seule barrette 16 Go TEAMGROUP UD4-2666 (DIMM 0, canal B) — cadencée à 2400 MHz (sous sa fréquence nominale de 2666)
Disque système SSD SATA Patriot Burst 240 Go (C: — 71 Go libres)
Disque secondaire HDD SATA Toshiba HDWD110 1 To (D:)
Périphériques USB ASMT 2115 1 To (E:), Seagate Backup+ Hub 8 To (F:), SanDisk 3.2Gen1 64 Go
OS Windows 10 Famille 22H2 — build 19045, installé le 28/10/2025
Boîtier / assemblage Megaport, modèle 130430

Remarque sur l'âge : Julien évalue le PC à 9 ans d'usage intensif. Le Ryzen 2600X date de 2018 et la carte A320M-A PRO de 2019-2020 — le cœur de la machine a donc plutôt 6-7 ans. Le boîtier, l'alimentation et les ventilateurs peuvent être plus anciens s'ils ont été repris d'une configuration précédente. L'alimentation n'a pas pu être identifiée par logiciel — c'est une information à relever physiquement (marque, modèle, wattage, année), elle est déterminante pour la suite du diagnostic.


2. Méthode

Sources exploitées

Sans privilèges particuliers :

Avec élévation (c'est ce qui a permis d'aboutir) :

Outils installés le 2026-08-03

Outil Version Usage
smartmontools 7.5 C:\Program Files\smartmontools\bin\smartctl.exe — SMART réel, à lancer en administrateur
Microsoft.WinDbg 1.2606 Expose de vrais alias console dans %LOCALAPPDATA%\Microsoft\WindowsApps : cdbX64.exe, kdX64.exe, WinDbgX.exe

Analyse d'un vidage en ligne de commande :

cdbX64.exe -z dump.dmp -y "srv*C:\symbols*https://msdl.microsoft.com/download/symbols" -c "!analyze -v; q" -logo sortie.txt

Trois points de méthode qui ont compté

  1. Les événements 41 et 1001 sont horodatés au redémarrage suivant, pas à l'instant du crash. Le redémarrage automatique étant activé (AutoReboot=1), les deux coïncident à la minute près pour les BSOD — mais il ne faut jamais lire ces horodatages comme « l'heure de la panne » sans cette vérification.
  2. Une corrélation temporelle, même serrée, ne vaut pas une preuve. Trois crashes à 12 secondes d'une erreur VBoxNetLwf m'ont fait désigner le mauvais coupable. Seule la lecture du vidage a tranché.
  3. Les preuves les plus utiles sont derrière une élévation de privilèges. Tant qu'on lit le journal en session standard, on tourne autour du problème.

3. Chiffres clés

Indicateur Valeur
Arrêts anormaux (Kernel-Power 41) 45 en 9 mois
dont écrans bleus réels (avec code bugcheck) 15 (33 %)
dont coupures sèches sans BSOD (BugcheckCode = 0) 30 (67 %)
Erreurs matérielles WHEA-Logger 0
Erreurs disque / NTFS critiques (90 j) 2 Ntfs 50, 3 volmgr 161 (échec d'écriture du dump)
Bouton d'alimentation maintenu (arrêt forcé manuel) 1 seul cas — 20/05/2026 20:35
Arrêt propre demandé (1074/13) juste avant le crash 0 cas sur 45
Arrêts brutaux au compteur SMART du SSD système 146 pour 1 066 mises sous tension (13,7 %)

Fréquence : environ 1 arrêt anormal tous les 6 jours en moyenne, avec des grappes (2 crashes le même jour à 5 reprises).

Écart entre les deux compteurs : 146 arrêts brutaux au SMART contre 45 dans le journal Windows. La centaine manquante est antérieure à la réinstallation du 28/10/2025 — voir §7.


4. Deux populations de pannes bien distinctes

Population A — 30 coupures sèches, sans écran bleu (67 %)

BugcheckCode = 0, PowerButtonTimestamp = 0 : le système s'est arrêté sans produire de vérification d'erreur, sans que le bouton d'alimentation ait été maintenu, et sans qu'un arrêt ait été demandé. Le dernier événement journalisé précède le redémarrage de moins d'une minute dans la quasi-totalité des cas.

C'est le profil d'une coupure d'alimentation, d'un reset matériel ou d'un gel total du noyau — Windows n'a pas eu le temps d'écrire quoi que ce soit.

Ces 30 événements sont répartis sur toute la période (octobre 2025 → juillet 2026), sans lien avec les vagues de BSOD. C'est la population la plus préoccupante pour une hypothèse matérielle, et c'est aussi celle sur laquelle les logs sont, par construction, muets.

Population B — 15 écrans bleus avec code d'erreur (33 %)

Code Nb Nom Signification
0x0000009F 10 DRIVER_POWER_STATE_FAILURE Un pilote bloque une requête de changement d'état d'alimentation (mise en veille, reprise, arrêt). Paramètre 1 = 0x3 dans les 10 cas : un objet de périphérique bloque une IRP trop longtemps.
0x00000050 3 PAGE_FAULT_IN_NONPAGED_AREA Accès à une adresse mémoire invalide. Paramètre 2 = 0 → il s'agit d'une lecture (confirmé par WinDbg : AV.Type = Read).
0x0000003B 1 SYSTEM_SERVICE_EXCEPTION Exception 0xC0000005 (violation d'accès) en mode noyau.
0x000000A0 1 INTERNAL_POWER_ERROR Erreur interne du gestionnaire d'alimentation.

12 des 15 BSOD (80 %) sont des erreurs liées à une transition d'alimentation (0x9F + 0xA0). Aucun n'est précédé d'une demande d'arrêt propre → il s'agit de transitions veille S3 / reprise / démarrage rapide, pas d'un arrêt lancé depuis le menu Démarrer.


5. Chronologie — trois régimes successifs

Période Régime dominant
29/10/2025 → 23/12/2025 Uniquement des coupures sèches (2 événements)
25/12/2025 → 29/03/2026 Vague de 10 × 0x9F — DRIVER_POWER_STATE_FAILURE, souvent 2 le même jour, entremêlée de coupures sèches
16/04/2026 → 19/05/2026 1 × 0xA0, 1 × 0x3B — puis les BSOD cessent
19/05/2026 → 28/07/2026 Retour aux seules coupures sèches (7 événements)
31/07/2026 → 03/08/2026 Nouvelle vague : 3 × 0x50 en 4 jours

Les BSOD ne sont donc pas un phénomène continu : ce sont des vagues qui apparaissent et disparaissent, ce qui est le comportement typique d'une cause logicielle (installation ou mise à jour d'un pilote). Les coupures sèches, elles, sont présentes en continu du premier au dernier jour — comportement typique d'une cause matérielle.


6. Cause de la vague en cours : amdfendr.sys (AMD Crash Defender)

6.1 — La preuve : l'analyse du vidage mémoire

!analyze -v sur C:\Windows\Minidump\073126-9718-01.dmp (crash du 31/07/2026 08:54) donne la pile d'appels complète :

nt!KeBugCheckEx
nt!MiSystemFault
nt!MmAccessFault
nt!KiPageFault
nt!LZNT1FindMatchStandard+0xce      <-- instruction fautive
nt!LZNT1CompressChunk+0xd7
nt!RtlCompressBufferLZNT1+0x80
nt!RtlCompressBuffer+0x6f
amdfendr+0x13e1b                    <-- L'APPELANT
MODULE_NAME:        amdfendr
IMAGE_NAME:         amdfendr.sys
FAILURE_BUCKET_ID:  AV_amdfendr!unknown_function
AV.Type:            Read
AV.Page.Virtual:    0xffff830288400000
Adresse fautive:    0xffff830288401000

Le mécanisme est limpide. amdfendr.sys appelle RtlCompressBuffer, la fonction de compression du noyau Windows, en lui passant un tampon dont la taille annoncée dépasse la taille réellement allouée. Le compresseur LZNT1 lit donc au-delà de la fin du tampon, tombe sur une page non mappée, et le noyau plante.

L'adresse fautive …88401000 est exactement à 0x1000 octets (une page mémoire) du début de la page valide …88400000. C'est la signature manuelle du dépassement de tampon : la lecture a franchi la frontière de page juste après la fin de la zone légitime.

Correction d'un point technique de la première version : le déplacement constant 0x3ae que j'avais relevé sur les trois écrans bleus se situe dans ntoskrnl (nt!LZNT1FindMatchStandard+0xce), pas dans amdfendr.sys. C'est pour cette raison qu'il est identique d'un crash à l'autre : c'est toujours la même fonction du noyau qui est mise en défaut. L'intuition — « le même chemin de code plante à chaque fois » — était juste ; son attribution à un pilote tiers ne l'était pas.

Notons enfin que l'horodatage réel du binaire est le 4 juin 2021 — encore plus ancien que sa date de fichier.

6.2 — Confirmation par les rapports WER

Après élévation de privilèges, les deux rapports d'erreur Windows du 31/07/2026 ont pu être lus. Ils contiennent le verdict de Microsoft :

EventType            = BlueScreen
Response.BucketId    = AV_amdfendr!unknown_function
Sig[0] Code          = 50
Sig[3] Paramètre 3   = fffff8031db903ae   /   fffff805091903ae
Sig[4] Paramètre 4   = 2

AV_amdfendr!unknown_function : Access Violation imputée au module amdfendr.sys. Les deux rapports du 31/07, produits indépendamment, aboutissent au même classement — et au même que l'analyse du vidage ci-dessus. Trois déterminations convergentes.

6.3 — Ce qu'est ce pilote

Fichier Description Version Date
amdfendr.sys AMD Crash Defender 21.30.0.9 07/02/2022
amdfendrmgr.sys AMD Crash Defender Manager Driver 21.30.0.9 07/02/2022

AMD Crash Defender est un composant des pilotes Radeon Adrenalin de la génération 21.30 / 22.x. Son rôle est d'intercepter les plantages du pilote graphique pour tenter de rétablir l'affichage sans redémarrer. Autrement dit : c'est le mécanisme censé éviter les écrans bleus qui les provoque.

Ce composant est connu pour être instable dans cette génération de pilotes. AMD l'a profondément remanié dans les versions ultérieures.

Le pilote graphique de cette machine date du 18/01/2022 (version 30.0.14023.3004) pour une Radeon RX 6500 XT — soit quatre ans et demi sans mise à jour, alors que la carte est toujours pleinement prise en charge par les pilotes Adrenalin actuels.

Le pilote graphique est également un candidat classique pour la famille 0x9F (DRIVER_POWER_STATE_FAILURE) : c'est typiquement lui qui bloque une transition de mise en veille ou de reprise. La vague de 10 × 0x9F de l'hiver 2025-2026 pourrait donc relever de la même origine — à confirmer par l'analyse des vidages.

6.4 — Anomalie secondaire : le pilote réseau VirtualBox

VBoxNetLwf — VirtualBox NDIS6 Bridged Networking Driver, version 7.2.14.174565

Le journal System contient 20 erreurs VBoxNetLwf (ID 12) : « Le pilote a détecté une erreur de pilote interne sur \Device\VBoxNetLwf ».

Toutes sont comprises entre le 24/07/2026 06:55 et le 03/08/2026 07:43 — soit exactement depuis l'installation d'Oracle VirtualBox 7.2.14 le 24/07/2026 (fichiers pilotes datés du 17/07/2026). Aucune avant.

Le rythme est de deux erreurs par jour — une le matin, une le soir :

Date Matin Soir
24/07 06:55 18:02
27/07 08:02 18:22
29/07 06:27 17:36
30/07 07:25 19:10
02/08 06:36 21:54

C'est-à-dire à chaque allumage et à chaque extinction — exactement le symptôme décrit par Julien.

Corrélation avec les 3 écrans bleus 0x50 :

Erreur VBoxNetLwf Écran bleu correspondant Écart
31/07/2026 06:27:21 BugCheck 0x50 à 06:27:34 13 s
31/07/2026 08:54:01 BugCheck 0x50 à 08:54:14 13 s
03/08/2026 07:43:26 BugCheck 0x50 à 07:43:37 11 s

Et le 28/07/2026 07:52:02, une erreur VBoxNetLwf coïncide à la seconde près avec une coupure sèche sans BSOD.

Verdict sur VBoxNetLwf : la corrélation temporelle est réelle, mais elle ne fait pas de lui la cause des écrans bleus. Les rapports WER placent le code fautif dans amdfendr.sys. L'explication cohérente est que les deux pilotes sont sollicités à la même seconde, lors de la même transition d'alimentation : VBoxNetLwf y journalise une erreur interne à chaque fois (20 fois), et amdfendr.sys y plante occasionnellement (3 fois).

VBoxNetLwf reste néanmoins un défaut logiciel réel à traiter : un filtre NDIS qui signale une erreur interne à chaque démarrage et à chaque extinction n'est pas un fonctionnement normal. Il a été désactivé (voir §10). Mais il faut être clair : ce n'est pas lui qui provoquait les écrans bleus.

Leçon de méthode : une corrélation temporelle à 12 secondes sur 3 événements, aussi frappante soit-elle, ne vaut pas une preuve. C'est la lecture du rapport WER — qui a nécessité une élévation de privilèges — qui a tranché. La constance du déplacement 0x3ae était bien la bonne piste ; c'est son attribution à VBoxNetLwf qui était fausse.

6.5 — Deux attributions antérieures invalidées

RustDesk — le fichier CLAUDE.md attribuait les écrans bleus 0x50 du 31/07/2026 aux pilotes RustDesk (écran virtuel usbmmidd_v2, imprimante virtuelle), désactivés le jour même. Cette explication ne tient pas : un troisième 0x50 s'est produit le 03/08 avec la même signature, alors que usbmmidd_v2 était désactivé depuis trois jours. Le dossier C:\Program Files\RustDesk\drivers — qui ne contenait déjà plus qu'un RustDeskPrinterDriver.disabled — a été renommé en drivers.disabled par sécurité.

VirtualBox — voir ci-dessus. Suspect n°1 de la première version de cette page, disculpé par les rapports WER.

Ces deux erreurs ont la même origine : trois logiciels installés dans la même fenêtre de temps (VirtualBox le 24/07, RustDesk le 31/07) ont capté l'attention, alors que le vrai coupable était un pilote présent depuis longtemps et sans rapport avec ces installations.


7. Ce que les logs ne permettent PAS d'accuser

Il faut être clair sur ce point : à ce stade, aucune donnée ne désigne un composant électronique défectueux.

Vérification Résultat Interprétation
WHEA-Logger (erreurs machine remontées par le firmware : CPU, mémoire, bus PCIe, cache) 0 événement sur 9 mois Aucune erreur matérielle corrigible ou fatale détectée par le processeur. C'est un signal négatif fort contre une défaillance CPU ou mémoire franche.
État de santé des 5 disques Tous Healthy / OK Aucune alerte
Erreurs disque / NTFS 2 Ntfs 50 + 3 volmgr 161 en 90 jours Les volmgr 161 sont des conséquences des crashes (échec d'écriture du fichier de vidage), pas des causes

Nuance importante sur WHEA : l'absence d'erreur WHEA ne disculpe pas le matériel pour la Population A. Une coupure d'alimentation brutale ou un gel total ne laisse par définition aucune trace, puisque le système n'a plus le temps d'écrire. WHEA ne couvre pas non plus les défaillances d'alimentation, de VRM ou de connectique.

SMART complet — relevé du 2026-08-03 via smartmontools 7.5

Windows n'exposait aucun compteur SMART sur ce chipset A320 (Get-StorageReliabilityCounter ne renvoie rien). smartctl a permis de lire directement les contrôleurs.

Disque Rôle Santé Heures Cycles Secteurs réalloués Erreurs CRC Temp. Usure
Patriot Burst 240 Go (SSD) Système (C:) PASSED 11 111 1 066 0 0 33 °C SSD_Life_Left = 88 % — 15 To écrits
Toshiba HDWD110 1 To (P300 CMR) Données (D:) PASSED 14 481 1 416 0 0 41 °C (max 49)
Seagate/Samsung ST1000LM024 1 To Boîtier USB (E:) PASSED 60 187 5 800 0 0 39 °C (max 56)
Seagate ST8000AS0002 8 To (SMR) Sauvegarde USB (F:) PASSED 19 823 2 166 0 0 46 °C (seuil 45 dépassé par le passé)

Conclusion : aucun disque n'est défaillant. Zéro secteur réalloué, zéro secteur en attente, zéro erreur CRC, aucun journal d'erreurs SMART sur les quatre. Le SSD système conserve 88 % de sa durée de vie et n'a que 15 To d'écriture au compteur — il est hors de cause. L'hypothèse « SSD en fin de vie » est écartée.

Deux points annexes : le disque du boîtier USB affiche 60 187 heures, soit près de 7 ans d'allumage cumulé — sans le moindre défaut, mais il mérite une surveillance. Et le Seagate 8 To de sauvegarde a franchi son seuil de température de flux d'air (46 °C pour un seuil à 45 °C) : à surveiller, sans rapport avec les plantages.

⚠ Le compteur qui change la perspective

Unsafe_Shutdown_Count du SSD système : 146 arrêts brutaux pour 1 066 mises sous tension — soit 13,7 % des extinctions qui se sont mal passées.

Or le journal d'événements ne recense que 45 arrêts anormaux depuis octobre 2025. Une centaine d'arrêts brutaux sont donc antérieurs à la réinstallation de Windows du 28/10/2025.

C'est une information importante : le problème n'est ni récent, ni né de la réinstallation. Il est installé de longue date, et la réinstallation de Windows ne l'a pas réglé — ce qui affaiblit d'autant les hypothèses purement logicielles pour la Population A, et renforce la piste matérielle.


8. Facteurs aggravants identifiés dans la configuration

Démarrage rapide — était activé, ✅ désactivé le 2026-08-03

Le démarrage rapide (Fast Startup, HiberbootEnabled = 1) était actif jusqu'au 03/08/2026. Avec ce réglage, « Arrêter » n'éteint pas réellement Windows : le noyau et les pilotes sont mis en hibernation dans un fichier, puis rechargés tels quels au démarrage suivant.

Conséquences sur ce cas :

  1. C'est la première cause connue des écrans bleus 0x9F — qui représentent 10 des 15 BSOD relevés ici.
  2. Cela explique qu'aucun crash ne soit précédé d'un arrêt propre dans les logs : les transitions concernées sont des hibernations/reprises, pas de vrais arrêts.
  3. Un état de pilote corrompu était réinjecté à chaque démarrage au lieu d'être réinitialisé — ce qui transforme un bug ponctuel en panne récurrente.

Depuis la désactivation, chaque arrêt réinitialise complètement l'état des pilotes. Effet de bord attendu : le démarrage est un peu plus lent — c'est normal et souhaitable ici.

Mémoire : une seule barrette, sous-cadencée

Un seul module de 16 Go occupant DIMM 0 (canal B) — donc pas de double canal, et la barrette tourne à 2400 MHz au lieu de 2666. Ce n'est pas une cause de panne en soi, mais c'est une configuration à connaître. Point positif pour le diagnostic : avec une seule barrette, un test mémoire est simple à interpréter et il n'y a pas de problème d'appairage possible.

BIOS de 2020

Version 1.40 datée du 08/12/2020, jamais mise à jour depuis. MSI a publié des révisions ultérieures pour l'A320M-A PRO, comportant des correctifs AGESA sur la gestion de l'alimentation et la compatibilité mémoire — directement pertinents pour des erreurs 0x9F et 0xA0.

Pilote graphique de 2022 — le facteur central

Pilote Radeon 30.0.14023.3004 du 18/01/2022 sur une RX 6500 XT, alors que la carte est toujours pleinement prise en charge par les Adrenalin actuels. C'est ce pilote qui embarque le amdfendr.sys fautif (binaire du 4 juin 2021). Plus qu'un facteur aggravant : c'est la cause identifiée des 0x50, et un suspect sérieux pour les 0x9F.

Espace disque système

71 Go libres sur 223 Go (C:) — suffisant, mais à surveiller : MEMORY.DMP occupe 2,4 Go, et sa copie de sauvegarde 2,31 Go de plus sur D:. Avec AlwaysKeepMemoryDump = 1 désormais actif, le vidage complet ne sera plus supprimé automatiquement par le nettoyage de disque — c'est voulu, mais cela demande de surveiller l'espace libre.


9. Diagnostic de synthèse

Il y a très probablement deux problèmes distincts, pas un seul.

Problème 1 — logiciel, identifié formellement. amdfendr.sys (AMD Crash Defender, version 21.30.0.9 de février 2022) provoque les écrans bleus 0x50. Les rapports WER de Windows le désignent nommément, deux fois indépendamment (AV_amdfendr!unknown_function), et la signature d'adresse constante (0x3ae) confirme un défaut de code reproductible. Il est embarqué dans un pilote Radeon de janvier 2022, jamais mis à jour depuis. Le démarrage rapide amplifiait le phénomène. Ce même pilote est un candidat sérieux pour la vague de 0x9F de l'hiver. C'est traitable immédiatement et sans risque : il suffit d'installer un pilote AMD à jour.

Problème 2 — cause encore indéterminée, présent en continu depuis au moins octobre 2025. 30 coupures sèches réparties sur 9 mois, sans aucune trace exploitable. La vague de 10 × 0x9F de l'hiver 2025-2026 relève probablement aussi d'un pilote (autre que VirtualBox, installé bien plus tard), mais les coupures sèches restent inexpliquées. Les hypothèses ouvertes, par ordre de vraisemblance sur une machine de cet âge :

Le SMART apporte ici un élément décisif : avec 146 arrêts brutaux pour 1 066 démarrages, dont une centaine antérieurs à la réinstallation de Windows, le phénomène est ancien et a survécu à une remise à zéro complète du système. Hypothèses ouvertes, par ordre de vraisemblance :

  1. Alimentation vieillissante — condensateurs fatigués, tension instable sous charge. C'est l'hypothèse n°1 pour des coupures nettes sans trace, c'est le composant le plus âgé et le plus sollicité, et c'est cohérent avec un problème qui traverse les réinstallations de Windows.
  2. Thermique — pâte thermique sèche après 6-7 ans, ventilateur de CPU encrassé, coupure de protection.
  3. Connectique / oxydation — barrette mémoire, connecteurs d'alimentation, câbles SATA.
  4. Mémoire — moins probable (aucune erreur WHEA), mais non écartée tant qu'un test complet n'a pas été passé.
  5. SSD systèmeécarté : SMART impeccable, 88 % de durée de vie restante, aucune erreur.

10. Actions recommandées, par ordre de priorité

✅ Déjà appliqué le 2026-08-03 à 08h29

# Action Résultat
1 Filtre VirtualBox NDIS6 Bridged Networking désactivé sur les cartes Ethernet et Ethernet 2 Enabled = False sur les deux
2 Démarrage rapide désactivé (HiberbootEnabled : 1 → 0)
3 Mini-vidages portés à 50 (MinidumpsCount : 5 → 50) et AlwaysKeepMemoryDump = 1 ✅ Le vidage complet ne sera plus supprimé par le nettoyage de disque
4 C:\Program Files\RustDesk\drivers renommé en drivers.disabled ✅ (ne contenait déjà plus qu'un RustDeskPrinterDriver.disabled)
5 MEMORY.DMP du crash du 03/08 sauvegardéD:\MEMORY_20260803_0743_bugcheck50.DMP (2,31 Go) ✅ La preuve est préservée du prochain écrasement

Priorité 1 — l'action décisive

# Action Objectif
6 Réinstaller proprement le pilote AMD Radeon. Télécharger l'Adrenalin actuel pour RX 6500 XT sur le site AMD, désinstaller l'ancien avec DDU (Display Driver Uninstaller) en mode sans échec, puis installer le nouveau. Choisir une installation minimale/personnalisée, sans les composants optionnels. Traite la cause identifiée. Remplace amdfendr.sys 21.30.0.9 (2022) par une version où AMD Crash Defender a été remanié. Susceptible de régler à la fois les 0x50 et les 0x9F.

Pourquoi DDU et pas une simple mise à jour : l'installateur AMD ne remplace pas systématiquement les pilotes de la génération 21.30 et peut laisser amdfendr.sys en place. DDU garantit une table rase.

Priorité 2 — confirmer et combler les angles morts

# Action Objectif
7 Installer WinDbg et analyser le vidage Fait le 03/08 à 09h00 — voir §6.1. Verdict confirmé et mécanisme établi
8 Installer smartmontools et relever le SMART Fait le 03/08 à 09h00 — voir §7. Aucun disque défaillant
9 Test mémoire complet : MemTest86 sur clé USB, au moins 4 passes complètes, de préférence une nuit entière. L'outil intégré à Windows est insuffisant. Écarte ou confirme la RAM de façon définitive. Simple ici : une seule barrette.
10 Mettre à jour le BIOS MSI A320M-A PRO (actuel : 1.40 de 2020). Correctifs AGESA sur la gestion d'alimentation, directement liés aux 0x9F/0xA0

Priorité 3 — intervention physique, pour la Population A

À n'engager qu'après avoir traité le pilote AMD et laissé passer deux ou trois semaines de relevés : si les coupures sèches s'arrêtent aussi, cette section devient inutile.

# Action Objectif
11 Relever les références de l'alimentation (marque, modèle, wattage, année). Information manquante et déterminante
12 Dépoussiérer : ventilateur et radiateur CPU, ventilateur de la carte graphique, ventilateurs de boîtier, filtres, bloc d'alimentation. Cause thermique
13 Réextraire et réinsérer la barrette mémoire et les connecteurs d'alimentation ATX 24 broches et EPS 8 broches. Nettoyer les contacts. Cause connectique / oxydation
14 Refaire la pâte thermique du processeur si elle n'a pas été changée depuis l'assemblage. Cause thermique
15 Surveiller les températures et les tensions en fonctionnement (HWiNFO64), en particulier les rails 12 V, 5 V et 3,3 V sous charge. Détecte une alimentation qui décroche
16 Si les coupures sèches persistent après tout ce qui précède : tester avec une autre alimentation. Test décisif de l'hypothèse n°1. À noter : la RX 6500 XT est peu gourmande (~107 W), mais une alimentation fatiguée décroche sur les pics de la carte graphique

11. Limites de cette analyse

À mentionner pour que les conclusions soient lues à leur juste valeur :


12. Journal des relevés

Un relevé toutes les 1 à 2 semaines suffit à mesurer l'effet des actions engagées. Le script ajoute automatiquement une ligne à ce tableau :

powershell -ExecutionPolicy Bypass -File "D:\Syncthing\Jux_univers\Jux-scripts\PC-Health\collect_bsod.ps1"

À lancer en administrateur pour que les compteurs SMART soient lus ; sinon la ligne est écrite sans eux. L'option -DryRun affiche le résultat sans rien écrire dans BookStack. Le script relève les arrêts anormaux, les codes bugcheck, les erreurs VBoxNetLwf, les erreurs WHEA, la version du pilote AMD (pour dater sa réinstallation), et signale tant que amdfendr 21.30 est en place.

La colonne « Actions faites depuis » est à compléter à la main.

Date du relevé Arrêts anormaux depuis le dernier relevé dont BSOD Codes rencontrés Erreurs VBoxNetLwf WHEA Actions faites depuis Observations
2026-08-03 45 (cumul 9 mois) 15 10×9F, 3×50, 1×3B, 1×A0 20 0 — (état initial) Analyse initiale. Deux populations distinctes. Cause des 0x50 établie par analyse du vidage : amdfendr.sys déborde un tampon sur RtlCompressBuffer. Actions 1 à 5 + 7 + 8 faites. SMART : 4 disques sains, mais 146 arrêts brutaux au compteur du SSD → problème antérieur à la réinstallation.

Référence — état au 2026-08-03 : SSD système 11 111 h / 1 066 cycles / 146 arrêts brutaux / 88 % de vie restante. Comparer Unsafe_Shutdown_Count d'un relevé à l'autre donne un compteur de coupures indépendant du journal Windows :

& 'C:\Program Files\smartmontools\bin\smartctl.exe' -A /dev/pd0

13. Indicateur de succès

Le suivi doit permettre de trancher entre les deux problèmes :

Point de vigilance sur la carte graphique : c'est le composant qui concentre désormais le plus de signaux. Si des artefacts visuels, des écrans noirs momentanés ou des gels pendant un jeu ou une vidéo apparaissent, il faut le noter dans le journal — ce serait l'indice d'un défaut matériel de la RX 6500 XT.

Deuxième indicateur, indépendant du journal Windows : l'écart de Unsafe_Shutdown_Count entre deux relevés. Il compte les coupures réelles même quand Windows n'a rien pu journaliser — donc il capte la Population A, celle qui est invisible autrement. C'est le chiffre à surveiller pour trancher la question matérielle.


14. Fichiers et emplacements

Quoi
Script de relevé D:\Syncthing\Jux_univers\Jux-scripts\PC-Health\collect_bsod.ps1 (Syncthing, disponible sur toutes les machines)
Vidage du crash du 03/08, sauvegardé D:\MEMORY_20260803_0743_bugcheck50.DMP (2,31 Go)
Mini-vidage exploitable du 31/07 C:\Windows\Minidump\073126-9718-01.dmp (1,1 Mo) — accès administrateur
Vidages suivants C:\Windows\Minidump\ — 50 conservés désormais (au lieu de 5)
smartctl C:\Program Files\smartmontools\bin\smartctl.exe
Débogueur console cdbX64.exe (dans le PATH via %LOCALAPPDATA%\Microsoft\WindowsApps)
Contexte projet CLAUDE.md du dépôt Claude-pcelio+jux, section « Stabilité du PC Windows eliob »

Page tenue à jour par Claude Code. Analyse initiale et investigation complète le 2026-08-03.