<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/"><channel><title>Arthur Perrot — Journal complet</title><link>https://perrot.pt/fr/posts/</link><description>Tous les articles — retours d'expérience terrain, blue team, homelab et automatisation.</description><generator>Hugo</generator><language>fr</language><copyright>© 2026 Arthur Perrot</copyright><lastBuildDate>Fri, 23 Oct 2026 12:00:00 +0000</lastBuildDate><atom:link href="https://perrot.pt/fr/categories/sysadmin/index.xml" rel="self" type="application/rss+xml"/><item><title>Spotify ne démarre plus après un changement de hostname (Linux)</title><link>https://perrot.pt/fr/posts/spotify-ne-demarre-plus-apres-un-changement-de-hostname/</link><pubDate>Fri, 23 Oct 2026 12:00:00 +0000</pubDate><dc:creator>Arthur Perrot</dc:creator><category>Admin. sys</category><category>Astuces rapides</category><guid isPermaLink="true">https://perrot.pt/fr/posts/spotify-ne-demarre-plus-apres-un-changement-de-hostname/</guid><description>Spotify ne s'ouvre plus et quitte avec le code 1 après un changement de hostname : un verrou Singleton* orphelin, et comment le supprimer.</description><content:encoded><![CDATA[<h2 id="le-symptôme">Le symptôme</h2>
<p>Spotify (paquet <code>.deb</code> officiel, <code>spotify-client</code> 1.2.95, Debian 13) ne s&rsquo;ouvre plus. Aucune fenêtre, aucun message. Depuis le lanceur, rien ne se passe. Depuis un terminal :</p>
<div class="codeblock"><span class="codelang mono" aria-hidden="true">console</span><button type="button" class="codecopy mono" data-done="Copié !" aria-label="Copier">Copier</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="gp">$</span> spotify
</span></span><span class="line"><span class="cl"><span class="gp">$</span> <span class="nb">echo</span> <span class="nv">$?</span>
</span></span><span class="line"><span class="cl"><span class="go">1
</span></span></span></code></pre></div></div>
<p>Avec <code>--enable-logging=stderr</code>, une seule ligne sort :</p>
<div class="codeblock"><span class="codelang mono" aria-hidden="true">text</span><button type="button" class="codecopy mono" data-done="Copié !" aria-label="Copier">Copier</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">INFO:cef/libcef/common/crash_reporting.cc:209] Crash reporting enabled for process: browser</span></span></code></pre></div></div>
<p>Puis le processus se termine avec le code 1.</p>
<h2 id="fausses-pistes">Fausses pistes</h2>
<ul>
<li><strong>Le GPU</strong> : <code>--disable-gpu</code>, <code>--in-process-gpu</code>, <code>--ozone-platform=x11|wayland</code>, <code>--use-gl=egl</code> ne changent rien, le code de sortie reste 1.</li>
<li><strong>Le paquet</strong> : aucune mise à jour récente de Spotify, le binaire est intact.</li>
<li><strong>La config</strong> : <code>~/.config/spotify/prefs</code> est lisible et cohérent.</li>
</ul>
<p>Un test avec un profil vierge (<code>XDG_CONFIG_HOME</code> et <code>XDG_CACHE_HOME</code> pointés vers un dossier temporaire) démarrait, lui. Le problème venait donc du profil existant, pas de l&rsquo;installation.</p>
<h2 id="la-cause">La cause</h2>
<p>Spotify est une application CEF (Chromium Embedded Framework). Comme Chromium, elle n&rsquo;autorise qu&rsquo;une instance par profil, avec trois liens symboliques dans <code>~/.cache/spotify</code> :</p>
<div class="codeblock"><span class="codelang mono" aria-hidden="true">text</span><button type="button" class="codecopy mono" data-done="Copié !" aria-label="Copier">Copier</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">SingletonLock   -&gt; huntervcx-imac151-10745
</span></span><span class="line"><span class="cl">SingletonCookie -&gt; 11476635766705238864
</span></span><span class="line"><span class="cl">SingletonSocket -&gt; /tmp/org.chromium.Chromium.sCe4Pv/SingletonSocket</span></span></code></pre></div></div>
<p>Le contenu de <code>SingletonLock</code> est <code>&lt;hostname&gt;-&lt;pid&gt;</code>. Au démarrage, Chromium lit ce lien :</p>
<ul>
<li>si le hostname est celui de la machine, il vérifie si le PID tourne encore et récupère le verrou s&rsquo;il est mort ;</li>
<li><strong>si le hostname est différent, il suppose que le profil est utilisé par une autre machine (profil sur un partage réseau) et ne touche à rien.</strong></li>
</ul>
<p>Ici, le verrou avait été laissé la veille par une session qui n&rsquo;avait pas fermé proprement l&rsquo;appli, avec l&rsquo;ancien hostname <code>huntervcx-imac151</code>. Entre-temps, la machine était devenue <code>iMac-Arthur</code>. Spotify voyait donc un verrou « d&rsquo;une autre machine » et quittait sans rien dire.</p>
<h2 id="le-correctif">Le correctif</h2>
<p>Vérifier qu&rsquo;aucun Spotify ne tourne, puis supprimer les trois liens :</p>
<div class="codeblock"><span class="codelang mono" aria-hidden="true">bash</span><button type="button" class="codecopy mono" data-done="Copié !" aria-label="Copier">Copier</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">pgrep -af spotify
</span></span><span class="line"><span class="cl">rm ~/.cache/spotify/SingletonLock <span class="se">\
</span></span></span><span class="line"><span class="cl">   ~/.cache/spotify/SingletonCookie <span class="se">\
</span></span></span><span class="line"><span class="cl">   ~/.cache/spotify/SingletonSocket</span></span></code></pre></div></div>
<p>Ces fichiers ne contiennent que le verrou : ni compte, ni cache de morceaux, ni préférences. Spotify redémarre normalement.</p>
<h2 id="à-retenir">À retenir</h2>
<ul>
<li>Changer de hostname avec une appli Chromium/Electron/CEF qui a planté ou qui est restée ouverte laisse un verrou orphelin que l&rsquo;appli ne sait plus reprendre.</li>
<li>Même symptôme possible avec Chrome, Edge, VS Code, Discord, etc. : <code>ls -la</code> sur le dossier de profil et regarder les <code>Singleton*</code>.</li>
<li>Un code de sortie 1 sans message est souvent un verrou ou un profil, pas un souci de GPU.</li>
<li>Si tu changes de hostname, ferme tes applications avant, ou nettoie les <code>Singleton*</code> après.</li>
</ul>
]]></content:encoded></item><item><title>Minecraft 26.3 (Prism) : icônes d’items transparentes</title><link>https://perrot.pt/fr/posts/minecraft-icones-items-transparentes-prism/</link><pubDate>Fri, 23 Oct 2026 12:00:00 +0000</pubDate><dc:creator>Arthur Perrot</dc:creator><category>Astuces rapides</category><guid isPermaLink="true">https://perrot.pt/fr/posts/minecraft-icones-items-transparentes-prism/</guid><description>Icônes d'items transparentes dans Minecraft 26.3 sous Prism Launcher : la piste de l'atlas, la fausse piste GPU et la vraie cause, SDL sur XWayland.</description><content:encoded><![CDATA[<p>Minecraft 26.3, lancé depuis Prism Launcher sur Linux : à l&rsquo;ouverture de l&rsquo;inventaire créatif, une partie des icônes d&rsquo;items est transparente. Les items sont bien là (on peut les prendre), mais on ne voit rien. Voici comment j&rsquo;ai fini par trouver, et pourquoi la bonne piste n&rsquo;était pas la première.</p>
<h2 id="le-symptôme">Le symptôme</h2>
<p>Instance : <code>~/.local/share/PrismLauncher/instances/26.3/minecraft</code>. Écran 5120x2880 en plein écran, <code>guiScale:3</code>. Dans <code>logs/latest.log</code>, à l&rsquo;ouverture de l&rsquo;inventaire créatif :</p>
<div class="codeblock"><span class="codelang mono" aria-hidden="true">text</span><button type="button" class="codecopy mono" data-done="Copié !" aria-label="Copier">Copier</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Too many items (64) in UI, some will be skipped! (Reached maximum texture size 512x512)</span></span></code></pre></div></div>
<p>Un petit dépannage en jeu existe : <code>F3+T</code> (recharger les packs) fait revenir les icônes, jusqu&rsquo;à la prochaine fois.</p>
<h2 id="première-piste--latlas-dicônes">Première piste : l&rsquo;atlas d&rsquo;icônes</h2>
<p>Les icônes d&rsquo;items de l&rsquo;interface sont dessinées dans un atlas plafonné à 512x512 px. Si tout ne rentre pas, les items en trop sont ignorés : icône transparente. Ça ne dépend pas du resource pack, et avec un écran 5120x2880 en <code>guiScale:3</code>, les icônes sont grandes, donc l&rsquo;atlas se remplit vite.</p>
<p>Mon hypothèse de calcul (non confirmée) : une icône fait 16 px x <code>guiScale</code>, donc un atlas de 512x512 contient 64 icônes à <code>guiScale:4</code> (64 px), environ 100 à 3 et 256 à 2. Ça colle avec les logs : le message est apparu le 21 (14:07, 66 items) et le 22 (22:30:53, 64 items).</p>
<p>Test : <code>guiScale:2</code> améliore les choses, mais l&rsquo;interface devient trop petite. Un contournement, pas une solution.</p>
<h2 id="fausse-piste--le-driver-et-le-noyau">Fausse piste : le driver et le noyau</h2>
<ul>
<li>Noyau <code>7.2.11-imac5kstitch</code>, <code>amdgpu</code> OK.</li>
<li>OpenGL fonctionne sur le système : <code>eglinfo</code> en Wayland donne radeonsi pitcairn, GL 4.6, et <code>glxinfo</code> sous XWayland est accéléré.</li>
</ul>
<p>Mais dans Minecraft, avant même la création de la fenêtre :</p>
<div class="codeblock"><span class="codelang mono" aria-hidden="true">text</span><button type="button" class="codecopy mono" data-done="Copié !" aria-label="Copier">Copier</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">GlBackend.loadLibrary ... Could not retrieve OpenGL functions</span></span></code></pre></div></div>
<p>Le jeu retombe alors sur Vulkan (RADV PITCAIRN, Mesa 25.0.7), et ça fonctionne. Les messages <code>Requested swapchain extent ... not within allowed extent</code> ne sont que le redimensionnement du plein écran sous XWayland, sans gravité.</p>
<h2 id="le-vrai-problème--sdl-retombe-sur-xwayland">Le vrai problème : SDL retombe sur XWayland</h2>
<p>J&rsquo;avais essayé de forcer OpenGL avec <code>preferredGraphicsBackend:&quot;default&quot;</code> dans <code>options.txt</code> (jeu fermé, sinon il réécrit le fichier). OpenGL échouait toujours avec la même erreur et le jeu repartait sur Vulkan : ce test n&rsquo;avait donc jamais vraiment eu lieu.</p>
<p>La cause : SDL retombait sur XWayland, parce que mon compositeur n&rsquo;a pas <code>fifo-v1</code>. La correction est une variable d&rsquo;environnement dans l&rsquo;instance Prism (Paramètres de l&rsquo;instance, variables d&rsquo;environnement, avec <code>OverrideEnv=true</code>) :</p>
<div class="codeblock"><span class="codelang mono" aria-hidden="true">text</span><button type="button" class="codecopy mono" data-done="Copié !" aria-label="Copier">Copier</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">SDL_VIDEO_DRIVER=wayland</span></span></code></pre></div></div>
<h2 id="résultat">Résultat</h2>
<p>Test du 23 octobre à 10h25 : OpenGL 4.6 radeonsi pitcairn, fenêtre SDL en <code>wayland</code>, <code>guiScale:3</code>, monde solo. Plus de souci d&rsquo;icônes et plus de <code>Too many items</code>.</p>
<p>Ce qui reste à vérifier : sur le serveur, avec plus d&rsquo;items (créatif en multi), quand il sera de nouveau en ligne. Si l&rsquo;avertissement revient, il faudra voir s&rsquo;il vient du serveur ou du backend. En attendant, mon avis : le vrai coupable était le repli sur XWayland, pas la taille de l&rsquo;atlas.</p>
<h2 id="à-retenir">À retenir</h2>
<ul>
<li>Si OpenGL échoue dans Minecraft alors que <code>eglinfo</code> et <code>glxinfo</code> fonctionnent, regarde quel backend SDL utilise vraiment.</li>
<li>Sous un compositeur Wayland sans <code>fifo-v1</code>, SDL peut retomber sur XWayland. <code>SDL_VIDEO_DRIVER=wayland</code> dans l&rsquo;environnement de l&rsquo;instance Prism règle le problème.</li>
<li>Un test n&rsquo;a de valeur que si on a vérifié qu&rsquo;il teste bien ce qu&rsquo;on croit (ici, le « test OpenGL » tournait en fait sur Vulkan).</li>
</ul>
]]></content:encoded></item><item><title>Configurer un Dynamic DNS Cloudflare sous Debian avec systemd</title><link>https://perrot.pt/fr/posts/configurer-un-dynamic-dns-cloudflare-sous-debian-avec-systemd/</link><pubDate>Tue, 29 Sep 2026 12:00:00 +0000</pubDate><dc:creator>Arthur Perrot</dc:creator><category>Admin. sys</category><guid isPermaLink="true">https://perrot.pt/fr/posts/configurer-un-dynamic-dns-cloudflare-sous-debian-avec-systemd/</guid><description>Un DDNS maison sous Debian : script Bash, API Cloudflare et timer systemd pour garder un enregistrement A à jour malgré une IP publique dynamique.</description><content:encoded><![CDATA[<p>Lorsqu&rsquo;on utilise un ordinateur portable ou un serveur derrière une connexion Internet avec une adresse IP publique dynamique, l&rsquo;adresse peut changer à tout moment.</p>
<p>L&rsquo;objectif est donc simple :</p>
<div class="codeblock"><span class="codelang mono" aria-hidden="true">text</span><button type="button" class="codecopy mono" data-done="Copié !" aria-label="Copier">Copier</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">arthur-perrot.example.com
</span></span><span class="line"><span class="cl">        ↓
</span></span><span class="line"><span class="cl">Adresse IPv4 publique actuelle</span></span></code></pre></div></div>
<p>Le PC vérifie régulièrement son IP publique et, lorsqu&rsquo;elle change, met automatiquement à jour l&rsquo;enregistrement DNS correspondant chez Cloudflare.</p>
<p>Dans ce guide, nous allons réaliser ce DDNS avec un simple script Bash, l&rsquo;API Cloudflare et un timer systemd sous Debian.</p>
<h2 id="prérequis">Prérequis</h2>
<p>Il faut :</p>
<ul>
<li>un domaine dont le DNS est géré par Cloudflare ;</li>
<li>Debian ou une distribution utilisant systemd ;</li>
<li><code>curl</code> ;</li>
<li><code>jq</code> ;</li>
<li>un token API Cloudflare.</li>
</ul>
<p>Installation des dépendances :</p>
<div class="codeblock"><span class="codelang mono" aria-hidden="true">bash</span><button type="button" class="codecopy mono" data-done="Copié !" aria-label="Copier">Copier</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo apt update
</span></span><span class="line"><span class="cl">sudo apt install curl jq</span></span></code></pre></div></div>
<p>Dans les exemples, nous utiliserons :</p>
<div class="codeblock"><span class="codelang mono" aria-hidden="true">text</span><button type="button" class="codecopy mono" data-done="Copié !" aria-label="Copier">Copier</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Zone DNS : example.com
</span></span><span class="line"><span class="cl">Record   : arthur-perrot.example.com</span></span></code></pre></div></div>
<h2 id="1-créer-le-token-api-cloudflare">1. Créer le token API Cloudflare</h2>
<p>Depuis Cloudflare, créer un <strong>API Token</strong> ayant uniquement les permissions nécessaires sur la zone concernée.</p>
<p>Permissions recommandées :</p>
<div class="codeblock"><span class="codelang mono" aria-hidden="true">text</span><button type="button" class="codecopy mono" data-done="Copié !" aria-label="Copier">Copier</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Zone → DNS → Edit
</span></span><span class="line"><span class="cl">Zone → Zone → Read</span></span></code></pre></div></div>
<p>Il est préférable de limiter le token à une seule zone DNS plutôt que de lui donner accès à l&rsquo;ensemble du compte.</p>
<p>On peut vérifier que le token fonctionne :</p>
<div class="codeblock"><span class="codelang mono" aria-hidden="true">bash</span><button type="button" class="codecopy mono" data-done="Copié !" aria-label="Copier">Copier</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">CF_API_TOKEN</span><span class="o">=</span><span class="s1">&#39;cfat_xxxxxxxxxxxxxxxxx&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">curl -s <span class="se">\
</span></span></span><span class="line"><span class="cl">  -H <span class="s2">&#34;Authorization: Bearer </span><span class="nv">$CF_API_TOKEN</span><span class="s2">&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  <span class="s2">&#34;https://api.cloudflare.com/client/v4/zones?name=example.com&#34;</span> <span class="p">|</span> jq</span></span></code></pre></div></div>
<p>Cloudflare doit retourner notamment :</p>
<div class="codeblock"><span class="codelang mono" aria-hidden="true">json</span><button type="button" class="codecopy mono" data-done="Copié !" aria-label="Copier">Copier</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;success&#34;</span><span class="p">:</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>La réponse contient également l&rsquo;identifiant de la zone :</p>
<div class="codeblock"><span class="codelang mono" aria-hidden="true">json</span><button type="button" class="codecopy mono" data-done="Copié !" aria-label="Copier">Copier</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="s2">&#34;id&#34;</span><span class="err">:</span> <span class="s2">&#34;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&#34;</span></span></span></code></pre></div></div>
<p>Conservez ce <strong>Zone ID</strong>, nous allons l&rsquo;utiliser directement dans le script.</p>
<h2 id="2-créer-lenregistrement-dns">2. Créer l&rsquo;enregistrement DNS</h2>
<p>Créer dans Cloudflare un enregistrement <code>A</code>, par exemple :</p>
<div class="codeblock"><span class="codelang mono" aria-hidden="true">text</span><button type="button" class="codecopy mono" data-done="Copié !" aria-label="Copier">Copier</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Type : A
</span></span><span class="line"><span class="cl">Name : arthur-perrot
</span></span><span class="line"><span class="cl">IPv4 : 1.2.3.4
</span></span><span class="line"><span class="cl">TTL  : Auto</span></span></code></pre></div></div>
<p>L&rsquo;adresse initiale importe peu : le script la remplacera lors de sa première exécution.</p>
<p>On pourra vérifier la résolution avec :</p>
<div class="codeblock"><span class="codelang mono" aria-hidden="true">bash</span><button type="button" class="codecopy mono" data-done="Copié !" aria-label="Copier">Copier</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">dig +short arthur-perrot.example.com @1.1.1.1</span></span></code></pre></div></div>
<p>Par exemple :</p>
<div class="codeblock"><span class="codelang mono" aria-hidden="true">text</span><button type="button" class="codecopy mono" data-done="Copié !" aria-label="Copier">Copier</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">1.2.3.4</span></span></code></pre></div></div>
<h2 id="3-créer-le-script-ddns">3. Créer le script DDNS</h2>
<p>Créons notre script :</p>
<div class="codeblock"><span class="codelang mono" aria-hidden="true">bash</span><button type="button" class="codecopy mono" data-done="Copié !" aria-label="Copier">Copier</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo nano /usr/local/bin/cloudflare-ddns</span></span></code></pre></div></div>
<p>Contenu :</p>
<div class="codeblock"><span class="codelang mono" aria-hidden="true">bash</span><button type="button" class="codecopy mono" data-done="Copié !" aria-label="Copier">Copier</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="cp">#!/bin/bash
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">set</span> -e
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">TOKEN</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$CF_API_TOKEN</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">ZONE_ID</span><span class="o">=</span><span class="s2">&#34;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">RECORD</span><span class="o">=</span><span class="s2">&#34;arthur-perrot.example.com&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Récupération de l&#39;IPv4 publique</span>
</span></span><span class="line"><span class="cl"><span class="nv">IP</span><span class="o">=</span><span class="k">$(</span>curl -fsS -4 https://api.ipify.org<span class="k">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Recherche de l&#39;identifiant du record DNS</span>
</span></span><span class="line"><span class="cl"><span class="nv">RECORD_ID</span><span class="o">=</span><span class="k">$(</span>curl -fsS <span class="se">\
</span></span></span><span class="line"><span class="cl">  -H <span class="s2">&#34;Authorization: Bearer </span><span class="nv">$TOKEN</span><span class="s2">&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  <span class="s2">&#34;https://api.cloudflare.com/client/v4/zones/</span><span class="nv">$ZONE_ID</span><span class="s2">/dns_records?type=A&amp;name=</span><span class="nv">$RECORD</span><span class="s2">&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  <span class="p">|</span> jq -r <span class="s1">&#39;.result[0].id&#39;</span><span class="k">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="o">[</span> <span class="s2">&#34;</span><span class="nv">$RECORD_ID</span><span class="s2">&#34;</span> <span class="o">=</span> <span class="s2">&#34;null&#34;</span> <span class="o">]</span> <span class="o">||</span> <span class="o">[</span> -z <span class="s2">&#34;</span><span class="nv">$RECORD_ID</span><span class="s2">&#34;</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;Record </span><span class="nv">$RECORD</span><span class="s2"> introuvable&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl"><span class="k">fi</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Récupération de l&#39;adresse actuellement publiée</span>
</span></span><span class="line"><span class="cl"><span class="nv">CURRENT</span><span class="o">=</span><span class="k">$(</span>curl -fsS <span class="se">\
</span></span></span><span class="line"><span class="cl">  -H <span class="s2">&#34;Authorization: Bearer </span><span class="nv">$TOKEN</span><span class="s2">&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  <span class="s2">&#34;https://api.cloudflare.com/client/v4/zones/</span><span class="nv">$ZONE_ID</span><span class="s2">/dns_records/</span><span class="nv">$RECORD_ID</span><span class="s2">&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  <span class="p">|</span> jq -r <span class="s1">&#39;.result.content&#39;</span><span class="k">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Rien à faire si l&#39;adresse n&#39;a pas changé</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="o">[</span> <span class="s2">&#34;</span><span class="nv">$CURRENT</span><span class="s2">&#34;</span> <span class="o">=</span> <span class="s2">&#34;</span><span class="nv">$IP</span><span class="s2">&#34;</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;IP inchangée : </span><span class="nv">$IP</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">exit</span> <span class="m">0</span>
</span></span><span class="line"><span class="cl"><span class="k">fi</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Mise à jour du record Cloudflare</span>
</span></span><span class="line"><span class="cl">curl -fsS -X PATCH <span class="se">\
</span></span></span><span class="line"><span class="cl">  -H <span class="s2">&#34;Authorization: Bearer </span><span class="nv">$TOKEN</span><span class="s2">&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  -H <span class="s2">&#34;Content-Type: application/json&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  <span class="s2">&#34;https://api.cloudflare.com/client/v4/zones/</span><span class="nv">$ZONE_ID</span><span class="s2">/dns_records/</span><span class="nv">$RECORD_ID</span><span class="s2">&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  --data <span class="s2">&#34;{\&#34;content\&#34;:\&#34;</span><span class="nv">$IP</span><span class="s2">\&#34;}&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  <span class="p">|</span> jq -e <span class="s1">&#39;.success&#39;</span> &gt;/dev/null
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;</span><span class="nv">$RECORD</span><span class="s2"> : </span><span class="nv">$CURRENT</span><span class="s2"> -&gt; </span><span class="nv">$IP</span><span class="s2">&#34;</span></span></span></code></pre></div></div>
<p>Rendre le script exécutable :</p>
<div class="codeblock"><span class="codelang mono" aria-hidden="true">bash</span><button type="button" class="codecopy mono" data-done="Copié !" aria-label="Copier">Copier</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo chmod <span class="m">700</span> /usr/local/bin/cloudflare-ddns</span></span></code></pre></div></div>
<p>Le fonctionnement est volontairement simple :</p>
<div class="codeblock"><span class="codelang mono" aria-hidden="true">text</span><button type="button" class="codecopy mono" data-done="Copié !" aria-label="Copier">Copier</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Récupération de l&#39;IP publique
</span></span><span class="line"><span class="cl">              ↓
</span></span><span class="line"><span class="cl">Lecture du record Cloudflare
</span></span><span class="line"><span class="cl">              ↓
</span></span><span class="line"><span class="cl">      IP identique ?
</span></span><span class="line"><span class="cl">        ↙           ↘
</span></span><span class="line"><span class="cl">      Oui           Non
</span></span><span class="line"><span class="cl">       ↓             ↓
</span></span><span class="line"><span class="cl">   Ne rien       Mise à jour
</span></span><span class="line"><span class="cl">    faire        Cloudflare</span></span></code></pre></div></div>
<h2 id="4-stocker-le-token-séparément">4. Stocker le token séparément</h2>
<p>Il est préférable de ne pas mettre le token Cloudflare directement dans le script.</p>
<p>Créer :</p>
<div class="codeblock"><span class="codelang mono" aria-hidden="true">bash</span><button type="button" class="codecopy mono" data-done="Copié !" aria-label="Copier">Copier</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo nano /etc/cloudflare-ddns.env</span></span></code></pre></div></div>
<p>Avec :</p>
<div class="codeblock"><span class="codelang mono" aria-hidden="true">ini</span><button type="button" class="codecopy mono" data-done="Copié !" aria-label="Copier">Copier</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="na">CF_API_TOKEN</span><span class="o">=</span><span class="s">cfat_xxxxxxxxxxxxxxxxx</span></span></span></code></pre></div></div>
<p>Restreindre ensuite les permissions :</p>
<div class="codeblock"><span class="codelang mono" aria-hidden="true">bash</span><button type="button" class="codecopy mono" data-done="Copié !" aria-label="Copier">Copier</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo chown root:root /etc/cloudflare-ddns.env
</span></span><span class="line"><span class="cl">sudo chmod <span class="m">600</span> /etc/cloudflare-ddns.env</span></span></code></pre></div></div>
<p>Ainsi, seul <code>root</code> peut lire le token.</p>
<h2 id="5-créer-le-service-systemd">5. Créer le service systemd</h2>
<p>Créer :</p>
<div class="codeblock"><span class="codelang mono" aria-hidden="true">bash</span><button type="button" class="codecopy mono" data-done="Copié !" aria-label="Copier">Copier</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo nano /etc/systemd/system/cloudflare-ddns.service</span></span></code></pre></div></div>
<p>Avec :</p>
<div class="codeblock"><span class="codelang mono" aria-hidden="true">ini</span><button type="button" class="codecopy mono" data-done="Copié !" aria-label="Copier">Copier</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="k">[Unit]</span>
</span></span><span class="line"><span class="cl"><span class="na">Description</span><span class="o">=</span><span class="s">Cloudflare DDNS updater</span>
</span></span><span class="line"><span class="cl"><span class="na">Wants</span><span class="o">=</span><span class="s">network-online.target</span>
</span></span><span class="line"><span class="cl"><span class="na">After</span><span class="o">=</span><span class="s">network-online.target</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">[Service]</span>
</span></span><span class="line"><span class="cl"><span class="na">Type</span><span class="o">=</span><span class="s">oneshot</span>
</span></span><span class="line"><span class="cl"><span class="na">EnvironmentFile</span><span class="o">=</span><span class="s">/etc/cloudflare-ddns.env</span>
</span></span><span class="line"><span class="cl"><span class="na">ExecStart</span><span class="o">=</span><span class="s">/usr/local/bin/cloudflare-ddns</span></span></span></code></pre></div></div>
<p>Puis recharger systemd :</p>
<div class="codeblock"><span class="codelang mono" aria-hidden="true">bash</span><button type="button" class="codecopy mono" data-done="Copié !" aria-label="Copier">Copier</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo systemctl daemon-reload</span></span></code></pre></div></div>
<p>Testons le service manuellement :</p>
<div class="codeblock"><span class="codelang mono" aria-hidden="true">bash</span><button type="button" class="codecopy mono" data-done="Copié !" aria-label="Copier">Copier</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo systemctl start cloudflare-ddns</span></span></code></pre></div></div>
<p>Puis :</p>
<div class="codeblock"><span class="codelang mono" aria-hidden="true">bash</span><button type="button" class="codecopy mono" data-done="Copié !" aria-label="Copier">Copier</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">systemctl status cloudflare-ddns</span></span></code></pre></div></div>
<p>Un résultat comme celui-ci indique que tout fonctionne :</p>
<div class="codeblock"><span class="codelang mono" aria-hidden="true">text</span><button type="button" class="codecopy mono" data-done="Copié !" aria-label="Copier">Copier</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Starting cloudflare-ddns.service - Cloudflare DDNS updater...
</span></span><span class="line"><span class="cl">arthur-perrot.example.com : 1.2.3.4 -&gt; 203.0.113.42
</span></span><span class="line"><span class="cl">Deactivated successfully.
</span></span><span class="line"><span class="cl">Finished cloudflare-ddns.service - Cloudflare DDNS updater.</span></span></code></pre></div></div>
<p>Lors des exécutions suivantes, si l&rsquo;IP n&rsquo;a pas changé :</p>
<div class="codeblock"><span class="codelang mono" aria-hidden="true">text</span><button type="button" class="codecopy mono" data-done="Copié !" aria-label="Copier">Copier</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">IP inchangée : 203.0.113.42</span></span></code></pre></div></div>
<p>Il est normal que le service apparaisse ensuite comme :</p>
<div class="codeblock"><span class="codelang mono" aria-hidden="true">text</span><button type="button" class="codecopy mono" data-done="Copié !" aria-label="Copier">Copier</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">inactive (dead)</span></span></code></pre></div></div>
<p>Il s&rsquo;agit d&rsquo;un service <code>oneshot</code> : il effectue son travail puis se termine.</p>
<h2 id="6-exécuter-automatiquement-le-ddns-toutes-les-5-minutes">6. Exécuter automatiquement le DDNS toutes les 5 minutes</h2>
<p>Nous pouvons maintenant utiliser un timer systemd.</p>
<p>Créer :</p>
<div class="codeblock"><span class="codelang mono" aria-hidden="true">bash</span><button type="button" class="codecopy mono" data-done="Copié !" aria-label="Copier">Copier</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo nano /etc/systemd/system/cloudflare-ddns.timer</span></span></code></pre></div></div>
<p>Avec :</p>
<div class="codeblock"><span class="codelang mono" aria-hidden="true">ini</span><button type="button" class="codecopy mono" data-done="Copié !" aria-label="Copier">Copier</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="k">[Unit]</span>
</span></span><span class="line"><span class="cl"><span class="na">Description</span><span class="o">=</span><span class="s">Update Cloudflare DDNS every 5 minutes</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">[Timer]</span>
</span></span><span class="line"><span class="cl"><span class="na">OnBootSec</span><span class="o">=</span><span class="s">30s</span>
</span></span><span class="line"><span class="cl"><span class="na">OnUnitActiveSec</span><span class="o">=</span><span class="s">5min</span>
</span></span><span class="line"><span class="cl"><span class="na">Persistent</span><span class="o">=</span><span class="s">true</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">[Install]</span>
</span></span><span class="line"><span class="cl"><span class="na">WantedBy</span><span class="o">=</span><span class="s">timers.target</span></span></span></code></pre></div></div>
<p>Activer le timer :</p>
<div class="codeblock"><span class="codelang mono" aria-hidden="true">bash</span><button type="button" class="codecopy mono" data-done="Copié !" aria-label="Copier">Copier</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo systemctl daemon-reload
</span></span><span class="line"><span class="cl">sudo systemctl <span class="nb">enable</span> --now cloudflare-ddns.timer</span></span></code></pre></div></div>
<p>Vérifier son état :</p>
<div class="codeblock"><span class="codelang mono" aria-hidden="true">bash</span><button type="button" class="codecopy mono" data-done="Copié !" aria-label="Copier">Copier</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">systemctl status cloudflare-ddns.timer</span></span></code></pre></div></div>
<p>On doit obtenir :</p>
<div class="codeblock"><span class="codelang mono" aria-hidden="true">text</span><button type="button" class="codecopy mono" data-done="Copié !" aria-label="Copier">Copier</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Active: active (waiting)</span></span></code></pre></div></div>
<p>Pour connaître la prochaine exécution :</p>
<div class="codeblock"><span class="codelang mono" aria-hidden="true">bash</span><button type="button" class="codecopy mono" data-done="Copié !" aria-label="Copier">Copier</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">systemctl list-timers cloudflare-ddns.timer</span></span></code></pre></div></div>
<p>Par exemple :</p>
<div class="codeblock"><span class="codelang mono" aria-hidden="true">text</span><button type="button" class="codecopy mono" data-done="Copié !" aria-label="Copier">Copier</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">NEXT                        LEFT     UNIT
</span></span><span class="line"><span class="cl">Tue 17:43:20 EDT            4min     cloudflare-ddns.timer</span></span></code></pre></div></div>
<h2 id="7-vérifier-les-logs">7. Vérifier les logs</h2>
<p>Les exécutions du script sont enregistrées dans le journal systemd.</p>
<p>Afficher les dernières exécutions :</p>
<div class="codeblock"><span class="codelang mono" aria-hidden="true">bash</span><button type="button" class="codecopy mono" data-done="Copié !" aria-label="Copier">Copier</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">journalctl -u cloudflare-ddns.service -n <span class="m">20</span></span></span></code></pre></div></div>
<p>Suivre les prochaines exécutions en direct :</p>
<div class="codeblock"><span class="codelang mono" aria-hidden="true">bash</span><button type="button" class="codecopy mono" data-done="Copié !" aria-label="Copier">Copier</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">journalctl -fu cloudflare-ddns.service</span></span></code></pre></div></div>
<p>Pour afficher uniquement les événements depuis le dernier démarrage :</p>
<div class="codeblock"><span class="codelang mono" aria-hidden="true">bash</span><button type="button" class="codecopy mono" data-done="Copié !" aria-label="Copier">Copier</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">journalctl -b -u cloudflare-ddns.service</span></span></code></pre></div></div>
<h2 id="8-vérifier-après-un-reboot">8. Vérifier après un reboot</h2>
<p>Le timer étant activé avec :</p>
<div class="codeblock"><span class="codelang mono" aria-hidden="true">bash</span><button type="button" class="codecopy mono" data-done="Copié !" aria-label="Copier">Copier</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo systemctl <span class="nb">enable</span> --now cloudflare-ddns.timer</span></span></code></pre></div></div>
<p>il redémarrera automatiquement avec Debian.</p>
<p>On peut vérifier :</p>
<div class="codeblock"><span class="codelang mono" aria-hidden="true">bash</span><button type="button" class="codecopy mono" data-done="Copié !" aria-label="Copier">Copier</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">systemctl is-enabled cloudflare-ddns.timer
</span></span><span class="line"><span class="cl">systemctl is-active cloudflare-ddns.timer</span></span></code></pre></div></div>
<p>Résultat attendu :</p>
<div class="codeblock"><span class="codelang mono" aria-hidden="true">text</span><button type="button" class="codecopy mono" data-done="Copié !" aria-label="Copier">Copier</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">enabled
</span></span><span class="line"><span class="cl">active</span></span></code></pre></div></div>
<p>Environ 30 secondes après le démarrage, une première vérification sera effectuée. Ensuite, le script sera exécuté toutes les cinq minutes.</p>
<h2 id="9-vérifier-le-résultat">9. Vérifier le résultat</h2>
<p>Afficher l&rsquo;IPv4 publique :</p>
<div class="codeblock"><span class="codelang mono" aria-hidden="true">bash</span><button type="button" class="codecopy mono" data-done="Copié !" aria-label="Copier">Copier</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">curl -4 -s https://api.ipify.org
</span></span><span class="line"><span class="cl">echo</span></span></code></pre></div></div>
<p>Puis vérifier le DNS :</p>
<div class="codeblock"><span class="codelang mono" aria-hidden="true">bash</span><button type="button" class="codecopy mono" data-done="Copié !" aria-label="Copier">Copier</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">dig +short arthur-perrot.example.com @1.1.1.1</span></span></code></pre></div></div>
<p>Les deux valeurs doivent correspondre :</p>
<div class="codeblock"><span class="codelang mono" aria-hidden="true">text</span><button type="button" class="codecopy mono" data-done="Copié !" aria-label="Copier">Copier</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">203.0.113.42
</span></span><span class="line"><span class="cl">203.0.113.42</span></span></code></pre></div></div>
<p>Le fonctionnement final est donc :</p>
<div class="codeblock"><span class="codelang mono" aria-hidden="true">text</span><button type="button" class="codecopy mono" data-done="Copié !" aria-label="Copier">Copier</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">                    ┌─────────────────┐
</span></span><span class="line"><span class="cl">                    │   PC Debian     │
</span></span><span class="line"><span class="cl">                    └────────┬────────┘
</span></span><span class="line"><span class="cl">                             │
</span></span><span class="line"><span class="cl">                     toutes les 5 min
</span></span><span class="line"><span class="cl">                             │
</span></span><span class="line"><span class="cl">                             ▼
</span></span><span class="line"><span class="cl">                    IP publique actuelle
</span></span><span class="line"><span class="cl">                             │
</span></span><span class="line"><span class="cl">                             ▼
</span></span><span class="line"><span class="cl">                   Comparaison Cloudflare
</span></span><span class="line"><span class="cl">                             │
</span></span><span class="line"><span class="cl">                    ┌────────┴────────┐
</span></span><span class="line"><span class="cl">                    │                 │
</span></span><span class="line"><span class="cl">               identique          différente
</span></span><span class="line"><span class="cl">                    │                 │
</span></span><span class="line"><span class="cl">                    ▼                 ▼
</span></span><span class="line"><span class="cl">                 rien          Cloudflare API
</span></span><span class="line"><span class="cl">                                      │
</span></span><span class="line"><span class="cl">                                      ▼
</span></span><span class="line"><span class="cl">                         arthur-perrot.example.com
</span></span><span class="line"><span class="cl">                                      │
</span></span><span class="line"><span class="cl">                                      ▼
</span></span><span class="line"><span class="cl">                            nouvelle IPv4 publique</span></span></code></pre></div></div>
<p>Cette solution a l&rsquo;avantage de n&rsquo;utiliser aucun daemon DDNS supplémentaire : seulement <code>curl</code>, <code>jq</code>, quelques lignes de Bash et les mécanismes natifs de systemd.</p>
<p>Elle convient notamment à un laptop qui change régulièrement de réseau : domicile, partage de connexion, Wi-Fi extérieur, etc.</p>
<p>Il faut cependant garder à l&rsquo;esprit que le DDNS ne contourne pas le NAT ou les restrictions du réseau. Le nom DNS pointera bien vers l&rsquo;adresse IP publique utilisée par le laptop, mais cela ne garantit pas que les connexions entrantes vers celui-ci soient possibles, notamment derrière un CGNAT ou un réseau qui bloque les connexions entrantes.</p>
]]></content:encoded></item><item><title>Claude Code + tmux : arrêter de perdre vos sessions terminal à chaque fermeture de Mac</title><link>https://perrot.pt/fr/posts/claude-code-tmux-arreter-de-perdre-vos-sessions-terminal-a-chaque-fermeture-de-mac/</link><pubDate>Tue, 02 Jun 2026 00:03:24 +0000</pubDate><dc:creator>Arthur Perrot</dc:creator><category>Admin. sys</category><category>Astuces rapides</category><category>Dev</category><guid isPermaLink="true">https://perrot.pt/fr/posts/claude-code-tmux-arreter-de-perdre-vos-sessions-terminal-a-chaque-fermeture-de-mac/</guid><description>Lancer Claude Code dans une session tmux persistante via un alias cc : fermer le terminal ou le Mac ne tue plus l'agent ni son contexte.</description><content:encoded><![CDATA[<p>Vous lancez une session Claude Code sur votre Mac.</p>
<p>Puis :</p>
<ul>
<li>vous fermez le capot,</li>
<li>le terminal crash,</li>
<li>votre SSH coupe,</li>
<li>ou VSCode redémarre.</li>
</ul>
<p>Et toute la session disparaît.</p>
<p>Contexte perdu. Historique perdu. Travail perdu.</p>
<p>Le problème ne vient pas de Claude Code.</p>
<p>Le problème, c’est que votre agent tourne directement dans le terminal actif.</p>
<p>Quand le terminal meurt, le processus aussi.</p>
<h2 id="pourquoi-tmux-change-complètement-lexpérience-avec-claude-code">Pourquoi tmux change complètement l’expérience avec Claude Code</h2>
<p><code>tmux</code> permet de détacher une session terminal de votre fenêtre actuelle.</p>
<p>Résultat :</p>
<ul>
<li>votre session continue même si vous fermez votre terminal,</li>
<li>vous pouvez reprendre plus tard exactement là où vous étiez,</li>
<li>votre contexte Claude reste chargé,</li>
<li>vos longues tâches continuent en arrière-plan.</li>
</ul>
<p>C’est particulièrement utile :</p>
<ul>
<li>en SSH sur un VPS,</li>
<li>sur des sessions longues,</li>
<li>en multi-projets,</li>
<li>avec plusieurs agents Claude simultanés,</li>
<li>sur des serveurs distants,</li>
<li>ou simplement pour éviter de garder un MacBook réveillé inutilement.</li>
</ul>
<h2 id="la-méthode--un-alias-cc">La méthode : un alias <code>cc</code></h2>
<p>L’objectif :</p>
<ul>
<li>créer automatiquement une session tmux,</li>
<li>lancer <code>claude remote-control</code>,</li>
<li>rattacher directement le terminal à la session,</li>
<li>générer un nom unique par projet.</li>
</ul>
<h2 id="installation-rapide">Installation rapide</h2>
<h3 id="bash">Bash</h3>
<div class="codeblock"><span class="codelang mono" aria-hidden="true">bash</span><button type="button" class="codecopy mono" data-done="Copié !" aria-label="Copier">Copier</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;alias cc=&#39;SESSION=claude-\$(basename \&#34;\$PWD\&#34;)-\$(date +%s); tmux new-session -d -s \$SESSION \&#34;cd \\\&#34;\$PWD\\\&#34; &amp;&amp; claude remote-control\&#34; &amp;&amp; tmux attach -t \$SESSION&#39;&#34;</span> &gt;&gt; ~/.bashrc <span class="o">&amp;&amp;</span> <span class="nb">source</span> ~/.bashrc</span></span></code></pre></div></div>
<h3 id="zsh">Zsh</h3>
<div class="codeblock"><span class="codelang mono" aria-hidden="true">bash</span><button type="button" class="codecopy mono" data-done="Copié !" aria-label="Copier">Copier</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;alias cc=&#39;SESSION=claude-\$(basename \&#34;\$PWD\&#34;)-\$(date +%s); tmux new-session -d -s \$SESSION \&#34;cd \\\&#34;\$PWD\\\&#34; &amp;&amp; claude remote-control\&#34; &amp;&amp; tmux attach -t \$SESSION&#39;&#34;</span> &gt;&gt; ~/.zshrc <span class="o">&amp;&amp;</span> <span class="nb">source</span> ~/.zshrc</span></span></code></pre></div></div>
<h2 id="utilisation">Utilisation</h2>
<p>Depuis n’importe quel projet :</p>
<div class="codeblock"><span class="codelang mono" aria-hidden="true">bash</span><button type="button" class="codecopy mono" data-done="Copié !" aria-label="Copier">Copier</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">cc</span></span></code></pre></div></div>
<p>Le script va :</p>
<ol>
<li>créer une session tmux dédiée,</li>
<li>lancer Claude Code dans le dossier courant,</li>
<li>rattacher automatiquement votre terminal.</li>
</ol>
<h2 id="reprendre-une-session-claude-code">Reprendre une session Claude Code</h2>
<p>Même après fermeture du terminal, la session reste active.</p>
<p>Pour voir les sessions :</p>
<div class="codeblock"><span class="codelang mono" aria-hidden="true">bash</span><button type="button" class="codecopy mono" data-done="Copié !" aria-label="Copier">Copier</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">tmux ls</span></span></code></pre></div></div>
<p>Pour reprendre une session :</p>
<div class="codeblock"><span class="codelang mono" aria-hidden="true">bash</span><button type="button" class="codecopy mono" data-done="Copié !" aria-label="Copier">Copier</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">tmux attach -t nom-session</span></span></code></pre></div></div>
<h2 id="pourquoi-cette-approche-est-intéressante-en-entreprise">Pourquoi cette approche est intéressante en entreprise</h2>
<p>Dans des environnements d’infrastructure ou de développement avancés, cette méthode permet :</p>
<ul>
<li>de garder des agents persistants,</li>
<li>de limiter les pertes de contexte,</li>
<li>de travailler proprement en SSH,</li>
<li>d’améliorer les workflows DevOps,</li>
<li>de lancer plusieurs environnements Claude en parallèle,</li>
<li>et d’éviter les interruptions liées aux postes utilisateurs.</li>
</ul>
<p>C’est aussi très pratique pour :</p>
<ul>
<li>Kubernetes,</li>
<li>CI/CD,</li>
<li>administration Linux,</li>
<li>automatisation,</li>
<li>développement backend,</li>
<li>cybersécurité,</li>
<li>ou support technique longue durée.</li>
</ul>
<h2 id="bonus--multi-projets-proprement-isolés">Bonus : multi-projets proprement isolés</h2>
<p>Comme le nom de session inclut automatiquement le dossier courant, chaque projet possède sa propre session Claude indépendante.</p>
<p>Exemple :</p>
<div class="codeblock"><span class="codelang mono" aria-hidden="true">text</span><button type="button" class="codecopy mono" data-done="Copié !" aria-label="Copier">Copier</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">claude-api-1748812941
</span></span><span class="line"><span class="cl">claude-frontend-1748812958
</span></span><span class="line"><span class="cl">claude-infra-prod-1748812972</span></span></code></pre></div></div>
<p>Vous pouvez donc travailler sur plusieurs environnements simultanément sans collision.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Beaucoup de développeurs utilisent Claude Code comme un simple terminal amélioré.</p>
<p>Mais avec <code>tmux</code>, on commence réellement à traiter l’agent comme un processus persistant.</p>
<p>Le gain en confort est immédiat :</p>
<ul>
<li>moins de pertes,</li>
<li>moins de redémarrages,</li>
<li>moins de contexte à recharger,</li>
<li>plus de stabilité.</li>
</ul>
<p>Pour des workflows techniques avancés, c’est rapidement indispensable.</p>
]]></content:encoded></item><item><title>Le client ne paye pas pour ton architecture parfaite</title><link>https://perrot.pt/fr/posts/le-client-ne-paye-pas-pour-ton-architecture-parfaite/</link><pubDate>Mon, 09 Mar 2026 10:35:00 +0000</pubDate><dc:creator>Arthur Perrot</dc:creator><category>Dev</category><guid isPermaLink="true">https://perrot.pt/fr/posts/le-client-ne-paye-pas-pour-ton-architecture-parfaite/</guid><description>Architecture parfaite ou code qui livre ? Un point de vue sans filtre sur la dette technique, Pareto et ce que le client achète vraiment.</description><content:encoded><![CDATA[<p>Il y a deux grandes écoles chez les développeurs.</p>
<p>Ceux qui parlent d’architecture hexagonale, de clean code, de patterns élégants, de séparation stricte des responsabilités.<br>
Et ceux qui livrent.</p>
<p>Je caricature volontairement. Mais le débat existe.</p>
<p>Et je le dis haut et fort : <strong>je suis un mauvais dev</strong>.<br>
5 ans chez Epitech m&rsquo;ont appris à coder, mais je n&rsquo;aime pas. Donc je ne suis pas le meilleur.<br>
Pourtant, je couche la plupart de vos devs car cette faiblesse est mon plus grand atout pour mes clients.</p>
<hr>
<h2 id="le-fantasme-du-code-parfait">Le fantasme du code parfait</h2>
<p>On a tous croisé ce profil :</p>
<ul>
<li>14 couches d’abstraction,</li>
<li>12 interfaces pour une seule implémentation,</li>
<li>une architecture “future proof”,</li>
<li>zéro feature visible côté client.</li>
</ul>
<p>Techniquement, c’est impressionnant.<br>
Conceptuellement, c’est propre.</p>
<p>Mais pendant ce temps-là, le client attend toujours que le bouton fonctionne.</p>
<p>Le client ne voit pas :</p>
<ul>
<li>ton injection de dépendances raffinée,</li>
<li>tes design patterns bien placés,</li>
<li>tes noms de variables exemplaires.</li>
</ul>
<p>Il voit :</p>
<blockquote>
<p>“Est-ce que ça marche ?”</p>
</blockquote>
<hr>
<h2 id="à-linverse--le-cow-boy-du-code">À l’inverse : le cow-boy du code</h2>
<p>Et puis il y a l’autre profil.</p>
<p>Celui qui écrit vite.<br>
Parfois sale.<br>
Parfois redondant.<br>
Mais fonctionnel.</p>
<p>Le produit sort.<br>
Le client est content.<br>
Le business avance.</p>
<p>Et souvent, c’est ce code-là qui fait rentrer l’argent.</p>
<hr>
<h2 id="la-vérité-est-entre-les-deux">La vérité est entre les deux</h2>
<p>Dire que seul le résultat compte serait faux.<br>
La dette technique existe. Elle est réelle. Elle coûte cher.</p>
<p>Mais se cacher derrière “l’architecture parfaite” pour retarder la livraison, c’est aussi une forme d’évitement.</p>
<p><img src="https://perrot.pt/posts/the-client-doesnt-pay-for-your-perfect-architecture/pareto.png" alt="Principe de Pareto, règle des 80/20 : efforts et résultats" width="800" height="642" loading="lazy" decoding="async"></p>
<p>La loi de Pareto (que j&rsquo;affectionne depuis mon adolescence) est utile ici :</p>
<blockquote>
<p>20 % d’effort bien placé produisent 80 % de la valeur.</p>
</blockquote>
<p>Les 80 % d’effort supplémentaires pour rendre le code “académiquement pur” ne produisent pas toujours 80 % de valeur supplémentaire.</p>
<p>Parfois, ils produisent 5 % de confort intellectuel.</p>
<hr>
<h2 id="ce-que-le-client-achète-vraiment">Ce que le client achète vraiment</h2>
<p>Un client n’achète pas :</p>
<ul>
<li>un pattern,</li>
<li>une structure élégante,</li>
<li>un repository bien organisé.</li>
</ul>
<p>Il achète :</p>
<ul>
<li>une solution à un problème,</li>
<li>un gain de temps,</li>
<li>une réduction de coût,</li>
<li>un avantage concurrentiel.</li>
</ul>
<p>L’architecture est un moyen.<br>
Pas une finalité.</p>
<hr>
<h2 id="mais-attention--fonctionnel-nest-pas-une-excuse">Mais attention : “fonctionnel” n’est pas une excuse</h2>
<p>Le discours inverse est tout aussi dangereux.</p>
<p>“Ça marche” n’est pas un argument suffisant si :</p>
<ul>
<li>personne ne peut maintenir le code,</li>
<li>chaque modification casse autre chose,</li>
<li>la scalabilité est impossible,</li>
<li>le refactoring devient hors de prix.</li>
</ul>
<p>Le code jetable en production est une bombe à retardement.</p>
<p>La vraie maturité consiste à savoir où mettre l’effort.</p>
<hr>
<h2 id="une-règle-simple">Une règle simple</h2>
<p>Avant d’ajouter une couche d’abstraction, pose-toi trois questions :</p>
<ol>
<li>Est-ce que ce besoin existe vraiment aujourd’hui ?</li>
<li>Est-ce que ça simplifie la maintenance future ?</li>
<li>Est-ce que le client en tirera une valeur mesurable ?</li>
</ol>
<p>Si la réponse est non aux trois…<br>
Tu es peut-être en train de coder pour toi, pas pour le produit.</p>
<hr>
<h2 id="ce-que-jai-appris-avec-le-temps">Ce que j’ai appris avec le temps</h2>
<p>Un bon développeur n’est pas celui qui écrit le code le plus élégant.</p>
<p>C’est celui qui sait :</p>
<ul>
<li>livrer vite quand il faut,</li>
<li>structurer quand c’est nécessaire,</li>
<li>simplifier au lieu de complexifier,</li>
<li>arbitrer entre vitesse et propreté,</li>
<li>et surtout stopper son onanisme architectural.</li>
</ul>
<p>Le code parfait n’existe pas.<br>
Le code utile, oui.</p>
<p>Et dans un projet :</p>
<blockquote>
<p>l’utilité doit toujours passer avant l’ego technique.</p>
</blockquote>
<p>Arrêtez de réinventer la roue.</p>
<p>Et par pitié, arrêtez de réinventer la hache qui va couper du bois pour réinventer cette foutue roue.</p>
<p>Pissez du code qui tourne, pas du code qui brille</p>
]]></content:encoded></item><item><title>Pourquoi os.scandir() est nettement plus rapide que os.walk() en python ?</title><link>https://perrot.pt/fr/posts/pourquoi-os-scandir-est-nettement-plus-rapide-que-os-walk-en-python/</link><pubDate>Thu, 22 Jan 2026 18:42:01 +0000</pubDate><dc:creator>Arthur Perrot</dc:creator><category>Dev</category><guid isPermaLink="true">https://perrot.pt/fr/posts/pourquoi-os-scandir-est-nettement-plus-rapide-que-os-walk-en-python/</guid><description>Benchmark réel de os.walk() contre os.scandir() en Python : environ deux fois plus rapide, pourquoi, et quand la différence compte vraiment.</description><content:encoded><![CDATA[<h3 id="retour-dexpérience-sur-un-benchmark-simple-en-environnement-réel">Retour d’expérience sur un benchmark simple en environnement réel</h3>
<p>Lorsqu’on développe des outils de synchronisation, d’indexation ou de traitement de fichiers (sauvegarde, scan de répertoires, etc.), le parcours récursif du système de fichiers devient très vite un <strong>point critique de performance</strong>.</p>
<p>Dans nos tests, nous avons voulu comparer concrètement deux approches courantes en Python :</p>
<ul>
<li><code>os.walk()</code> (méthode historique, très utilisée)</li>
<li><code>os.scandir()</code> (API plus récente et bas niveau)</li>
</ul>
<p>L’objectif : <strong>un benchmark simple, sans optimisation artificielle</strong>, exécuté sur des dossiers réels, avec quelques centaines à plusieurs dizaines de milliers de fichiers.</p>
<hr>
<h2 id="le-benchmark">Le benchmark</h2>
<p>Le test consiste à :</p>
<ul>
<li>parcourir récursivement un dossier</li>
<li>compter le nombre total de fichiers</li>
<li>mesurer le temps d’exécution</li>
</ul>
<p>Même logique, même machine, même code métier.<br>
La seule différence : la méthode de parcours.</p>
<hr>
<h2 id="résultats-observés">Résultats observés</h2>
<p>Voici quelques extraits représentatifs des tests réalisés :</p>
<table>
  <thead>
      <tr>
          <th>Nombre de fichiers</th>
          <th><code>os.walk()</code></th>
          <th><code>os.scandir()</code></th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>67 fichiers</td>
          <td>0,036 s</td>
          <td>0,019 s</td>
      </tr>
      <tr>
          <td>195 fichiers</td>
          <td>0,180 s</td>
          <td>0,100 s</td>
      </tr>
      <tr>
          <td>8 852 fichiers</td>
          <td>10,28 s</td>
          <td>5,54 s</td>
      </tr>
      <tr>
          <td>19 381 fichiers</td>
          <td>18,25 s</td>
          <td>9,95 s</td>
      </tr>
  </tbody>
