
Cliquez sur un ou plusieurs tags pour appliquer un filtre sur la liste des notes de type "Journaux" :
Résultat de la recherche (100 notes) :
Mercredi 8 octobre 2025
Journal du mercredi 08 octobre 2025 à 14:38
Il y a quelques jours, j'ai découvert osquery. Aujourd'hui, je découvre ici que l'agent du service Oneleet utilise osquery.
Cependant, son usage et son intérêt me semblent assez limités pour le moment 🤔 : https://docs.oneleet.com/oneleet-agent/data-collection .
Journal du mercredi 08 octobre 2025 à 14:29
#JaiDécouvert le nom officiel des attaques par social engineering suivantes :
Lundi 29 septembre 2025
Journal du lundi 29 septembre 2025 à 18:57
#OnMaPartagé ce projet d'imprimante Open source : Open Printer.
J'adore l'idée 🙂.
Articles qui parlent de ce projet :
- The Open Printer Is a Raspberry Pi Zero W-Powered, Fully-Open, Highly-Flexible Inkjet Printer
- Reddit : One Step Closer to an All-in-one Modern Word Processor: A Compact, Open-Source Printer/
Pour le moment, aucun commentaire sur Hacker News :
Vendredi 26 septembre 2025
Journal du vendredi 26 septembre 2025 à 14:12
Dans l'article "Bilan des Journées du Logiciel Libre 2025" du blog du LeBureau.coop, j'ai découvert et j'ai écouté la #vidéo "(Ne) détruisons (pas) le capitalisme avec une coopérative - Agnès Haasser et Arthur Vuillard" des Journées du Logiciel Libre 2025.
J'y ai appris de nombreuses précisions au sujet des SCOP, SCIC, CAE…
Mercredi 24 septembre 2025
Journal du mercredi 24 septembre 2025 à 18:03
Dans ce billet du blog de Bluefin #JaiDécouvert Bazaar (https://github.com/kolunmi/bazaar).
Bazaar is a new app store for GNOME with a focus on discovering and installing applications and add-ons from Flatpak remotes, particularly Flathub ...
Bazaar is fast and highly multi-threaded, guaranteeing a smooth experience in the user interface. You can queue as many downloads as you wish and run them while perusing Flathub's latest releases. This is due to the UI being completely decoupled from all backend operations.
Bazaar est une alternative à l'application officielle GNOME nommée gnome-software.
Contrairement à gnome-software qui est basée sur PackageKit et gère différents types de packages (rpm, DEB, Flatpak, Snap, etc.), Bazaar a un périmètre plus limité et se concentre exclusivement sur les packages Flatpak.
Dans un premier temps, je me suis demandé quel était l'intérêt de créer une nouvelle GUI pour installer des packages, pourquoi l'auteur n'a pas choisi de contribuer à gnome-software ?
J'ai trouvé une réponse dans ce thread.
Bazaar est une application avec une vision tranchée :
- support uniquement le repository Flathub (qui contient seulement des packages Flatpak) ;
- mise en avant de solution pour faire des donations.
Cette vision a permis à l'auteur de créer Bazaar en mai 2025, à partir de zéro, avec une implémentation plus direct (pas de support PackageKit…).
Cela lui a permis aussi de se consacrer fortement sur l'expérience utilisateur.
Après avoir testé l'application, je constate que contrairement à gnome-software, toutes les tâches s'exécutent de manière asynchrone. À la différence de gnome-software, Bazaar évite de recharger constamment l'index des packages après chaque opération , ce qui rend l'expérience utilisateur excellente 🙂.
Bazaar is fast and highly multi-threaded, guaranteeing a smooth experience in the user interface. You can queue as many downloads as you wish and run them while perusing Flathub's latest releases. This is due to the UI being completely decoupled from all backend operations.
Je tiens tout de même à préciser que la version 49 de gnome-software a fait des progrès à ce sujet, un gros travail de refactoring a été fait sur 3 ans (73 Merge Request 😮) pour apporter le support de threading dans gnome-software.
Je pense utiliser Bazaar dans Projet 26 - "Expérimentation de migration de deux utilisateurs grand public vers des laptops sous Fedora".
Journal du mercredi 24 septembre 2025 à 17:11
Dans la documentation de Flight Control #JaiDécouvert le terme ClickOps :
Features and use cases Flight Control aims to support:
- Declarative APIs well-suited for GitOps management.
- ...
- Web UI to manage and monitor devices and applications for ClickOps management.
- ...
Je trouve que ClickOps constitue une terminologie plus rigoureuse pour désigner ce qu'on appelle communément clickodrome.
Mardi 23 septembre 2025
Journal du mardi 23 septembre 2025 à 22:41
Un ami vient de me partager l'application Android et iOS nommée Saracroche (https://cbouvat.com/saracroche/) :
Saracroche est une application open source iOS et Android qui bloque les appels indésirables. Elle utilise la fonctionnalité native de blocage d'appels pour empêcher les appels provenant de numéros gênants, comme ceux utilisés pour le démarchage commercial ou les arnaques.
J'adore le nom de l'application 😉.
J'ai lu les articles suivants :
- Sarah qui ? Ça raccroche !
- Saracroche, l'appli libre par un frenchy usé par les appels indésirables, pour iOS
Depuis août 2024, j'utilise SpamBlocker. À ce stade, il me semble que l'avantage principal de Saracroche réside dans sa disponibilité sur iOS.
Cela fait longtemps que je n'ai plus d'appels indésirables sur mon Fairphone 5 - 5G.
Pour le moment, je ne vois pas l'intérêt de tester Saracroche.
Journal du mardi 23 septembre 2025 à 16:23
En vrac, quelques projets que je viens de découvrir :
L'origine du nom "Fedora Silverblue"
La lecture du document "Team Silverblue - The Origins " m'a appris de nombreuses choses sur les origines de la distribution Fedora Silverblue.
Avant 2018, le prédécesseur de Fedora Silverblue s'appelait "Fedora Atomic Workstation", défini comme : "image-mode container-based Fedora Workstation based on rpm-ostree".
À ses débuts, Fedora Atomic Workstation était un side project, développé et utilisé par quelques membres seulement de l'équipe Project Atomic (version du site de 2017).
Durant la conférence DevConf.CZ de janvier 2018, des participants ont présenté leur projet "Atomic Desktop". Suite à cette présentation, Owen Taylor, Sanja Bonic et Matthias Clasen ont créé un Special interest group (SIG) dédié à ce sujet dans la communauté Fedora : https://fedoraproject.org/wiki/SIGs/AtomicDesktops.
Voici comment le nom "Team Silverblue" a été choisi 2018 par Matthias Clasen et Sanja Bonic :
Couldn’t you come up with a better name?
No.
And in more detail: Matthias and Sanja have vetted over 150 words and word combination for something suitable, starting with tree names and going to atoms, physics, nature, landscapes.
Several other people have been asked to contribute. The entire process lasted for roughly 2 months, culminating in an open discussion of naming in the SIG meeting on April 16.
On April 19, Matthias and Sanja decided for the name Team Silverblue, because it was :
- available on Twitter, GitHub, and as a .org domain
- makes sense and sounds nice within the Fedora realm (color alignment)
- opens up fun and entertaining ways to have swag (silver-blue wigs, sports jerseys with the logo on it, phrasing like “Go, Team Silverblue!”, “Want to join the Team and improve Silverblue?”)
- works as Fedora Silverblue or Team Silverblue without losing branding investment
"Team Silverblue" a ensuite donné le nom Fedora Silverblue.
Le document indique que Red Hat a acquis CoreOS juste quelques jours après le lancement de cette idée de projet par les développeurs. S'agit-il d'une coïncidence heureuse 🤔 ? Le document ne le précise pas.
Lundi 22 septembre 2025
Journal du lundi 22 septembre 2025 à 21:26
Dans le cadre du Projet 34 - "Déployer un cluster k3s et Kubevirt sous CoreOS dans mon Homelab", j'étudie CoreOS.
Dans la page "Fedora CoreOS Release Notes stable" je vois quelques packages mis en avant :
Je constate que CoreOS installe par défaut containerd, moby-engine et podman.
Information de type #mémento #mémo au sujet de containerd, moby-engine et podman.
- Kubernetes intéragie directement avec containerd.
- Depuis 2017, Docker est basé sur containerd + moby-engine. Sous Fedora, la commande
docker
est installée par le packagedocker-cli
. - podman est une alternative rootless à Docker. podman n'est pas basé sur containerd, ni moby-engine.
Journal du lundi 22 septembre 2025 à 17:50
#JaiDécouvert la page Project MINI RACK : https://mini-rack.jeffgeerling.com
Ce sujet m'intéresse, car je cherche une solution élégante pour remplacer ce bazar !
Journal du lundi 22 septembre 2025 à 17:30
Je viens de publier le "Projet 34 - "Déployer un cluster k3s et Kubevirt sous CoreOS dans mon Homelab"".
Journal du lundi 22 septembre 2025 à 16:27
Dans le projet Homepage, #JaiDécouvert le projet Open-Meteo : https://open-meteo.com
Open-Meteo is an open-source weather API and offers free access for non-commercial use. No API key required. Start using it n
Mémento au sujet de QEMU et de sa configuration réseau
Note de type #mémento #mémo au sujet des fonctionnalités network de QEMU.
Pour bien comprendre le fonctionnement de la configuration network de QEMU, il est important de bien saisir deux concepts :
- les "virtual network device" (
-device
) - les "network backend" (
-netdev
)
There are two parts to networking within QEMU:
- the virtual network device that is provided to the guest (e.g. a PCI network card).
- the network backend that interacts with the emulated NIC (e.g. puts packets onto the host's network).
Voici toute la liste des network backend supportés par QEMU :
$ qemu-system-x86_64 -netdev help
Available netdev backend types:
socket
stream
dgram
hubport
tap
user
l2tpv3
bridge
af-xdp
vhost-user
vhost-vdpa
Dans cette note, je m'intéresse uniquement aux backend user, tap et bridge.
Le network backend user
permet uniquement des accès sortant de la machine virtuelle vers Internet, mais pas l'inverse sans configurer un forwarding de port.
La configuration s'effectue via les options -netdev
et -device
:
$ qemu-system-x86_64 \
... \
-netdev user,id=n1 \ # configuration du network backend
-device e1000,netdev=n1 \ # virtual network device
...
La version 2.12 de QEMU (2018) propose une alternative plus simplifiée avec l'option -nic
. Voici une exemple équivalent à la configuration ci-dessus :
$ qemu-system-x86_64 \
... \
-nic user,model=e1000
La version 2.12 de QEMU (2018) propose une alternative plus simplifiée avec l'option -nic
. Voici un exemple équivalent à la configuration ci-dessus :
$ ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host noprefixroute
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 52:54:00:12:34:56 brd ff:ff:ff:ff:ff:ff
altname enp0s3
altname ens3
altname enx525400123456
inet 10.0.2.15/24 brd 10.0.2.255 scope global dynamic noprefixroute eth0
valid_lft 47067sec preferred_lft 47067sec
inet6 fec0::5054:ff:fe12:3456/64 scope site dynamic noprefixroute
valid_lft 86251sec preferred_lft 14251sec
inet6 fe80::5054:ff:fe12:3456/64 scope link noprefixroute
valid_lft forever preferred_lft forever
En approfondissant mes recherches, j'ai appris que quand le network backend user
est configuré, QEMU prend en charge nativement les fonctionnalités DHCP et NAT. Il répond aux requêtes DHCP de la VM démarrée et gère aussi le routage IP en mode NAT.
On peut voir ici deux adresses IPv6 attachées à l'interface eth0
:
fec0::5054:ff:fe12:3456/64
fe80::5054:ff:fe12:3456/64
Une fois développées, ces deux adresses correspondent à :
fec0:0000:0000:0000:5054:00ff:fe12:3456
fe80:0000:0000:0000:5054:00ff:fe12:3456
Après avoir regardé cette vidéo, je pense avoir compris que dans une IPv6, la moitié gauche représente systématiquement un réseau (ici fe80:0000:0000:0000
), nommé aussi "network prefix" ou "routing prefix", tandis que la partie de droite (ici 5054:00ff:fe12:3456
) correspond à une adresse d'interface.
La partie interface de ces deux adresses est identique : 5054:00ff:fe12:3456
.
Cette adresse d'interface est générée par conversion EUI-64 de l'adresse MAC 52:54:00:12:34:56
=> 5054:00ff:fe12:3456
.
Exemple de génération d'une autre adresse IPv6 si j'ajoute une interface réseau supplémentaire à la machine virtuelle :
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 52:54:00:12:34:57 brd ff:ff:ff:ff:ff:ff
altname enp0s4
altname ens4
altname enx525400123457
inet 10.0.2.15/24 brd 10.0.2.255 scope global dynamic noprefixroute eth1
valid_lft 86388sec preferred_lft 86388sec
inet6 fec0::5054:ff:fe12:3457/64 scope site dynamic noprefixroute
valid_lft 86389sec preferred_lft 14389sec
inet6 fe80::5054:ff:fe12:3457/64 scope link noprefixroute
valid_lft forever preferred_lft forever
Ici on peut voir que la conversion EUI-64 de 52:54:00:12:34:57
donne 5054:ff:fe12:3457
.
Par rapport au fonctionnement d'IPv4, je trouve que le mécanisme de génération automatique des adresses d'interface réseau d'IPv6 très bien conçu 👌.
Je m'intéresse maintenant aux préfixes d'adresses IPv6 fec0::/10
et fe80::/10
.
Le préfixe fe80::/10
est réservé aux adresses Link-Local. Ces adresses sont automatiquement configurées sur toutes les interfaces IPv6 actives et permettent une communication uniquement au sein du même segment réseau.
Exemple, dans le schéma ci-dessous, tous les serveurs présents sur le réseau A sont joignables via l'adresse Link-Local.
Cependant, les serveurs A n'ont pas la possibilité d'atteindre les serveurs B via Link-Local.
Dimanche 21 septembre 2025
Journal du dimanche 21 septembre 2025 à 12:50
Après un an d'utilisation de mon Shokz OpenMove, j'ai enfin pris le temps de mettre en place l'appairage multipoint . Cela marche impeccablement, et je trouve cette fonctionnalité vraiment pratique, voire même indispensable 🙂 !
#JeMeDemande pourquoi le mode multipoint n'est pas activé pas défaut 🤔.
Vendredi 19 septembre 2025
Dans l'épisode "141 L’IA c’est comme les patates, il faut la faire garder pour la faire adopter" du podcast Clever Cloud, #JaiDécouvert la spécification agents.md (https://agents.md).
J'ai l'impression que AGENTS.md
ressemble à la fonctionnalité CONVENTIONS.md
du projet Aider.
J'ai trouvé une issue à ce sujet dans le projet Aider : Does Aider support AGENT.md or PLAN.md?.
Journal du vendredi 19 septembre 2025 à 16:51
Dans ma note du 2025-07-24_2231 au sujet de Containerlab je disais :
Je viens de poster le message suivant l'espace de discussion GitHub de Containerlab : « Does Containerlab support the creation of topologies with multiple subnets? ».
J'espère que le créateur de Containerlab pourra me suggérer une solution à mon problème, car je n'ai pas réussi à l'identifier dans la documentation 🤷♂️.
Depuis, j'ai eu une réponse et j'ai réussi à fixer mon problème : https://github.com/srl-labs/containerlab/discussions/2718#discussioncomment-14457884
Voici le résultat : https://github.com/srl-labs/containerlab/discussions/2814
Je ne suis pas satisfait de cette simulation, car de mon point de vue les Linux bridge créé par Docker configure trop de chose automatiquement.
Par exemple, l'IP 2001:db8:a:1::1
est automatiquement créé et assigné au bridge network-a
.
De plus, je ne suis pas satisfait de cette simulation parce que containerlab configure automatiquement des routes au niveau de l'hôte pour interconnecter les réseaux Docker network-a
, network-b
et network-c
, alors que dans un environnement réel, cette connectivité serait gérée par les équipements réseau de la topologie.
J'envisage peut-être de tester Mininet pour effectuer des simulations IPv6.
Journal du vendredi 19 septembre 2025 à 11:21
En mars 2025, j'ai pris la décision de contribuer financièrement à la hauteur de 10$ par mois au projet Servo (via Open Collective).
Aujourd'hui, j'ai eu la bonne surprise de découvrir l'article "Your Donations at Work: Funding Josh Matthews' Contributions to Servo".
The Servo project is excited to share that long-time maintainer Josh Matthews (@jdm) is now working part-time on improving the Servo contributor experience.
À l'heure actuelle, 287 contributeurs soutiennent Servo sur GitHub (montant non communiqué) et 388 personnes sur Open Collective pour 67 404 $ par an.
Je pense que la somme totale entre Open Collective et GitHub atteint probablement les 120 000 $ annuels.
Comparé aux 670 000 $ de revenus de Zig, les contributions pour Servo restent nettement plus modestes.
J'espère que la communauté Servo s'inspirera de la transparence de Zig : 2025 Financial Report and Fundraiser .
Dans cet article, #JaiDécouvert l'initiative Outreachy.
Jeudi 11 septembre 2025
Publication du projet 33 - "POC serveur Git HTTP qui injecte du contenu dans OpenSearch"
Je viens de terminer le "Projet 33 - "POC serveur Git HTTP qui injecte du contenu dans OpenSearch"" en 25h.
Si j'inclus le travail préliminaire du Projet 32 - "POC serveur Git HTTP avec exécution de scripts au push", cela représente 34h au total.
Voici le repository avec le résultat final : https://github.com/stephane-klein/poc-content-repository-git-to-opensearch.
J'ai réussi à implémenter preque tous les éléments que j'avais prévu :
- Un serveur Git HTTP supportant les opérations push et pull
- Après chaque git push, injection automatique des données reçues vers une base de données OpenSearch
- Intégration d'un système de job queue minimaliste qui permet de traiter les tâches d'importation des données Git vers OpenSearch de manière asynchrone. Cela permet entre autres de rendre l'opération git push non bloquante.
- Le modèle de données doit permettre l'accès au contenu de plusieurs branches.
- Upload des fichiers binaires vers un serveur Minio tout concervant leurs metadata (chemin, branche, etc) dans OpenSearch.
- La suppression d'une branche ou d'un commit doit aussi supprimer les données présentes dans OpenSearch et Minio.
- Utilisation de la librairie nodegit.
Le seul élément que je n'ai pas testé est celui-ci :
- L'accès aux données via l'API de OpenSearch ne doit pas être perturbé pendant les phases d'importation de données depuis Git.
Je précise d'emblée que l'implémentation de la fonctionnalité d'exploration web du content repository manque actuellement d'élégance.
Les dossiers suivants contiennent une quantité importante de code dupliqué :
src/routes/[...pathname]/
,src/routes/branches/[branch_name]/[...pathname]/
- et
src/routes/r/[revision]/[...pathname]/
src/routes
├── branches
│ ├── [branch_name]
│ │ ├── history
│ │ │ ├── +page.server.js
│ │ │ └── +page.svelte
│ │ ├── +page.server.js
│ │ ├── +page.svelte
│ │ └── [...pathname]
│ │ ├── +page.server.js
│ │ └── +page.svelte
│ ├── +page.server.js
│ └── +page.svelte
├── +page.server.js
├── +page.svelte
├── [...pathname]
│ ├── +page.server.js
│ ├── +page.svelte
│ └── raw
│ └── +server.js
└── r
├── +page.server.js
└── [revision]
├── history
│ ├── +page.server.js
│ └── +page.svelte
├── +page.server.js
├── +page.svelte
└── [...pathname]
├── +page.server.js
├── +page.svelte
└── raw
Pour le moment, je n'ai pas encore trouvé comment éviter cette duplication de manière élégante.
J'ai pensé à 3 approches pour améliorer cette implémentation :
- Factoriser la logique de query des fichiers
+page.server.js
dans une fonction partagée. - Migrer complètement ces pages d'exploration vers
src/hooks.server.js
(avec les Server hooks de SvelteKit ).
Comme cette partie n'était pas au cœur du projet, j'ai préféré ne pas y investir davantage de temps.
Dans ce projet, j'ai utilisé pour la première fois OpenSearch, le fork de Elasticsearch. J'ai dû faire quelques adaptations par rapport à Elasticsearch mais rien de vraiment complexe.
J'ai utilisé la librairie @opensearch-project/opensearch avec succès, bien aidé par Claude Sonnet 4 pour écrire mes query OpenSearch.
J'aimerais mieux maîtriser l'api de OpenSearch et Elasticsearch, mais je ne les utilise pas suffisamment.
Cette dépendance à un LLM pour écrire ces requêtes me contrarie, je me sens prolétaire et j'ai le sentiment de perdre l'habitude de l'effort. Je pense à cette recherche "Your Brain on ChatGPT: Accumulation of Cognitive Debt when Using an AI Assistant for Essay Writing Task" et cela me préoccupe.
J'ai développé un système de job queue minimaliste en NodeJS avec une persistance basée sur des fichiers json simples : src/lib/server/job-queue.js
.
Ma recherche avec Claude Sonnet 4 n'a révélé aucune librairie minimaliste existante qui se contente de fichiers pour la persistance.
Cette implémentation me paraît suffisamment robuste pour répondre à l'objectif que je me suis fixé.
J'ai implémenté la fonction importRevision
avec nodegit pour parcourir toutes les entrées d'une révision Git du repository et les importer dans OpenSearch.
Claude Sonnet 4 m'a encore été d'une grande aide, me permettant d'éviter de passer trop de temps dans la documentation d'API de NodeGit, qui reste assez minimaliste.
Mon expérience de 2015 avec git2go sur le projet CmsHub avait été nettement plus laborieuse, à l'époque pré-LLM. Cela dit, j'avais quand même réussi. 🙂
L'implémentation du endpoint /src/routes/post_recieve_hook_url/+server.js
n'a pas été très difficile.
J'ai réussi à implémenter le support de git push --force
sans trop de difficulté.
Qu'est-ce qui t'a amené à choisir OpenSearch pour ce projet, plutôt qu'un autre type de base de données ?
Suite à de multiples expérimentations durant l'été 2024 (voir 2024-08-17_1253 ou Projet 5), j'ai sélectionné Elasticsearch comme moteur de base de données pour sklein-pkm-engine.
La puissance du moteur de query d'Elasticsearch m'a vraiment séduit, comme on peut le voir dans cette implémentation. Ça me paraît beaucoup plus souple que ce que j'avais développé avec postgres-tags-model-poc
.
J'ai donc décidé d'explorer les possibilités d'Elasticsearch ou de son fork OpenSearch comme moteur de base de données de content repository. J'ai décidé d'en faire mon option par défaut tant que je ne rencontre pas d'obstacle majeur ou de point bloquant.
La partie où j'ai le plus hésité concerne le choix du modèle de données OpenSearch pour stocker efficacement le versioning Git.
J'ai décidé d'utiliser deux indexes distincts : files
et commits
:
await client.indices.create({
index: "files",
body: {
mappings: {
properties: {
content: {
type: "text"
},
mimetype: {
type: 'keyword'
},
commits: {
type: 'object',
dynamic: 'true'
}
}
}
}
});
await client.indices.create({
index: "commits",
body: {
mappings: {
properties: {
index: {
type: 'integer'
},
time: {
type: 'date',
format: 'epoch_second'
},
message: {
type: "text"
},
parents: {
type: 'keyword'
},
entries: {
type: 'object',
dynamic: 'true',
},
branches: {
type: 'keyword'
}
}
}
}
});
Après import des données depuis le repository dummy-content-repository-solar-system
, voici ce qu'on trouve dans files
:
[
{
_index: 'files',
_id: '2f729046cb0f02820226c1183aa04ab20ceb857d',
_score: 1,
_source: {
commits: {
'4da69e469145fe5603e57b9e22889738d066a5e2': 'mars.md',
d9bffc3da0c91366dda54fefa01383b109554054: 'mars.md'
},
mimetype: 'text/markdown; charset=utf-8'
}
},
{
_index: 'files',
_id: '1be731144f49282c43b5e7827bef986a52723a71',
_score: 1,
_source: {
commits: {
'4da69e469145fe5603e57b9e22889738d066a5e2': 'venus.md',
d9bffc3da0c91366dda54fefa01383b109554054: 'venus.md'
},
mimetype: 'text/markdown; charset=utf-8'
}
},
{
_index: 'files',
_id: 'ccc921b7a66f18e98f4887189824eefe83c7e0b3',
_score: 1,
_source: {
commits: {
'4da69e469145fe5603e57b9e22889738d066a5e2': 'terre/index.md',
a9272695d179e70cca15e89f1632b8fb76112dca: 'terre/index.md',
d9bffc3da0c91366dda54fefa01383b109554054: 'terre/index.md'
},
mimetype: 'text/markdown; charset=utf-8'
}
},
{
_index: 'files',
_id: '153d9d6e9dfedb253c624c9f25fbdb7d8691a042',
_score: 1,
_source: {
commits: {
'4da69e469145fe5603e57b9e22889738d066a5e2': 'terre/lune.md',
a9272695d179e70cca15e89f1632b8fb76112dca: 'terre/lune.md',
d9bffc3da0c91366dda54fefa01383b109554054: 'terre/lune.md'
},
mimetype: 'text/markdown; charset=utf-8'
}
},
{
_index: 'files',
_id: '97ef5b8f52f85c595bf17fac6cbec856ce80bd4a',
_score: 1,
_source: {
commits: { '4da69e469145fe5603e57b9e22889738d066a5e2': 'terre/terre.jpg' },
mimetype: 'image/jpeg'
}
}
]
et voici un exemple de contenu de commits
:
[
{
_index: 'commits',
_id: '7ce2ab6f8d29fec0348342d95bfe71899dcb44fa',
_score: 1,
_source: { index: 1, time: 1757420855, branches: [ 'main' ], parents: [] }
},
{
_index: 'commits',
_id: '4da69e469145fe5603e57b9e22889738d066a5e2',
_score: 1,
_source: {
entries: {
'venus.md': {
oid: '1be731144f49282c43b5e7827bef986a52723a71',
contentType: 'text/markdown; charset=utf-8'
},
'terre/lune.md': {
oid: '153d9d6e9dfedb253c624c9f25fbdb7d8691a042',
contentType: 'text/markdown; charset=utf-8'
},
'mars.md': {
oid: '2f729046cb0f02820226c1183aa04ab20ceb857d',
contentType: 'text/markdown; charset=utf-8'
},
'terre/terre.jpg': {
oid: '97ef5b8f52f85c595bf17fac6cbec856ce80bd4a',
contentType: 'image/jpeg'
},
'terre/index.md': {
oid: 'ccc921b7a66f18e98f4887189824eefe83c7e0b3',
contentType: 'text/markdown; charset=utf-8'
}
},
index: 4,
time: 1757429173,
branches: [ 'main' ],
parents: [ 'd9bffc3da0c91366dda54fefa01383b109554054' ]
}
},
{
_index: 'commits',
_id: 'd9bffc3da0c91366dda54fefa01383b109554054',
_score: 1,
_source: {
entries: {
'venus.md': {
oid: '1be731144f49282c43b5e7827bef986a52723a71',
contentType: 'text/markdown; charset=utf-8'
},
'terre/lune.md': {
oid: '153d9d6e9dfedb253c624c9f25fbdb7d8691a042',
contentType: 'text/markdown; charset=utf-8'
},
'mars.md': {
oid: '2f729046cb0f02820226c1183aa04ab20ceb857d',
contentType: 'text/markdown; charset=utf-8'
},
'terre/index.md': {
oid: 'ccc921b7a66f18e98f4887189824eefe83c7e0b3',
contentType: 'text/markdown; charset=utf-8'
}
},
index: 3,
time: 1757421171,
branches: [ 'main' ],
parents: [ 'a9272695d179e70cca15e89f1632b8fb76112dca' ]
}
},
{
_index: 'commits',
_id: 'a9272695d179e70cca15e89f1632b8fb76112dca',
_score: 1,
_source: {
entries: {
'terre/lune.md': {
oid: '153d9d6e9dfedb253c624c9f25fbdb7d8691a042',
contentType: 'text/markdown; charset=utf-8'
},
'terre/index.md': {
oid: 'ccc921b7a66f18e98f4887189824eefe83c7e0b3',
contentType: 'text/markdown; charset=utf-8'
}
},
index: 2,
time: 1757420956,
branches: [ 'main' ],
parents: [ '7ce2ab6f8d29fec0348342d95bfe71899dcb44fa' ]
}
}
]
Ensuite, je mise beaucoup sur la puissance du moteur de requête d'OpenSearch pour récupérer efficacement les données à afficher.
Voici l'exemple de src/routes/[...pathname]/+page.server.js
qui permet d'afficher le contenu d'un fichier de la branche main
.
Première requête :
const responseOid = await client().search({
index: 'commits',
body: {
query: {
bool: {
must: [
{
term: {
branches: 'main'
}
},
{
exists: {
field: `entries.${params.pathname}`
}
}
]
}
},
_source: [`entries.${params.pathname}`]
}
});
Seconde requête qui utilise la réponse de la première :
const responseFile = await client().get({
index: 'files',
id: responseOid.body.hits.hits[0]._source.entries[params.pathname].oid,
_source: ['content', 'mimetype']
});
Basé sur l'expérience de ce projet, je souhaite améliorer sklein-pkm-engine pour permettre la mise à jour de notes.sklein.xyz avec mes données locales uniquement via git push
, sans avoir besoin d'installer quoi que ce soit sur ma workstation.
Je pense que cette implémentation sera bien plus simple que le Projet 33, car je ne prévois pas d'inclure le support dans un premier temps. Peut-être que je supporterai les branches dans un second temps.
Mercredi 3 septembre 2025
J'ai découvert l'outil de build Javascript avec cache remote nommé "nx"
En étudiant un projet privé professionnel, #JaiDécouvert le projet nx qui est comme Turborepo un outil de build pour Javascript et TypeScript.
Pour commencer, je dois préciser que je n'apprécie pas du tout comment le projet se présente. On voit partout :
Cela me donne l'impression que ce "pitch" a été créé par une équipe marketing 🙉 !
J'ai découvert ce tout petit thread Hacker News qui date du 18 août 2022 sur Hacker News qui, je trouve, explique très bien le but de Nx :
I'm a core team member of Nx (nx.dev) and one of the core features we implemented quite a while ago, is "computation caching". Basically to speed up things, we get all the input to a given computation, which our case as a devtool means running your Jest tests, Webpack/esbuild/... build etc, and cache the result (logs & potential build artifacts).
Next time when the same computation is run, we look it up and restore it from the cache, obviously tremendously improving the speed of the run. The real value is when you distribute that cache among co-workers, CI agents etc., which you can do with Nx Cloud (nx.app).
We had played with the idea of potentially mapping this to CO2 emissions. If you start saving a lot of computation, this reduces the number of times a machine gets spin up & executed on your CI. Well, earlier this week we aggregated some stats of how much time we saved and we were pretty by the result ourselves!
I summed it up in this blog article: https://blog.nrwl.io/helping-the-environment-by-saving-two-centuries-of-compute-time-feea8e1ce22?source=friends_link&sk=9b1259d0b171a7b95ebe95b3795660b5
But basically we saved:
- last 7 days: ~5 years of compute time
- last 30 days: ~23 years
- since beginning of Nx Cloud: ~200 years
Je pense que ces mesures font référence à ce qu'on peut voir dans ce screenshot :
Je trouve cela très intéressant. Après avoir testé Bazel sans résultat concluant, sur la période 2018 à 2022, j'ai souvent cherché un outil comme Nx ou Turborepo, c'est-à-dire :
- Build distribué en parallèle sur différentes machines
- Partage de cache entre l'équipe de développement et les pipelines CI/CD
By default, Nx caches task results locally. The biggest benefit of caching comes from using remote caching in CI, where you can share the cache between different runs. Nx comes with a managed remote caching solution built on top of Nx Cloud.
To enable remote caching, connect your workspace to Nx Cloud by running the following command...
Je me demande si Nx permet de self host un composant de remote caching et si oui, je me demande si ce composant est open source ou non 🤔.
À noter que Turborepo permet de self host son propre service de remote cache : voir Turborepo - Remote Cache Self-hosting.
D'après mes recherches, Nx a été créé en juillet 2017, par Victor Savkin, un ancien développeur d'Angular chez Google. Selon cette description :
Software Engineer at Google (San Francisco Bay Area) between Jul 2014 - Dec 2016
One of the main developers of Angular 2. I've developed the dependency injection, change detection, forms, and router modules.
je pense que c'est pendant cette mission qu'il a eu l'idée de créer nx.
En janvier 2018, un second développeur Jason Jean, l'a rejoint sur le projet.
J'ai l'impression que Victor Savkin le CEO, n'a plus le temps de développer sur le projet depuis juillet 2023. Je pense que c'est à partir de là que le projet a eu de la traction.
Lundi 1 septembre 2025
Quel impact Tailwind CSS a-t-il sur la taille des pages d'un site de contenu ?
En février 2023, j'écrivais ceci :
Par ailleurs, je m'interroge sur l'impact du paradigme Tailwind CSS (utility CSS) concernant l'empreinte mémoire des pages.
J'ai le sentiment que la profusion d'attributsclass="..."
va probablement augmenter considérablement la taille des pages.
#JaimeraisUnJour prendre le temps de mesurer cet aspect.
Cette question continue de me trotter dans la tête et mon intuition a évolué au fil du temps. J'ai maintenant l'intuition qu'une compression Brotli fonctionnerait efficacement avec le code Tailwind CSS.
Ce matin j'ai enfin pris le temps de faire des mesures.
J'ai choisi d'effectuer mes mesures sur le site de Blast qui est un site de contenu implémenté en Tailwind CSS.
Voici les mesures que j'ai effectuées :
- Home page https://www.blast-info.fr
- Taille mémoire brute (non compressée) de la page complète sans dépendances :
453 Ko
- Taille mémoire du HTML du
body
, récupéré avec l'inspecteur Firefox via "copier l'intérieur du HTML" :178Ko
- En supprimant tous les attributs
class="..."
, je passe à145Ko
, soit une empreinte brute des attributsclass
de33Ko
(environ 18% du body) - Taille mémoire du texte seul :
5ko
(2%) - Taille du body compressé Brotli :
25Ko
- Taille du body sans attributs
class="..."
compressé Brotli :23Ko
, soit2Ko
pour les attributs classe (environ8%
)
- Taille mémoire brute (non compressée) de la page complète sans dépendances :
- Page intérieure https://www.blast-info.fr/articles/2025/macron-bayrou-vers-un-jour-sans-fin-I0AwyT5KTf6_0ywKmLShVQ
- Taille mémoire brute (non compressée) de la page complète sans dépendances :
404 Ko
- Taille mémoire brute du HTML du
body
, récupéré avec l'inspecteur Firefox via "copier l'intérieur du HTML" :281ko
- En supprimant tous les attributs
class="..."
, je passe à251Ko
, soit une empreinte brute des attributsclass
de30Ko
(environ 10% du body) - Taille mémoire du texte seul :
34ko
(environ 12%) - Taille du body compressé Brotli :
43Ko
- Taille du body sans attributs
class="..."
compressé Brotli :40Ko
, soit3Ko
pour les attributs classe (environ6%
)
- Taille mémoire brute (non compressée) de la page complète sans dépendances :
Une fois compressé, la partie Tailwind CSS représente 2Ko
à 3Ko
par page. Il faut noter que ces valeurs constituent probablement une estimation haute, car un site utilisant le paradigme CSS traditionnel emploie généralement aussi des class="…"
dans ses pages HTML.
Mon constat : je pense que le surcout de Tailwind CSS sur la taille des pages d'un site de contenu demeure négligeable après compression.
Une autre question que je me pose : quel est l'impact de Tailwind CSS sur l'utilisation mémoire du navigateur ?
Samedi 30 août 2025
Journal du samedi 30 août 2025 à 20:17
Je viens de publier : Projet 33 - "POC serveur Git HTTP qui injecte du contenu dans OpenSearch".
Journal du samedi 30 août 2025 à 16:50
#JaiDécouvert la formalisation du concept de content-modeling as code :
Mercredi 27 août 2025
Bilan de mon temps de travail de ma première année de Freelance
Après ma note "Retour XP posts LinkedIn et mes canaux d'acquisition de mission Freelance", voici ma seconde note bilan de ma première année d'activité Freelance.
En 2022, ma dernière année complète en CDI qui me sert de référence, j'ai travaillé 219 jours. Cette année-là, 6 jours fériés sur 10 sont tombés en semaine et j'ai pris en tout 35 jours de congé.
Pour ma première année de Freelance, du 1er juillet 2024 au 30 juin 2025 :
- J'ai travaillé l'équivalent de 206 journées complètes :
- J'ai facturé 168 jours de prestation
- À cela s'ajoute 39 journées complètes non facturées. Il s'agit soit de prestations supplémentaires que je n'ai pas facturées, soit du temps consacré à la prospection et aux échanges avec des prospects.
- 266 jours où j'ai travaillé plus de 30min
- 288 jours où j'ai travaillé au moins 1min
- 59 jours de vacances officielles, dont 18 qui sont tombés en weekend, donc j'ai posé l'équivalent de 41 jours de congés. Pendant ces 59 jours, j'ai au moins travaillé 18 jours durant plus de 30 minutes.
Si je fais le bilan, pour ma première année, j'ai travaillé 13 jours de moins que ma dernière année en CDI, pour un chiffre d'affaires qui correspond approximativement à un CDI à 75 K€ brut par an.
Répartition des jours que j'ai facturée de juillet 2024 à fin mai 2025 :
Journal du mercredi 27 août 2025 à 09:57
Dans le livre "Politikon - Tout ce qu'il faut savoir sur les idéologies qui ont façonné notre monde", #JaiDécouvert le mouvement philosopique nommé le "convivialisme".
Autour du noyau du M.A.U.S.S. s'élabore depuis quelques années une idéologie politique qui ambitionne de s'opposer massivement à l'idéologie économique dominante : le convivialisme. Plusieurs manifestes et ouvrages sont parus et ont été soutenus par de nombreuses personnalités internationales aussi différentes que Noam Chomsky, Jean-Claude Michéa, Chantal Mouffe ou Edgar Morin.
Le terme « convivialisme » trouve sa source dans l'ouvrage La convivialité du philosophe Ivant Illich.
Il s'agit ici de penser une société fondée sur une volonté commune de vivre ensemble en dehors du principe de l'utilité à maximiser et de l'illimitation de la croissance économique.
Mardi 26 août 2025
Journal du mardi 26 août 2025 à 21:45
Voici une note pour présenter la seconde #iteration du Projet 32 - "POC serveur Git HTTP avec exécution de scripts au push".
J'ai tout d'abord implémenté dans ce commit un mécanisme qui exécute du code JavaScript automatiquement après chaque git push
.
Pour cela, j'ai choisi de me baser sur le mécanisme Server-Side Hooks natif de Git : post-receive
.
Voici le contenu de ce script hook en Bash :
#!/bin/bash
while read oldrev newrev refname; do
branch=$(git rev-parse --symbolic --abbrev-ref $refname)
curl -X POST \
-H "Content-Type: application/json" \
-d "{
\"oldrev\": \"${oldrev}\",
\"newrev\": \"${newrev}\",
\"refname\": \"${refname}\",
\"branch\": \"${branch}\",
\"repository\": \"$(basename $(pwd))\"
}" \
"${POST_RECIEVE_HOOK_URL}" >> /dev/null
done
Chaque événement git push déclenche un appel HTTP vers le endpoint http://localhost:3334/post_recieve_hook_url/
exposé par le serveur NodeJS. Le payload contient notamment :
oldrev
: l'identifiant du dernier commit présent dans le repository avant le pushnewrev
: l'identifiant du commit le plus récent envoyé lors du push
L'intervalle entre oldrev
et newrev
permet d'identifier précisément l'ensemble des commits qui ont été poussés lors de cette opération.
J'ai ensuite implémenté une version SvelteKit iso-fonctionnelle de node-git-http-server
. Voici le repository poc-node-git-server-in-sveltekit
.
Contrairement à ce que j'avais prévu initialement, pour cette implémentation, je ne me suis pas basé sur SvelteKit Custom Server, mais sur la fonctionnalité Server hooks : src/hooks.server.js#L11
.
Dimanche 24 août 2025
Retour XP posts LinkedIn et mes canaux d'acquisition de mission Freelance
J'ai prévu de publier quelques notes de bilan de ma première année d'activité Freelance. Voici la première, une note d'analyse de mes canaux d'acquisition de missions.
Il y a environ un an, fin septembre 2024, j'ai publié 3 posts sur LinkedIn dans le but de faire connaître ma nouvelle activité Freelance :
Bien que ces messages aient généré 78 réactions, 23 reposts et 7163 impressions cumulées, ils ne m'ont apporté aucune mission directe.
Cependant, j'ai observé qu'ils ont efficacement informé l'ensemble de mon réseau professionnel de ma nouvelle activité.
Malgré cet échec sur ce canal d'acquisition, j'ai tout de même facturé 168 jours de prestation sur une période de 12 mois, du 1er juillet 2024 (jour du dépôt légal de ma micro-entreprise) au 30 juin 2025, en utilisant d'autres approches d'acquisition client.
Voici la répartition de ces 168 jours selon le canal d'acquisition :
- Mon réseau direct, des anciens collègues très proches : 3 clients pour un total de 98 jours
- Des candidatures spontanées : 2 clients pour un total de 50 jours
- Recommandation indirecte d'un client qui n'était pas dans mon cercle de connaissance avant d'avoir travaillé avec lui : 1 client pour un total de 20 jours
Je n'ai reçu aucune proposition de mission sur Malt, pas même un message. Mon taux journalier de 700 € est peut-être trop élevé, ou alors c'est un problème de positionnement de profil. Je dois reconnaitre que je n'ai fait aucun effort à améliorer mon profil Malt depuis ma dernière phase d'optimisation présentée dans cette note du 25 septembre 2024.
Je n'ai répondu à aucune annonce de mission.
J'ai fait seulement deux candidatures spontanées et toutes les deux ont abouti.
Pour la première, j'ai ciblé un service que j'apprécie beaucoup et dont je suis client depuis presque 10 ans. J'avais repéré par leurs posts qu'ils avaient peut-être un budget pour des missions de développement.
Pour la seconde, j'ai visé un collectif que j'avais identifié il y a plus de 10 ans et pour lequel j'avais toujours eu envie de travailler.
Dans les deux cas, mes messages étaient parfaitement sincères, très éloignés des modèles de lettres de motivation qu'on trouve en ligne. J'avais fait mes recherches sur ces organisations et j'ai soigneusement détaillé mes motivations ainsi que les compétences que je pouvais leur apporter.
En repensant à ma stratégie d'acquisition de missions Freelance, je constate qu'elle ressemble étonnamment à ma façon d'obtenir mes CDI durant ma carrière :
- J'ai répondu à une annonce d'emploi
- J'ai répondu à une annonce d'emploi (Jobboard : Lolix)
- Une amie proche m'a contacté pour une mission
- Un ami proche m'a contacté pour un emploi
- Une amie proche m'a contactée pour un emploi
- J'ai ciblé une organisation en contactant une connaissance d'une communauté
- J'ai lancé une bouteille à la mer à une connaissance d'une communauté
- J'ai eu une proposition autour d'une bière alors que je n'avais rien prévu
- J'ai lancé une bouteille à la mer à une connaissance d'une communauté
Voir aussi : Bilan de mon temps de travail de ma première année de Freelance
Journal du dimanche 24 août 2025 à 12:42
Je viens de publier la première #iteration du Projet 32 - "POC serveur Git HTTP avec exécution de scripts au push" dans le repository node-git-http-server
.
L'implémentation d'un serveur Git HTTP via Apache ou nginx, en s'appuyant sur git-http-backend
, paraît plutôt simple à réaliser.
Comme mon objectif est d'intégrer cette fonctionnalité dans le projet sklein-pkm-engine et que j'ai une préférence pour les monolith, j'ai exploré les solutions basées sur NodeJS.
J'ai dans un premier temps étudié le projet node-git-http-server
et ensuite node-git-server
.
Ces deux projets semblent peu actifs.
J'ai échoué à faire fonctionner le projet node-git-server, probablement à cause d'une erreur de ma part — j'ai sans doute oublié d'initialiser au préalable les dépôts Git en mode bare
.
Par la suite, en utilisant Claude Sonnet 4, j'ai créé une implémentation basée uniquement sur les modules natifs de NodeJS et l'exécutable git-http-backend
, sans recourir à aucun package NodeJS externe.
Voici le résultat : node-git-http-server/server.js
.
Prochaines étapes
- Implémenter un système qui exécute du code JavaScript automatiquement après chaque
git push
, en lui transmettant la branche concernée et la liste des nouveaux commits publiés. - Implémenter une déclinaison de ce projet dans un SvelteKit Custom Server.
Samedi 23 août 2025
Journal du samedi 23 août 2025 à 18:29
Je viens de publier : Projet 32 - "POC serveur Git HTTP avec exécution de scripts au push".
Journal du samedi 23 août 2025 à 14:13
Dans l'épisode "Une nouvelle histoire du Moyen Âge, avec Florian Mazel " de la web radio Storiavoce, #JaiDécouvert l'ouvrage collectif dirigé par Florian Mazel : "Nouvelle Histoire du Moyen Âge" (https://www.seuil.com/ouvrage/nouvelle-histoire-du-moyen-age-collectif/9782021460353 ).
L'émission m'a donné envie de l'acheter et de le feuilleter.
Mardi 19 août 2025
Journal du mardi 19 août 2025 à 11:05
Ces derniers jours, j'ai lu :
- le thread Hacker News : "Sunny days are warm: why LinkedIn rewards mediocrity"
- et le billet "Le personal branding, c’est fini " de Laëtitia Vitaud
à propos de LinkedIn.
Cela me rassure d'observer que d'autres partagent mon opinion sur la médiocrité générale des posts LinkedIn.
Dès les années 2010, j'avais remarqué une dérive : la plupart des gens cherchaient à gonfler leur réseau en acceptant toutes les connexions, y compris de parfaits inconnus.
Selon moi, LinkedIn devrait représenter les relations authentiques entre les gens. J'ai toujours suivi cette règle personnelle pour accepter une connexion : si je croise cette personne dans la rue, est-ce que je prendrais le temps de la saluer ?
J'utilise LinkedIn essentiellement comme carnet d'adresses et base de données de CV dynamiques, ainsi que pour comprendre la structure et la dynamique des organisations.
En revanche, je consulte très peu mon feed. Pour éviter de le voir, j'ai installé l'extension Browser "LinkedIn Feed Locker".
Dimanche 17 août 2025
J'ai découvert Badsender, une agence spécialisée en e-mailing et email deliverability
Il y a quelques semaines, un ami m'a demandé si je connaissais quelqu'un pour réaliser un audit Email deliverability. N'ayant personne de confiance à lui recommander dans ce domaine, je lui ai proposé de faire moi-même une prestation d'une demi-journée sur le sujet, malgré le fait que ce ne soit pas mon cœur de métier.
Pendant mes études, pour monter en compétence sur ce sujet (voir mes notes dans Email deliverability), j'ai découvert l'agence "d'emailing" nommée Badsender (https://www.badsender.com) via leur excellent billet sur BIMI.
Badsender, c’est une équipe de 10 spécialistes du numérique dotés d’une forte expertise en emailing et newsletter. Nous intervenons sur toute la chaîne email : délivrabilité, conseils stratégiques, rédaction, conception et production de campagnes.
J'ai beaucoup apprécié leur démarche de transparence : "Cap à 9 mois : Quel cap dans les 9 prochains mois ?".
J'ai beaucoup aimé le contenu de leurs billets d'analyse de newletters : https://www.badsender.com/newsletter/exemples/.
J'ai l'impression que Sébastien Fischer a produit la majorité des bons articles de Badsender pendant sa période chez eux (2021-2024).
En étudiant les dépôts de comptes sur INPI jusqu'à 2019 et les profils LinkedIn, il me semble que Badsender repose principalement sur 3 experts, notamment Jonathan Loriaux , le fondateur qui paraît avoir une solide expérience en Email deliverability.
Avec leur marge brute de 2019 d'environ 220 K€ et un panier moyen qui tourne autour de 2000 €, j'estime leur portefeuille client entre 100 et 150 comptes.
Après mon audit de délivrabilité d'e-mail pour mon client, si mon tarif à la journée est trop élevé, si mes disponibilités ne correspondent pas à ses besoins, ou s'il souhaite établir un partenariat à long terme avec un prestataire, alors je lui recommanderai sans doute de se tourner vers l'agence Badsender ou Sébastien Fischer.
Je vais informer Badsender de la publication de cette note pour savoir si elle contient des informations qui leur posent un problème.
Vendredi 15 août 2025
Pour le moment, je n'ai pas d'avis sur l'intérêt des services d'email warming
Je continue mes explorations sur l'Email deliverability avec cette note dédiée à l'email warming (pour rappel, mes notes précédentes : 2025-08-04_1449, 2025-08-08_2222, 2025-08-14_2229).
Quel est le fonctionnement technique des services d'email warming ?
L'email warming consiste à établir progressivement une réputation positive pour une nouvelle adresse e-mail, d'un nouveau domaine ou d'une nouvelle IP SMTP auprès des ESP.
Mon hypothèse sur le fonctionnement de ces services :
- Ces plateformes créent en premier lieu des comptes e-mail chez un maximum d'email service providers (Gmail, Yahoo (Mail), Fastmail, La Poste (mail)…).
- Elles expédient graduellement, sur plusieurs jours, des e-mails vers ces adresses, avec des contenus variés.
- Elles simulent via un headless browser des interactions utilisateur dans les webmails de ces boîtes : ouverture de mails, clics sur des liens, etc.
J'imagine qu'elles doivent créer des scénarios aléatoires au niveau des timings et des actions pour éviter d'être trop facilement détectées par les email service providers 🤔.
J'ai essayé d'en savoir plus sur l'efficacité de l'email warming. J'ai effectué des recherches sur le Subreddit EmailMarketing. J'ai par exemple trouvé les threads suivants :
- Do I need to warm up domain before sending behavioral emails?
- What exactly has email warming changed for you?
- Is there any evidence that domain/IP warm up actually works?
Une minorité d'utilisateurs (exemple ) affirment que l'email warming est un scam, tandis qu'une majorité soutient que l'email warming reste indispensable pour toute nouvelle adresse e-mail. Cependant, je me demande si ces avis positifs ne viennent pas principalement des fournisseurs de services d'email warming 🤔.
Idéalement, j'aimerais trouver une étude d'un cas concret, menée par quelqu'un dont je ne pourrais pas questionner la crédibilité. Sinon, il me reste l'option de mener un test par moi-même.
Les systèmes SPF, DKIM, et DMARC me paraissent efficaces, mais les outils non officiels d'inbox placement et d'email warming ressemblent plutôt à du bricolage dans un écosystème incomplet.
Ces pratiques obscures me font penser à l'univers du SEO.
Il s'agit d'un système de coévolution antagoniste, semblable au jeu du chat et de la souris, ou plus précisément, un jeu séquentiel à somme variable avec des composantes antagonistes.
Dans ce genre d'écosystème, chaque acteur investit massivement en énergie pour maintenir le statu quo. Je trouve cette dynamique absurde et n'y vois aucun intérêt.
Concernant les tarifs, je trouve que le prix de MailReach est correct, $25 par mois pour "faire chauffer" une adresse mail :
Journal du vendredi 15 août 2025 à 08:59
En étudiant le service DMARCwise, #JaiDécouvert et étudié le mécanisme SMTP MTA Strict Transport Security.
Jeudi 14 août 2025
J'ai testé des services de inbox placement
Toujours dans le contexte d'une mission freelance d'audit de délivrabilité d'e-mail (voir notes : 2025-08-04_1449 et 2025-08-08_2222), j'ai creusé le sujet de inbox placement.
À l'origine, je croyais qu'un test d'inbox placement permettait de simuler l'envoi d'emails vers différents providers de messagerie pour déterminer leur destination finale, par exemple :
- Boîte de réception principale
- Dossier spam/indésirables
- Dossier "Promotions"
- Dossier "Social"
- Dossier "Notifications"
Après avoir testé GlockApps, MailReach et MailerCheck, j'ai l'impression que leurs fonctionnalités d'inbox placement se limitent à vérifier si l'email atterrit en "spam" ou en "inbox", sans distinguer entre "Boite de réception principale", "Promotions" ou "Social". Cependant, j'ai un doute pour GlockApps, car j'ai aperçu "Other" sur un screenshot .
Cette fonctionnalité inbox placement est parfois appelée "spam checker" ou "Inbox insight".
Mes recherches sur le Subreddit EmailMarketing m'ont également permis d'identifier plusieurs autres services : Gwarm, Folderly, Warmup Inbox, MailGenius et Mail-Tester. Je n'ai pas pris le temps de les tester.
Comment les services d'inbox placement fonctionnent techniquement ?
Mon hypothèse sur le fonctionnement de ces services :
- Ces services créent d'abord des boîtes mail chez un maximum de email service providers (Gmail, Yahoo (Mail), Fastmail, La Poste (mail)…).
- Ils mettent en place un système de surveillance IMAP pour monitorer les mails reçus sur chacune de ces boîtes.
- Ils demandent à leurs utilisateurs d'envoyer un mail sur ces boîtes avec un code d'identification dans le corps pour faire le lien entre l'utilisateur et le mail reçu dans les mailbox.
- Ils communiquent ensuite à l'utilisateur les résultats observés : placement dans le dossier principal ou dans le spam pour chaque boîte.
Voici quelques exemples de résultats de mes tests d'inbox placement.
Pour l'instant, GlockApps semble être le service qui teste le plus grand nombre de types de boîtes e-mail différentes.
J'ai observé quelque chose de surprenant lors de mon premier test de MailReach avec mon adresse mail contact@stephane-klein.info
.
J'ai envoyé un mail avec ce contenu et il a atterri dans le dossier spam des boîtes Microsoft Office 365 Mail, des boîtes qui appartiennent à la catégorie Professionnal.
J'ai écrit au support technique de MailReach car je n'arrivais pas à bien saisir le sens du message de tooltip qui s'affiche au survol des pastilles "spam" :
« Not good BUT the question is : do you really target professional inboxes? Because if not, it's not relevant to take it into account. Spam filters work differently between Professional and Personal inboxes. »
J'ai demandé si le style du contenu du message envoyé pouvait être pris en compte par les règles de spam et j'ai eu comme réponse :
Ca peut tre lié a ton contenu ou la réputation de ta boite mail.
Suite à cela, un jour après, j'ai fait un nouveau test, avec ce contenu plus professionnel et cette fois, tous les mails ont été placés dans le dossier principal, aucun spam :
3 jours plus tard, j'ai fait un test en envoyant à nouveau le premier contenu mail dans un style plus personnel et cette fois le mail a été placé dans inbox dans toutes les boites mails.
Pour le moment, je n'ai pas d'explication. Est-ce une illustration de l'intérêt de la fonctionnalité email warming 🤔 ?
J'ai également testé le service MailTester, mais je n'ai pas réussi à le faire fonctionner. Tous les mails que j'ai envoyés m'ont été renvoyés avec un message d'erreur indiquant que les boîtes mail n'existaient pas :
Voici quelques informations concernant les tarifs de ces services d'inbox placement.
- $9,6 pour 25 tests
- $28 pour 100 tests
La tarification de MailerCheck est un peu compliquée :
- $10 pour 1000 crédits, un test d'inbox placement coûte 200 crédits, cela fait donc $2 par test.
GlockApps paraît être l'outil qui teste la plus large gamme de email service providers et qui détecte le classement des e-mails dans les dossiers "Promotions", "Social"… mais c'est probablement aussi la solution la plus coûteuse :
- $16.99 pour 3 tests
- $47,99 pour 10 tests
- $75,99 pour 20 tests
GlockApps offre 2 tests gratuits à la création du compte, mais j'ai mal utilisé les miens.
J'ai contacté leur service commercial pour demander des crédits gratuits supplémentaires, en leur expliquant que je voulais uniquement tester mon adresse personnelle sans visée commerciale, peut-être avant de suggérer leur outil à un client. Ils ont décliné ma demande 😔.
Je n'ai donc pas pu explorer davantage cette solution.
Dans un premier temps, je vais probablement proposer à mon client la solution MailReach et éventuellement GlockApps par la suite s'il souhaite des tests plus approfondis.
Pour effectuer mes tests, j'ai utilisé ce script d'envoi d'e-mail : https://github.com/stephane-klein/send-mail-to-test-inbox-placement.
Pour l'instant, je ne sais pas encore quelle approche je suggérerai à mon client pour qu'il puisse réaliser des tests d'inbox placement de façon autonome, par exemple mensuellement.
#UnJourPeuxÊtre je développerai un POC de fonctionnalité d'inbox placement. Il me semble qu'en dehors de la création de nombreuses adresses mail, les vérifications basées sur l'imap ne doivent pas être très compliquées à implémenter.
Journal du jeudi 14 août 2025 à 11:20
J'ai cherché en Javascript, l'équivalent de la fonction inspect.cleandoc
de la librairie standard de Python (voir note 2025-06-13_1437).
J'ai trouvé la librairie string-dedent
.
Exemple d'utilisation (source) :
const info = await transporter.sendMail({
from: `"${process.env.FROM_NAME}" <${process.env.FROM_EMAIL}>`,
to: toEmail,
subject: 'Nouvelle et projet',
text: dedent`
Salut ${firstname},
Comment vas-tu ? Ça fait un bail qu'on ne s'est pas parlé ! J'espère que tu vas bien et que tout se passe pour le mieux de ton côté. De mon côté, ça tourne plutôt bien. Je suis toujours sur Fedora, et j'ai pas mal bidouillé avec la ligne de commande ces derniers temps. J'ai réussi à automatiser quelques trucs qui me simplifient la vie, c'est toujours un plaisir de pouvoir tout configurer comme on veut.
En ce moment, je suis replongé dans Neovim. J'essaie de configurer un workflow un peu plus efficace pour le développement. J'ai découvert quelques plugins géniaux qui me font gagner pas mal de temps. C'est un peu chronophage de tout configurer, mais le résultat en vaut la peine. Plus besoin de sortir de l'éditeur pour pas mal de tâches !
Quoi de neuf de ton côté ? Des projets intéressants en cours ? On devrait essayer de se voir bientôt pour en discuter autour d'un verre. Dis-moi ce que tu en penses !
${codeId}
À bientôt,
Stéphane
`,
html: dedent`
<p>Salut ${firstname},</p>
<p>Comment vas-tu ? Ça fait un bail qu'on ne s'est pas parlé ! J'espère que tu vas bien et que tout se passe pour le mieux de ton côté. De mon côté, ça tourne plutôt bien. Je suis toujours sur Fedora, et j'ai pas mal bidouillé avec la ligne de commande ces derniers temps. J'ai réussi à automatiser quelques trucs qui me simplifient la vie, c'est toujours un plaisir de pouvoir tout configurer comme on veut.</p>
<p>En ce moment, je suis replongé dans Neovim. J'essaie de configurer un workflow un peu plus efficace pour le développement. J'ai découvert quelques plugins géniaux qui me font gagner pas mal de temps. C'est un peu chronophage de tout configurer, mais le résultat en vaut la peine. Plus besoin de sortir de l'éditeur pour pas mal de tâches !</p>
<p>Quoi de neuf de ton côté ? Des projets intéressants en cours ? On devrait essayer de se voir bientôt pour en discuter autour d'un verre. Dis-moi ce que tu en penses !</p>
<p>${codeId}</p>
<p>À bientôt,<br>
Stéphane</p>
`
});
Vendredi 8 août 2025
J'ai pris conscience de l'intérêt de DMARC et de l'alignement SPF et DKIM
Dans le contexte de ma mission Freelance, je poursuis l'actualisation de mes compétences en délivrabilité d'e-mail. J'en profite pour rédiger une note sur DMARC.
DMARC existe depuis 2012, mais je n'avais jamais vraiment creusé le sujet. Je l'avais seulement survolé. Jusqu'à récemment, je n'avais en tête que la fonction "monitoring" :
Sans avoir mesuré l'importance de la partie policy
:
If the email fails the check, depending on the instructions held within the DMARC record the email could be delivered, quarantined or rejected.
Je pensais naïvement que les vérifications SPF et DKIM réalisées par les email service providers étaient suffisantes.
Je n'avais pas réalisé l'importance du SPF alignment and DKIM alignment.
Le problème vient du fait que SPF et DKIM vérifient le domaine contenu dans MailFrom
(connu aussi sous les noms Return-Path
, Bounce Address
, ou Envelope From
). Ces contrôles s'assurent que le serveur émetteur peut légitimement envoyer des emails pour ce domaine et que le message n'a pas été modifié durant le transport.
Cependant, ces vérifications ne protègent pas du spoofing. Les clients mail n'affichent pas le champ MailFrom
, mais le champ From:
. Un attaquant peut donc envoyer un email avec un domaine validé par SPF et DKIM tout en utilisant un champ From:
qui ne lui appartient pas.
L'alignement vérifie que le domaine utilisé pour les contrôles SPF et DKIM correspond au domaine du champ From:
.
Si les domaines diffèrent, le serveur receveur exécute la politique DMARC : reject
pour rejeter l'email ou quarantine
pour le diriger vers les spam.
De plus, j'ai découvert que DMARC était devenu petit à petit obligatoire :
Comply with email providers requirements: in 2024, Google and Yahoo started requiring DMARC on incoming mail from high-volume senders, and Microsoft followed in 2025. If you send emails to Gmail addresses, you may be affected by this. Even if you aren’t, this is likely just Google’s and Yahoo’s first step in a path to enforce DMARC checks on all incoming email, and organizations must prepare in advance.
Je viens de réaliser que c'est sans doute à cause de l'absence de DMARC sur mon domaine (stephane-klein.info
) qui explique pourquoi en janvier 2024, un ami ne recevait aucun de mes mails sur sa boite mail Orange.
$ dig TXT _dmarc.stephane-klein.info +short
;; communications error to 127.0.0.53#53: timed out
Il y a quelques jours, je me suis lancé dans la configuration DMARC de mon domaine.
J'ai commencé par chercher des services de DMARC reporting.
Je suis dans un premier temps tombé sur Google Postmaster Tools, mais celui-ci est limité aux boites mails Gmail.
En cherchant des outils d'inbox placement dans le Subreddit EmailMarketing, j'ai découvert GlockApps qui permet aussi de faire du DMARC reporting.
Ensuite, en étudiant l'excellente documentation dmarc.wiki
, j'ai découvert le service DMARCwise réalisé par un Indie Hacker italien : Matteo Contrini.
Il est gratuit pour un usage personnel :
J'ai testé ce service et je l'ai trouvé excellent !
Au départ, j'ai commencé par activer graduellement DMARC comme conseillé ici :
$ dig TXT _dmarc.stephane-klein.info +short
"v=DMARC1; p=none; rua=mailto:rua+v1c8xvv8a2yv@dmarcwise.email;"
L'adresse mail de collecte rua+v1c8xvv8a2yv@dmarcwise.email
m'a été donné par DMARCwise :
J'ai ensuite lancé un "DMARC diagnostics" :
Et j'ai constaté que tout était parfaitement configuré :
Après réflexion, étant donné que je suis le seul émetteur d'e-mail pour mon domaine, j'ai jugé que je pouvais directement passer de pas de policy
(p=none
) à p=reject; pct=100;
.
$ dig TXT _dmarc.stephane-klein.info +short
"v=DMARC1; p=reject; pct=100; rua=mailto:rua+v1c8xvv8a2yv@dmarcwise.email;"
Après 3 jours d'utilisation de DMARCwise, l'expérience utilisateur me plaît énormément. Il me semble que tout est soigneusement conçu, Matteo Contrini fait clairement attention aux détails !
Voici à quoi cela ressemble :
J'ai bien envie de conseiller DMARCwise à mon client.
Je sais qu'il envoie environ 3 millions d'e-mails par mois, ce qui ferait une facture de 1188 € HT par an.
Une autre option serait GlockApps, à $1548 HT par an mais avec une plus 1800 crédit de tests de inbox placement.
#JaimeraisUnJour prendre le temps de tester le free software de DMARC reporting nommé parsedmarc.
Ma prochaine note sur l'Email deliverability portera probablement sur l'inbox placement.
Lundi 4 août 2025
J'ai découvert la spécification "Brand Indicators for Message Identification"
En travaillant sur une mission freelance d'audit de délivrabilité d'e-mail, #JaiDécouvert la spécification "Brand Indicators for Message Identification".
Il s'agit de la spécification la plus récente qui s'ajoute aux spécifications de lutte contre l'usurpation d'identité email : SPF, DKIM, DMARC, ARC.
BIMI permet d'afficher le logo "certifié" de l'expéditeur du mail dans un certain nombre de clients mails (Apple, Fastmail, Gmail, La Poste, Yahoo).
Par exemple, cela donne ceci pour l'email noreply@notif-colissimo-laposte.info
avec mon client mail Fastmail :
Autre exemple avec Gmail avec le "badge certifié" :
Pour avoir plus d'exemples concrets, je vous conseille de consulter la section [« Quelques exemples d’affichage de BIMI chez les fournisseurs de messagerie »](https://www.badsender.com/guides/bimi-pourquoi-et-comment-le-deployer/#:~:text=les fournisseurs de-,messagerie,-Apple Icloud (Mail) de l'excellent article « Formation BIMI : pourquoi et comment déployer BIMI ? » de l'agence française Badsender, qui offre entre autre des services d'audit de délivrabilité d'e-mail.
Vous pouvez, par exemple, vérifier la configuration BIMI sur cette page, voici le résultat, toujours avec l'adresse mail noreply@notif-colissimo-laposte.info
:
Voici la configuration DNS TXT BIMI du domaine notif-colissimo-laposte.info
:
$ dig TXT default._bimi.notif-colissimo-laposte.info +short
"v=BIMI1;l=https://notif-colissimo-laposte.info/logo.svg;a=https://notif-colissimo-laposte.info/la_poste_sa.pem;"
v=BIMI1
indique le numéro de version de la spécification.l=https://notif-colissimo-laposte.info/logo.svg
contient l'URL vers le logo au format SVGa=https://notif-colissimo-laposte.info/la_poste_sa.pem
contient l'URL du certificat qui permet de certifier que l'expéditeur d'un email est autorisé à utiliser le logo Colissimo.
Voici ce que contient le certificat :
Issuer: CN=DigiCert Verified Mark RSA4096 SHA256 2021 CA1, O="DigiCert, Inc.", C=US
Expires: 3 months
Valid From: 9/30/2024
Valid To: 11/1/2025
Ce certifact a été généré par DigiCert.
Liste des entreprises de type Mark Verifying Authority pouvant actuellement générer des Verified Mark Certificate ou Common Mark Certificate :
D'après ce que j'ai compris, pour obvenir un Verified Mark Certificate, il est nécessaire de fournir au Mark Verifying Authority une preuve de dépôt de marque, par exemple via l'INPI.
Je pense que "Common" dans Common Mark Certificate est en lien avec le système juridique "Common law". Pour obtenir un Common Mark Certificate, il suffit de prouver qu'on utilise le logo depuis plus de 12 mois. DigiCert indique qu'ils effectuent une vérification en utilisant archive.org.
Depuis fin 2024, un autre type de certificat est disponible. C’est le CMC(Common Mark Certificate). Celui-ci permet de s’affranchir du dépôt de marque. Avoir une marque et un logo déposé sont donc maintenant optionnels. Néanmoins, le certificat CMC ne permet pas de garantir le même niveau de légitimité au destinataire. Certaines messageries, même si elles afficheront le logo BIMI dans le cas d’un certificat CMC n’ajouteront pas de certification de la marque (par exemple, dans Gmail, le checkmark bleu n’est pas affiché en cas de certificat CMC).
Lorsqu’un certificat VMC est choisi, une marque bleue est affichée dans Gmail afin de renforcer le sentiment de légitimité pour le destinataire. Ce qui ne sera pas le cas avec un certificat CMC.
Voici les prix d'un Verified Mark Certificate chez DigiCert : 1668 € par an.
Et 1236 € par an pour un Common Mark Certificate.
Jusqu'à maintenant, je croyais que les services Gravatar ou Libravatar permettaient d'afficher un avatar dans les clients mail, mais je réalise que ce n'est pas le cas et il semble que je ne sois pas le seul à avoir cette idée fausse :
Many users set up their Gravatar expecting it to be shown when sending emails from their email address. This is not always the case, this page explains why.
Truth be told, there aren’t many email clients (meaning the app or platform your users use to read their emails) that support Gravatar. Most popular email services (like Gmail, Outlook or Apple Mail) don’t. Unfortunately there is nothing we can do.
If you have confirmed your reader’s email client support, then there might be some setting (or addon) that your readers will need to tweak.
Je me suis demandé si BIMI pouvait améliorer l'Email deliverability.
En parcourant le Subreddit EmailMarketing, j'ai découvert ce thread : Is BIMI & VMC worth it? . Tous les contributeurs s'accordent à dire que BIMI n'apporte aucune amélioration à l'Email deliverability.
Pour le moment, aucune information ne suggère que BIMI présente un avantage pour l'Email deliverability.
À ce stade, il me semble que la mise en place d'un Verified Mark Certificate est pertinente pour tout service ciblé par des attaques d'arnaque numérique.
Pour les autres services aux moyens limités, je pense qu'investir 1668 € annuels dans un Verified Mark Certificate n'est probablement pas justifié.
Je conseille néanmoins de configurer un logo BIMI sans certificat. Cette approche permet d'améliorer l'User experience en affichant le logo dans les boîtes mail avec un effort minimal.
Je compte configurer prochainement un logo BIMI sans certificat pour mon domaine personnel stephane-klein.info.
Pendant que j'écrivais cette note, je me suis encore interrogé sur l'absence d'acteurs qui tentent d'intégrer correctement une authentification mail via PKI étatique 🤔.
#JaimeraisUnJour creuser cette question dans une note dédiée.
Samedi 2 août 2025
Journal du samedi 02 août 2025 à 13:07
#JaiÉcouté l'épisode « Open Source Experts : conseiller et faire du support Open Source » du podcast Projets Libres.
J'y ai découvert :
- La société Open Source Experts nommée aussi OSE
- Le projet Qualification and Selection of Open Source Software
J'ai trouvé cette interview à 3 intéressante.
Si je décide de continuer à vendre des prestations freelance à l'avenir, j'aimerais prendre le temps de les contacter pour leur demander si mes compétences pourraient être utiles à l'un de leurs clients.
Jeudi 31 juillet 2025
Équivalence de l'empreinte carbone de l'entrainement de Mistral Large 2
#JaiLu cet article à propos de l'impact environnemental de Mistral Large 2 : « Notre contribution pour la création d'un standard environnemental mondial pour l'IA ».
Bien que cet article ne propose aucun lien vers le rapport complet, le fait que l'étude ait été menée en collaboration avec Carbon 4 me donne confiance. D'autant que Carbon 4 a publié un article dédié sur leur site : « Nouveau jalon dans la transparence environnementale de l'IA générative ».
Dans une note du 14 juillet 2025, j'ai écrit :
Pour Claude Sonnet 3.7 que j'ai fréquemment utilisé, je lis ceci :
- 100 in => 100 out : 0.4g
- 1k in => 1k out : 1g
- 10k in => 10k out : 2g
L'étude de Mistral AI indique un peu plus du double d'émission de CO2 pour l'inférence :
Les impacts marginaux de l'inférence, plus précisément l'utilisation de notre assistant IA Le Chat pour une réponse de 400 tokens:
- 1,14 gCO₂e
- 45 mL d'eau
- 0,16 mg de Sb eq.
1 g
pour 1000 tokens
versus 1,14g
pour 400 tokens
.
Concernant l'entrainement de Mistral Large 2, je retiens ceci :
L'empreinte environnementale de l'entraînement de Mistral Large 2 : en janvier 2025, et après 18 mois d'utilisation, Large 2 a généré les impacts suivants :
- 20,4 ktCO₂e,
- 281 000 m3 d'eau consommée, et
- 660 kg Sb eq (unité standard pour l'épuisement des ressources).
Si j'applique le référentiel de ma note du 14 juillet 2025, cette émission de CO2 lors de l'entraînement représente 115 606 trajets aller-retour Paris - Crest-Voland (Savoie) effectués avec ma voiture.
Détail du calcul : 20×1000×1000 / 173 = 115 606
.
Voici une estimation grossière pour établir une comparaison.
D'après ce rapport , 8% des Français partent au ski chaque année, soit environ 5 millions de personnes (68 000 000 * 0,08 = 5 440 000
).
Selon cet article BFMTV , 90% d'entre elles s'y rendent en voiture.
En supposant 4 personnes par véhicule, cela représente 1,2 million de voitures (5 440 000 * 0,9 / 4 = 1224000
).
Si la moitié effectue un trajet de 500 km x 2 (aller-retour), j'obtiens 600 000 trajets.
En reprenant l'estimation d'émission de ma voiture pour cette distance, le calcul donne 600 000 * 172 kg = 103 200 000 kg
, soit 130 kt de CO2, ce qui représente plus de 6 fois l'entraînement de Mistral Large 2.
Pour résumer cette Estimation de Fermi : les déplacements des parisiens vers les Alpes pour une saison de ski émettent probablement 6 fois plus de CO2 que l'entraînement de Mistral Large 2.
Dans cette note, mon but n'est pas de justifier l'intérêt de cet entraînement. Je cherchais plutôt à avoir des points de repère et des comparaisons pour mieux évaluer cet impact.
Mardi 29 juillet 2025
Première description du gestionnaire de projet de mes rêves
Introduction
Cela fait depuis 2022 que je souhaite prototyper un outil de gestion de tâches (issues) avec certaines fonctionnalités que je n'ai trouvées dans aucun outils Open source ou closed-source.
En novembre 2022, j'ai commencé le tout début d'un modèle de données PostgreSQL, mais je n'ai pas continué.
Je souhaite, dans cette note, présenter mon idée de prototype, présenter les fonctionnalités que j'aimerais implémenter.
Nom du projet : Projet 24 - Prototyper le gestionnaire de projet de mes rêves
Ces idées de fonctionnalité sont tirées de besoin personnel que j'ai rencontré depuis 2018, dans mes différents projets professionnel en équipe.
Pour réduire mon temps de rédaction de cette note et la publier au plus tôt, je ne souhaite pas détailler ici l'origine de ces besoins.
Je souhaite juste décrire quelques fonctionnalités que je souhaite et quelque détail technique sans expliquer l'origine de mon besoin.
Sources d'inspiration
Mes principales sources d'inspiration :
- Certaines fonctionnalités issues et projects de GitHub et ses dernières améliorations.
- Certaines fonctionnalités Plan and track work de GitLab.
- Certaines fonctionnalités de Basecamp, par exemple, j'adore les Hill Charts (https://basecamp.com/hill-charts).
- Certaines fonctionnalités de Linear.
- Certaines fonctionnalités de OpenProject
Je me projette d'utiliser Projet 24 dans les framework de gestion de projets suivants :
Ainsi qu'avec la technologie sociale Sociocratie 3.0.
Liste de fonctionnalités en vrac
- Permettre d'importer / exporter une ou plusieurs issues dans un format de fichier YAML.
- Permettre d'importer / exporter ces fichiers via Git.
- Permettre l'utilisation de branche : création, suppression, merge de branches.
- Permettre la gestion des branches via l'interface web.
- Visualisation web des diff entre deux branches.
- Permettre de commit ou créer des snapshots d'une branche.
- Permettre d'attribuer à une issue une estimation basse et haute de temps d'implémentation.
- Permettre d'activer un Hill Charts sur toute issue.
- Permettre d'indiquer un niveau d'approximation d'une issue
- Permettre aux lectures d'une issue d'indiquer leur niveau de compréhension de l'issue
- Permettre de configurer la taille maximum en mots d'une issue. Pour forcer un certain niveau de synthèse.
- Permettre de calculer le poids d'une issue en faisant la somme basse et haute de toutes ses dépendances.
- Système inspiré de Tinder pour prioriser les issues. L'application présente deux issues choisies selon un algorithme Elo et invite l'utilisateur à désigner celle qu'il considère comme prioritaire.
- Implémenter un système de tags d'issues personnalisés où chaque utilisateur peut créer ses propres étiquettes. La visibilité de ces tags serait configurable : mode privé pour un usage personnel ou mode partagé pour les rendre disponibles aux autres utilisateurs.
- Permettre de créer des portfolios d'issue par utilisateurs.
- Pas de séparation des entités Epic (gestion de projet logiciel) / Issue contrairement à ce que fait GitLab.
- Permettre d'utilisation d'une extension Browser pour enrichir les pages GitHub, GitLab, Linear ou Forgejo avec les fonctionnalités de Projet 24.
- Permettre au Projet 24 d'améliorer une instance privé Forgejo avec un wrapper HTTP.
- Système de dashboard pratiquement identique à GitHub projects.
- Système de commentaire comme GitHub, mais avec un système de thread.
- Support de wikilink et alias au niveau de toutes les ressources texte.
- Support d'une fonctionnalité de publication de notes éphémères attachées à chaque utilisateur.
- Permettre la création d'issues ou de notes "flottantes". Une issue "flottante" n'appartient à aucune ressource spécifique — elle n'est rattachée ni à un projet, ni à un groupe. Cette fonctionnalité me semble essentielle et je compte la détailler dans une note dédiée prochainement.
- Proposer une extension Browser qui détecte automatiquement les issues liées à l'URL de la page actuelle. Cela permettrait d'accéder rapidement aux issues ou notes "flottantes" selon le contexte de navigation.
- Très bon support Markdown, contrairement aux implémentations de Slack, Notion ou Linear. Il devrait être possible de basculer entre le mode d'édition riche et le mode markdown. Le contenu copié doit générer du markdown valide dans le presse-papier.
- Respect strict des conventions Web : permettre l'ouverture de toutes les pages dans un nouvel onglet, etc.
- Mettre l'accent sur la performance de rendu des pages. Implémenter en priorité un système de métriques pour mesurer les temps de rendu.
- Proposer un système de génération de titre d'issue et de tag basé sur un LLM.
- Mettre en place un système qui utilise un LLM pour proposer automatiquement des titres d'issues et des tags.
- Alimenter une base de données vectorielle avec les descriptions d'issues et leurs commentaires pour activer la recherche sémantique.
Expérience utilisateur
Comme SilverBullet.mb, un outil fait dans un premier temps pour les hackers.
Détails techniques
- Stockage dans Elasticsearch pour faciliter les recherches par tags et plain text.
- Utilisation de nanoid de 5 caractères pour identifier les issues.
- Utilisation de Git hook pre-receive côté serveur pour importer des données (issues, notes, etc)
Journal du mardi 29 juillet 2025 à 10:14
Hier, j'ai perdu 1h43 à corriger un dysfonctionnement de mon navigateur Internet LibreWolf : même en sélectionnant "OS Default" ou "Thème system", l'affichage des sites web ne suivait pas le thème (light/dark) de mon environnement desktop (GNOME).
Depuis que j'utilise LibreWolf, tous les problèmes que j'ai rencontrés étaient liés à des paramétrages de sécurité trop stricts à mon goût, par exemple :
Quand j'ai découvert ce dysfonctionnement du support de thème, j'ai immédiatement pensé à un problème de sécurité et je me suis dirigé vers la page Settings and librewolf.overrides.cfg pour explorer les options disponibles.
J'ai rapidement trouvé cette issue : "Follow system theme while keeping RFP enabled ".
Dans un premier temps, j'ai tenté d'ajouter ces options à ma configuration librewolf.overrides.cfg :
pref("privacy.fingerprintingProtection", false)
pref("privacy.trackingprotection.pbmode.enabled", true)
Cela n'a pas fonctionné, le navigateur ignore ces paramètres. Il me semble que privacy.fingerprintingProtection
est automatiquement remis à true
à chaque lancement de LibreWolf.
J'ai ensuite appliqué la configuration suivante :
pref("privacy.fingerprintingProtection.overrides", "+AllTargets,-CSSPrefersColorScheme");
Cette configuration a bien été prise en compte, mais ce changement n'a pas résolu le dysfonctionnement.
Ensuite, dans la page about:config
de mon navigateur, j'ai remarqué qu'en définissant le paramètre browser.theme.content-theme
à 2
, l'adaptation automatique des sites au thème du système fonctionnait de nouveau.
Cependant, ce paramètre revenait systématiquement à 0
à chaque redémarrage de LibreWolf.
Après quelques minutes de recherche, j'ai réalisé que c'était le thème "Sombre" de mon navigateur qui forçait automatiquement browser.theme.content-theme
à 0
. En passant au thème "Thème système - auto", le problème était résolu 🤦♂️.
Je pensais que le thème du navigateur se limitait à personnaliser le chrome du navigateur, pas l'affichage des sites web en plus.
Dimanche 27 juillet 2025
Journal du dimanche 27 juillet 2025 à 21:38
#JaiDécouvert la technologie sociale "Sociocratie 3.0" qui, d'après ce que j'ai compris, est une évolution de la sociocratie de Gerard Endenburg.
Toutes les ressources qui décrivent la "Sociocratie 3.0" sont diffusées sous licence Creative Commons.
Jeudi 24 juillet 2025
Journal du jeudi 24 juillet 2025 à 22:31
Suite à ma note "J'ai découvert ContainerLab, un projet qui permet de simuler des réseaux", j'ai implémenté et publié containerlab-playground
.
Mon but était d'utiliser Containerlab pour simuler deux réseaux IPv6 connectés entre eux : 3 serveurs sur le premier réseau et 2 serveurs sur le second.
Comme je l'observe fréquemment depuis quelques mois, Claude Sonnet 4 m'a produit une implémentation qui, en pratique, ne fonctionne pas (voir son contenu dans 2025-07-20_1241).
La note courante reprend principalement en français le contenu du README.md de mon playground .
Voici les instructions que j'ai exécutées pour installer Containerlab sous Fedora :
$ sudo dnf config-manager addrepo --set=baseurl="https://netdevops.fury.site/yum/" && \
$ echo "gpgcheck=0" | sudo tee -a /etc/yum.repos.d/netdevops.fury.site_yum_.repo
$ sudo dnf install containerlab
$ sudo usermod -aG clab_admins stephane && newgrp clab_admins
$ sudo semanage fcontext -a -t textrel_shlib_t $(which containerlab)
$ sudo restorecon $(which containerlab)
Voici les instructions que j'ai exécutées pour installer Containerlab sous Fedora :
$ sudo containerlab deploy
11:27:03 INFO Containerlab started version=0.69.0
11:27:03 INFO Parsing & checking topology file=network-a-b.clab.yaml
11:27:03 INFO Creating docker network name=net-custom IPv4 subnet="" IPv6 subnet=2001:db8:a::0/48 MTU=0
11:27:03 INFO Creating lab directory path=/home/stephane/git/github.com/stephane-klein/containerlab-playground/clab-network-a
11:27:04 INFO Creating container name=vm-b1
11:27:04 INFO Creating container name=vm-a1
11:27:04 INFO Creating container name=vm-a2
11:27:04 INFO Creating container name=vm-a3
11:27:04 INFO Adding host entries path=/etc/hosts
11:27:04 INFO Adding SSH config for nodes path=/etc/ssh/ssh_config.d/clab-network-a.conf
╭──────────────────────┬───────────────────┬─────────┬─────────────────╮
│ Name │ Kind/Image │ State │ IPv4/6 Address │
├──────────────────────┼───────────────────┼─────────┼─────────────────┤
│ clab-network-a-vm-a1 │ linux │ running │ 192.168.0.2 │
│ │ mitchv85/ohv-host │ │ 2001:db8:a:1::1 │
├──────────────────────┼───────────────────┼─────────┼─────────────────┤
│ clab-network-a-vm-a2 │ linux │ running │ 192.168.0.3 │
│ │ mitchv85/ohv-host │ │ 2001:db8:a:1::2 │
├──────────────────────┼───────────────────┼─────────┼─────────────────┤
│ clab-network-a-vm-a3 │ linux │ running │ 192.168.0.4 │
│ │ mitchv85/ohv-host │ │ 2001:db8:a:1::3 │
├──────────────────────┼───────────────────┼─────────┼─────────────────┤
│ clab-network-a-vm-b1 │ linux │ running │ 192.168.0.5 │
│ │ mitchv85/ohv-host │ │ 2001:db8:a:2::5 │
╰──────────────────────┴───────────────────┴─────────┴─────────────────╯
Globalement, j'ai trouvé l'expérience utilisateur de la cli Containerlab très agréable à utiliser :
$ containerlab help
deploy container based lab environments with a user-defined interconnections
USAGE
containerlab [command] [--flags]
COMMANDS
completion [bash|zsh|fish] Generate completion script
config [command] [--flags] Configure a lab
deploy [--flags] Deploy a lab
destroy [--flags] Destroy a lab
exec [--flags] Execute a command on one or multiple containers
generate [--flags] Generate a Clos topology file, based on provided flags
graph [--flags] Generate a topology graph
help [command] [--flags] Help about any command
inspect [command] [--flags] Inspect lab details
redeploy [--flags] Destroy and redeploy a lab
save [--flags] Save containers configuration
tools [command] Various tools your lab might need
version [command] Show containerlab version or upgrade
FLAGS
-d --debug Enable debug mode
-h --help Help for containerlab
--log-level Logging level; one of [trace, debug, info, warning, error, fatal] (info)
--name Lab name
-r --runtime Container runtime
--timeout Timeout for external API requests (e.g. container runtimes), e.g: 30s, 1m, 2m30s (2m0s)
-t --topo Path to the topology definition file, a directory containing one, 'stdin', or a URL
--vars Path to the topology template variables file
-v --version Version for containerlab
J'ai eu quelques difficultés à trouver une image Docker à utiliser qui fournit directement un serveur ssh :
vm-a1:
kind: linux
# image: alpine-ssh
# https://hub.docker.com/r/mitchv85/ohv-host
image: mitchv85/ohv-host
mgmt-ipv6: 2001:db8:a:1::1
Le déploiement de cette image permet de facilement se connecter à l'host en ssh:
$ ssh admin@clab-network-a-vm-a1
admin@vm-a1:~$ exit
Il est possible de facilement lancer une commande sur tous les hosts :
$ sudo containerlab exec -t network-a-b.clab.yaml --cmd 'ip addr'
11:29:52 INFO Parsing & checking topology file=network-a-b.clab.yaml
11:29:52 INFO Executed command node=clab-network-a-vm-a2 command="ip addr"
stdout=
│ 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
│ link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
│ inet 127.0.0.1/8 scope host lo
│ valid_lft forever preferred_lft forever
│ inet6 ::1/128 scope host
│ valid_lft forever preferred_lft forever
│ 2: eth0@if192: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
│ link/ether aa:86:42:84:81:de brd ff:ff:ff:ff:ff:ff link-netnsid 0
│ inet 192.168.0.3/20 brd 192.168.15.255 scope global eth0
│ valid_lft forever preferred_lft forever
│ inet6 2001:db8:a:1::2/48 scope global nodad
│ valid_lft forever preferred_lft forever
│ inet6 fe80::a886:42ff:fe84:81de/64 scope link
│ valid_lft forever preferred_lft forever
11:29:52 INFO Executed command node=clab-network-a-vm-a3 command="ip addr"
stdout=
│ 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
│ link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
│ inet 127.0.0.1/8 scope host lo
│ valid_lft forever preferred_lft forever
│ inet6 ::1/128 scope host
│ valid_lft forever preferred_lft forever
│ 2: eth0@if193: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
│ link/ether b2:42:6c:2b:d0:9d brd ff:ff:ff:ff:ff:ff link-netnsid 0
│ inet 192.168.0.4/20 brd 192.168.15.255 scope global eth0
│ valid_lft forever preferred_lft forever
│ inet6 2001:db8:a:1::3/48 scope global nodad
│ valid_lft forever preferred_lft forever
│ inet6 fe80::b042:6cff:fe2b:d09d/64 scope link
│ valid_lft forever preferred_lft forever
11:29:52 INFO Executed command node=clab-network-a-vm-a1 command="ip addr"
stdout=
│ 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
│ link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
│ inet 127.0.0.1/8 scope host lo
│ valid_lft forever preferred_lft forever
│ inet6 ::1/128 scope host
│ valid_lft forever preferred_lft forever
│ 2: eth0@if191: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
│ link/ether 26:9f:87:52:d6:1c brd ff:ff:ff:ff:ff:ff link-netnsid 0
│ inet 192.168.0.2/20 brd 192.168.15.255 scope global eth0
│ valid_lft forever preferred_lft forever
│ inet6 2001:db8:a:1::1/48 scope global nodad
│ valid_lft forever preferred_lft forever
│ inet6 fe80::249f:87ff:fe52:d61c/64 scope link
│ valid_lft forever preferred_lft forever
11:29:52 INFO Executed command node=clab-network-a-vm-b1 command="ip addr"
stdout=
│ 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
│ link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
│ inet 127.0.0.1/8 scope host lo
│ valid_lft forever preferred_lft forever
│ inet6 ::1/128 scope host
│ valid_lft forever preferred_lft forever
│ 2: eth0@if194: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
│ link/ether e2:81:7b:c7:eb:64 brd ff:ff:ff:ff:ff:ff link-netnsid 0
│ inet 192.168.0.5/20 brd 192.168.15.255 scope global eth0
│ valid_lft forever preferred_lft forever
│ inet6 2001:db8:a:2::5/48 scope global nodad
│ valid_lft forever preferred_lft forever
│ inet6 fe80::e081:7bff:fec7:eb64/64 scope link
│ valid_lft forever preferred_lft forever
Ou alors sur un host en particulier :
$ sudo containerlab exec -t network-a-b.clab.yaml --label clab-node-name=vm-a1 --cmd 'ip addr'
11:02:44 INFO Parsing & checking topology file=network-a-b.clab.yaml
11:02:44 INFO Executed command node=clab-network-a-vm-a1 command="ip addr"
stdout=
│ 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
│ link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
│ inet 127.0.0.1/8 scope host lo
│ valid_lft forever preferred_lft forever
│ inet6 ::1/128 scope host
│ valid_lft forever preferred_lft forever
Par contre, je n'ai pas réussi à atteindre mon objectif 😟.
J'ai l'impression que pour le moment, Containerlab ne permet pas de créer plusieurs réseaux à partir d'un fichier topologie.
Je n'ai pas compris comment définir la longueur du prefix IPv6 des interfaces eth0
au niveau des nodes.
Pour le moment, tous les nodes appartiennent au même sous-réseau 2001:db8:a::0/48
, alors que j'aimerais les séparer dans les deux sous-réseaux suivants :
2001:db8:a:1
2001:db8:a:2
# network-a-b.clab.yaml
...
topology:
nodes:
vm-a1:
kind: linux
# image: alpine-ssh
# https://hub.docker.com/r/mitchv85/ohv-host
image: mitchv85/ohv-host
mgmt-ipv6: 2001:db8:a:1::1
# <== ici je ne sais pas comment définir : 2001:db8:a:1::1/64
...
J'ai découvert l'issue suivante du principal développeur de Containerlab : « Multiple management networks ».
Je pense comprendre que ce que je cherche à faire n'est actuellement pas possible avec Containerlab.
Pour atteindre mon objectif, peut-être que je devrais plutôt m'orienter vers des alternatives mentionnées dans ce billet de blog : Open-source network simulation roundup 2024.
Je viens de poster le message suivant l'espace de discussion GitHub de Containerlab : « Does Containerlab support the creation of topologies with multiple subnets? ».
J'espère que le créateur de Containerlab pourra me suggérer une solution à mon problème, car je n'ai pas réussi à l'identifier dans la documentation 🤷♂️.
Voir la suite de cette note : 2025-09-19_1651.
Mardi 22 juillet 2025
Journal du mardi 22 juillet 2025 à 14:16
Dans la vidéo "Entretiens philosophiques, épisode 2 : Michéa et la critique du libéralisme" de Philo-Man et Le Précepteur, j'ai découvert la signification de mot "conséquent".
- Qui raisonne, qui agit avec un esprit de suite, rationnel, logique dans ses actions et sa conduite.
- (Philosophie) Qui qualifie une démarche logique avec soi-même, cohérent.
- Qui est la suite raisonnable de quelque chose, en accord avec elle, qui peut être logiquement déduit.
Dimanche 20 juillet 2025
J'ai découvert ContainerLab, un projet qui permet de simuler des réseaux
Pendant mon travail d'étude pratique de IPv6, #JaiDécouvert le projet Containerlab :
Containerlab was meant to be a tool for provisioning networking labs built with containers. It is free, open and ubiquitous. No software apart from Docker is required! As with any lab environment it allows the users to validate features, topologies, perform interop testing, datapath testing, etc. It is also a perfect companion for your next demo. Deploy the lab fast, with all its configuration stored as a code -> destroy when done.
Projet qui a commencé en 2020 et semble principalement développé par un développeur de chez Nokia.
D'après ce que j'ai compris, Containerlab me permet de facilement créer des réseaux dans un simulateur.
Je me souviens que je cherchais ce type d'outil en 2018, quand je travaillais sur un projet baremetal as service chez Scaleway.
Voici un exemple de fichier créé par Claude.ia pour simuler un environnement composé de deux réseaux IPv6 connectés entre eux : 3 serveurs sur le premier réseau et 2 serveurs sur le second.
Je précise que je n'ai pas encore testé ce fichier. J'ignore donc s'il fonctionne correctement.
name: dual-network-ipv6-lab
topology:
nodes:
# Routeur avec IPv6
router:
kind: linux
image: alpine:latest
exec:
# Activer IPv6
- sysctl -w net.ipv6.conf.all.disable_ipv6=0
- sysctl -w net.ipv6.conf.all.forwarding=1
# Adresses IPv6 sur les interfaces
- ip -6 addr add 2001:db8:1::1/64 dev eth1
- ip -6 addr add 2001:db8:2::1/64 dev eth2
# IPv4 en parallèle (dual-stack)
- ip addr add 192.168.1.1/24 dev eth1
- ip addr add 192.168.2.1/24 dev eth2
- echo 1 > /proc/sys/net/ipv4/ip_forward
# Réseau A (2001:db8:1::/64)
vm-a1:
kind: linux
image: alpine:latest
exec:
- sysctl -w net.ipv6.conf.all.disable_ipv6=0
- ip -6 addr add 2001:db8:1::10/64 dev eth1
- ip -6 route add default via 2001:db8:1::1
- ip addr add 192.168.1.10/24 dev eth1
- ip route add default via 192.168.1.1
vm-a2:
kind: linux
image: alpine:latest
exec:
- sysctl -w net.ipv6.conf.all.disable_ipv6=0
- ip -6 addr add 2001:db8:1::11/64 dev eth1
- ip -6 route add default via 2001:db8:1::1
- ip addr add 192.168.1.11/24 dev eth1
- ip route add default via 192.168.1.1
vm-a3:
kind: linux
image: alpine:latest
exec:
- sysctl -w net.ipv6.conf.all.disable_ipv6=0
- ip -6 addr add 2001:db8:1::12/64 dev eth1
- ip -6 route add default via 2001:db8:1::1
- ip addr add 192.168.1.12/24 dev eth1
- ip route add default via 192.168.1.1
# Réseau B (2001:db8:2::/64)
vm-b1:
kind: linux
image: alpine:latest
exec:
- sysctl -w net.ipv6.conf.all.disable_ipv6=0
- ip -6 addr add 2001:db8:2::10/64 dev eth1
- ip -6 route add default via 2001:db8:2::1
- ip addr add 192.168.2.10/24 dev eth1
- ip route add default via 192.168.2.1
vm-b2:
kind: linux
image: alpine:latest
exec:
- sysctl -w net.ipv6.conf.all.disable_ipv6=0
- ip -6 addr add 2001:db8:2::11/64 dev eth1
- ip -6 route add default via 2001:db8:2::1
- ip addr add 192.168.2.11/24 dev eth1
- ip route add default via 192.168.2.1
links:
# Réseau A
- endpoints: ["router:eth1", "vm-a1:eth1"]
- endpoints: ["router:eth1", "vm-a2:eth1"]
- endpoints: ["router:eth1", "vm-a3:eth1"]
# Réseau B
- endpoints: ["router:eth2", "vm-b1:eth1"]
- endpoints: ["router:eth2", "vm-b2:eth1"]
Samedi 19 juillet 2025
Journal du samedi 19 juillet 2025 à 11:58
Cette semaine, deux amis m'ont partagé deux expressions que j'ai trouvées pertinentes.
La première est une déclinaison de « c'est à nous de le faire » : « C'est à nous de ne pas le faire ! ».
Elle s'utilise quand une équipe a la responsabilité d'un périmètre, ne fait pas le travail et empêche d'autres équipes de le faire.
La seconde expression est la « Vérité partagée » (au sein d'un groupe).
Je trouve cette expression remarquablement simple et directe, ce qui la rend très pratique.
Je pense que « les vérités partagées » sont essentielles pour créer une équipe.
Je pense que plus le nombre et la force « des vérités partagées » au sein d'une équipe sont élevés, plus cette équipe sera performante.
Il me paraît difficile, voire impossible, de mener à bien un projet politique, de gérer une entreprise ou de développer une application informatique si l'équipe ou l'organisation manque de vérités partagées.
Ayant développé depuis avril 2024 une relation particulière avec le mot vérité, je préfère à l'avenir utiliser l'expression « croyance partagée ».
Jeudi 17 juillet 2025
Journal du jeudi 17 juillet 2025 à 15:44
Voici comment perdre presque deux heures bêtement !
#!/usr/bin/env python3
import requests
import os
session = requests.Session()
session.headers.update({"Content-Type": "application/json"})
auth_response = session.post(
"http://localhost:3000/api/v1/auths/signin",
json={
"email": "contact+admin@stephane-klein.info",
"password": os.environ['OPEN_WEBUI_ADMIN_PASSWORD']
}
)
session.headers.update({
"Authorization": f'Bearer {auth_response.json()["token"]}'
})
with open("hello_world.py", "r") as f:
response = session.post(
"http://localhost:3000/api/v1/pipelines/upload",
files={
"file": ("hello_world3.py", f, "text/x-python")
},
data={
"urlIdx": "2"
}
)
print(response.text)
L'API de Open WebUI envoyait cette erreur :
{"detail":[{"type":"missing","loc":["body","urlIdx"],"msg":"Field required","input":null},{"type":"missing","loc":["body","file"],"msg":"Field required","input":null}]}
Mon erreur se situé au niveau de la ligne :
session.headers.update({"Content-Type": "application/json"})
Si je supprime cette ligne, le script fonctionne parfaitement.
Il semble que la fonction session.post()
ne configure pas correctement le Content-Type
multipart lorsque ce header a été défini au préalable au niveau de la session.
J'ai cherché pendant quelques minutes une issue à ce sujet, sans succès.
Dans l'idéal, requests devrait émettre un warning dans cette situation.
Je viens de créer cette issue : "POST Multipart-Encoding does not work if the Content-Type header has been previously defined in the session"
#UnJourPeuxÊtre je proposerai aussi une implémentation.
D'autre part, ma configuration :
session.headers.update({"Content-Type": "application/json"})
n'avait aucune utilité puisque post
définit automatiquement "Content-Type": "application/json"
quand on utilise le paramètre json=
🙈.
Mardi 15 juillet 2025
J'utilise les LLMs comme des amis experts et jamais comme des écrivains fantômes
Un ami m'a posé la question suivante :
J'aimerais ton avis sur l'utilisation des LLM au quotidien (hors code). Les utilises-tu ? En tires-tu quelque chose de positif ? Quelles en sont les limites ?
Je vais tenter de répondre à cette question dans cette note.
Danger des LLMs : le risque de prolétarisation
Mon père et surtout mon grand-père m'ont inculqué par tradition familiale la valeur du savoir-faire. Plus tard, Bernard Stiegler m'a donné les outils théoriques pour comprendre cet enseignement à travers le concept de processus de prolétarisation.
La prolétarisation est, d’une manière générale, ce qui consiste à priver un sujet (producteur, consommateur, concepteur) de ses savoirs (savoir-faire, savoir-vivre, savoir concevoir et théoriser).
Ici, j'utilise la définition de prolétaire suivante :
Personne qui ne possède plus ses savoirs, desquels elle a été dépossédée par l’utilisation d’une technique.
En analysant mon parcours, je réalise que ma quête d'autonomie technique et de compréhension — en somme, ma recherche d'émancipation — a systématiquement guidé mes choix, comme le fait d'avoir pris le chemin du logiciel libre en 1997.
Sensibilisé à ces questions, j'ai immédiatement perçu les risques dès que j'ai découvert la puissance des LLM mi 2023 .
J'utilise les LLMs comme des amis expert d'un domaine
Les LLMs sont pour moi des pharmakons : ils sont à la fois un potentiel remède et un poison. J'essaie de rester conscient de leurs toxicités.
J'ai donc décidé d'utiliser les IA générative de texte comme je le ferais avec un ami expert d'un domaine.
Concrètement, je continue d'écrire la première version de mes notes, mails, commentaires, messages de chat ou issues sans l'aide d'IA générative de texte.
C'est seulement dans un second temps que je consulte un LLM, comme je le ferais avec un ami expert : pour lui demander un commentaire, lui poser une question ou lui demander une relecture.
J'utilise les IA générative de texte par exemple pour :
- vérifier si mon texte est explicite et compréhensible
- obtenir des suggestions d'amélioration de ma rédaction
Tout comme avec un ami, je lui partage l'intégralité de mon texte pour donner le contexte, et ensuite je lui pose des questions ciblées sur une phrase ou un paragraphe spécifique. Cette méthode me permet de mieux cadrer ses réponses.
À ce sujet, voir mes notes suivantes :
- Idée d'application de réécriture de texte assistée par IA
- Prompt - Reformulation de paragraphes pour mon notes.sklein.xyz
Par respect pour mes interlocuteurs, je ne demande jamais à un LLM de rédiger un texte à ma place.
(source)
Lorsque je trouve pertinent un contenu produit par un LLM, je le partage en tant que citation en indiquant clairement la version du modèle qui l'a généré. Je le cite comme je citerai les propos d'un humain.
En résumé, je ne m'attribue jamais les propos générés par un LLM. Je n'utilise jamais un LLM comme un écrivain fantôme.
Seconde utilisation : exploration de sujets
J'utilise aussi les LLMs pour explorer des sujets.
Je dirais que cela me permet de faire l'expérience de ce que j'appellerais "de la sérendipité dirigée".
Par exemple, je lui expose une idée et comme à un ami, je lui demande si cela a du sens pour lui, qu'est-ce que cela lui évoque et très souvent, je découvre dans ses réponses des auteurs ou des concepts que je n'ai jamais entendus parler.
J'utilise beaucoup les LLMs pour obtenir un "overview" avec une orientation très spécifique, sur des sujets tech, politique, historique…
Je l'utilise aussi souvent pour comprendre l'origine des noms des projets, ce qui me permet de mieux m'en souvenir.
Voir aussi cette note que j'ai publiée en mai 2024 : Je constate que j'utilise de plus en plus ChatGPT à la place de DuckDuckGo.
Les limites ?
En matière d'exploration, je pense que les LLMs sont d'une qualité exceptionnelle pour cette tâche. Je n'ai jamais expérimenté quelque chose d'aussi puissant. Peut-être que j'obtiendrais de meilleurs résultats en posant directement des questions à des experts mondiaux dans les domaines concernés, mais la question ne se pose pas puisque je n'ai pas accès à ces personnes.
Pour l'aide à la rédaction, il me semble que c'est nettement plus efficace que ce qu'un ami serait en mesure de proposer. Même si ce n'est pas parfait, je ne pense pas qu'un LLMs soit en mesure de deviner précisément, par lui-même, ce que j'ai l'intention d'exprimer. Il n'y a pas de magie : il faut que mes idées soient suffisamment claires dans mon cerveau pour être formulées de façon explicite. En ce qui concerne ces tâches, je constate d'importantes différences entre les modèles. Actuellement, Claude Sonnet 4 reste mon préféré pour la rédaction En revanche, j'obtiens de moins bons résultats avec les modèles chain-of-thought, ce qui est sans doute visible dans les LLM Benchmark.
Par contre, dès que je m'éloigne des questions générales pour aborder la résolution de problèmes précis, j'obtiens pour le moment des résultats très faibles. Je remarque quotidiennement des erreurs dans le domaine tech, comme :
- des paramètres inexistants
- des parties de code qui ne s'exécutent pas
- ...
Comment a évolué mon utilisation des LLMs depuis 2023 ?
J'ai publié sur https://data.sklein.xyz mes statistiques d'utilisation des LLMs de janvier 2023 à mai 2025.
Ces statistiques ne sont plus représentatives à partir de juin 2025, parce que j'ai commencé à utiliser fortement Open WebUI couplé à OpenRouter et aussi LMArena. J'aimerais prendre le temps d'intégrer les statistiques de ces plateformes prochainement.
Comme on peut le voir sur https://data.sklein.xyz, mon usage de ChatGPT a réellement démarré en avril 2024, pour évoluer vers une consommation mensuelle d'environ 300 threads.
Je suis surpris d'avoir si peu utilisé ChatGPT entre avril 2023 et janvier 2024 🤔. Je l'utilisais peut-être en mode non connecté et dans ce cas, j'ai perdu toute trace de ces interactions.
Voir aussi ma note : Estimation de l'empreinte carbone de mon usage des IA génératives de textes.
Combien je dépense en inférence LLM par mois ?
De mars à septembre 2024, 22 € par mois pour ChatGPT.
De mars à mai 2025, 22 € par mois pour Claude.ia.
Depuis juin 2025, je pense que je consomme moins de 10 € par mois, depuis que je suis passé à OpenRouter. Plus d'informations à ce sujet dans : Quelle est mon utilisation d'OpenRouter.ia ?
J'aurais encore beaucoup à dire sur le sujet des LLMs, mais j'ai décidé de m'arrêter là pour cette note.
Pour aller plus loin sur ce sujet, sous un angle très technique, je conseille cette série d'articles sur LinuxFr :
- Nouvelles sur l’IA de février 2025
- Nouvelles sur l’IA de mars 2025
- Nouvelles sur l’IA d’avril 2025
- Nouvelles sur l’IA de mai 2025
- Nouvelles sur l’IA de juin 2025
Et toutes mes notes associées au tag : #llm
Pas de notes plus récentes | [ Notes plus anciennes (50) >> ]