« -L » relaie UN service ; « -D » ouvre un relais SOCKS, par lequel le
navigateur atteint n'importe quelle destination depuis la machine distante —
une interface qui n'écoute que sur sa boucle locale, un hôte de son réseau sans
route depuis ici. Le port par défaut est 1080, modifiable, et contrôlé libre
avant d'ouvrir. La saisie de l'adresse est celle de sshfs, désormais partagée :
l'alias de ~/.ssh/config part tel quel à ssh, faute de quoi son ProxyJump se
perdrait et une VM imbriquée deviendrait injoignable. Le réglage de Firefox
s'affiche AVANT le lancement, la commande ne rendant la main qu'au Ctrl+C.
Vérifié : 9 tests neufs, et la numérotation du menu que deux autres gardent.
--- EN ---
"-L" relays ONE service; "-D" opens a SOCKS relay, through which the browser
reaches any destination from the remote machine — an interface listening only
on its loopback, a host of its network with no route from here. The default
port is 1080, changeable, and checked free before opening. The address prompt
is sshfs's, now shared: the ~/.ssh/config alias goes to ssh as is, failing
which its ProxyJump would be lost and a nested VM become unreachable. The
Firefox settings print BEFORE the launch, the command only returning on
Ctrl+C.
Checked: 9 new tests, and the menu numbering two others guard.
Assisted-by: Claude Opus 5
install.sh et make lançaient todo.py par son hashbang, donc par le python3 du
PATH, sans rien garantir de sa version : une syntaxe plus récente l'arrête en
SyntaxError au chargement, et le poste hors d'âge perd tout moyen de lancer
l'installation qui l'en sortirait. L'ordre est désormais le venv d'outillage,
sinon le python3 du PATH s'il suffit, sinon l'installation. TODO lui-même se
relance dans le venv, vérifie version et modules avant ses imports, et propose
l'installation en terminal — ce que « make » sur un clone neuf promet depuis
toujours sans le tenir.
Vérifié : venv à jour, venv périmé, systèmes en 3.10, 3.14 et 3.15 vont chacun
où il faut ; un import du module ne relance rien et lève comme avant.
--- EN ---
install.sh and make ran todo.py through its hashbang, so through the python3 of
the PATH, with no guarantee about its version: a newer syntax stops it with a
SyntaxError at load, and the ageing machine loses every way to start the
install that would rescue it. The order is now the tools venv, else the python3
of the PATH when it suffices, else the install. TODO itself relaunches in the
venv, checks version and modules before its imports, and offers the install in
a terminal — what "make" on a fresh clone has always promised without
keeping.
Checked: fresh venv, stale venv, systems on 3.10, 3.14 and 3.15 each go where
they should; importing the module relaunches nothing and raises as before.
Assisted-by: Claude Opus 5
install_erplibre.sh bâtissait .venv.erplibre avec le python3 du système, quelle
que soit sa version : un 3.10 y donnait un venv que l'outillage ne sait pas
charger. Il délègue à install_venv.sh, qui obtient la version par mise, pyenv
ou la distribution, et rebâtit un venv hors service. Le PATCH ne borne que le
venv d'Odoo, dont le pyproject exige « >=3.12.10,<3.13 » : l'exiger ailleurs
écartait un Python de distribution d'un cran en retard — NixOS 25.11 livre
3.14.2 — et faisait compiler CPython pour rien. Le module NixOS déclare
désormais les DEUX Python, substitués depuis les fichiers de version.
Vérifié : 15 tests sur des venvs factices, conservés, rebâtis ou refusés selon
leur état, et un chemin sans pyvenv.cfg garde son contenu.
--- EN ---
install_erplibre.sh built .venv.erplibre with the system python3, whatever its
version: a 3.10 gave a venv the tooling cannot load. It delegates to
install_venv.sh, which obtains the version through mise, pyenv or the
distribution, and rebuilds a venv out of service. The PATCH bounds only Odoo's
venv, whose pyproject requires ">=3.12.10,<3.13": requiring it elsewhere turned
away a distribution Python one step behind — NixOS 25.11 ships 3.14.2 — and
compiled CPython for nothing. The NixOS module now declares BOTH Pythons,
substituted from the version files.
Checked: 15 tests on stub venvs, kept, rebuilt or refused by their state, and a
path without pyvenv.cfg keeps its content.
Assisted-by: Claude Opus 5
Une syntaxe plus récente que le Python du dépôt ne casse qu'au chargement, et
aucun outil en place ne la voyait : la cible de black borne ce qu'il ÉCRIT,
jamais ce qu'il accepte, et flake8 analyse avec l'interpréteur courant. Le
contrôle compile les fichiers indexés avec un vrai interpréteur de la version
déclarée, trouvé sans réseau par mise, pyenv ou le PATH, et se prend lui-même
quand il y tourne déjà. Sans interpréteur, il DIT qu'il n'a pas vérifié plutôt
que de laisser croire le contraire. Le hook relaie, sans bloquer le commit, et
écarte les dépôts rapatriés dont ce dépôt ne répond pas.
Vérifié : les f-strings PEP 701 de la révision précédente sont signalées, rien
ne l'est sur l'arbre courant, et l'absence du fichier de version se dit.
--- EN ---
A syntax newer than the repository's Python only breaks at load, and no tool in
place saw it: black's target bounds what it WRITES, never what it accepts, and
flake8 parses with the running interpreter. The check compiles staged files
with a real interpreter of the declared version, found without network through
mise, pyenv or the PATH, and takes itself when it already runs under it.
Without one, it SAYS it did not check rather than implying it did. The hook
relays, without blocking the commit, and skips the checked-out repositories
this one does not answer for.
Checked: the PEP 701 f-strings of the previous revision are reported, nothing
is on the current tree, and a missing version file is announced.
Assisted-by: Claude Opus 5
Cinq fichiers employaient des f-strings PEP 701 — guillemets imbriqués du même
type, backslash dans l'expression, expression sur plusieurs lignes — que
Python refuse avant 3.12. Or les hooks de script/git/hooks portent
« #!/usr/bin/env python3 » et tournent donc sur l'interpréteur du système,
qu'une distribution livre encore en 3.10 : la SyntaxError tombe au chargement,
sans qu'aucun garde puisse nommer la commande à taper. Les chaînes traduites
gardent leur texte à l'octet près, sans quoi leur clé se perdrait. Les
commentaires de ces fichiers passent du récit au présent, comme la règle le
demande de ce qu'on touche.
Vérifié : tout l'arbre Python parse sous 3.10, 3.11, 3.12 et 3.14.
--- EN ---
Five files used PEP 701 f-strings — quotes of the same kind nested, a backslash
in the expression, an expression spanning lines — which Python refuses before
3.12. The hooks in script/git/hooks carry "#!/usr/bin/env python3" and so run
on the system interpreter, which a distribution still ships as 3.10: the
SyntaxError lands at load, before any guard can name the command to type.
Translated strings keep their text byte for byte, else their key would be lost.
The comments of those files move from tale to present tense, as the rule asks
of what one touches.
Checked: the whole Python tree parses under 3.10, 3.11, 3.12 and 3.14.
Assisted-by: Claude Opus 5
Le dernier écran avant de créer portait « ERPLibre ne s'installe pas encore
sur NixOS : la VM est créée, l'installation échoue ». Il s'y installe, de
bout en bout : clone, make install_os, make install_odoo_18, Odoo enregistré
en service et servant 8069.
Un avertissement périmé à cet endroit ne fait pas qu'induire en erreur, il
décourage d'essayer ce qui marche — et c'est la seule ligne de l'écran qui
parle d'un échec. Sa traduction part avec lui, une entrée que personne
n'appelle vieillissant sans qu'on le sache.
--- EN ---
The last screen before creating carried « ERPLibre does not install on NixOS
yet: the VM is created, the install fails ». It does install, end to end:
clone, make install_os, make install_odoo_18, Odoo registered as a service
and serving 8069.
A stale warning there does not merely mislead, it discourages trying what
works — and it is the screen's only line that speaks of a failure. Its
translation goes with it, an entry nobody calls ageing unnoticed.
Assisted-by: Claude Opus 5
Les décisions que l'hôte prend avant de lancer sont gardées pour le journal
de la VM, celui qu'on rouvre le lendemain quand l'installation a échoué.
Celle du miroir apt restait sur la console, qui a défilé depuis.
C'est pourtant là qu'on la cherche : le magasin range ses index sous l'HÔTE
demandé, et une VM qui en réclame un autre ne retrouve rien de ce qui est
gardé — hors ligne, l'installation échoue plus bas sur des dépendances
introuvables, un message qui accuse le dépôt et jamais le miroir. Le nom du
miroir posé, ou le code de l'échec, rejoignent donc les autres notes.
--- EN ---
The decisions the host takes before launching are kept for the VM's log, the
one reopened the next day when the install has failed. The apt mirror's
stayed on the console, which has scrolled away since.
Yet that is where it is looked for: the store files its indexes under the
HOST asked for, and a VM asking for another finds nothing of what is kept —
offline, the install fails further down on unresolvable dependencies, a
message that blames the repository and never the mirror. The pinned mirror's
name, or the failure's code, therefore join the other notes.
Assisted-by: Claude Opus 5
NixOS était SOUSTRAIT du cache faute d'ancre de confiance par fichier, ce
qui lui fermait le hors ligne : le magasin est alors la seule source, et
l'exception ne laisse rien. Une déclaration arriverait trop tard, la
première reconstruction étant le premier téléchargement.
L'autorité est donc POINTÉE, consommateur par consommateur, et
l'environnement se perd à trois frontières : nix-daemon, activé par
socket, qu'un fragment sous /run/systemd/system atteint ; sudo, que
« env_keep » traverse — le nix de root parle droit au magasin local et
télécharge lui-même ; et la session ssh, ouverte une seconde avant que
cloud-init n'écrive le faisceau. Vérifié : installation complète, 0 refus.
--- EN ---
NixOS was EXEMPTED from the cache for want of a per-file trust anchor,
which closed offline deployment to it: the store is then the only source,
and an exemption leaves nothing. A declaration would come too late, the
first rebuild being the first download.
The authority is therefore POINTED AT, consumer by consumer, and the
environment is lost at three boundaries: nix-daemon, socket-activated,
reached by a drop-in under /run/systemd/system; sudo, crossed by
« env_keep » — root's nix talks straight to the local store and downloads
itself; and the ssh session, opened one second before cloud-init writes
the bundle. Checked: a complete install, 0 refusals.
Assisted-by: Claude Opus 5
Le rebase apporte trois choses auxquelles ce travail devait s'accorder.
Les messages du binaire se traduisent désormais : ceux du refus de place
et de l'aide de --oublie passent par T(), avec leur entrée au catalogue —
deux garde-fous refusent sinon un message sans traduction ou l'inverse.
Soustraire l'hôte au cache est REFUSÉ hors ligne : le magasin est alors la
seule source, et l'excepter ne le ferait pas télécharger en direct, cela
le priverait de tout, ses propres paquets compris.
Et le guide de connexion annonce les outils sur la voie Proxmox aussi ; il
n'y était plus écrit du tout, l'argument manquant étant avalé.
--- EN ---
The rebase brings three things this work had to align with. The binary's
messages are now translatable: the out-of-space refusal and the --oublie
help go through T(), with their catalogue entry — two guard rails
otherwise refuse a message without a translation, or the reverse.
Exempting the host from the cache is REFUSED offline: the store is then
the only source, and exempting it would not make it download directly, it
would leave it with nothing, its own packages included.
And the connection guide announces the tools on the Proxmox path too; it
was no longer written there at all, the missing argument being swallowed.
Assisted-by: Claude Opus 5
On entrait en ssh sans savoir que nix, PyCharm ou la forge étaient là, ni
par quelle commande s'en servir : le guide ne connaissait que la
distribution, ERPLibre et la présence d'un bureau. La liste vient du menu
FILTRÉE par la machine — Android Studio n'existe qu'en x86_64, PyCharm
veut un bureau — et chaque ligne est confrontée au code qui l'installe.
Le bloc NixOS porte la racine du dépôt, seule ligne qui n'était ni absolue
ni commande, et trois lignes qu'on ne devine pas : « --rollback », qui
défait une déclaration fautive ; « nixos-version » ; et « ls /bin », vide
alors que /bin/bash s'exécute. Une recherche de paquet a été essayée puis
écartée, muette sur cette image.
--- EN ---
One logged in over ssh with no way to know nix, PyCharm or the forge were
there, nor which command to use: the guide knew only the distribution,
ERPLibre and whether a desktop was installed. The list comes from the menu
FILTERED by the machine — Android Studio is x86_64 only, PyCharm wants a
desktop — and every line is checked against the code that installs it.
The NixOS block carries the checkout root, its only line neither absolute
nor a command, and three lines nobody guesses: « --rollback », which
undoes a faulty declaration; « nixos-version »; and « ls /bin », empty
while /bin/bash runs. A package search was tried then dropped, silent on
this image.
Assisted-by: Claude Opus 5
La vérification existait sous un drapeau, et pour Ubuntu SEULEMENT : les
autres images arrivaient sans que rien ne les regarde, alors que leurs
éditeurs publient tous une somme. Six y entrent, relevées sur les dépôts
plutôt que devinées — Debian est en sha512, les familles RHEL nomment
« CHECKSUM », Rocky l'écrit en forme BSD, Arch et openSUSE posent une
somme par image. Fedora en reste dehors, son fichier portant un numéro de
construction que l'URL de l'image ne donne pas.
Injoignable n'arrête plus rien, un écart si. Le menu du cache sait aussi
oublier une URL, « --detient » faisant l'aperçu.
--- EN ---
Verification existed behind a flag, and for Ubuntu ONLY: the other images
arrived with nothing looking at them, though every publisher ships a sum.
Six now enter, read off the repositories rather than guessed — Debian is
sha512, the RHEL families name it "CHECKSUM", Rocky writes the BSD form,
Arch and openSUSE ship a sum per image. Fedora stays out, its file
carrying a build number the image URL does not give.
Unreachable no longer stops anything, a mismatch does. The cache menu can
also forget one URL, "--detient" serving as the preview.
Assisted-by: Claude Opus 5
Un invité sans magasin de confiance ne peut RIEN recevoir, et sur un hôte
Proxmox c'est l'HÔTE qu'il faut excepter : imbriqué, l'invité sort masqué
derrière lui et le pont ne voit jamais sa propre adresse. Mesuré depuis
l'invité : code 000 et vérification SSL 19, puis 200 et 0. Sans cela le
gestionnaire de paquets se rabattait sur 564 dérivations à construire.
Le journal par VM ne portait AUCUN message du menu : la cause vivait sur
une console qui défile, et le fichier qu'on rouvre après l'échec n'avait
que le symptôme. Et « db_drop_all » annonçait détruites des bases qui ne
l'étaient pas, son code de retour jeté.
--- EN ---
A guest with no trust store can receive NOTHING, and on a Proxmox host it
is the HOST that must be exempted: nested, the guest leaves masqueraded
behind it and the bridge never sees its own address. Measured from the
guest: code 000 and SSL verification 19, then 200 and 0. Without it the
package manager fell back to building 564 derivations.
The per-VM log carried NO message from the menu: the cause lived on a
console that scrolls away, and the file reopened after failure held only
the symptom. And "db_drop_all" announced databases as dropped that were
not, its exit status discarded.
Assisted-by: Claude Opus 5
Trois obstacles enchaînés, aucun visible sans une vraie machine. /etc est
généré depuis le store : le « tee » d'une unité y rend « Read-only file
system », et l'installation échouait à sa DERNIÈRE étape, après que le
clone, le venv et un démarrage d'Odoo avaient réussi. ExecStart=/bin/bash
rendait 203/EXEC, /bin étant un montage FUSE que systemd ne voit pas ; et
le PATH d'une unité n'a pas bash, que réclament les shebangs de run.sh.
Déclarée, l'unité est démarrée par la reconstruction — qui a lieu pendant
« make install_os », quand la source d'Odoo n'arrive qu'après : une
condition la fait sauter plutôt qu'échouer 21 fois de suite. Mesuré :
service actif, 8069 servi par lui, login en 303.
--- EN ---
Three chained obstacles, none visible without a real machine. /etc is
generated from the store: « tee » of a unit there returns « Read-only file
system », and the install failed at its LAST step, after the clone, the
venv and an Odoo start had all succeeded. ExecStart=/bin/bash returned
203/EXEC, /bin being a FUSE mount systemd does not see; and a unit's PATH
carries no bash, which run.sh's shebangs ask for.
Declared, the unit is started by the rebuild — which happens during
« make install_os », while Odoo's source lands later: a condition makes it
skip rather than fail 21 times over. Measured: service active, 8069 served
by it, login in 303.
Assisted-by: Claude Opus 5
Le détournement est TRANSPARENT et vaut pour tout le pont : ne pas donner
l'autorité à une VM ne la dispense pas d'être interceptée, elle échoue sur
« self-signed certificate in certificate chain ». NixOS n'a pas d'ancre de
confiance par fichier, et la poser par déclaration arriverait trop tard —
la première reconstruction EST le premier téléchargement. La VM est donc
exceptée par son adresse MAC, avant sa création, et cela se dit.
L'image Proxmox n'est mise en place qu'une fois complète : « wget -O »
écrivait dans la cible, et une coupure y figeait un fichier tronqué que
le test de présence acceptait à chaque déploiement suivant.
--- EN ---
Interception is TRANSPARENT and covers the whole bridge: withholding the
authority from a VM does not spare it, it fails on "self-signed
certificate in certificate chain". NixOS has no per-file trust anchor, and
declaring one would come too late — the first rebuild IS the first
download. The VM is therefore exempted by MAC, before creation, and it is
said.
The Proxmox image is put in place only once complete: "wget -O" wrote into
the target, and an interruption froze a truncated file there that the
presence test accepted on every later deployment.
Assisted-by: Claude Opus 5
Aucune lecture de code ne les aurait trouvés. Son image n'a pas de secteur
d'amorçage BIOS : une VM créée en SeaBIOS se déclare « running » avec une
console MUETTE, d'où un marqueur par distribution — Debian 13 démarre en
SeaBIOS sur le même hôte, et renverser le défaut coûterait un disque EFI
à chaque VM du parc.
« --ciuser » s'en remet au compte par DÉFAUT de l'image, que NixOS nomme
autrement : le cloud-config du dépôt part en extrait pour toutes les
distributions. Et le lecteur cloud-init passe sur le bus SCSI, une image
bâtie pour virtio seul ne voyant jamais un lecteur IDE.
--- EN ---
No code reading would have found them. Its image has no BIOS boot sector:
a VM created in SeaBIOS reports "running" with a SILENT console, hence a
per-distribution marker — Debian 13 boots in SeaBIOS on the same host, and
flipping the default would cost an EFI disk on every VM.
"--ciuser" defers to the image's DEFAULT account, which NixOS names
otherwise: the repository's cloud-config now travels as a snippet for
every distribution. And the cloud-init drive moves to the SCSI bus, an
image built for virtio alone never seeing an IDE drive.
Assisted-by: Claude Opus 5
Une machine, une question binaire : le chemin que le MENU emprunte
aboutit-il sur un système déclaratif. La commande d'installation est celle
du menu, prise telle quelle — un test qui installerait par ses propres
soins prouverait SON chemin, pas celui du produit, et c'est là que les
pannes se cachaient.
Le verdict est l'ÉTAT de la machine, non un code de retour :
« nixos-rebuild » rend 4 quand une unité n'a pas redémarré alors que le
système est activé. Il vit dans long_test/ parce qu'il crée une VM et
prend des heures ; le lanceur unitaire doit rester lançable en secondes.
--- EN ---
One machine, one binary question: does the path the MENU takes succeed on
a declarative system. The install command is the menu's, taken as is — a
test installing by its own means would prove ITS path, not the product's,
and that is where the failures hid.
The verdict is the machine's STATE, not an exit code: "nixos-rebuild"
returns 4 when a unit failed to restart while the system is active. It
lives in long_test/ because it creates a VM and takes hours; the unit
runner must stay runnable in seconds.
Assisted-by: Claude Opus 5
L'amorçage d'ERPLibre n'atteignait pas NixOS : le Makefile force
« SHELL := /bin/bash », que l'image ne porte pas, et le fournisseur de
Python ne cherche l'interpréteur du système qu'en /usr/bin/pythonX.Y.
Les deux répondent une fois envfs déclaré.
Ce qui n'a pas de roue amont se compile et réclame ses en-têtes : ils sont
liés au profil du système, leurs bibliothèques vont au chargeur, et
pg_config — dérivation à part dans nixpkgs — est nommé. La sortie « doc »
de CPython est écartée : sa construction est un Sphinx de 3 000 pages qui
domine le temps d'installation d'une VM de 4 Go.
--- EN ---
ERPLibre's bootstrap did not reach NixOS: the Makefile forces "SHELL :=
/bin/bash", which the image lacks, and the Python provider only looks for
the system interpreter at /usr/bin/pythonX.Y. Both answer once envfs is
declared.
What has no upstream wheel compiles and demands its headers: they are
linked into the system profile, their libraries reach the loader, and
pg_config — a separate derivation in nixpkgs — is named. CPython's "doc"
output is left out: building it is a 3000-page Sphinx run that dominates
install time on a 4 GB VM.
Assisted-by: Claude Opus 5
Deux réglages régionaux échouaient sur toute VM Debian, et le seul signe
en était le mot « error » dans un compte-rendu qui le porte à chaque fois.
« update-locale » refuse un locale qui n'est pas généré, et le module du
clavier finit par redémarrer console-setup, absent de l'image
genericcloud : le réglage est écrit avant cet échec, donc le poser
nous-mêmes ne perd que la console texte.
Et l'installateur nix n'écrit plus dans ~/.bashrc : un fichier que
l'utilisateur possède ne se modifie pas pour la durée d'un amorçage.
--- EN ---
Two regional settings failed on every Debian VM, and the only sign was the
word "error" in a report that carries it every time. "update-locale"
refuses a locale that is not generated, and the keyboard module ends by
restarting console-setup, absent from the genericcloud image: the setting
is written before that failure, so placing it ourselves loses only the
text console.
And the nix installer no longer writes into ~/.bashrc: a file the user
owns is not edited for the duration of a bootstrap.
Assisted-by: Claude Opus 5
Neuvième système du catalogue, et le seul dont aucune distribution ne
publie d'image cloud : celle d'un tiers est épinglée et sa somme vérifiée
à chaque téléchargement. Son seed diffère aussi — networkd prend la clé
d'un bloc pour un NOM d'interface là où netplan honore un « match: », et
sshd refuse un compte dont le shell n'existe pas, d'où /bin/sh.
Ses dépendances se DÉCLARENT dans conf/nixos/erplibre.nix : envfs répond
à /usr/bin/env, nix-ld donne aux roues manylinux leur chargeur. Vérifié
sur une VM : le verrou de 362 paquets s'installe et Odoo répond.
--- EN ---
Ninth system of the catalogue, and the only one no distribution publishes
a cloud image for: a third party's is pinned and its sum verified at every
download. Its seed differs too — networkd reads a block's key as an
interface NAME where netplan honours a "match:", and sshd refuses an
account whose shell does not exist, hence /bin/sh.
Its dependencies are DECLARED in conf/nixos/erplibre.nix: envfs answers
/usr/bin/env, nix-ld gives manylinux wheels their loader. Checked on a VM:
the 362-package lock installs and Odoo answers.
Assisted-by: Claude Opus 5
« cloud-init status --wait » rend la main dès que cloud-init se déclare en
erreur — un module accessoire y suffit — alors que son étape finale écrit
encore l'autorité du cache, les variables et le fichier sudoers. Une session
ouverte dans cette seconde-là vit sans ces variables, PAM ne relisant plus le
fichier, et sudo n'a alors rien à conserver : l'installation lancée par sudo
rejette le certificat du cache. L'attente guette désormais l'unité
cloud-final, et son seul état « activating » : ce oneshot reste ACTIF une fois
fini, si bien qu'« is-active » y ferait attendre la borne entière pour rien.
--- EN ---
« cloud-init status --wait » returns as soon as cloud-init declares itself in
error — an accessory module is enough — while its final stage still writes the
cache authority, the variables and the sudoers file. A session opened in that
second lives without those variables, PAM never rereading the file, and sudo
then has nothing to keep: an install run by sudo rejects the cache
certificate. The wait now watches the cloud-final unit, and only its
« activating » state: this oneshot stays ACTIVE once finished, so « is-active »
would wait the whole bound for nothing.
Assisted-by: Claude Opus 5
Le cache ne diminuait jamais de lui-même et vit sur le disque de
l'orchestrateur : un parc qui déploie chaque jour finissait par le remplir,
sans que rien ne le signale. Deux règles existent désormais, éteintes par
défaut et réglées depuis le menu : l'âge du dernier usage, et un plafond de
taille qui efface le moins récemment servi jusqu'à tenir dessous, objets et
dépôts git confondus. Un minuteur systemd quotidien les applique, et une
réinstallation garde les valeurs choisies. Le menu montre d'abord ce qui
partirait.
--- EN ---
The cache never shrank by itself and lives on the orchestrator's disk: a site
deploying every day filled it up with nothing to say so. Two rules now exist,
off by default and set from the menu: the age of last use, and a size ceiling
that erases the least recently served until the cache fits under it, objects
and git mirrors alike. A daily systemd timer applies them, and a reinstall
keeps the chosen values. The menu first shows what would go.
Assisted-by: Claude Opus 5
L'audit de npm interroge un service qu'aucun cache ne rejoue : amont coupé,
il échoue à chaque installation sans rien vérifier. Une VM déployée hors
ligne reçoit donc NPM_CONFIG_AUDIT=false, écrit dans /etc/environment et
relu après cloud-init ; une VM en ligne garde son audit. Le retrait d'une
exception du détournement parle désormais la langue du menu, son message
allant à l'erreur standard tandis que la sortie standard garde les règles
que nft lit, qui ne se traduisent jamais.
--- EN ---
The npm audit queries a service no cache can replay: upstream cut, it fails
every install without checking anything. A VM deployed offline therefore gets
NPM_CONFIG_AUDIT=false, written in /etc/environment and read back after
cloud-init; an online VM keeps its audit. Removing a redirection exception
now speaks the menu's language, its message going to standard error while
standard output keeps the rules nft reads, which are never translated.
Assisted-by: Claude Opus 5
Sur une installation complète, les index pip, les métadonnées npm et le
bundle de repo pesaient environ 110 Mo par VM, repris en entier à chaque
fois. Un index déjà gardé part désormais avec son ETag : l'amont juge chaque
requête, et un « 304 » sert le corps depuis le disque. Sous « Vary: Accept »,
chaque représentation est rangée à part, faute de quoi deux clients qui
alternent les retéléchargent tour à tour. Les métadonnées PEP 658 de pip,
nommées « .whl.metadata », sont figées comme l'archive qu'elles décrivent.
--- EN ---
On a full install the pip indexes, npm metadata and repo bundle weighed about
110 MB per VM, taken whole every time. An index already kept now leaves with
its ETag: upstream judges every request, and a « 304 » serves the body from
disk. Under « Vary: Accept » each representation is stored apart, without
which two clients alternating between them re-download each in turn. The
PEP 658 metadata of pip, named « .whl.metadata », are as immutable as the
archive they describe.
Assisted-by: Claude Opus 5
Three Transform data tests wrote an Excel workbook with openpyxl, which the
.venv.erplibre requirements never declared, so the unit suite reported three
errors on every install. openpyxl is now declared there. The tests skip when it
cannot be imported in their own interpreter, since a venv built before this
line still lacks it; the dedicated engine venv is not the right probe, as the
tests write their workbook in-process. Checked: 707 Transform tests pass, and
with openpyxl made unimportable the three are skipped, not errors.
--- FR ---
Trois tests de Transform data écrivaient un classeur Excel avec openpyxl, que
les dépendances de .venv.erplibre ne déclaraient pas : la suite unitaire
rendait trois erreurs sur toute installation. openpyxl y est désormais
déclaré. Les tests se sautent quand il ne s'importe pas dans leur interpréteur,
un venv bâti avant cette ligne ne l'ayant toujours pas ; le venv dédié du
moteur n'est pas la bonne sonde, les tests écrivant leur classeur sur place.
Vérifié : 707 tests de Transform passent, et openpyxl rendu inimportable, les
trois sont sautés et non en erreur.
Assisted-by: Claude Opus 5
Le binaire ne parlait que français alors que le menu suit EL_LANG : un
opérateur anglophone lisait un statut qu'il ne comprenait pas. Chaque message
humain passe par T(), dont la clé EST le texte français, et un catalogue
anglais le traduit ; la langue vient de --lang, puis d'EL_LANG, le français
restant le défaut. Le menu demande le français pour ce qu'il RELIT, ses
motifs ne connaissant que cette langue. Un test refuse un message sans
traduction, une traduction orpheline et un verbe de format qui diverge.
--- EN ---
The binary spoke French only while the menu follows EL_LANG: an English
reader got a status he could not read. Every human message goes through T(),
whose key IS the French text, and an English catalogue translates it; the
language comes from --lang, then EL_LANG, French remaining the default. The
menu asks for French for what it READS BACK, its patterns knowing that
language alone. A test refuses a message without translation, an orphan
translation, and a format verb that drifts.
Assisted-by: Claude Opus 5
A Proxmox VM always used the serial console as its display, so nothing inside
it could render. The form offers 3D: the VM gets « --vga virtio-gl » and the
account joins the render and video groups in the guest, which « qm set »
cannot write; without them GL falls back to software while VIRGL reports
success. The box is offered only where the host has a render node, VIRGL, GL
and EGL, Proxmox refusing to start the VM otherwise once its disk is written.
What is missing is named with its package, and a button installs it, then
reads the host again. Covered by test/test_proxmox_form.py.
--- FR ---
Une VM Proxmox prenait toujours la console série pour affichage : rien ne
pouvait y rendre. Le formulaire offre la 3D : la VM reçoit « --vga virtio-gl »
et le compte entre dans les groupes render et video dans l'invité, ce que
« qm set » ne sait pas écrire ; sans eux, GL retombe en logiciel alors que
VIRGL annonce un succès. La case n'est offerte que si l'hôte a un nœud de
rendu, VIRGL, GL et EGL, Proxmox refusant sinon de démarrer la VM une fois son
disque écrit. Ce qui manque est nommé avec son paquet, et un bouton le pose
puis relit l'hôte. Couvert par test/test_proxmox_form.py.
Assisted-by: Claude Opus 5
Without pipefail, « curl … | sh » returns the status of sh, 0 on empty input:
a failed download passed for an install, its fallback never ran, and the
failure surfaced later as « command not found ». mise and pyenv are downloaded
into a mktemp file, then run, and a check refuses any fallback behind a pipe
without pipefail. starship's installer runs as root under a root timeout, never
reaching the « sudo -v » that sudo-rs refuses, and its shell hook is guarded.
Each tool then reports the version found or « not installed », without
tripping « set -e ».
--- FR ---
Sans pipefail, « curl … | sh » rend le statut de sh, 0 sur une entrée vide :
un téléchargement raté passait pour une pose, son repli ne tournait jamais, et
l'échec se lisait plus loin sous « command not found ». mise et pyenv sont
téléchargés dans un fichier tiré par mktemp, puis exécutés, et un contrôle
refuse tout repli derrière un tube sans pipefail. L'installateur de starship
tourne en root sous un délai root, sans atteindre le « sudo -v » que sudo-rs
refuse, et son crochet de shell est gardé. Chaque outil dit ensuite la version
trouvée ou « non installé », sans faire tomber « set -e ».
Assisted-by: Claude Opus 5
Ten of the eighteen entries carried no pictogram, and the eye picks one out
of such a list before it reads. Each takes the icon its siblings already use
elsewhere in the tool — 📋 lists, 🧹 cleans, 📊 measures, 🔍 previews, 📥
downloads, 🗂 catalogues, 🖥 tunnels a desktop — so the same picture keeps
meaning the same thing from one menu to the next. A table in the tests holds
the pairing, in both languages, and checks the menu still shows the entries
it freezes.
--- FR ---
Dix des dix-huit entrées ne portaient aucun pictogramme, et l'œil s'y repère
avant de lire. Chacune reçoit l'icône que ses semblables portent déjà
ailleurs dans l'outil — 📋 liste, 🧹 nettoie, 📊 mesure, 🔍 prévisualise, 📥
télécharge, 🗂 catalogue, 🖥 ouvre un bureau —, si bien qu'une même image
garde le même sens d'un menu à l'autre. Un tableau dans les tests fige le
couple, dans les deux langues, et vérifie que le menu affiche encore les
entrées qu'il fige.
Assisted-by: Claude Opus 5
The long-test menu printed the command and ran it at once: a mistyped digit
created real VMs, and « --detruire » removes machines with their disks. The
command stays printed, since it is what makes the question answerable, and the
prompt follows it. Warning and question are chosen from the arguments, so a
destruction is never confirmed with the words of a launch. A dry run or a
report creates nothing and asks nothing. Covered by
test/test_longtest_confirm.py.
--- FR ---
Le menu des tests longs affichait la commande et l'exécutait aussitôt : un
chiffre tapé de travers créait de vraies VM, et « --detruire » efface des
machines avec leurs disques. La commande reste affichée, c'est elle qui rend
la question répondable, et l'invite la suit. L'avertissement et la question se
choisissent sur les arguments : une destruction n'est jamais confirmée avec
les mots d'un lancement. Un plan à blanc ou un rapport ne crée rien et ne
demande rien. Couvert par test/test_longtest_confirm.py.
Assisted-by: Claude Opus 5
VMs of one host pulled the same packages again and again, and an install
could not be proven to work without the network. A Go service intercepts the
bridge transparently: a package file is served from disk, an index is always
revalidated upstream and replayed only when upstream is mute, git is mirrored.
Deployment poses the cache authority in QEMU and Proxmox VE guests, and can cut
the network for a whole install. The TODO menu installs, diagnoses, fills,
cleans and copies the store; long_test/qemu_cache.py measures that a second VM
pulls no package upstream. Covered by Go tests and test/test_qemu_cache_*.py.
--- FR ---
Les VM d'un hôte tiraient sans cesse les mêmes paquets, et rien ne prouvait
qu'une installation marche sans réseau. Un service Go intercepte le pont de
façon transparente : un fichier de paquet est servi du disque, un index est
toujours revalidé à l'amont et rejoué seulement quand l'amont est muet, git
est mis en miroir. Le déploiement pose l'autorité du cache dans les invités
QEMU et Proxmox VE, et peut couper le réseau pour toute une installation. Le
menu TODO installe, diagnostique, remplit, nettoie et emporte le magasin ;
long_test/qemu_cache.py mesure qu'une seconde VM ne tire aucun paquet de
l'amont. Couvert par les tests Go et test/test_qemu_cache_*.py.
Assisted-by: Claude Opus 5
A desktop install repeated « Could not get lock » for minutes, then could run
on an index never refreshed. Disabling the apt-daily timer does not interrupt
the apt-get it already started, so its service is stopped too. The guest agent
is laid by a detached unit that runs its own apt-get update: « cloud-init
status --wait » says done while that lock is still held, so the wait covers
that unit. Retries come back every two seconds, DPkg::Lock::Timeout not
covering the list lock, and an update that never succeeds says so at once.
--- FR ---
Une installation de bureau répétait « Impossible d'obtenir le verrou » pendant
des minutes, puis pouvait tourner sur un index jamais rafraîchi. Désactiver le
minuteur apt-daily n'interrompt pas l'apt-get qu'il a lancé : son service est
donc arrêté aussi. L'agent invité est posé par une unité détachée qui fait son
propre apt-get update : « cloud-init status --wait » dit « done » pendant que
ce verrou est tenu, et l'attente couvre donc cette unité. La reprise repasse
toutes les deux secondes, DPkg::Lock::Timeout ne couvrant pas le verrou des
listes, et un update qui n'aboutit pas le dit sur-le-champ.
Assisted-by: Claude Opus 5
L'étiquette « - Default » ne paraissait jamais, ni au choix de version ni au
choix d'environnement : les deux lectures interrogeaient « Default » avec une
majuscule, quand conf/supported_version_erplibre.json écrit « default ».
dict.get rend None sur une clé absente, sans rien dire — la version par
défaut passait donc pour une version ordinaire, et rien ne le signalait.
Le garde-fou ne vérifie pas ce seul cas : il apparie les deux côtés, et toute
clé que le menu interroge doit exister dans le fichier. Vérifié : 4 tests,
dont deux tombent si la majuscule revient.
--- EN ---
The « - Default » label never appeared, neither on the version choice nor on
the environment one: both reads asked for « Default » with a capital, while
conf/supported_version_erplibre.json writes « default ». dict.get returns None
on a missing key without a word — the default version therefore looked like an
ordinary one, and nothing said otherwise.
The guard does not check that single case: it pairs both sides, and every key
the menu asks for must exist in the file. Checked: 4 tests, two of which fail
if the capital comes back.
Assisted-by: Claude Opus 5
Partager un fichier ou une base demandait d'anonymiser à la main,
colonne par colonne, ou de ne pas partager. L'entrée ouvre un fichier
externe — Excel, Access, CSV, XML, JSON — ou une base Odoo, dit ce
qu'elle porte, puis en tire une copie. L'original n'est jamais touché.
Deux règles gouvernent ce qui sort. Une colonne n'est laissée intacte
que sur son CONTENU mesuré, hors onze étiquettes structurelles : décider
sur l'étiquette recopiait les colonnes les plus identifiantes en les
annonçant protégées. Et la copie est relue octet par octet, un classeur
portant de la donnée en une douzaine d'endroits hors des cellules.
Vérifié : 5 748 tests — 13 rouges, déjà rouges sur master.
--- EN ---
Sharing a file or a database meant anonymising by hand, column by
column, or not sharing. The entry opens an external file — Excel,
Access, CSV, XML, JSON — or an Odoo database, says what it holds, then
draws a copy of it. The original is never touched.
Two rules govern what comes out. A column is left alone on its measured
CONTENT, bar eleven structural labels: deciding on the label copied the
most identifying columns verbatim while announcing them as protected.
And the copy is reread byte by byte, a workbook holding data in a dozen
places that are not cells.
Checked: 5,748 tests — 13 red, already red on master.
Assisted-by: Claude Opus 5
`click.prompt` ajoute son propre « : » à l'étiquette qu'on lui donne, et
quinze étiquettes le portaient déjà. Le menu le plus vu du logiciel demandait
donc « Commande :: », et sept invites de déploiement à distance affichaient
un deux-points suivi d'un autre.
Le suffixe est désormais explicite partout où l'étiquette ponctue elle-même,
et sa valeur se déduit de l'étiquette : rien quand elle porte déjà l'espace
qui suit, un espace quand le deux-points est nu. Un contrôle lit l'arbre et
résout les DEUX langues, le français mettant une espace avant le deux-points
là où l'anglais n'en met pas — chercher dans le texte du code ne verrait que
la clé anglaise.
--- EN ---
`click.prompt` adds its own ":" to the label it is given, and fifteen labels
already carried one. The most-seen menu of the software therefore asked
"Command:: ", and seven remote-deployment prompts showed a colon followed by
another.
The suffix is now explicit wherever the label punctuates itself, and its value
follows from the label: nothing when it already carries the trailing space, a
space when the colon is bare. A check reads the tree and resolves BOTH
languages, French putting a space before the colon where English does not —
searching the code text would only see the English key.
Assisted-by: Claude Opus 5
Trois clés étaient déclarées deux fois dans un littéral de trois mille
entrées. Rien n'avertit et rien ne lève : la seconde gagne, et le prix s'est
déjà payé en étiquette de menu, la traduction lue n'étant pas celle qu'on
venait d'écrire au premier endroit. Les trois portaient la même traduction des
deux côtés, donc aucun écran ne change ; c'est le piège qui part.
Le contrôle passe de « pas plus de trois » à « aucune », et il vit désormais
avec les tests d'internationalisation plutôt qu'avec ceux d'un menu. Il lit
l'ARBRE : une fois le dictionnaire construit, le doublon a déjà disparu et il
n'y a plus rien à demander.
--- EN ---
Three keys were declared twice in a literal of three thousand entries.
Nothing warns and nothing raises: the second wins, and the price was already
paid on a menu label, the translation read not being the one just written at
the first place. All three carried the same translation on both sides, so no
screen changes; what goes is the trap.
The check moves from "no more than three" to "none at all", and it now lives
with the internationalisation tests rather than with a menu's. It reads the
TREE: once the dictionary is built the duplicate is already gone, and there is
nothing left to ask.
Assisted-by: Claude Opus 5
Onze sous-menus n'avaient pas d'étiquette de fil d'Ariane. Leur segment
manquait à l'en-tête, et la télémétrie les traitait comme des COMMANDES : ils
apparaissaient en feuille, sous leur nom de méthode brut.
Le contrôle qui devait l'attraper cherchait dans la table de répartition du
menu Exécution, donc il ne voyait que les sous-menus atteints depuis là — un
menu ouvert depuis ailleurs, ou défini dans un mixin, n'était jamais examiné.
Il cherche maintenant par ce que le code APPELLE, dans tout le paquet, et
trois exemptions périmées ont disparu avec lui. Cinq écrans restent exemptés
et disent pourquoi : ce sont des actions qui posent une question.
--- EN ---
Eleven submenus had no breadcrumb label. Their segment was missing from the
header, and telemetry treated them as COMMANDS: they showed up as leaves,
under their raw method name.
The check meant to catch this looked in the Execute menu's dispatch table, so
it only saw submenus reached from there — a menu opened from elsewhere, or
defined in a mixin, was never examined. It now searches by what the code
CALLS, across the whole package, and three stale exemptions went with it. Five
screens stay exempt and say why: they are actions that ask a question.
Assisted-by: Claude Opus 5
Trois lecteurs de ~/.ssh/config tranchaient la même question autrement : l'un
comparait le mot-clé avec la casse, un autre exigeait un espace là où une
tabulation est légale, un troisième laissait passer le motif nié `!nom`. Un
alias déclaré « host » en minuscules était donc vu par l'un et invisible aux
deux autres, ce qui se lit comme une panne intermittente de la découverte.
La décision passe dans un module qui ne lit aucun fichier. Il rend None sur ce
qui n'est pas une déclaration et une liste — possiblement vide — sur ce qui en
est une : « Host * » ne nomme aucune machine mais reste une déclaration, sinon
ses directives se rattachent au bloc précédent. Vérifié : 19 tests, et le menu
reconnaît toujours le serveur du réseau par sa source SSH.
--- EN ---
Three readers of ~/.ssh/config settled the same question differently: one
compared the keyword case-sensitively, another required a space where a tab is
legal, a third let the negated pattern `!name` through. An alias declared with
a lowercase "host" was therefore seen by one and invisible to the other two,
which reads as intermittent discovery failure.
The decision moves into a module that reads no file. It returns None for what
is not a declaration and a list — possibly empty — for what is: "Host *" names
no machine yet remains a declaration, otherwise its directives attach to the
previous block. Checked: 19 tests, and the menu still recognises the server on
the network through its SSH source.
Assisted-by: Claude Opus 5
Un nom d'organisation servait d'exemple dans une docstring et dans quatre
tests — nom de base, chemin de compte, préfixe de copie. La règle du dépôt
l'interdit partout hors de `private/`, et ces fichiers suivent le dépôt en
amont : sur un fork rendu public, l'exemple le devient aussi.
Un test le fige pour toujours, et trois des cinq sites servaient justement à
démontrer qu'un chemin de compte ou un nom est refusé. Une valeur inventée
démontre aussi bien, et celle-ci ne paraît nulle part ailleurs. La docstring
perd son exemple : il n'illustrait que « long », et la phrase le dit
maintenant. Le détecteur du dépôt ne voit pas cette classe — il n'annonçait
rien ici.
--- EN ---
An organisation's name served as an example in one docstring and in four
tests — a database name, an account path, a copy prefix. The repository's rule
forbids it anywhere outside `private/`, and these files follow the repository
upstream: on a fork made public, the example becomes public too.
A test freezes it forever, and three of the five sites existed precisely to
demonstrate that an account path or a name is refused. An invented value
demonstrates just as well, and this one appears nowhere else. The docstring
loses its example: it only illustrated "long", which the sentence now says.
The repository's detector does not see this class — it reported nothing here.
Assisted-by: Claude Opus 5
La lecture passait par « sudo sh -c cat » sans jamais demander si le privilège
était nécessaire. Elle est appelée une fois par VM pour afficher une liste, et
toutes les trois secondes pendant dix minutes par l'attente d'une VM : l'invite
root tombait donc en boucle au milieu d'un écran, ce qui entraîne à taper un
mot de passe root dans ce qui le demande.
La lecture directe passe d'abord et suffit sur une installation standard, ces
fichiers d'état étant en 0644 là où le « .conf » voisin est en 0600. Le
privilège n'est tenté qu'ensuite, avec « -n », qui échoue au lieu de demander ;
un répertoire interdit rend un glob vide, d'où l'essai même sans chemin trouvé.
Les deux appelants se replient déjà sur une autre source. Vérifié : 12 tests.
--- EN ---
The read went through "sudo sh -c cat" without ever asking whether the
privilege was needed. It is called once per VM to display a list, and every
three seconds for ten minutes while waiting on a VM: the root prompt therefore
landed in a loop in the middle of a screen, which trains someone to type a root
password into whatever asks for it.
The direct read comes first and suffices on a standard install, those status
files being 0644 where the neighbouring ".conf" is 0600. The privilege is only
tried afterwards, with "-n", which fails instead of asking; a forbidden
directory returns an empty glob, hence the attempt even with no path found.
Both callers already fall back to another source. Checked: 12 tests.
Assisted-by: Claude Opus 5
Le filtre ne connaissait que trois noms de variable — mot de passe, secret,
jeton — donc `OPENAI_API_KEY=` partait en clair dans le terminal, dans les
journaux et dans toute sortie CI qui les capture. Un jeton d'en-tête échappait
aux deux règles par construction : il ne porte ni nom d'option ni nom de
variable, il suit le mot « Bearer ».
Le filtre couvre maintenant `API_KEY` côté variables et `Authorization:
Bearer|Basic` côté en-têtes, sans casse, la valeur allant jusqu'au premier
blanc. Il protège au même titre la restauration de base, qui l'appelle sur
six sorties. Reste le dernier rempart et non le premier : argv est lisible par
tout compte de la machine, où aucun caviardage n'atteint.
--- EN ---
The filter knew only three variable names — password, secret, token — so
`OPENAI_API_KEY=` went out in the clear to the terminal, to the logs and to
any CI output capturing them. A header token escaped both rules by
construction: it carries neither an option name nor a variable name, it
follows the word "Bearer".
The filter now covers `API_KEY` on the variable side and `Authorization:
Bearer|Basic` on the header side, case-insensitively, the value running to the
first blank. It protects database restore just as much, which calls it on six
outputs. It stays the last line of defence, not the first: argv is readable by
every account on the machine, where no redaction reaches.
Assisted-by: Claude Opus 5
L'entrée « Question IA » envoyait toute question à une seule API distante, sur
un modèle figé et sans historique : aucun serveur local n'était atteignable,
et rien ne disait où partait le texte.
Deux règles gouvernent ce qui la remplace. Un port ne dit jamais qui répond —
un seul en héberge jusqu'à trois, et une famille réémet l'API d'une autre en
entier — donc l'identité se lit dans le CORPS, par une échelle ordonnée. Et
une adresse ne devient jamais du texte d'invite : le détecteur du dépôt ne
voit pas les noms d'hôte, d'où une poignée opaque à sa place.
Vérifié : 284 tests, et contre un serveur du réseau — reconnaissance,
capacités lues, conversation multi-tours, triage juste.
--- EN ---
The "AI question" entry sent every question to a single remote API, on a
frozen model and with no history: no local server was reachable, and nothing
said where the text was going.
Two rules govern what replaces it. A port never says who answers — one hosts
up to three products, and one family re-serves another's native API in full —
so identity is read from the BODY, by an ordered ladder. And an address never
becomes prompt text: the repository's detector does not see host names, hence
an opaque handle in its place.
Checked: 284 tests, and against a server on the network — recognition,
capabilities read, a multi-turn conversation, a correct triage.
Assisted-by: Claude Opus 5
Le formulaire demandait « tout le trafic ? » à un pilote dont le SERVEUR
décide du routage, ne faisait rien de la réponse, et `status` la jugeait
quand même : un ✗ permanent sur un tunnel sain, et un profil annonçant
« tout le trafic » sans l'obtenir.
Un drapeau, sur le modèle de celui du MTU, dit quels pilotes posent cette
route. Les autres ne sont ni interrogés ni jugés, et un drapeau laissé à
vrai n'est plus conservé. L'honorer serait pire qu'inutile : forcer une
route par défaut contre une passerelle en tunnel scindé donne un trou
noir, une passerelle ne routant pas ce qu'elle n'a pas annoncé. Les routes
déclarées, elles, restent honorées — le formulaire le dit.
--- EN ---
The form asked "all traffic?" of a driver whose SERVER decides the
routing, did nothing with the answer, and `status` judged it anyway: a
permanent ✗ on a healthy tunnel, and a profile announcing "all traffic"
without getting it.
A flag, modelled on the MTU one, says which drivers lay that route. The
others are neither asked nor judged, and a flag left true is no longer
kept. Honouring it would be worse than useless: forcing a default route
against a split-tunnel gateway gives a black hole, a gateway not routing
what it never advertised. Declared routes are still honoured — the form
says so.
Assisted-by: Claude Opus 5
Le verdict se fondait sur le seul fichier d'état, que RIEN n'efface quand
un tunnel meurt sans passer par « down » — machine redémarrée, processus
tué, session expirée. Un état laissé derrière était annoncé monté sur
l'écran même qui déclarait le processus mort et l'interface absente.
L'interface arbitre désormais : son nom vient de l'état s'il y en a un,
sinon du pilote pour ceux qui la nomment d'avance, si bien qu'un tunnel
monté hors de l'outil est vu aussi. Sshuttle n'en crée aucune, là le
processus reste seul juge. La liste des profils le marque, connecter un
profil déjà monté demande confirmation, et déconnecter un profil déjà
tombé le dit sans l'empêcher : « down » nettoie l'état laissé.
--- EN ---
The verdict rested on the state file alone, which NOTHING clears when a
tunnel dies without going through "down" — machine rebooted, process
killed, session expired. A leftover state was reported as mounted on the
very screen declaring the process gone and the interface absent.
The interface now arbitrates: its name comes from the state when there is
one, otherwise from drivers that name it in advance, so a tunnel brought
up outside the tool is seen too. Sshuttle creates none, and there the
process stays the only judge. The profile list marks it, connecting an
already-mounted profile asks first, and disconnecting an already-down one
says so without refusing: "down" clears the leftover state.
Assisted-by: Claude Opus 5
Personne ne retient « /opt/cisco/secureclient/vpn/profile ». Le chemin
tapé oblige à le connaître ; le parcours seul n'aide pas quand le fichier
vient d'ailleurs, d'un courriel ou du portail d'un site. Les deux, dans
cet ordre, et la saisie reprend la main dès qu'on sort du parcours sans
rien choisir ou qu'on en rapporte un répertoire.
Le parcours n'est proposé que si l'un des répertoires connus existe :
l'ouvrir sur un chemin absent afficherait une liste vide, ce qui ressemble
à une panne. Vérifié : 7 tests unitaires, dont chacun des replis.
--- EN ---
Nobody remembers "/opt/cisco/secureclient/vpn/profile". A typed path
requires knowing it; browsing alone does not help when the file comes
from elsewhere, from an e-mail or a site's portal. Both, in that order,
and typing takes over as soon as one leaves the browser without choosing
anything, or brings back a directory from it.
Browsing is offered only if one of the known directories exists: opening
it on an absent path would show an empty list, which looks like a fault.
Checked: 7 unit tests, one for each fallback.
Assisted-by: Claude Opus 5
openconnect refuse les passerelles qui exigent un navigateur intégré, sur
« No SSO handler » : les distributions le bâtissent sans webview. Rien
n'installait le greffon qui fait cette étape, que le pilote attendait
pourtant — une machine paraissait équipée sans l'être.
La question n'est posée que pour le pilote qui peut s'en servir, et
seulement quand le greffon manque. Son amont est arrêté depuis 2023 :
épingles intenables sur un Python récent, Qt et lxml pris de la
distribution, correctif rejoué à chaque installation. Le venv appartient
à l'utilisateur, non à root, qui n'a ni affichage ni trousseau. Vérifié :
installation depuis rien en 6,5 s, paquets éprouvés sur debian et ubuntu.
--- EN ---
openconnect refuses gateways demanding an embedded browser, on "No SSO
handler": distributions build it without a webview. Nothing installed the
helper that performs that step, which the driver expected all the same —
a machine looked equipped without being so.
The question is asked only for the driver that can use it, and only when
the helper is missing. Its upstream has been unmaintained since 2023:
pins unsatisfiable on a recent Python, Qt and lxml taken from the
distribution, a patch replayed on every install. The venv belongs to the
user, not root, which has neither display nor keyring. Checked: install
from nothing in 6.5 s, package names proven on debian and ubuntu only.
Assisted-by: Claude Opus 5
Deux mécanismes désignent un service sur un concentrateur : un chemin
d'URL, une valeur de menu déroulant. Les confondre rend le formulaire d'un
AUTRE service — identifiants justes refusés, rien ne désignant le groupe.
Et certains ne comparent que les N premiers caractères du mot de passe,
que le profil déclare désormais sans jamais rien tronquer.
Une passerelle qui exige un navigateur intégré arrête openconnect sur
« No SSO handler », les distributions le bâtissant sans webview. Un greffon
fait l'étape web et rend un cookie ; le pilote monte lui-même, et le profil
garde son interface, son état et son diagnostic.
Vérifié : 249 tests unitaires, un tunnel monté contre une passerelle SAML.
--- EN ---
Two mechanisms designate a service on one concentrator: a URL path, and a
dropdown value. Confusing them hands over ANOTHER service's login form —
correct credentials refused, with nothing pointing at the group. And some
compare only the first N characters of the password, which the profile now
declares without ever truncating anything.
A gateway demanding an embedded browser stops openconnect on "No SSO
handler", distributions building it without a webview. A helper does the
web step and returns a cookie; the driver mounts the tunnel itself, so the
profile keeps its interface, its state and its diagnosis.
Checked: 249 unit tests, and one tunnel mounted against a SAML gateway.
Assisted-by: Claude Opus 5
Les 39 puces du non-publié décrivent du travail fini qu'aucun numéro ne
permettait de citer. La section 1.8.0 les porte, avec la note de migration que
réclament le choix de l'interpréteur Python et le retrait d'Ubuntu 20.04 et
22.04. La version déclarée suit, comme le veut doc/RELEASE.base.md : le
fichier semver que lit env_var.sh, les huit pyproject, npm, CLAUDE.md et la
note des versions supportées. La ligne de contracts/compatibility.json reste
en 1.7.0, elle atteste un appariement mobile éprouvé et non la version
courante. Le commentaire de todo.py ne nomme plus de version, celle du dépôt
étant marquée « default » dans le fichier des versions supportées.
Vérifié : ERPLIBRE_VERSION=1.8.0, parité 17/8/10/2/2, tests de version verts.
--- EN ---
The unreleased list's 39 entries describe finished work no number could cite.
The 1.8.0 section carries them, with the migration note that choosing the
Python interpreter and dropping Ubuntu 20.04 and 22.04 call for. The declared
version follows, as doc/RELEASE.base.md wants: the semver file env_var.sh
reads, the eight pyproject files, npm, CLAUDE.md and the supported-version
note. The contracts/compatibility.json row stays at 1.7.0, attesting a tested
mobile pairing and not the current version. The todo.py comment names no
version any more, the repository's own being marked « default » in the
supported-version file. Checked: ERPLIBRE_VERSION=1.8.0, 17/8/10/2/2 parity,
version tests green.
Assisted-by: Claude Opus 5
Le tag v1.7.0 existe depuis mars et chaque déclaration disait encore 1.6.0.
« .erplibre-semver-version » est la source que env_var.sh lit dans
ERPLIBRE_VERSION. « pyproject.toml » est un lien vers le fichier de la version
d'Odoo active : les huit portent la version d'ERPLibre, en laisser sept à
1.6.0 ferait dépendre la version déclarée de la bascule d'Odoo. Les étiquettes
d'images docker restent à 1.6.0 : elles nomment des images publiées, empreinte
de compilation comprise, que la construction n'a pas encore produites.
Vérifié : env_var.sh rend 1.7.0, les quatre JSON se relisent, et les tests de
version et de configuration passent.
--- EN ---
Tag v1.7.0 has existed since March and every declaration still said 1.6.0.
`.erplibre-semver-version` is the source env_var.sh reads into
ERPLIBRE_VERSION. `pyproject.toml` is a link to the file of the active Odoo
version: all eight carry the ERPLibre version, and leaving seven at 1.6.0
would make the declared version depend on an Odoo switch. The docker image
tags stay at 1.6.0: they name published images, build hash included, that the
build step has not produced yet. Checked: env_var.sh renders 1.7.0, the four
JSON files reload, and the version and configuration tests pass.
Assisted-by: Claude Opus 5
Une VM prend son adresse dans le /24 de son réseau et en sort par le .1,
porté par le pont : déplacer ce /24 sous une VM allumée lui laisse un bail
qui ne mène nulle part, et abattre le réseau détache son tap, que libvirt
n'y remet pas. network_qemu.py tient les trois gestes dans l'ordre — arrêter
les VM attachées, redéfinir le réseau sur le préfixe voulu, rallumer celles
qu'il a arrêtées, elles seules. Une VM sourde au shutdown annule la
redéfinition plutôt que d'y perdre son pont ; --force-off coupe le courant.
Un préfixe qui recouvre ce que l'hôte route déjà est refusé.
Vérifié : 13 tests neufs, la numérotation du menu, une passe à blanc.
--- EN ---
A VM takes its address in its network's /24 and leaves through the .1,
carried by the bridge: moving that /24 under a live VM leaves it a lease
leading nowhere, and tearing the network down detaches its tap, which
libvirt does not put back. network_qemu.py holds the three steps in order —
shut down the attached VMs, redefine the network on the wanted prefix, start
again the ones it stopped, those only. A VM deaf to the shutdown cancels the
redefinition rather than losing its bridge; --force-off cuts the power.
A prefix overlapping what the host already routes is refused.
Checked: 13 new tests, the menu numbering, one dry run.
Assisted-by: Claude Opus 5
sudo ne dit jamais ce qu'il sert à faire : son invite tombe entre deux lignes
de journal, et l'on tape un mot de passe sans savoir s'il porte sur libvirt,
sur un paquet ou sur un fichier — d'autant qu'appartenir au groupe libvirt a
l'air de suffire. Il ne suffit pas, et libvirt n'y est pour rien : le disque
et le seed s'écrivent dans le pool par défaut, répertoire de root où le
groupe ne donne pas l'écriture. Le motif est constaté, non déduit :
l'écriture s'essaie, une ACL pouvant l'accorder là où le mode semble la
refuser. Dit une fois avant la première commande privilégiée, et en dernière
ligne du récapitulatif. Vérifié : 18 tests, root compris, à qui rien n'est dit.
--- EN ---
sudo never states what it is about to do: its prompt lands between two log
lines, and one types a password without knowing whether it covers libvirt, a
package or a file — the more so as being in the libvirt group looks like it
should be enough. It is not, and libvirt is not the reason: the disk and the
seed are written into the default pool, a root-owned directory where the
group grants no write right. The reason is checked, not deduced: writing is
tried, since an ACL can grant it where the mode seems to refuse it. Said once
before the first privileged command, and last on the review page. Checked: 18
tests, root included, which is told nothing.
Assisted-by: Claude Opus 5