</table>
<p>👉 <strong>Dans tous les cas, <code>os.scandir()</code> est environ 2× plus rapide.</strong></p>
<p>Et plus l’arborescence est grande, plus l’écart devient significatif.</p>
<p><img src="https://perrot.pt/posts/why-os-scandir-is-significantly-faster-than-os-walk-in-python/benchmark.png" alt="Résultats du benchmark os.walk contre os.scandir dans un terminal" width="488" height="520" loading="lazy" decoding="async"></p>
<hr>
<h2 id="pourquoi-cette-différence-">Pourquoi cette différence ?</h2>
<h3 id="1-le-coût-caché-des-stat">1. Le coût caché des <code>stat()</code></h3>
<p><code>os.walk()</code> repose sur une abstraction plus haut niveau.<br>
Lorsqu’il parcourt les fichiers, Python effectue <strong>beaucoup d’appels système <code>stat()</code></strong> pour déterminer si une entrée est un fichier ou un dossier.</p>
<p>Ces appels sont :</p>
<ul>
<li>coûteux sur disque</li>
<li>encore plus lents sur des <strong>partages réseau (SMB, NFS, NAS)</strong></li>
<li>multipliés par le nombre total d’entrées</li>
</ul>
<h3 id="2-osscandir-travaille-plus-bas-niveau">2. <code>os.scandir()</code> travaille plus bas niveau</h3>
<p><code>os.scandir()</code> utilise directement les informations retournées par le système de fichiers (via <code>dirent</code> sous Linux).</p>
<p>Résultat :</p>
<ul>
<li>le type (fichier/dossier) est souvent déjà connu</li>
<li>pas besoin de <code>stat()</code> systématique</li>
<li>beaucoup moins de passages kernel ↔ userland</li>
</ul>
<p>C’est précisément ce qui explique le <strong>gain massif de performance</strong>.</p>
<hr>
<h2 id="mais-oswalk-utilise-déjà-scandir-non-">“Mais <code>os.walk()</code> utilise déjà scandir, non ?”</h2>
<p>Oui… <strong>en interne</strong>, depuis Python 3.5.</p>
<p>Mais :</p>
<ul>
<li><code>os.walk()</code> reste une API généraliste</li>
<li>elle génère des tuples complets (<code>root</code>, <code>dirs</code>, <code>files</code>)</li>
<li>elle fait plus de travail que nécessaire dans des cas simples</li>
</ul>
<p>👉 Quand on veut <strong>juste parcourir vite</strong>, <code>os.scandir()</code> utilisé directement reste imbattable.</p>
<hr>
<h2 id="pourquoi-choisir-python-malgré-des-performances-inférieures-à-certains-langages-">Pourquoi choisir Python, malgré des performances inférieures à certains langages ?</h2>
<p>Il est largement admis que Python est, sur le plan strictement technique, plus lent que des langages compilés comme C, C++ ou Rust. Cette réalité ne constitue cependant pas un défaut en soi, mais plutôt un <strong>compromis assumé</strong>. Le choix d’un langage ne se limite jamais à la seule performance brute : il implique des considérations de budget, de complexité, de maintenabilité et de dette technique sur le long terme.</p>
<p>Python se distingue avant tout par sa <strong>simplicité de conception et de lecture</strong>. Un code plus clair réduit le temps de développement, facilite les relectures, limite les erreurs et permet une montée en compétence plus rapide des équipes. À budget égal, cela se traduit par plus de fonctionnalités livrées, plus vite, et avec un risque projet nettement inférieur.</p>
<p>Par ailleurs, tous les projets ne nécessitent pas une optimisation maximale dès leur conception. Dans de nombreux cas, la performance n’est pas le facteur limitant principal, ou peut être améliorée ponctuellement, uniquement là où c’est nécessaire. Python permet précisément cette approche progressive : commencer simple, puis optimiser de manière ciblée, sans sur-ingénierie prématurée.</p>
<p>Enfin, la dette technique est souvent sous-estimée lorsqu’on privilégie des technologies plus complexes dès le départ. Des langages très performants mais plus exigeants en expertise peuvent générer des coûts élevés en maintenance, en recrutement et en évolution fonctionnelle. Python, en réduisant cette complexité initiale, offre un <strong>équilibre durable</strong> entre efficacité, évolutivité et maîtrise des coûts.</p>
<p>Bref, un peu d&rsquo;amour pour Python 😉</p>
<hr>
<h2 id="cas-concrets-où-la-différence-est-critique">Cas concrets où la différence est critique</h2>
<p>Dans nos projets, ce genre de gain est loin d’être théorique. Il est déterminant pour :</p>
<ul>
<li>🔄 <strong>outils de synchronisation</strong></li>
<li>📂 <strong>scan de dossiers autonomes</strong> avec plusieurs dizaines de milliers de fichiers</li>
<li>🧾 <strong>pipelines de traitement data</strong></li>
<li>🌐 <strong>serveurs de fichiers</strong></li>
<li>🧠 <strong>moteurs d’indexation</strong></li>
</ul>
<p>Diviser par deux le temps de scan, c’est :</p>
<ul>
<li>moins de charge CPU</li>
<li>moins d’I/O disque</li>
<li>moins de latence perçue</li>
<li>et une meilleure scalabilité globale</li>
</ul>
<hr>
<h2 id="recommandation">Recommandation</h2>
<p>👉 <strong>Pour tout nouveau développement Python impliquant un parcours récursif de fichiers :</strong></p>
<ul>
<li>privilégier <code>os.scandir()</code> avec une récursion explicite</li>
<li>réserver <code>os.walk()</code> aux scripts simples ou non critiques</li>
</ul>
<p>Ce choix seul peut transformer un script “acceptable” en un outil <strong>réellement industriel</strong>.</p>
<hr>
<p>Si vous travaillez sur des problématiques de <strong>performance, stockage, automatisation ou traitement de fichiers à grande échelle</strong>, je peux vous accompagner de l’architecture jusqu’à l’optimisation bas niveau.</p>
<p>📩 <strong>Un projet ?</strong> <a href="https://perrot.pt/contact/">Contactez-moi</a> : <strong>Arthur Perrot</strong>, expert infrastructure &amp; cybersécurité.</p>
]]></content:encoded></item><item><title>Pourquoi votre disque dur enterprise ne démarre pas : l’explication du 3.3 V et la solution Kapton</title><link>https://perrot.pt/fr/posts/pourquoi-votre-disque-dur-enterprise-ne-demarre-pas-lexplication-du-3-3-v-et-la-solution-kapton/</link><pubDate>Sun, 26 Oct 2025 20:05:39 +0000</pubDate><dc:creator>Arthur Perrot</dc:creator><category>Admin. sys</category><guid isPermaLink="true">https://perrot.pt/fr/posts/pourquoi-votre-disque-dur-enterprise-ne-demarre-pas-lexplication-du-3-3-v-et-la-solution-kapton/</guid><description>Un disque dur Enterprise ne tourne pas sur une alimentation PC récente ? La broche PWDIS du SATA 3.3 est en cause, et un ruban Kapton règle le problème.</description><content:encoded><![CDATA[<p>Chez nous, on aime aller au fond des choses.</p>
<p>Quand j&rsquo;installe dans notre infra un disque dur, qui refuse obstinément de tourner, je ne me contente pas d’un diagnostic rapide. Et dans bien des cas - surtout avec les <strong>Seagate Exos</strong>, <strong>HGST Ultrastar</strong> ou <strong>Toshiba Enterprise</strong> - la cause n’est pas le disque lui-même, mais… <strong>le connecteur d’alimentation SATA</strong>.</p>
<hr>
<h2 id="-le-coupable--la-norme-sata-33-et-la-fameuse-broche-33-v">🔍 Le coupable : la norme SATA 3.3 et la fameuse broche 3.3 V</h2>
<p>Depuis la norme <strong>SATA 3.3</strong>, trois petites broches à l’extrémité du connecteur d’alimentation ont vu leur rôle évoluer :</p>
<table>
  <thead>
      <tr>
          <th>Broche</th>
          <th>Tension</th>
          <th>Fonction (SATA 3.3)</th>
          <th>Effet</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>1</td>
          <td>3.3 V</td>
          <td>Alimentation</td>
          <td>—</td>
      </tr>
      <tr>
          <td>2</td>
          <td>3.3 V</td>
          <td>Alimentation</td>
          <td>—</td>
      </tr>
      <tr>
          <td>3</td>
          <td>3.3 V</td>
          <td><strong>PWDIS (Power Disable)</strong></td>
          <td>Permet de désactiver le disque à distance</td>
      </tr>
  </tbody>
</table>
<p>Les disques <strong>Enterprise</strong>, souvent conçus pour les serveurs Dell, HP ou Supermicro, interprètent la <strong>broche 3</strong> comme une commande de désactivation.<br>
👉 Résultat : <strong>si votre alimentation PC envoie du 3.3 V sur cette ligne</strong>, le disque croit qu’on lui ordonne de s’éteindre. Il ne tourne pas, ne chauffe pas, et reste invisible dans le BIOS.</p>
<p><img src="https://perrot.pt/posts/why-your-enterprise-hard-drive-wont-start-the-3-3-v-explanation-and-the-kapton-fix/sata-pinout.png" alt="Connecteur SATA d’un disque dur avec les broches 3.3 V, 5 V et 12 V annotées" width="800" height="523" loading="lazy" decoding="async"></p>
<hr>
<h2 id="-pourquoi-certaines-alimentations-posent-problème">⚡ Pourquoi certaines alimentations posent problème</h2>
<p>Les alimentations modernes (comme les <strong>Corsair CV550</strong>, BeQuiet!, Cooler Master ou Seasonic grand public) respectent <strong>scrupuleusement la norme SATA 3.3</strong>.<br>
Elles transmettent donc la tension <strong>3.3 V</strong> (le fameux fil <strong>orange</strong>) jusqu’aux broches du connecteur SATA.<br>
Sur un disque standard, aucun souci.<br>
Mais sur un <strong>disque Enterprise</strong>, cette tension active le signal <strong>PWDIS</strong>, qui bloque tout démarrage.</p>
<p>Et même si vous <strong>retirez le câble orange</strong>, certains modèles continuent à poser problème :</p>
<ul>
<li>le connecteur SATA peut <strong>faire contact mécanique</strong> avec les broches 1–3,</li>
<li>un <strong>rail interne</strong> de l’alimentation peut réinjecter une faible tension parasite,</li>
<li>ou le câblage interne garde la broche active malgré l’absence de fil apparent.</li>
</ul>
<p>👉 Le disque reste donc désactivé, même sans fil orange visible.</p>
<hr>
<h2 id="-les-solutions-connues-et-testées">🧠 Les solutions connues (et testées)</h2>
<table>
  <thead>
      <tr>
          <th>Méthode</th>
          <th>Description</th>
          <th>Fiabilité</th>
          <th>Revers</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>Adaptateur Molex → SATA</strong></td>
          <td>Le Molex ne transporte pas de 3.3 V (seulement 5 V &amp; 12 V).</td>
          <td>⭐⭐⭐⭐</td>
          <td>Nécessite un adaptateur.</td>
      </tr>
      <tr>
          <td><strong>Rallonge SATA “no 3.3 V”</strong></td>
          <td>Câble sans ligne orange.</td>
          <td>⭐⭐⭐</td>
          <td>Variable selon modèle.</td>
      </tr>
      <tr>
          <td><strong>Ruban Kapton sur les broches 1–3</strong></td>
          <td>Isolation physique des 3 premières broches du connecteur SATA.</td>
          <td>⭐⭐⭐⭐⭐</td>
          <td>Faut bien viser !</td>
      </tr>
      <tr>
          <td><strong>Scotch classique / téflon</strong></td>
          <td>Isolants, mais pas durables ni résistants à la chaleur.</td>
          <td>⭐</td>
          <td>Usage temporaire.</td>
      </tr>
  </tbody>
</table>
<hr>
<p><img src="https://perrot.pt/posts/why-your-enterprise-hard-drive-wont-start-the-3-3-v-explanation-and-the-kapton-fix/kapton-test_hu_2a63f23ee51d0d3.webp" srcset="/posts/why-your-enterprise-hard-drive-wont-start-the-3-3-v-explanation-and-the-kapton-fix/kapton-test_hu_80a68fbe0b5307d4.webp 1000w, /posts/why-your-enterprise-hard-drive-wont-start-the-3-3-v-explanation-and-the-kapton-fix/kapton-test_hu_2a63f23ee51d0d3.webp 1350w" sizes="(min-width: 800px) 732px, 100vw" alt="Connecteur SATA d’un disque dur avec un ruban jaune collé sur les premières broches" width="1350" height="1800" loading="lazy" decoding="async"></p>
<blockquote>
<p>Bon ok là c&rsquo;est pas du kapton, mais c&rsquo;était à titre de test !</p>
</blockquote>
<h2 id="-pourquoi-le-kapton-est-la-meilleure-solution">🟡 Pourquoi le <strong>Kapton</strong> est la meilleure solution</h2>
<p>Le <strong>Kapton</strong> (polyimide) est un ruban isolant conçu pour l’électronique :</p>
<ul>
<li>il résiste à la chaleur (jusqu’à +260 °C),</li>
<li>il ne laisse <strong>aucun résidu</strong> en cas de retrait,</li>
<li>il est <strong>ultra-fin</strong>, donc le connecteur SATA s’enfiche sans forcer.</li>
</ul>
<p>Pour neutraliser la ligne 3.3 V, il suffit :</p>
<ol>
<li>De couper un petit rectangle (~5 × 3 mm),</li>
<li>De le coller sur les <strong>3 premières broches</strong> à gauche du connecteur SATA (côté contacts dorés),</li>
<li>De rebrancher le câble d’alimentation.</li>
</ol>
<p>Votre disque redémarrera <strong>instantanément</strong> — et sans aucune modification définitive du câblage.</p>
<hr>
<h2 id="-en-résumé">🧩 En résumé</h2>
<table>
  <thead>
      <tr>
          <th>Symptôme</th>
          <th>Cause probable</th>
          <th>Solution</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Le disque ne tourne pas</td>
          <td>Ligne 3.3 V active → PWDIS bloqué</td>
          <td>Kapton sur pins 1–3</td>
      </tr>
      <tr>
          <td>Le disque marche sur un serveur, mais pas sur un PC</td>
          <td>Backplane serveur coupe la 3.3 V</td>
          <td>Adaptateur Molex → SATA</td>
      </tr>
      <tr>
          <td>Le câble SATA sans fil orange ne change rien</td>
          <td>Rail interne toujours actif</td>
          <td>Isolation physique nécessaire</td>
      </tr>
  </tbody>
</table>
<hr>
<h2 id="-conclusion">🧰 Conclusion</h2>
<p>Ce petit détail de conception, invisible à l’œil nu, explique <strong>des centaines de faux diagnostics “disque HS”</strong> chaque année.</p>
<p>Chez nous, on préfère comprendre plutôt que remplacer.<br>
Alors avant de renvoyer un disque “mort”, vérifiez vos câbles, vos rails… et si besoin, sortez le ruban <strong>Kapton</strong>.</p>
<blockquote>
<p>💡 Astuce : si vous travaillez souvent avec des disques Enterprise, gardez toujours quelques adaptateurs <strong>Molex → SATA</strong> et un rouleau de <strong>Kapton tape</strong> dans votre atelier.<br>
Cela vous évitera bien des sueurs froides.</p>
</blockquote>
]]></content:encoded></item><item><title>Bug réseau Bouygues – perturbation WireGuard entre IPv6 publique et IPv4 fixe</title><link>https://perrot.pt/fr/posts/bug-reseau-bouygues-perturbation-wireguard-entre-ipv6-publique-et-ipv4-fixe/</link><pubDate>Wed, 22 Oct 2025 00:54:57 +0000</pubDate><dc:creator>Arthur Perrot</dc:creator><category>Réseau</category><guid isPermaLink="true">https://perrot.pt/fr/posts/bug-reseau-bouygues-perturbation-wireguard-entre-ipv6-publique-et-ipv4-fixe/</guid><description>Depuis une mise à jour firmware, les Bbox Bouygues Wi-Fi 6 perturbent WireGuard : symptômes, cause probable (MTU réduite) et correctif MTU = 1380.</description><content:encoded><![CDATA[<h3 id="-constat-depuis-mi-octobre-2025">📅 Constat depuis mi-octobre 2025</h3>
<p>Une PME française cliente, spécialisée dans le conseil et employant une dizaine de collaborateurs en télétravail, nous a contactés : depuis environ deux semaines, plusieurs de ses utilisateurs connectés via des Bbox Wi-Fi 6 ou Wi-Fi 6E de Bouygues Telecom rencontraient une instabilité importante sur leurs connexions VPN de type <strong>WireGuard</strong>. Nous l&rsquo;avons aidée à diagnostiquer le problème.<br>
Le phénomène touche principalement les configurations suivantes :</p>
<ul>
<li>Tunnel <strong>entre un client en IPv6 publique (Bbox)</strong> et un <strong>serveur en IPv4 fixe</strong></li>
<li>Accès via <strong>pont WireGuard / réseau local routé</strong></li>
<li>Protocole <strong>UDP encapsulant du trafic TCP (SMB, SSH, RDP, HTTPS)</strong></li>
</ul>
<hr>
<h3 id="-symptômes-observés">🚨 Symptômes observés</h3>
<ul>
<li><strong>Ping</strong> fonctionnel (ICMP non impacté)</li>
<li><strong>RDP, SSH, SMB</strong> ou <strong>interfaces web</strong> qui se connectent puis se figent ou se déconnectent</li>
<li><strong>Fortes pertes de paquets</strong>, latence anormale sur les flux TCP lourds</li>
<li>Aucun blocage firewall apparent, ni reset TCP</li>
<li>Problème limité aux <strong>clients derrière Bbox Bouygues</strong>, les autres connexions (Free, Orange, SFR, Bouygues 5G, etc.) restant stables</li>
</ul>
<hr>
<h3 id="-cause-probable">🔍 Cause probable</h3>
<p>Les analyses réseau réalisées pendant le diagnostic indiquent une <strong>réduction effective de la MTU</strong> sur les interfaces WAN des Bbox Bouygues Wi-Fi 6 depuis la dernière mise à jour firmware.<br>
Cette mise à jour semble également <strong>forcer l’activation d’IPv6 (SLAAC / DHCPv6-PD)</strong>, créant un mélange IPv6/IPv4 non transparent pour les tunnels WireGuard.</p>
<p>Résultat :</p>
<ul>
<li>Les paquets UDP WireGuard dépassant <strong>~1408 octets</strong> sont fragmentés ou perdus</li>
<li>Le <strong>ping (petits paquets)</strong> continue de fonctionner</li>
<li>Les <strong>flux TCP encapsulés</strong> deviennent instables, particulièrement sur les connexions vers des serveurs IPv4 fixes.</li>
</ul>
<hr>
<h3 id="-solution-temporaire">🧰 Solution temporaire</h3>
<p>Aucune communication officielle n’a encore été publiée par Bouygues Telecom, mais la solution est simple et efficace :</p>
<h4 id="-sur-le-poste-client-wireguard-derrière-la-bbox">✅ Sur le poste client WireGuard (derrière la Bbox)</h4>
<p>Modifier le fichier de configuration :</p>
<div class="codeblock"><span class="codelang mono" aria-hidden="true">ini</span><button type="button" class="codecopy mono" data-done="Copié !" aria-label="Copier">Copier</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="k">[Interface]</span>
</span></span><span class="line"><span class="cl"><span class="na">MTU</span> <span class="o">=</span> <span class="s">1380</span></span></span></code></pre></div></div>
<p>Puis redémarrer le tunnel.</p>
<p>Cette seule ligne force la taille maximale des paquets WireGuard à un seuil compatible avec la nouvelle MTU Bouygues (~1408 octets effectifs), supprimant immédiatement les pertes et ralentissements.</p>
<h4 id="-en-complément">💡 En complément</h4>
<ul>
<li><strong>Désactiver IPv6</strong> sur la Bbox (ou côté client) si possible (non possible sur les bbox grand public, pas de retex coté pro)</li>
<li>Vérifier que le <strong>DNS utilisé</strong> est bien en IPv4</li>
<li>Ne <strong>pas modifier le MTU côté serveur</strong> (pfSense ou Proxmox) si les autres clients fonctionnent correctement</li>
</ul>
<hr>
<h3 id="-exemple-typique">🧩 Exemple typique</h3>
<p>Dans le cas de ce client, un poste avec l’adresse WireGuard <code>10.61.0.10</code> derrière une Bbox Bouygues tentant d’accéder à un serveur Proxmox <code>10.172.41.1</code> voyait :</p>
<ul>
<li>Ping stable</li>
<li>mais interface web (<code>:8006</code>) et SMB gelées.</li>
</ul>
<p>Après avoir ajouté <code>MTU = 1380</code>, le trafic est redevenu fluide immédiatement.</p>
<hr>
<h3 id="-en-résumé">🧠 En résumé</h3>
<table>
  <thead>
      <tr>
          <th>Élément</th>
          <th>Statut</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>FAI concerné</td>
          <td>Bouygues Telecom (Bbox Wi-Fi 6 / Wi-Fi 6E)</td>
      </tr>
      <tr>
          <td>Impact</td>
          <td>Fortes pertes de paquets sur tunnels WireGuard IPv6→IPv4</td>
      </tr>
      <tr>
          <td>Cause probable</td>
          <td>Réduction de la MTU et activation IPv6 auto via firmware</td>
      </tr>
      <tr>
          <td>Solution</td>
          <td>Forcer <code>MTU = 1380</code> côté client WireGuard</td>
      </tr>
      <tr>
          <td>Correctif Bouygues</td>
          <td>Non communiqué à ce jour</td>
      </tr>
  </tbody>
</table>
<hr>
<h3 id="-notre-recommandation">🛡️ Notre recommandation</h3>
<p>Je conseille à tous mes clients utilisant des tunnels WireGuard sur infrastructures Bouygues de :</p>
<ol>
<li><strong>vérifier la stabilité du tunnel via <code>ping -f -l 1472</code> (Windows) ou <code>ping -M do -s 1472</code> (Linux)</strong>,</li>
<li><strong>ajuster le MTU à 1380</strong> si nécessaire,</li>
<li><strong>désactiver temporairement IPv6</strong> si des instabilités persistent.</li>
</ol>
<p>Je suis ce comportement de près et mettrai à jour cette note dès qu’un correctif officiel Bouygues ou une évolution de firmware sera confirmée.</p>
<hr>
<p>💬 <strong>Besoin d’assistance ?</strong><br>
Je peux auditer votre tunnel WireGuard ou votre interconnexion multi-site.<br>
👉 <a href="https://perrot.pt/contact/">Contactez-moi</a> : <strong>Arthur Perrot</strong>, expert infrastructure &amp; cybersécurité.</p>
]]></content:encoded></item><item><title>Bien comprendre la notion de débits pour dimensionner votre réseau</title><link>https://perrot.pt/fr/posts/bien-comprendre-la-notion-de-debits-pour-dimensionner-votre-reseau/</link><pubDate>Mon, 22 Sep 2025 22:31:46 +0000</pubDate><dc:creator>Arthur Perrot</dc:creator><category>Réseau</category><category>Admin. sys</category><guid isPermaLink="true">https://perrot.pt/fr/posts/bien-comprendre-la-notion-de-debits-pour-dimensionner-votre-reseau/</guid><description>Bits ou octets, disque ou réseau : comment comparer les débits, estimer la durée d'un backup et dimensionner un réseau sans goulot d'étranglement.</description><content:encoded><![CDATA[<p>Nous voyons souvent des infrastructures bien équipées en serveurs et en stockage… mais limitées par leur réseau. Comprendre les notions de <strong>débit</strong> est essentiel pour éviter les goulots d’étranglement, en particulier lors de sauvegardes massives ou de transferts de données entre sites.</p>
<hr>
<h2 id="bits-octets--la-base">Bits, octets : la base</h2>
<ul>
<li><strong>1 octet (B)</strong> = <strong>8 bits (b)</strong>.</li>
<li>Les débits réseaux sont généralement exprimés en <strong>bits par seconde</strong> (ex. 1 Gb/s).</li>
<li>Les vitesses de disques ou de transferts de fichiers sont exprimées en <strong>octets par seconde</strong> (ex. 150 MB/s).</li>
</ul>
<p>👉 Résultat : <strong>1 Gb/s ≈ 125 MB/s</strong> en pratique.</p>
<hr>
<h2 id="tableau-comparatif-des-débits">Tableau comparatif des débits</h2>
<p>Voici un tableau simple pour mieux visualiser les vitesses typiques des disques et des réseaux, avec leurs équivalents en anglais et en français :</p>
<table>
  <thead>
      <tr>
          <th>Technologie</th>
          <th>Débit typique</th>
          <th>Équivalent en MB/s (Mo/s)</th>
          <th>Terme anglais</th>
          <th>Terme français</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>HDD 7200 rpm (14 To)</strong></td>
          <td>1,2–1,6 Gbit/s</td>
          <td>150–200 MB/s</td>
          <td>Hard Disk Drive</td>
          <td>Disque dur</td>
      </tr>
      <tr>
          <td><strong>SSD SATA 6G</strong></td>
          <td>4–4,4 Gbit/s</td>
          <td>500–550 MB/s</td>
          <td>SATA SSD</td>
          <td>SSD SATA</td>
      </tr>
      <tr>
          <td><strong>SSD NVMe PCIe 3.0 x4</strong></td>
          <td>24–28 Gbit/s</td>
          <td>3000–3500 MB/s</td>
          <td>NVMe SSD</td>
          <td>SSD NVMe</td>
      </tr>
      <tr>
          <td><strong>SSD NVMe PCIe 4.0 x4</strong></td>
          <td>40–56 Gbit/s</td>
          <td>5000–7000 MB/s</td>
          <td>Gen4 NVMe SSD</td>
          <td>SSD NVMe Gen4</td>
      </tr>
      <tr>
          <td><strong>Réseau 1 GbE</strong></td>
          <td>1 Gbit/s</td>
          <td>~125 MB/s</td>
          <td>1 Gigabit Ethernet</td>
          <td>Réseau 1 Gigabit</td>
      </tr>
      <tr>
          <td><strong>Réseau 2,5 GbE</strong></td>
          <td>2,5 Gbit/s</td>
          <td>~312 MB/s</td>
          <td>2.5 Gigabit Ethernet</td>
          <td>Réseau 2,5 Gigabit</td>
      </tr>
      <tr>
          <td><strong>Réseau 10 GbE</strong></td>
          <td>10 Gbit/s</td>
          <td>~1250 MB/s</td>
          <td>10 Gigabit Ethernet</td>
          <td>Réseau 10 Gigabit</td>
      </tr>
      <tr>
          <td><strong>Réseau 40 GbE</strong></td>
          <td>40 Gbit/s</td>
          <td>~5000 MB/s</td>
          <td>40 Gigabit Ethernet</td>
          <td>Réseau 40 Gigabit</td>
      </tr>
  </tbody>
</table>
<hr>
<h2 id="exemple-concret--une-sauvegarde-de-1-to">Exemple concret : une sauvegarde de 1 To</h2>
<p>Imaginons une sauvegarde de <strong>1 To</strong> (≈ 1000 Go) à transférer vers un serveur de backup. Voici le temps que cela prend selon le réseau :</p>
<ul>
<li><strong>Réseau 1 GbE</strong> (125 MB/s max) :<br>
1000 Go ÷ 125 MB/s ≈ <strong>2h15</strong></li>
<li><strong>Réseau 10 GbE</strong> (1250 MB/s max) :<br>
1000 Go ÷ 1250 MB/s ≈ <strong>13 minutes</strong></li>
<li><strong>Réseau 40 GbE</strong> (5000 MB/s max) :<br>
1000 Go ÷ 5000 MB/s ≈ <strong>3 minutes</strong></li>
</ul>
<p>👉 Vous voyez le problème : sur du <strong>1 GbE</strong>, un simple backup peut durer <strong>des heures</strong>, et pendant ce temps votre réseau est saturé.</p>
<hr>
<h2 id="le-goulot-détranglement">Le goulot d’étranglement</h2>
<p>Un principe clé à retenir : <strong>la vitesse finale dépend toujours du maillon le plus lent</strong>.</p>
<p>Prenons un exemple : vous devez sauvegarder <strong>4 To de données</strong>.</p>
<ul>
<li>Sur des <strong>SSD NVMe</strong> capables de 3500 MB/s, la copie pure disque → disque pourrait théoriquement se faire en <strong>moins de 20 minutes</strong>.</li>
<li>Mais si votre réseau n’est qu’en <strong>1 GbE</strong> (≈ 125 MB/s réels), le transfert tombe à :<br>
4000 Go ÷ 125 MB/s ≈ <strong>9 heures</strong>.</li>
<li>Avec un <strong>réseau 10 GbE</strong> (≈ 1250 MB/s), le même backup prendrait seulement :<br>
4000 Go ÷ 1250 MB/s ≈ <strong>53 minutes</strong>.</li>
</ul>
<p>👉 Moralité : vos disques peuvent être ultra rapides, mais si le réseau est trop lent, c’est lui qui <strong>bloquera la performance globale</strong>.</p>
<hr>
<h2 id="comment-dimensionner-intelligemment-">Comment dimensionner intelligemment ?</h2>
<ol>
<li><strong>Évaluer vos flux de données</strong> : combien de To de backup chaque nuit, combien de VM répliquées, quels temps de transfert acceptables.</li>
<li><strong>Comparer disque et réseau</strong> : s’assurer que le réseau ne bride pas les disques, et inversement.</li>
<li><strong>Prévoir de la marge</strong> : avec la croissance des données, il vaut mieux surdimensionner légèrement.</li>
<li><strong>Segmenter vos usages</strong> : séparer trafic de production, backups, et répliques pour éviter la saturation globale.</li>
</ol>
<hr>
<h2 id="conclusion">Conclusion</h2>
<p>Bien dimensionner le <strong>débit réseau</strong> est aussi critique que de choisir les bons serveurs.<br>
J’accompagne les entreprises à <strong>auditer, optimiser et moderniser leurs infrastructures réseau et stockage</strong>. L’objectif : garantir que vos sauvegardes passent en temps voulu, sans saturer vos flux critiques.</p>
<p>📩 <strong>Un doute sur le dimensionnement de votre réseau ?</strong><br>
👉 <a href="https://perrot.pt/contact/">Contactez-moi</a> : <strong>Arthur Perrot</strong>, expert infrastructure &amp; cybersécurité.</p>
]]></content:encoded></item><item><title>Comprendre la différence entre useradd et adduser sous Linux</title><link>https://perrot.pt/fr/posts/comprendre-la-difference-entre-useradd-et-adduser-sous-linux/</link><pubDate>Wed, 10 Sep 2025 14:07:05 +0000</pubDate><dc:creator>Arthur Perrot</dc:creator><category>Admin. sys</category><guid isPermaLink="true">https://perrot.pt/fr/posts/comprendre-la-difference-entre-useradd-et-adduser-sous-linux/</guid><description>useradd est l'outil bas niveau, adduser un wrapper Perl interactif propre à Debian : différences pratiques et lequel choisir selon le contexte.</description><content:encoded><![CDATA[<p>Lorsqu’on administre un système Linux, il est fréquent d’avoir besoin de créer de nouveaux utilisateurs. Deux commandes existent pour cela : <strong><code>useradd</code></strong> et <strong><code>adduser</code></strong>. Elles semblent équivalentes à première vue, mais leur origine et leur fonctionnement diffèrent sensiblement.</p>
<h2 id="1-useradd--la-commande-native">1. <code>useradd</code> : la commande native</h2>
<p><code>useradd</code> fait partie des <strong>outils bas niveau de gestion des comptes utilisateurs</strong> fournis par le paquet <code>passwd</code> (souvent issu du projet <a href="https://github.com/shadow-maint/shadow">shadow-utils</a> dans les distributions modernes).</p>
<ul>
<li><strong>Rôle</strong> : créer un compte utilisateur en manipulant directement les fichiers systèmes comme <code>/etc/passwd</code>, <code>/etc/shadow</code>, <code>/etc/group</code> et <code>/etc/skel</code>.</li>
<li><strong>Comportement</strong> : par défaut, <code>useradd</code> est assez strict et minimaliste. Si aucune option n’est passée, l’utilisateur peut être créé <strong>sans répertoire personnel</strong> ni shell défini.</li>
<li><strong>Syntaxe</strong> : <code>useradd -m -s /bin/bash nouvelutilisateur</code> Ici, <code>-m</code> force la création du home directory et <code>-s</code> définit le shell par défaut.</li>
</ul>
<p>En résumé, <code>useradd</code> est un outil <strong>bas niveau</strong>, destiné à être utilisé dans des scripts ou par des administrateurs expérimentés qui savent exactement quels paramètres fournir.</p>
<hr>
<h2 id="2-adduser--le-wrapper-convivial">2. <code>adduser</code> : le wrapper convivial</h2>
<p><code>adduser</code> n’est pas une commande native du noyau ou des utilitaires de base, mais un <strong>script Perl</strong> (dans Debian/Ubuntu et dérivées). Son but est de rendre l’expérience plus simple et plus interactive.</p>
<ul>
<li><strong>Rôle</strong> : fournir une <strong>interface de haut niveau</strong> pour <code>useradd</code>.</li>
<li><strong>Fonctionnement</strong> : il agit comme un <strong>wrapper</strong> : il appelle <code>useradd</code> (et parfois <code>usermod</code>) en arrière-plan, mais ajoute une logique de vérification et une interaction avec l’administrateur.</li>
<li><strong>Interaction</strong> : lorsque vous tapez : <code>adduser nouvelutilisateur</code> le script va vous poser des questions : mot de passe, nom complet, informations supplémentaires… puis créer automatiquement le répertoire personnel, copier les fichiers de <code>/etc/skel</code>, configurer les droits, etc.</li>
</ul>
<h3 id="pourquoi-en-perl-">Pourquoi en Perl ?</h3>
<p>Historiquement, dans Debian et ses dérivés, <code>adduser</code> a été écrit en <strong>Perl</strong> afin de bénéficier rapidement d’un langage de script puissant et portable pour ajouter cette couche d’ergonomie au-dessus des outils système existants.</p>
<ul>
<li>Cela permettait d’avoir une logique conditionnelle et interactive, plus difficile à maintenir en pur shell.</li>
<li>Avec Perl, la gestion des chaînes, des formulaires interactifs et de la configuration est plus simple.</li>
</ul>
<hr>
<h2 id="3-différences-pratiques-entre-useradd-et-adduser">3. Différences pratiques entre <code>useradd</code> et <code>adduser</code></h2>
<table>
  <thead>
      <tr>
          <th>Aspect</th>
          <th><code>useradd</code></th>
          <th><code>adduser</code></th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Origine</td>
          <td>Outil bas niveau (<code>shadow-utils</code>)</td>
          <td>Script Perl (Debian/Ubuntu)</td>
      </tr>
      <tr>
          <td>Répertoire home</td>
          <td>Non créé par défaut</td>
          <td>Toujours créé</td>
      </tr>
      <tr>
          <td>Shell par défaut</td>
          <td>Peut être <code>/bin/sh</code> ou non défini</td>
          <td>Défini automatiquement (souvent <code>/bin/bash</code>)</td>
      </tr>
      <tr>
          <td>Interaction</td>
          <td>Aucune, nécessite toutes les options</td>
          <td>Pose des questions (mot de passe, infos)</td>
      </tr>
      <tr>
          <td>Portabilité</td>
          <td>Présent sur toutes les distros</td>
          <td>Spécifique aux dérivées Debian (Ubuntu, Mint, etc.)</td>
      </tr>
      <tr>
          <td>Cible</td>
          <td>Scripts et admins experts</td>
          <td>Administrateurs cherchant la simplicité</td>
      </tr>
  </tbody>
</table>
<hr>
<h2 id="4-conclusion--lequel-utiliser-">4. Conclusion : lequel utiliser ?</h2>
<ul>
<li>Sur <strong>Debian/Ubuntu</strong> et distributions dérivées, <strong><code>adduser</code></strong> est recommandé pour la plupart des cas, car il est plus sûr et évite les oublis (home directory, shell, etc.).</li>
<li>Sur d’autres systèmes comme <strong>Red Hat, CentOS, Fedora</strong>, seule la commande <strong><code>useradd</code></strong> est disponible — ce qui reflète bien sa nature d’outil universel.</li>
</ul>
<p>En pratique, <strong><code>adduser</code> n’est qu’un wrapper en Perl autour de <code>useradd</code></strong>, mais il illustre bien une philosophie Debian : rendre l’administration plus accessible tout en gardant la puissance des outils Unix en arrière-plan.</p>
<hr>
<p>👉 Lorsqu’on automatise la gestion des comptes dans nos infrastructures clientes, on privilégie <code>useradd</code> dans les scripts (prévisible, universel), et <code>adduser</code> dans l’administration manuelle quotidienne (ergonomique, sécurisé).</p>
]]></content:encoded></item><item><title>Config VSCode : appliquer un coding style avec les rulers</title><link>https://perrot.pt/fr/posts/config-vscode-appliquer-un-coding-style-avec-les-rulers/</link><pubDate>Sun, 24 Aug 2025 01:29:23 +0000</pubDate><dc:creator>Arthur Perrot</dc:creator><category>Dev</category><guid isPermaLink="true">https://perrot.pt/fr/posts/config-vscode-appliquer-un-coding-style-avec-les-rulers/</guid><description>Une config settings.json minimale pour VSCode : ruler à 80 colonnes, indentation et espaces visibles, pour un coding style partagé dans l'équipe.</description><content:encoded><![CDATA[<h2 id="pourquoi-un-ruler-">Pourquoi un ruler ?</h2>
<p>Dans une équipe, respecter un <strong>style de code</strong> est essentiel.<br>
Un des outils simples de VSCode est le <strong>ruler</strong> : une ligne verticale qui s’affiche dans l’éditeur à une colonne donnée (par ex. 80 caractères).</p>
<p>Cela permet de se rappeler <strong>jusqu’où on écrit par ligne</strong> : meilleure lisibilité, cohérence entre développeurs, respect des bonnes pratiques.</p>
<hr>
<h2 id="exemple-de-configuration-minimaliste">Exemple de configuration minimaliste</h2>
<p>Voici une configuration <code>settings.json</code> pour VSCode, concentrée sur l’essentiel :</p>
<div class="codeblock"><span class="codelang mono" aria-hidden="true">json</span><button type="button" class="codecopy mono" data-done="Copié !" aria-label="Copier">Copier</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;editor.rulers&#34;</span><span class="p">:</span> <span class="p">[</span><span class="mi">80</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;workbench.startupEditor&#34;</span><span class="p">:</span> <span class="s2">&#34;newUntitledFile&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;git.autofetch&#34;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;editor.detectIndentation&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;editor.renderWhitespace&#34;</span><span class="p">:</span> <span class="s2">&#34;all&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;workbench.panel.defaultLocation&#34;</span><span class="p">:</span> <span class="s2">&#34;right&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;terminal.integrated.cursorStyle&#34;</span><span class="p">:</span> <span class="s2">&#34;line&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;terminal.integrated.fontFamily&#34;</span><span class="p">:</span> <span class="s2">&#34;monospace&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;window.menuBarVisibility&#34;</span><span class="p">:</span> <span class="s2">&#34;toggle&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;diffEditor.ignoreTrimWhitespace&#34;</span><span class="p">:</span> <span class="kc">false</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<hr>
<h2 id="explications-rapides">Explications rapides</h2>
<ol>
<li><strong><code>&quot;editor.rulers&quot;: [80]</code></strong><br>
Affiche un guide vertical à 80 colonnes → idéal pour garder un code lisible et homogène.</li>
<li><strong><code>&quot;workbench.startupEditor&quot;: &quot;newUntitledFile&quot;</code></strong><br>
VSCode ouvre directement un fichier vide au démarrage → gain de temps, on code tout de suite.</li>
<li><strong><code>&quot;git.autofetch&quot;: true</code></strong><br>
Met automatiquement à jour les infos Git distantes → on voit toujours l’état réel du projet sans fetch manuel.</li>
<li><strong><code>&quot;editor.detectIndentation&quot;: false</code></strong><br>
Empêche VSCode d’essayer de “deviner” les indentations → l’éditeur suit vos règles, pas celles détectées dans le fichier.</li>
<li><strong><code>&quot;editor.renderWhitespace&quot;: &quot;all&quot;</code></strong><br>
Affiche visuellement espaces et tabulations → évite les erreurs de style invisibles.</li>
<li><strong><code>&quot;workbench.panel.defaultLocation&quot;: &quot;right&quot;</code></strong><br>
Place la console à droite plutôt qu’en bas.<br>
👉 Si on se limite volontairement à des lignes de 80 colonnes, mieux vaut garder plus d’espace vertical que horizontal.<br>
Cela reste toutefois un <strong>choix personnel</strong>, chacun adapte à son confort.</li>
</ol>
<hr>
<h2 id="un-style-clair-pour-toute-léquipe">Un style clair, pour toute l’équipe</h2>
<p>Mettre en place ces règles simples dans VSCode permet :</p>
<ul>
<li>d’avoir un <strong>code plus lisible</strong>,</li>
<li>de garantir une <strong>cohérence entre développeurs</strong>,</li>
<li>et d’éviter les erreurs invisibles (espaces, indentations incohérentes).</li>
</ul>
<p>Un petit ajustement dans votre <code>settings.json</code>, et vous posez les bases d’un <strong>coding style partagé</strong> dans vos projets.</p>
]]></content:encoded></item><item><title>Snippet VSCode : Générer automatiquement vos #ifndef en C/C++</title><link>https://perrot.pt/fr/posts/snippet-vscode-generer-automatiquement-vos-ifndef-en-c-c/</link><pubDate>Sun, 24 Aug 2025 01:25:42 +0000</pubDate><dc:creator>Arthur Perrot</dc:creator><category>Astuces rapides</category><category>Dev</category><guid isPermaLink="true">https://perrot.pt/fr/posts/snippet-vscode-generer-automatiquement-vos-ifndef-en-c-c/</guid><description>Un snippet VSCode qui génère les header guards #ifndef / #define / #endif d'un fichier C ou C++ à partir de son nom, en un Tab.</description><content:encoded><![CDATA[<h2 id="pourquoi-perdre-du-temps-à-réécrire-des-garde-fous-de-header-">Pourquoi perdre du temps à réécrire des garde-fous de header ?</h2>
<p>Quand on code en C ou C++, chaque fichier <code>.h</code> doit être protégé avec un <strong>header guard</strong> (<code>#ifndef</code>, <code>#define</code>, <code>#endif</code>) pour éviter les inclusions multiples.<br>
On a tous déjà fait du <strong>copier-coller</strong> un peu mécanique… mais VSCode permet d’automatiser ça en <strong>un snippet ultra simple</strong>.</p>
<h2 id="le-snippet-magique">Le snippet magique</h2>
<p>Voici un exemple de snippet à ajouter dans vos <strong>User Snippets</strong> VSCode (menu <em>Preferences &gt; User Snippets</em> → choisir <code>cpp.json</code>):</p>
<div class="codeblock"><span class="codelang mono" aria-hidden="true">json</span><button type="button" class="codecopy mono" data-done="Copié !" aria-label="Copier">Copier</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="s2">&#34;C C++ Header&#34;</span><span class="err">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;scope&#34;</span><span class="p">:</span> <span class="s2">&#34;c, cpp&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;prefix&#34;</span><span class="p">:</span> <span class="s2">&#34;header&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;description&#34;</span><span class="p">:</span> <span class="s2">&#34;Add #ifndef, #define and #endif&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;body&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;#ifndef ${TM_FILENAME_BASE/(.*)/${1:/upcase}/}_H&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;#define ${TM_FILENAME_BASE/(.*)/${1:/upcase}/}_H&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;$0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;#endif&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<h2 id="comment-ça-marche-">Comment ça marche ?</h2>
<ul>
<li>Tapez simplement <code>header</code> dans un fichier <code>.h</code></li>
<li>Appuyez sur <code>Tab</code> → VSCode génère automatiquement :</li>
</ul>
<div class="codeblock"><span class="codelang mono" aria-hidden="true">c</span><button type="button" class="codecopy mono" data-done="Copié !" aria-label="Copier">Copier</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#ifndef MONFICHIER_H
</span></span></span><span class="line"><span class="cl"><span class="cp">#define MONFICHIER_H
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Votre code ici
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cp">#endif</span></span></span></code></pre></div></div>
<p>Le nom du fichier est repris automatiquement et converti en majuscules. 🚀</p>
<h2 id="gain-de-temps--de-rigueur">Gain de temps &amp; de rigueur</h2>
<ul>
<li>Plus besoin d’écrire les guards à la main</li>
<li>Uniformité garantie sur tous vos headers</li>
<li>Moins d’erreurs d’inclusion multiple</li>
</ul>
<p>Un petit snippet, un <strong>gros confort au quotidien</strong>.</p>
]]></content:encoded></item><item><title>Désactiver le beep agaçant de Bash</title><link>https://perrot.pt/fr/posts/desactiver-le-beep-agacant-de-bash/</link><pubDate>Sun, 24 Aug 2025 01:22:50 +0000</pubDate><dc:creator>Arthur Perrot</dc:creator><category>Astuces rapides</category><guid isPermaLink="true">https://perrot.pt/fr/posts/desactiver-le-beep-agacant-de-bash/</guid><description>Une ligne dans /etc/inputrc suffit pour supprimer le bip de Bash, ou le remplacer par un signal visuel.</description><content:encoded><![CDATA[<p>Qui n’a jamais été surpris (ou agacé 😅) par le <strong>“bip” sonore</strong> que Bash émet lorsqu’on fait une mauvaise commande ou une erreur de frappe ?<br>
Bonne nouvelle : il est possible de désactiver ce signal sonore très simplement.</p>
<p>Il suffit d’éditer le fichier <code>/etc/inputrc</code> et d’ajouter (ou décommenter) la ligne suivante :</p>
<div class="codeblock"><span class="codelang mono" aria-hidden="true">ini</span><button type="button" class="codecopy mono" data-done="Copié !" aria-label="Copier">Copier</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="na">set bell-style none</span></span></span></code></pre></div></div>
<p>Enregistrez, rechargez votre session, et terminé : plus de <em>beep</em> intempestif ! 🎉</p>
<p>👉 Astuce bonus : vous pouvez aussi remplacer le <em>beep</em> par un <strong>signal visuel</strong> en utilisant :</p>
<div class="codeblock"><span class="codelang mono" aria-hidden="true">ini</span><button type="button" class="codecopy mono" data-done="Copié !" aria-label="Copier">Copier</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="na">set bell-style visible</span></span></span></code></pre></div></div>
]]></content:encoded></item><item><title>Transformer un dossier en serveur de fichiers instantané</title><link>https://perrot.pt/fr/posts/transformer-un-dossier-en-serveur-de-fichiers-instantane/</link><pubDate>Sun, 24 Aug 2025 01:22:12 +0000</pubDate><dc:creator>Arthur Perrot</dc:creator><category>Admin. sys</category><category>Astuces rapides</category><guid isPermaLink="true">https://perrot.pt/fr/posts/transformer-un-dossier-en-serveur-de-fichiers-instantane/</guid><description>Partager un dossier en une commande avec le serveur HTTP intégré à Python, sans cloud ni clé USB, et les limites à connaître.</description><content:encoded><![CDATA[<p>Besoin de partager rapidement des fichiers présents dans un dossier, sans passer par un cloud ou une clé USB ?<br>
Python propose une solution ultra-rapide :</p>
<div class="codeblock"><span class="codelang mono" aria-hidden="true">bash</span><button type="button" class="codecopy mono" data-done="Copié !" aria-label="Copier">Copier</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">python3 -m http.server <span class="m">8000</span></span></span></code></pre></div></div>
<p>Par défaut, cette commande lance un <strong>serveur HTTP</strong> qui rend accessible le dossier courant sur le port 8000.<br>
Il suffit alors de se connecter à <code>http://votre_ip:8000</code> depuis un navigateur pour télécharger vos fichiers.</p>
<p>✅ Idéal pour :</p>
<ul>
<li>partager des fichiers sur un réseau local,</li>
<li>dépanner un collègue,</li>
<li>accéder à vos données depuis un autre appareil.</li>
</ul>
<p>⚠️ Attention : ce serveur est basique et ne gère pas la sécurité. À utiliser plutôt dans un cadre privé ou ponctuel.</p>
]]></content:encoded></item><item><title>Simuler une frappe clavier depuis le presse-papier</title><link>https://perrot.pt/fr/posts/simuler-une-frappe-clavier-depuis-le-presse-papier/</link><pubDate>Sun, 24 Aug 2025 01:21:19 +0000</pubDate><dc:creator>Arthur Perrot</dc:creator><category>Astuces rapides</category><guid isPermaLink="true">https://perrot.pt/fr/posts/simuler-une-frappe-clavier-depuis-le-presse-papier/</guid><description>Contourner un champ qui bloque le copier-coller sous Linux en rejouant le presse-papier comme une frappe clavier, avec xclip et xdotool.</description><content:encoded><![CDATA[<p>Il arrive parfois qu’un site ou une application bloque le <strong>copier-coller</strong> dans certains champs de texte (par exemple un mot de passe ou un formulaire).<br>
Bonne nouvelle : sous Linux, on peut contourner cette limitation en simulant la frappe du contenu du presse-papier comme si vous le tapiez au clavier.</p>
<p>Voici la commande magique :</p>
<div class="codeblock"><span class="codelang mono" aria-hidden="true">bash</span><button type="button" class="codecopy mono" data-done="Copié !" aria-label="Copier">Copier</button><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">xclip -selection clipboard -out <span class="p">|</span> tr <span class="se">\n</span> <span class="se">\r</span> <span class="p">|</span> xdotool selectwindow windowfocus <span class="nb">type</span> --clearmodifiers --delay <span class="m">25</span> --window %@ --file -</span></span></code></pre></div></div>
<p>👉 Explications rapides :</p>
<ul>
<li><strong>xclip</strong> : récupère le contenu du presse-papier.</li>
<li><strong>tr \n \r</strong> : transforme les retours à la ligne pour éviter les bugs.</li>
<li><strong>xdotool type</strong> : simule la frappe caractère par caractère, comme un clavier humain.</li>
</ul>
<p>Pratique, simple et 100% efficace pour gagner du temps !</p>
]]></content:encoded></item></channel></rss>