Description: Autogenerated patch header for a single-debian-patch file.
 The delta against upstream is either kept as a single patch, or maintained
 in some VCS, and exported as a single patch instead of more manageable
 atomic patches.
Forwarded: not-needed

---
diff -ruN '--exclude=debian' '--exclude=.git' '--exclude=.pc' '--exclude=.codex' '--exclude=target' Pixie-1.1.0/.github/workflows/docker-image.yml new/.github/workflows/docker-image.yml
--- Pixie-1.1.0/.github/workflows/docker-image.yml	2026-04-10 23:11:11.000000000 +0200
+++ new/.github/workflows/docker-image.yml	2026-04-14 16:09:02.960653948 +0200
@@ -1,12 +1,6 @@
 name: docker-image
 
 on:
-  push:
-    branches:
-      - main
-    tags:
-      - "v*.*.*"
-  pull_request:
   workflow_dispatch:
 
 permissions:
diff -ruN '--exclude=debian' '--exclude=.git' '--exclude=.pc' '--exclude=.codex' '--exclude=target' Pixie-1.1.0/.github/workflows/rust.yml new/.github/workflows/rust.yml
--- Pixie-1.1.0/.github/workflows/rust.yml	2026-04-10 23:11:11.000000000 +0200
+++ new/.github/workflows/rust.yml	2026-04-21 08:24:54.000000000 +0200
@@ -113,11 +113,49 @@
         working-directory: pixie
         run: cargo test --all-features --locked --verbose
 
+  coverage:
+    name: Coverage (min 70%)
+    runs-on: ubuntu-latest
+    needs:
+      - test
+
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+
+      - name: Install Rust toolchain
+        run: |
+          rustup toolchain install stable --profile minimal
+          rustup default stable
+          rustup component add llvm-tools-preview
+
+      - name: Install cargo-llvm-cov
+        run: cargo install cargo-llvm-cov --locked
+
+      - name: Cache cargo
+        uses: actions/cache@v4
+        with:
+          path: |
+            ~/.cargo/registry
+            ~/.cargo/git
+            pixie/target
+          key: ${{ runner.os }}-cargo-coverage-${{ hashFiles('**/Cargo.lock') }}
+          restore-keys: |
+            ${{ runner.os }}-cargo-coverage-
+            ${{ runner.os }}-cargo-
+
+      - name: Check coverage threshold
+        working-directory: pixie
+        run: >
+          cargo llvm-cov --all-features --locked --lib --bins
+          --test config_tests --test logger_tests --test router_tests --test threadpool_tests
+          --summary-only --fail-under-lines 70
+
   build:
     name: Build release
     runs-on: ubuntu-latest
     needs:
-      - test
+      - coverage
 
     steps:
       - name: Checkout
diff -ruN '--exclude=debian' '--exclude=.git' '--exclude=.pc' '--exclude=.codex' '--exclude=target' Pixie-1.1.0/.github/workflows/wiki-docs.yml new/.github/workflows/wiki-docs.yml
--- Pixie-1.1.0/.github/workflows/wiki-docs.yml	1970-01-01 01:00:00.000000000 +0100
+++ new/.github/workflows/wiki-docs.yml	2026-04-14 16:08:54.996388485 +0200
@@ -0,0 +1,68 @@
+name: wiki-docs
+
+on:
+  workflow_dispatch:
+
+permissions:
+  contents: write
+
+jobs:
+  sync-wiki:
+    name: Sync wikidoc to GitHub Wiki
+    runs-on: ubuntu-latest
+
+    steps:
+      - name: Checkout repository
+        uses: actions/checkout@v4
+
+      - name: Publish wikidoc to Wiki
+        env:
+          GH_TOKEN: ${{ secrets.WIKI_TOKEN }}
+          REPO: ${{ github.repository }}
+          SHA: ${{ github.sha }}
+        run: |
+          set -euo pipefail
+
+          if [ -z "${GH_TOKEN}" ]; then
+            echo "Secret WIKI_TOKEN manquant."
+            echo "Configure un PAT (classic) avec scope 'repo' dans Settings > Secrets and variables > Actions."
+            exit 1
+          fi
+
+          API_URL="https://api.github.com/repos/${REPO}"
+          HAS_WIKI="$(curl -fsSL \
+            -H "Authorization: Bearer ${GH_TOKEN}" \
+            -H "Accept: application/vnd.github+json" \
+            "$API_URL" | jq -r '.has_wiki')"
+
+          if [ "$HAS_WIKI" != "true" ]; then
+            echo "Le Wiki GitHub est desactive pour ${REPO}. Active-le dans Settings > General > Features > Wikis."
+            exit 1
+          fi
+
+          WIKI_URL="https://x-access-token:${GH_TOKEN}@github.com/${REPO}.wiki.git"
+          WIKI_WEB_URL="https://github.com/${REPO}/wiki"
+
+          if ! git ls-remote "$WIKI_URL" >/dev/null 2>&1; then
+            echo "Depot wiki inaccessible: ${REPO}.wiki.git"
+            echo "Actions requises:"
+            echo "1) Ouvre ${WIKI_WEB_URL} et cree une premiere page (initialise le repo wiki)."
+            echo "2) Verifie que WIKI_TOKEN est un PAT classic avec scope 'repo'."
+            exit 1
+          fi
+
+          git clone "$WIKI_URL" wiki
+          rsync -av --delete --exclude '.git/' wikidoc/ wiki/
+
+          cd wiki
+          git config user.name "github-actions[bot]"
+          git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
+
+          if [ -n "$(git status --porcelain)" ]; then
+            git add -A
+            git commit -m "docs: sync wikidoc (${SHA})"
+            git push origin HEAD:master
+            echo "Wiki updated"
+          else
+            echo "No wiki changes to publish"
+          fi
diff -ruN '--exclude=debian' '--exclude=.git' '--exclude=.pc' '--exclude=.codex' '--exclude=target' Pixie-1.1.0/.gitignore new/.gitignore
--- Pixie-1.1.0/.gitignore	2026-04-10 23:11:11.000000000 +0200
+++ new/.gitignore	2026-04-21 08:31:06.286364963 +0200
@@ -21,22 +21,35 @@
 #.idea/
 
 *.codex
+.env
+.env.*
+!.env.example
 
 # Debian packaging artifacts (dpkg-buildpackage / debhelper)
-pixie_*.deb
-pixie_*.udeb
-pixie_*.buildinfo
-pixie_*.changes
-pixie_*.dsc
-pixie_*.tar.*
+*.deb
+*.udeb
+*.ddeb
+*.build
+*.buildinfo
+*.changes
+*.dsc
+*.orig.tar.*
+*.debian.tar.*
+*_source.buildinfo
+*_source.changes
+*.mentors.upload
+*.profraw
+*.profdata
 
 debian/.debhelper/
 debian/debhelper-build-stamp
 debian/files
 debian/tmp/
 debian/pixie/
+debian/*.debhelper
 debian/*.debhelper.log
 debian/*.substvars
+.pc/
 
 # Generated local APT repository
 apt-repo/
diff -ruN '--exclude=debian' '--exclude=.git' '--exclude=.pc' '--exclude=.codex' '--exclude=target' Pixie-1.1.0/.pre-commit-config.yaml new/.pre-commit-config.yaml
--- Pixie-1.1.0/.pre-commit-config.yaml	1970-01-01 01:00:00.000000000 +0100
+++ new/.pre-commit-config.yaml	2026-04-21 10:10:15.799603072 +0200
@@ -0,0 +1,19 @@
+repos:
+  - repo: local
+    hooks:
+      - id: rustfmt
+        name: rustfmt (pixie)
+        language: system
+        entry: bash -c 'cd pixie && cargo fmt --all -- --check'
+        pass_filenames: false
+        files: ^pixie/(src/|tests/|Cargo\.toml|Cargo\.lock)
+        stages: [pre-commit]
+
+      - id: clippy
+        name: clippy (pixie)
+        language: system
+        entry: bash -c 'cd pixie && cargo clippy --locked --all-targets --all-features -- -D warnings'
+        pass_filenames: false
+        files: ^pixie/(src/|tests/|Cargo\.toml|Cargo\.lock)
+        stages: [pre-commit]
+
diff -ruN '--exclude=debian' '--exclude=.git' '--exclude=.pc' '--exclude=.codex' '--exclude=target' Pixie-1.1.0/CONTRIBUTING.md new/CONTRIBUTING.md
--- Pixie-1.1.0/CONTRIBUTING.md	2026-04-10 23:11:11.000000000 +0200
+++ new/CONTRIBUTING.md	2026-04-21 08:24:54.000000000 +0200
@@ -22,7 +22,6 @@
 - `pixie/`: code Rust (librairie + binaire)
 - `web/`: pages HTML servies par le serveur
 - `debian/`: packaging Debian
-- `scripts/apt/`: scripts pour publier/configurer le depot APT
 
 ## Nommage des branches
 
@@ -65,18 +64,26 @@
 cargo test --all-features --locked
 ```
 
-## Si vous modifiez le packaging Debian
+## Pre-commit
 
-Depuis la racine:
+A la racine du repo:
 
 ```bash
-dpkg-buildpackage -us -uc -b
+pre-commit install
+pre-commit run --all-files
 ```
 
-Optionnel, pour tester le repo APT:
+Le hook `pre-commit` execute automatiquement:
+
+- `cargo fmt --all -- --check` (dans `pixie/`)
+- `cargo clippy --locked --all-targets --all-features -- -D warnings` (dans `pixie/`)
+
+## Si vous modifiez le packaging Debian
+
+Depuis la racine:
 
 ```bash
-GPG_KEY_ID=<KEY_ID> DIST=bookworm COMPONENT=main ./scripts/apt/publish-repo.sh
+dpkg-buildpackage -us -uc -b
 ```
 
 ## Pull Request checklist
@@ -85,10 +92,22 @@
 
 - [ ] La branche respecte le format de nommage.
 - [ ] `cargo fmt`, `cargo clippy` et `cargo test` passent en local.
+- [ ] Le seuil de couverture CI reste >= 70%.
 - [ ] Les changements sont documentes si necessaire (`README.md`).
 - [ ] Aucun secret/cle privee/fichier sensible n est commit.
 - [ ] La CI GitHub est verte.
 
+## Hygiene securite (repo public)
+
+- Ne pas commiter de secrets (`token`, `password`, cle privee, `.env`).
+- Ne pas commiter d artefacts de packaging (`*.deb`, `*.dsc`, `*.changes`, `*.buildinfo`, `*.orig.tar.*`).
+- Verifier rapidement avant push:
+
+```bash
+git status --short
+git diff --cached
+```
+
 ## Style de commit recommande
 
 - Messages clairs et courts, a l imperatif.
diff -ruN '--exclude=debian' '--exclude=.git' '--exclude=.pc' '--exclude=.codex' '--exclude=target' Pixie-1.1.0/Dockerfile new/Dockerfile
--- Pixie-1.1.0/Dockerfile	2026-04-10 23:11:11.000000000 +0200
+++ new/Dockerfile	2026-04-21 08:24:54.000000000 +0200
@@ -27,4 +27,3 @@
 USER pixie
 
 ENTRYPOINT ["/usr/local/bin/pixie"]
-CMD ["serve"]
diff -ruN '--exclude=debian' '--exclude=.git' '--exclude=.pc' '--exclude=.codex' '--exclude=target' Pixie-1.1.0/README.md new/README.md
--- Pixie-1.1.0/README.md	2026-04-10 23:11:11.000000000 +0200
+++ new/README.md	2026-04-14 15:37:35.678475714 +0200
@@ -1,113 +1,41 @@
 # Pixie
 
-Serveur HTTP en Rust.
+Pixie est un serveur HTTP statique ecrit en Rust.
 
-## Installation rapide (machine cliente)
+Version actuelle: `1.1.0`
 
-1. Configurer le depot APT Pixie:
+## Installation
+
+### Docker
 
 ```bash
-./scripts/apt/configure-client.sh https://repo.example.org/pixie bookworm main
+docker pull ghcr.io/laflut3/pixie:latest
+docker run --rm -p 8080:8080 ghcr.io/laflut3/pixie:latest
 ```
 
-`repo.example.org` est un exemple. Remplace-le par ton vrai domaine.
+### Debian / Ubuntu
+
+Publication en cours dans les depots officiels Debian.
 
-2. Installer Pixie:
+Quand le paquet sera disponible dans la distribution cible:
 
 ```bash
 sudo apt update
 sudo apt install -y pixie
 ```
 
-Le service `pixie` est demarre automatiquement et active au boot.
+Suivi publication Debian: `ITP #1133770`, `RFS #1133771`.
 
-## Mettre a jour Pixie
-
-```bash
-sudo apt update
-sudo apt install --only-upgrade -y pixie
-```
-
-## Installation Arch Linux
+### Arch Linux
 
 ```bash
 yay -S pixie-git
 ```
 
-Activer le service:
-
-```bash
-sudo systemctl daemon-reload
-sudo systemctl enable --now pixie.service
-```
-
-Guide detaille:
-[doc/arch_linux.md](/home/ltorres/perso/Pixie/doc/arch_linux.md)
-
-## Verifier le service et les logs
-
-```bash
-sudo systemctl status pixie
-sudo journalctl -u pixie.service -n 100 --no-pager
-sudo journalctl -u pixie.service -f
-```
-
-## Configuration Pixie (`config-pixie.yml`)
-
-Chemin recommande en installation systeme (meme logique que nginx):
-
-`/etc/pixie/config-pixie.yml`
-
-Pour le developpement local, `./config-pixie.yml` est aussi supporte.
-
-Exemple:
-
-```yml
-addr: 0.0.0.0:8080
-workers: 4
-```
-
-Champs supportes:
-- `addr` (prioritaire sur `host` + `port`, ex: `0.0.0.0:8080`)
-- `host` (ex: `127.0.0.1`)
-- `port` (ex: `8080`)
-- `workers` (alias accepte: `nb_worker`)
-
-Ordre de resolution:
-1. `PIXIE_CONFIG` (chemin de fichier explicite)
-2. `/etc/pixie/config-pixie.yml`
-3. `./config-pixie.yml`
-4. Valeurs hardcodees dans le code (`127.0.0.1:80`, `4` workers)
-
-Guide complet (Docker, Kubernetes, APT):
-[doc/configuration.md](/home/ltorres/perso/Pixie/doc/configuration.md)
-
-Publication multi-canaux (Docker + Debian + Arch/AUR):
-[doc/release_all_channels.md](/home/ltorres/perso/Pixie/doc/release_all_channels.md)
-
-## Deploiement Docker
-
-Construire l'image localement:
-
-```bash
-docker build -t pixie:local .
-docker run --rm -p 8080:8080 pixie:local
-```
-
-Puis ouvrir:
-
-`http://localhost:8080`
-
-Image publiee (GitHub Container Registry):
+Le service est gere automatiquement par le systeme d'installation.
 
-`ghcr.io/<owner>/<repo>:latest`
-
-Utiliser une image publiee:
-
-```bash
-docker pull ghcr.io/<owner>/<repo>:latest
-docker run --rm -p 8080:8080 ghcr.io/<owner>/<repo>:latest
-```
+## Securite du depot public
 
-Variable utile:
-- `PIXIE_CONFIG` (chemin explicite vers un fichier YAML de configuration)
+- Ne jamais commiter de cle privee, token, mot de passe ou fichier `.env`.
+- Les artefacts de build/packaging (`*.deb`, `*.dsc`, `*.changes`, `*.buildinfo`, `*.orig.tar.*`) sont ignores par `.gitignore`.
+- Les secrets CI/CD doivent rester dans GitHub Actions Secrets, jamais dans le code.
diff -ruN '--exclude=debian' '--exclude=.git' '--exclude=.pc' '--exclude=.codex' '--exclude=target' Pixie-1.1.0/arch/.SRCINFO new/arch/.SRCINFO
--- Pixie-1.1.0/arch/.SRCINFO	1970-01-01 01:00:00.000000000 +0100
+++ new/arch/.SRCINFO	2026-04-10 23:17:31.234574958 +0200
@@ -0,0 +1,17 @@
+pkgbase = pixie-git
+	pkgdesc = Pixie HTTP server written in Rust
+	pkgver = 1.1.0.r0.g0000000
+	pkgrel = 1
+	url = https://github.com/laflut3/Pixie
+	arch = x86_64
+	arch = aarch64
+	license = MIT
+	makedepends = cargo
+	makedepends = git
+	depends = glibc
+	depends = systemd
+	backup = etc/pixie/config-pixie.yml
+	source = pixie::git+https://github.com/laflut3/Pixie.git
+	sha256sums = SKIP
+
+pkgname = pixie-git
diff -ruN '--exclude=debian' '--exclude=.git' '--exclude=.pc' '--exclude=.codex' '--exclude=target' Pixie-1.1.0/config-pixie.yml new/config-pixie.yml
--- Pixie-1.1.0/config-pixie.yml	2026-04-10 23:11:11.000000000 +0200
+++ new/config-pixie.yml	2026-04-14 13:36:43.577123890 +0200
@@ -1,5 +1,3 @@
-# Configuration principale de Pixie
-# Utilise ce fichier pour les paramètres importants du serveur.
-
+# Configuration par defaut de Pixie
 addr: 0.0.0.0:8080
 workers: 4
diff -ruN '--exclude=debian' '--exclude=.git' '--exclude=.pc' '--exclude=.codex' '--exclude=target' Pixie-1.1.0/doc/arch_linux.md new/doc/arch_linux.md
--- Pixie-1.1.0/doc/arch_linux.md	2026-04-10 23:11:11.000000000 +0200
+++ new/doc/arch_linux.md	1970-01-01 01:00:00.000000000 +0100
@@ -1,69 +0,0 @@
-# Installer Pixie Sur Arch Linux
-
-Ce guide fournit un paquet Arch Linux (`pixie-git`) pour installer Pixie via `pacman`.
-
-## Installation depuis AUR (recommande pour les utilisateurs)
-
-Avec un helper AUR:
-
-```bash
-yay -S pixie-git
-```
-
-Ou manuellement:
-
-```bash
-git clone https://aur.archlinux.org/pixie-git.git
-cd pixie-git
-makepkg -si
-```
-
-## Prerequis
-
-```bash
-sudo pacman -Syu --needed base-devel git
-```
-
-## Installation via PKGBUILD local (mainteneur/dev)
-
-```bash
-git clone https://github.com/laflut3/Pixie.git
-cd Pixie/arch
-makepkg -si
-```
-
-Le paquet installe:
-- `/usr/bin/pixie`
-- `/usr/share/pixie/web/*.html`
-- `/etc/pixie/config-pixie.yml`
-- `/usr/lib/systemd/system/pixie.service`
-
-## Demarrer Le Service
-
-```bash
-sudo systemctl daemon-reload
-sudo systemctl enable --now pixie.service
-```
-
-## Mettre A Jour
-
-Depuis un clone local:
-
-```bash
-cd Pixie
-git pull
-cd arch
-makepkg -si
-```
-
-## Configuration
-
-Le fichier principal est:
-
-`/etc/pixie/config-pixie.yml`
-
-L'ordre de resolution de la config runtime est documente ici:
-[doc/configuration.md](/home/ltorres/perso/Pixie/doc/configuration.md)
-
-Pour publier/mettre a jour le paquet AUR:
-[doc/release_all_channels.md](/home/ltorres/perso/Pixie/doc/release_all_channels.md)
diff -ruN '--exclude=debian' '--exclude=.git' '--exclude=.pc' '--exclude=.codex' '--exclude=target' Pixie-1.1.0/doc/configuration.md new/doc/configuration.md
--- Pixie-1.1.0/doc/configuration.md	2026-04-10 23:11:11.000000000 +0200
+++ new/doc/configuration.md	1970-01-01 01:00:00.000000000 +0100
@@ -1,75 +0,0 @@
-# Configuration Pixie
-
-Ce document definit ou placer `config-pixie.yml` selon le mode de deploiement.
-
-## Chemin standard (comme Nginx)
-
-Pour une installation systeme (APT), le chemin standard est:
-
-`/etc/pixie/config-pixie.yml`
-
-Objectif:
-- meme logique que `nginx` (fichiers de conf sous `/etc/...`),
-- configuration separee du code applicatif,
-- simple a gerer avec systemd et outils d'administration.
-
-## Format du fichier
-
-Exemple minimal:
-
-```yml
-addr: 0.0.0.0:8080
-workers: 4
-```
-
-Cles supportees:
-- `addr` (prioritaire sur `host` + `port`, ex: `0.0.0.0:8080`)
-- `host`
-- `port`
-- `workers` (alias accepte: `nb_worker`)
-
-## Ordre de resolution
-
-Pixie lit la configuration dans cet ordre:
-1. `PIXIE_CONFIG` (chemin explicite)
-2. `/etc/pixie/config-pixie.yml`
-3. `./config-pixie.yml`
-4. Valeurs hardcodees dans le code (`127.0.0.1:80`, `4` workers)
-
-## Docker: monter le YAML en volume
-
-Le fichier doit etre monte dans le conteneur:
-
-```bash
-docker run --rm -p 8080:8080 \
-  -v "$(pwd)/config-pixie.yml:/etc/pixie/config-pixie.yml:ro" \
-  pixie:local
-```
-
-## Kubernetes: monter le YAML via ConfigMap/volume
-
-Exemple de `volumeMount`:
-
-```yaml
-volumeMounts:
-  - name: pixie-config
-    mountPath: /etc/pixie/config-pixie.yml
-    subPath: config-pixie.yml
-    readOnly: true
-volumes:
-  - name: pixie-config
-    configMap:
-      name: pixie-config
-```
-
-## APT: emplacement attendu
-
-Avec `apt install pixie`, le fichier de configuration doit etre gere sous:
-
-`/etc/pixie/config-pixie.yml`
-
-Puis appliquer la conf:
-
-```bash
-sudo systemctl restart pixie
-```
diff -ruN '--exclude=debian' '--exclude=.git' '--exclude=.pc' '--exclude=.codex' '--exclude=target' Pixie-1.1.0/doc/publish_repo.md new/doc/publish_repo.md
--- Pixie-1.1.0/doc/publish_repo.md	2026-04-10 23:11:11.000000000 +0200
+++ new/doc/publish_repo.md	1970-01-01 01:00:00.000000000 +0100
@@ -1,93 +0,0 @@
-# Publier Le Depot APT Pixie
-
-Ce guide explique comment publier une nouvelle version de `pixie` dans le depot APT, puis comment les clients recuperent la mise a jour.
-
-## Prerequis
-
-```bash
-sudo apt update
-sudo apt install -y build-essential debhelper devscripts rustc cargo reprepro gnupg
-```
-
-## 1. Preparation d'une nouvelle version
-
-1. Mettre a jour le code de `pixie`.
-2. Mettre a jour `debian/changelog` avec une nouvelle version Debian (ex: `0.1.0-3`).
-
-Exemple de verification:
-
-```bash
-head -n 20 debian/changelog
-```
-
-## 2. Creer et publier le paquet dans le repo
-
-1. Recuperer l'ID de ta cle GPG:
-
-```bash
-gpg --list-keys --keyid-format LONG
-```
-
-2. Publier:
-
-```bash
-GPG_KEY_ID=<KEY_ID> DIST=bookworm COMPONENT=main ./scripts/apt/publish-repo.sh
-```
-
-Ce script:
-- build le `.deb`,
-- ajoute le paquet dans `apt-repo/`,
-- signe les metadonnees du depot,
-- met a jour `apt-repo/keyrings/pixie-archive-keyring.gpg`.
-
-## 3. Mettre en ligne le dossier `apt-repo/`
-
-Publier le contenu de `apt-repo/` sur ton serveur HTTP(S), par exemple sur:
-
-`https://repo.example.org/pixie`
-
-Important:
-- en production, preferer HTTPS,
-- pour un usage local avec `file:`, eviter un chemin sous `/home` (preferer `/srv/pixie-apt`).
-
-## 4. Cote client: installation initiale
-
-```bash
-./scripts/apt/configure-client.sh https://repo.example.org/pixie bookworm main
-```
-
-Le script configure la cle, ajoute la source APT, installe `pixie` et active le service.
-
-Configuration apres installation (comme nginx sous `/etc`):
-
-`/etc/pixie/config-pixie.yml`
-
-## 5. Cote client: recuperer une mise a jour Pixie
-
-Quand une nouvelle version est publiee:
-
-```bash
-sudo apt update
-sudo apt install --only-upgrade -y pixie
-```
-
-Verifier:
-
-```bash
-sudo systemctl status pixie
-sudo journalctl -u pixie.service -f
-```
-
-## 6. Checks utiles en cas de probleme
-
-Verifier la cle publiee:
-
-```bash
-gpg --show-keys --with-fingerprint apt-repo/keyrings/pixie-archive-keyring.asc
-```
-
-Verifier la version dispo dans le repo:
-
-```bash
-apt-cache policy pixie
-```
diff -ruN '--exclude=debian' '--exclude=.git' '--exclude=.pc' '--exclude=.codex' '--exclude=target' Pixie-1.1.0/doc/release_all_channels.md new/doc/release_all_channels.md
--- Pixie-1.1.0/doc/release_all_channels.md	2026-04-10 23:11:11.000000000 +0200
+++ new/doc/release_all_channels.md	1970-01-01 01:00:00.000000000 +0100
@@ -1,108 +0,0 @@
-# Publier Pixie Pour Tous Les Canaux
-
-Ce guide mainteneur publie Pixie sur:
-- image Docker (GHCR)
-- Debian (APT)
-- Arch Linux (AUR)
-
-## 1. Verifier Que Le Serveur N'est Plus Local-Only
-
-Avant toute publication, verifie que la config serveur ecoute hors loopback:
-
-```yml
-addr: 0.0.0.0:8080
-workers: 4
-```
-
-Chemin conseille en production:
-
-`/etc/pixie/config-pixie.yml`
-
-Verification:
-
-```bash
-sudo systemctl restart pixie
-ss -ltnp | grep 8080
-```
-
-## 2. Publier Docker (GHCR)
-
-Prerequis:
-- le repo GitHub est public
-- GitHub Actions activees
-
-Etapes:
-1. Creer un tag semver.
-2. Pousser branche + tag.
-3. Laisser le workflow Docker publier l'image.
-
-Commandes:
-
-```bash
-git tag v1.1.0
-git push origin main --tags
-```
-
-Workflow utilise:
-
-`/.github/workflows/docker-image.yml`
-
-Image finale:
-
-`ghcr.io/laflut3/pixie:latest`
-
-## 3. Publier Debian (APT)
-
-Etapes:
-1. Mettre a jour `debian/changelog`.
-2. Construire et signer via le script de publication.
-3. Sync le dossier `apt-repo/` sur ton hebergement HTTPS.
-
-Commandes:
-
-```bash
-GPG_KEY_ID=<KEY_ID> DIST=bookworm COMPONENT=main ./scripts/apt/publish-repo.sh
-```
-
-Puis publier `apt-repo/` sur:
-
-`https://repo.example.org/pixie`
-
-Doc detaillee:
-
-`/doc/publish_repo.md`
-
-## 4. Publier Arch Linux (AUR)
-
-Le package est defini dans:
-
-`/arch/PKGBUILD`
-
-Etapes:
-1. Generer le metadata AUR.
-2. Pousser `PKGBUILD` + `.SRCINFO` dans le repo AUR `pixie-git`.
-
-Commandes (depuis `arch/`):
-
-```bash
-makepkg --printsrcinfo > .SRCINFO
-```
-
-Exemple de publication AUR:
-
-```bash
-git clone ssh://aur@aur.archlinux.org/pixie-git.git
-cp PKGBUILD .SRCINFO pixie-git/
-cd pixie-git
-git add PKGBUILD .SRCINFO
-git commit -m "pixie-git: update"
-git push
-```
-
-## 5. Checklist De Release
-
-1. `cargo test --locked` vert.
-2. Tag Git cree et pousse.
-3. Image GHCR visible.
-4. Repo APT synchronise et signe.
-5. AUR mis a jour (`PKGBUILD` + `.SRCINFO`).
diff -ruN '--exclude=debian' '--exclude=.git' '--exclude=.pc' '--exclude=.codex' '--exclude=target' Pixie-1.1.0/pixie/src/config.rs new/pixie/src/config.rs
--- Pixie-1.1.0/pixie/src/config.rs	2026-04-10 23:11:11.000000000 +0200
+++ new/pixie/src/config.rs	2026-04-21 08:24:54.000000000 +0200
@@ -1,3 +1,11 @@
+//! Gestion de la configuration runtime de Pixie.
+//!
+//! Priorité de résolution:
+//! 1. fichier pointé par `PIXIE_CONFIG` (si non vide)
+//! 2. `config-pixie.yml` dans le répertoire courant
+//! 3. `/etc/pixie/config-pixie.yml`
+//! 4. valeurs par défaut compilées
+
 use serde::Deserialize;
 use std::{env, fs, io, path::Path};
 
@@ -8,12 +16,16 @@
 const SYSTEM_CONFIG_PATH: &str = "/etc/pixie/config-pixie.yml";
 const CONFIG_ENV_VAR: &str = "PIXIE_CONFIG";
 
+/// Configuration finale utilisée par le serveur.
 #[derive(Debug, Clone, PartialEq, Eq)]
 pub struct RuntimeConfig {
+    /// Adresse d'écoute HTTP, au format `hote:port`.
     pub addr: String,
+    /// Nombre de workers du thread-pool.
     pub workers: usize,
 }
 
+/// Représentation partielle du YAML avant normalisation.
 #[derive(Debug, Clone, Default, Deserialize, PartialEq, Eq)]
 struct FileConfig {
     #[serde(default, alias = "address")]
@@ -26,46 +38,49 @@
     workers: Option<usize>,
 }
 
-// Builds the fallback bind address from default host and port.
+/// Construit l'adresse par défaut (`127.0.0.1:80`).
 fn default_addr() -> String {
     format!("{DEFAULT_HOST}:{DEFAULT_PORT}")
 }
 
-// Reads an optional config path override from PIXIE_CONFIG.
+/// Lit `PIXIE_CONFIG` et renvoie sa valeur uniquement si elle est non vide.
 fn env_config_path() -> Option<String> {
-    env_value(CONFIG_ENV_VAR)
+    env::var(CONFIG_ENV_VAR)
+        .ok()
+        .filter(|value| !value.is_empty())
 }
 
-// Resolves runtime config with precedence: file values, then hardcoded defaults.
+/// Résout la configuration runtime en appliquant les priorités de fichiers,
+/// puis les valeurs par défaut si nécessaire.
 pub fn runtime_config() -> io::Result<RuntimeConfig> {
     let file_config = load_file_config()?;
 
     let addr = file_addr(file_config.as_ref()).unwrap_or_else(default_addr);
 
-    let workers = file_config
-        .and_then(|cfg| cfg.workers)
-        .filter(|value| *value > 0)
-        .unwrap_or(DEFAULT_THREADS);
+    let workers = match file_config.and_then(|cfg| cfg.workers) {
+        Some(value) if value > 0 => value,
+        _ => DEFAULT_THREADS,
+    };
 
     Ok(RuntimeConfig { addr, workers })
 }
 
-// Loads configuration from file, trying env override first, then system/local defaults.
+/// Charge le premier fichier de configuration trouvé selon l'ordre de priorité.
 fn load_file_config() -> io::Result<Option<FileConfig>> {
     if let Some(path) = env_config_path() {
         return read_config_if_exists(&path);
     }
 
-    for path in [SYSTEM_CONFIG_PATH, LOCAL_CONFIG_PATH] {
-        if let Some(cfg) = read_config_if_exists(path)? {
-            return Ok(Some(cfg));
+    for path in [LOCAL_CONFIG_PATH, SYSTEM_CONFIG_PATH] {
+        if let Some(config) = read_config_if_exists(path)? {
+            return Ok(Some(config));
         }
     }
 
     Ok(None)
 }
 
-// Reads and parses a YAML file only when it exists on disk.
+/// Lit et parse un fichier YAML uniquement s'il existe et s'il s'agit d'un fichier.
 fn read_config_if_exists(path: &str) -> io::Result<Option<FileConfig>> {
     let path = Path::new(path);
 
@@ -85,22 +100,24 @@
     Ok(Some(config))
 }
 
-// Derives an address from file config using `addr` first, else `host` + `port`.
+/// Calcule l'adresse depuis la config fichier.
+///
+/// Règles:
+/// - `addr` gagne toujours
+/// - sinon `host`/`port` sont combinés avec les valeurs par défaut manquantes
+/// - sinon `None`
 fn file_addr(config: Option<&FileConfig>) -> Option<String> {
     let config = config?;
 
-    config.addr.clone().or_else(|| {
-        (config.host.is_some() || config.port.is_some()).then(|| {
-            let host = config.host.as_deref().unwrap_or(DEFAULT_HOST);
-            let port = config.port.unwrap_or(DEFAULT_PORT);
-            format!("{host}:{port}")
-        })
-    })
-}
+    if let Some(addr) = config.addr.clone() {
+        return Some(addr);
+    }
 
-// Returns a non-empty environment variable value without extra normalization.
-fn env_value(key: &str) -> Option<String> {
-    env::var(key)
-        .ok()
-        .filter(|value| !value.is_empty())
+    if config.host.is_none() && config.port.is_none() {
+        return None;
+    }
+
+    let host = config.host.as_deref().unwrap_or(DEFAULT_HOST);
+    let port = config.port.unwrap_or(DEFAULT_PORT);
+    Some(format!("{host}:{port}"))
 }
diff -ruN '--exclude=debian' '--exclude=.git' '--exclude=.pc' '--exclude=.codex' '--exclude=target' Pixie-1.1.0/pixie/src/lib.rs new/pixie/src/lib.rs
--- Pixie-1.1.0/pixie/src/lib.rs	2026-04-10 23:11:11.000000000 +0200
+++ new/pixie/src/lib.rs	2026-04-14 10:43:41.966175198 +0200
@@ -1,3 +1,10 @@
+//! Bibliothèque principale de Pixie.
+//!
+//! Cette crate expose un serveur HTTP statique minimal:
+//! - résolution de configuration runtime
+//! - résolution de routes HTML
+//! - exécution serveur TCP avec thread-pool
+
 pub mod config;
 pub mod logger;
 pub mod router;
diff -ruN '--exclude=debian' '--exclude=.git' '--exclude=.pc' '--exclude=.codex' '--exclude=target' Pixie-1.1.0/pixie/src/logger.rs new/pixie/src/logger.rs
--- Pixie-1.1.0/pixie/src/logger.rs	2026-04-10 23:11:11.000000000 +0200
+++ new/pixie/src/logger.rs	2026-04-14 09:44:05.366515644 +0200
@@ -1,13 +1,18 @@
+//! Journalisation minimale de Pixie vers la sortie d'erreur.
+
 use std::fmt;
 
+/// Écrit un message de niveau `info` sur stderr.
 pub fn log_info(args: fmt::Arguments<'_>) {
     eprintln!("[pixie][info] {args}");
 }
 
+/// Écrit un message de niveau `warn` sur stderr.
 pub fn log_warn(args: fmt::Arguments<'_>) {
     eprintln!("[pixie][warn] {args}");
 }
 
+/// Écrit un message de niveau `error` sur stderr.
 pub fn log_error(args: fmt::Arguments<'_>) {
     eprintln!("[pixie][error] {args}");
 }
diff -ruN '--exclude=debian' '--exclude=.git' '--exclude=.pc' '--exclude=.codex' '--exclude=target' Pixie-1.1.0/pixie/src/main.rs new/pixie/src/main.rs
--- Pixie-1.1.0/pixie/src/main.rs	2026-04-10 23:11:11.000000000 +0200
+++ new/pixie/src/main.rs	2026-04-14 11:27:30.575155509 +0200
@@ -1,28 +1,19 @@
-use std::{env, io, process};
+use std::{io, process};
 
-use pixie::{log_error, log_info, run_server, runtime_config};
-
-const USAGE: &str = "Usage:\n  pixie serve";
+use pixie::{log_error, run_server, runtime_config};
 
+/// Point d'entree binaire.
+///
+/// Le binaire lance uniquement le serveur HTTP.
+/// Code de sortie `2` en cas d'erreur de configuration initiale.
 fn main() -> io::Result<()> {
-    let mut args = env::args().skip(1);
-
-    match args.next().as_deref() {
-        None | Some("serve") => {
-            let config = match runtime_config() {
-                Ok(config) => config,
-                Err(err) => {
-                    log_error(format_args!("{err}"));
-                    process::exit(2);
-                }
-            };
-
-            run_server(&config.addr, config.workers)
-        }
-        Some(command) => {
-            log_error(format_args!("unknown command: {command}"));
-            log_info(format_args!("{USAGE}"));
+    let config = match runtime_config() {
+        Ok(config) => config,
+        Err(err) => {
+            log_error(format_args!("{err}"));
             process::exit(2);
         }
-    }
+    };
+
+    run_server(&config.addr, config.workers)
 }
diff -ruN '--exclude=debian' '--exclude=.git' '--exclude=.pc' '--exclude=.codex' '--exclude=target' Pixie-1.1.0/pixie/src/router.rs new/pixie/src/router.rs
--- Pixie-1.1.0/pixie/src/router.rs	2026-04-10 23:11:11.000000000 +0200
+++ new/pixie/src/router.rs	2026-04-21 08:24:54.000000000 +0200
@@ -1,3 +1,10 @@
+//! Routage HTTP vers des fichiers HTML statiques.
+//!
+//! Ce module:
+//! - résout le répertoire web (`PIXIE_WEB_ROOT` -> dev -> système)
+//! - mappe une request-line HTTP GET vers un fichier `.html`
+//! - renvoie `404.html` quand la route est invalide ou absente
+
 use std::{
     env,
     path::{Path, PathBuf},
@@ -9,26 +16,31 @@
 const INDEX_PAGE: &str = "hello.html";
 const NOT_FOUND_PAGE: &str = "404.html";
 
+/// Résout le répertoire web utilisé par le serveur.
+///
+/// Priorité:
+/// 1. `PIXIE_WEB_ROOT` si la variable pointe vers un dossier existant
+/// 2. `../web` depuis le manifeste Cargo (mode dev)
+/// 3. `/usr/share/pixie/web` (install système)
 pub fn resolve_web_root() -> PathBuf {
-    if let Ok(path) = env::var("PIXIE_WEB_ROOT") {
-        let trimmed = path.trim();
-
-        if !trimmed.is_empty() {
-            let candidate = PathBuf::from(trimmed);
+    if let Some(path) = env::var("PIXIE_WEB_ROOT")
+        .ok()
+        .filter(|path| !path.is_empty())
+    {
+        let candidate = PathBuf::from(path);
 
-            if candidate.is_dir() {
-                return candidate;
-            }
-            
-            log_warn(format_args!(
-                "PIXIE_WEB_ROOT='{}' is not a directory, using fallback",
-                candidate.display()
-            ));
+        if candidate.is_dir() {
+            return candidate;
         }
+
+        log_warn(format_args!(
+            "PIXIE_WEB_ROOT='{}' is not a directory, using fallback",
+            candidate.display()
+        ));
     }
 
     let dev_root = Path::new(env!("CARGO_MANIFEST_DIR")).join("../web");
-    
+
     if dev_root.is_dir() {
         return dev_root;
     }
@@ -36,6 +48,12 @@
     PathBuf::from(DEFAULT_WEB_ROOT)
 }
 
+/// Résout une request-line HTTP vers un status + chemin de fichier.
+///
+/// Exemples:
+/// - `GET / HTTP/1.1` -> `hello.html`
+/// - `GET /about HTTP/1.1` -> `about.html`
+/// - route invalide ou absente -> `404.html`
 pub fn resolve_route(request_line: &str, web_root: &Path) -> (&'static str, PathBuf) {
     let not_found = || ("HTTP/1.1 404 NOT FOUND", web_root.join(NOT_FOUND_PAGE));
 
@@ -56,6 +74,12 @@
     }
 }
 
+/// Extrait la route d'une request-line HTTP GET.
+///
+/// Retourne:
+/// - `Some("")` pour `/`
+/// - `Some("about")` pour `/about` ou `/about?x=1`
+/// - `None` si la méthode n'est pas GET ou si la ligne est invalide
 fn extract_route(request_line: &str) -> Option<&str> {
     let mut parts = request_line.split_whitespace();
     let method = parts.next()?;
diff -ruN '--exclude=debian' '--exclude=.git' '--exclude=.pc' '--exclude=.codex' '--exclude=target' Pixie-1.1.0/pixie/src/server.rs new/pixie/src/server.rs
--- Pixie-1.1.0/pixie/src/server.rs	2026-04-10 23:11:11.000000000 +0200
+++ new/pixie/src/server.rs	2026-04-21 08:24:54.000000000 +0200
@@ -1,6 +1,11 @@
+//! Serveur TCP/HTTP de Pixie.
+//!
+//! Le serveur accepte des connexions TCP, lit la première ligne HTTP,
+//! résout une page statique avec le routeur, puis renvoie une réponse HTML.
+
 use std::{
     fs,
-    io::{self, BufReader, prelude::*},
+    io::{self, BufRead, BufReader, Write},
     net::{TcpListener, TcpStream},
     path::Path,
     sync::Arc,
@@ -8,10 +13,13 @@
 
 use crate::{
     ThreadPool,
-    logger::{log_error, log_info, log_warn},
+    logger::{log_error, log_info},
     router::{resolve_route, resolve_web_root},
 };
 
+/// Démarre le serveur HTTP sur `addr` avec `pool_size` workers.
+///
+/// La fonction boucle sur les connexions entrantes tant que le listener reste ouvert.
 pub fn run_server(addr: &str, pool_size: usize) -> io::Result<()> {
     let listener = TcpListener::bind(addr)?;
     let pool = ThreadPool::new(pool_size);
@@ -23,35 +31,34 @@
     ));
 
     for stream in listener.incoming() {
-        let stream = match stream {
-            Ok(stream) => stream,
-            Err(err) => {
-                log_warn(format_args!("incoming connection failed: {err}"));
-                continue;
-            }
-        };
-
-        let web_root = Arc::clone(&web_root);
-
-        pool.execute(move || {
-            if let Err(err) = handle_connection(stream, web_root.as_path()) {
-                log_error(format_args!("failed to handle connection: {err}"));
+        match stream {
+            Ok(stream) => {
+                let web_root = Arc::clone(&web_root);
+                pool.execute(move || {
+                    if let Err(err) = handle_connection(stream, web_root.as_path()) {
+                        log_error(format_args!("failed to handle connection: {err}"));
+                    }
+                });
             }
-        });
+            Err(err) => log_error(format_args!("failed to accept connection: {err}")),
+        }
     }
 
     Ok(())
 }
 
+/// Lit la request-line HTTP, résout la page cible puis écrit la réponse.
 fn handle_connection(mut stream: TcpStream, web_root: &Path) -> io::Result<()> {
-    let buf_reader = BufReader::new(&stream);
-    let request_line = match buf_reader.lines().next().transpose()? {
-        Some(line) => line,
-        None => return Ok(()),
-    };
+    let mut request_line = String::new();
+    
+    if BufReader::new(&stream).read_line(&mut request_line)? == 0 {
+        return Ok(());
+    }
+
+    let request_line = request_line.trim_end_matches(['\r', '\n']);
 
-    let (status_line, filename) = resolve_route(&request_line, web_root);
-    let contents = fs::read_to_string(filename)?;
+    let (status_line, filename) = resolve_route(request_line, web_root);
+    let contents = fs::read_to_string(&filename)?;
     let length = contents.len();
 
     let response = format!(
diff -ruN '--exclude=debian' '--exclude=.git' '--exclude=.pc' '--exclude=.codex' '--exclude=target' Pixie-1.1.0/pixie/src/threadpool/job.rs new/pixie/src/threadpool/job.rs
--- Pixie-1.1.0/pixie/src/threadpool/job.rs	2026-04-10 23:11:11.000000000 +0200
+++ new/pixie/src/threadpool/job.rs	1970-01-01 01:00:00.000000000 +0100
@@ -1 +0,0 @@
-pub(crate) type Job = Box<dyn FnOnce() + Send + 'static>;
diff -ruN '--exclude=debian' '--exclude=.git' '--exclude=.pc' '--exclude=.codex' '--exclude=target' Pixie-1.1.0/pixie/src/threadpool/pool.rs new/pixie/src/threadpool/pool.rs
--- Pixie-1.1.0/pixie/src/threadpool/pool.rs	2026-04-10 23:11:11.000000000 +0200
+++ new/pixie/src/threadpool/pool.rs	2026-04-21 08:24:54.000000000 +0200
@@ -1,16 +1,17 @@
 use std::sync::{Arc, Mutex, mpsc};
 
-use super::{job::Job, worker::Worker};
+use super::{Job, worker::Worker};
 
+/// Pool de threads fixe utilisé pour exécuter des jobs concurrents.
 pub struct ThreadPool {
     workers: Vec<Worker>,
     sender: Option<mpsc::Sender<Job>>,
 }
 
 impl ThreadPool {
-    /// Create a new ThreadPool.
+    /// Crée un thread-pool de taille `size`.
     ///
-    /// The size is the number of threads in the pool.
+    /// Panique si `size == 0`.
     pub fn new(size: usize) -> ThreadPool {
         assert!(size > 0, "thread pool size must be greater than zero");
 
@@ -28,26 +29,24 @@
         }
     }
 
+    /// Soumet un job au pool pour exécution asynchrone.
     pub fn execute<F>(&self, f: F)
     where
         F: FnOnce() + Send + 'static,
     {
-        let job = Box::new(f);
+        let Some(sender) = self.sender.as_ref() else {
+            crate::logger::log_warn(format_args!("thread pool is shutting down; dropping job"));
+            return;
+        };
 
-        match &self.sender {
-            Some(sender) => {
-                if let Err(err) = sender.send(job) {
-                    eprintln!("[pixie][error] failed to send job to worker: {err}");
-                }
-            }
-            None => {
-                eprintln!("[pixie][warn] thread pool is shut down; rejecting new job");
-            }
+        if let Err(err) = sender.send(Box::new(f)) {
+            crate::logger::log_error(format_args!("failed to send job to worker: {err}"));
         }
     }
 }
 
 impl Drop for ThreadPool {
+    /// Ferme proprement le canal puis attend la terminaison de tous les workers.
     fn drop(&mut self) {
         drop(self.sender.take());
 
diff -ruN '--exclude=debian' '--exclude=.git' '--exclude=.pc' '--exclude=.codex' '--exclude=target' Pixie-1.1.0/pixie/src/threadpool/worker.rs new/pixie/src/threadpool/worker.rs
--- Pixie-1.1.0/pixie/src/threadpool/worker.rs	2026-04-10 23:11:11.000000000 +0200
+++ new/pixie/src/threadpool/worker.rs	2026-04-21 08:24:54.000000000 +0200
@@ -3,36 +3,31 @@
     thread,
 };
 
-use super::job::Job;
+use super::Job;
 
+/// Worker interne du thread-pool.
 pub(super) struct Worker {
     id: usize,
     thread: Option<thread::JoinHandle<()>>,
 }
 
 impl Worker {
+    /// Démarre un worker qui consomme des jobs depuis `receiver`.
     pub(super) fn new(id: usize, receiver: Arc<Mutex<mpsc::Receiver<Job>>>) -> Self {
         let thread = thread::spawn(move || {
             loop {
-                let message = match receiver.lock() {
-                    Ok(guard) => guard.recv(),
-                    Err(_) => {
-                        eprintln!(
-                            "[pixie][error] worker {id} receiver lock poisoned; shutting down"
-                        );
-                        break;
-                    }
+                let message = receiver
+                    .lock()
+                    .expect("worker receiver lock poisoned")
+                    .recv();
+                let Ok(job) = message else {
+                    crate::logger::log_info(format_args!(
+                        "worker {id} disconnected; shutting down"
+                    ));
+                    break;
                 };
 
-                match message {
-                    Ok(job) => {
-                        job();
-                    }
-                    Err(_) => {
-                        eprintln!("[pixie][info] worker {id} disconnected; shutting down");
-                        break;
-                    }
-                }
+                job();
             }
         });
 
@@ -42,11 +37,12 @@
         }
     }
 
+    /// Attend la fin du thread worker.
     pub(super) fn join(&mut self) {
-        if let Some(thread) = self.thread.take()
-            && let Err(err) = thread.join()
-        {
-            eprintln!("[pixie][error] worker {} panicked: {:?}", self.id, err);
+        if let Some(thread) = self.thread.take() {
+            if thread.join().is_err() {
+                crate::logger::log_error(format_args!("worker {} panicked", self.id));
+            }
         }
     }
 }
diff -ruN '--exclude=debian' '--exclude=.git' '--exclude=.pc' '--exclude=.codex' '--exclude=target' Pixie-1.1.0/pixie/src/threadpool.rs new/pixie/src/threadpool.rs
--- Pixie-1.1.0/pixie/src/threadpool.rs	2026-04-10 23:11:11.000000000 +0200
+++ new/pixie/src/threadpool.rs	2026-04-21 08:24:54.000000000 +0200
@@ -1,5 +1,9 @@
-mod job;
+//! Thread-pool minimal utilisé par le serveur pour traiter les connexions en parallèle.
+
 mod pool;
 mod worker;
 
+/// Type d'un travail asynchrone exécuté par un worker.
+pub(crate) type Job = Box<dyn FnOnce() + Send + 'static>;
+
 pub use pool::ThreadPool;
diff -ruN '--exclude=debian' '--exclude=.git' '--exclude=.pc' '--exclude=.codex' '--exclude=target' Pixie-1.1.0/pixie/tests/common/mod.rs new/pixie/tests/common/mod.rs
--- Pixie-1.1.0/pixie/tests/common/mod.rs	1970-01-01 01:00:00.000000000 +0100
+++ new/pixie/tests/common/mod.rs	2026-04-21 08:24:54.000000000 +0200
@@ -0,0 +1,155 @@
+#![allow(dead_code)]
+
+use std::{
+    env, fs,
+    io::{self, Read, Write},
+    net::{Shutdown, TcpListener, TcpStream},
+    path::PathBuf,
+    sync::Mutex,
+    thread,
+    time::{Duration, SystemTime, UNIX_EPOCH},
+};
+
+/// Verrou global pour sérialiser les tests qui modifient l'environnement.
+pub static ENV_LOCK: Mutex<()> = Mutex::new(());
+
+/// Récupère le verrou environnement même si un test précédent a paniqué.
+pub fn env_lock() -> std::sync::MutexGuard<'static, ()> {
+    ENV_LOCK.lock().unwrap_or_else(|err| err.into_inner())
+}
+
+/// Garde qui restaure une variable d'environnement en fin de test.
+pub struct EnvGuard {
+    key: &'static str,
+    old: Option<String>,
+}
+
+impl EnvGuard {
+    /// Définit une variable d'environnement pour la durée du scope.
+    pub fn set(key: &'static str, value: &str) -> Self {
+        let old = env::var(key).ok();
+        unsafe { env::set_var(key, value) };
+        Self { key, old }
+    }
+}
+
+impl Drop for EnvGuard {
+    /// Restaure la valeur précédente de la variable d'environnement.
+    fn drop(&mut self) {
+        match &self.old {
+            Some(value) => unsafe { env::set_var(self.key, value) },
+            None => unsafe { env::remove_var(self.key) },
+        }
+    }
+}
+
+/// Fichier temporaire supprimé automatiquement.
+pub struct TempFile {
+    path: PathBuf,
+}
+
+impl TempFile {
+    /// Crée un fichier temporaire YAML avec le contenu donné.
+    pub fn new(prefix: &str, content: &str) -> Self {
+        let path = unique_path(prefix, "yml");
+        fs::write(&path, content).expect("écriture du fichier temporaire impossible");
+        Self { path }
+    }
+
+    /// Retourne le chemin UTF-8 du fichier temporaire.
+    pub fn as_str(&self) -> &str {
+        self.path.to_str().expect("chemin non UTF-8")
+    }
+}
+
+impl Drop for TempFile {
+    /// Supprime le fichier temporaire s'il existe encore.
+    fn drop(&mut self) {
+        let _ = fs::remove_file(&self.path);
+    }
+}
+
+/// Dossier temporaire supprimé automatiquement.
+pub struct TempDir {
+    /// Chemin absolu du dossier temporaire.
+    pub path: PathBuf,
+}
+
+impl TempDir {
+    /// Crée un nouveau dossier temporaire vide.
+    pub fn new(prefix: &str) -> Self {
+        let path = unique_path(prefix, "dir");
+        fs::create_dir_all(&path).expect("création du dossier temporaire impossible");
+        Self { path }
+    }
+
+    /// Écrit un fichier dans le dossier temporaire.
+    pub fn write(&self, relative: &str, content: &str) {
+        let file = self.path.join(relative);
+        if let Some(parent) = file.parent() {
+            fs::create_dir_all(parent).expect("création du parent impossible");
+        }
+        fs::write(file, content).expect("écriture du fichier temporaire impossible");
+    }
+}
+
+impl Drop for TempDir {
+    /// Supprime récursivement le dossier temporaire.
+    fn drop(&mut self) {
+        let _ = fs::remove_dir_all(&self.path);
+    }
+}
+
+/// Construit un chemin temporaire unique.
+pub fn unique_path(prefix: &str, suffix: &str) -> PathBuf {
+    let nanos = SystemTime::now()
+        .duration_since(UNIX_EPOCH)
+        .expect("horloge système invalide")
+        .as_nanos();
+
+    env::temp_dir().join(format!("{prefix}-{nanos}.{suffix}"))
+}
+
+/// Réserve un port local disponible en loopback.
+pub fn reserve_local_port() -> u16 {
+    let listener = TcpListener::bind("127.0.0.1:0").expect("bind local impossible");
+    let port = listener
+        .local_addr()
+        .expect("adresse locale introuvable")
+        .port();
+    drop(listener);
+    port
+}
+
+/// Attend qu'un serveur TCP soit joignable jusqu'au timeout.
+pub fn wait_for_server(addr: &str, timeout: Duration) {
+    let start = SystemTime::now();
+
+    loop {
+        if TcpStream::connect(addr).is_ok() {
+            return;
+        }
+
+        let elapsed = SystemTime::now()
+            .duration_since(start)
+            .expect("durée invalide");
+
+        if elapsed > timeout {
+            panic!("serveur non disponible à {addr} après {timeout:?}");
+        }
+
+        thread::sleep(Duration::from_millis(20));
+    }
+}
+
+/// Envoie une requête HTTP brute et renvoie la réponse complète.
+pub fn send_http_request(addr: &str, request: &str) -> io::Result<String> {
+    let mut stream = TcpStream::connect(addr)?;
+    stream.write_all(request.as_bytes())?;
+    stream.shutdown(Shutdown::Write)?;
+
+    let mut buf = Vec::new();
+    stream.read_to_end(&mut buf)?;
+    String::from_utf8(buf)
+        .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err.to_string()))
+}
diff -ruN '--exclude=debian' '--exclude=.git' '--exclude=.pc' '--exclude=.codex' '--exclude=target' Pixie-1.1.0/pixie/tests/config.rs new/pixie/tests/config.rs
--- Pixie-1.1.0/pixie/tests/config.rs	2026-04-10 23:11:11.000000000 +0200
+++ new/pixie/tests/config.rs	1970-01-01 01:00:00.000000000 +0100
@@ -1,114 +0,0 @@
-use pixie::runtime_config;
-use std::{
-    env, fs,
-    path::PathBuf,
-    sync::Mutex,
-    time::{SystemTime, UNIX_EPOCH},
-};
-
-static ENV_LOCK: Mutex<()> = Mutex::new(());
-
-struct EnvGuard {
-    key: &'static str,
-    old: Option<String>,
-}
-
-impl EnvGuard {
-    fn set(key: &'static str, value: &str) -> Self {
-        let old = env::var(key).ok();
-        unsafe { env::set_var(key, value) };
-        Self { key, old }
-    }
-
-    fn unset(key: &'static str) -> Self {
-        let old = env::var(key).ok();
-        unsafe { env::remove_var(key) };
-        Self { key, old }
-    }
-}
-
-impl Drop for EnvGuard {
-    fn drop(&mut self) {
-        match &self.old {
-            Some(value) => unsafe { env::set_var(self.key, value) },
-            None => unsafe { env::remove_var(self.key) },
-        }
-    }
-}
-
-fn write_temp_config(content: &str) -> PathBuf {
-    let nanos = SystemTime::now()
-        .duration_since(UNIX_EPOCH)
-        .expect("time should be monotonic")
-        .as_nanos();
-    let path = env::temp_dir().join(format!("pixie-config-{nanos}.yml"));
-    fs::write(&path, content).expect("temp config should be writable");
-    path
-}
-
-#[test]
-fn reads_yaml_from_pixie_config_path() {
-    let _lock = ENV_LOCK.lock().expect("env lock poisoned");
-    let _addr = EnvGuard::unset("PIXIE_ADDR");
-    let _threads = EnvGuard::unset("PIXIE_THREADS");
-
-    let path = write_temp_config("host: 0.0.0.0\nport: 9090\nworkers: 8\n");
-    let _config = EnvGuard::set("PIXIE_CONFIG", path.to_str().expect("utf-8 path"));
-
-    let config = runtime_config().expect("runtime config should load");
-    assert_eq!(config.addr, "0.0.0.0:9090");
-    assert_eq!(config.workers, 8);
-
-    let _ = fs::remove_file(path);
-}
-
-#[test]
-fn keeps_defaults_for_invalid_workers() {
-    let _lock = ENV_LOCK.lock().expect("env lock poisoned");
-    let _addr = EnvGuard::unset("PIXIE_ADDR");
-    let _threads = EnvGuard::unset("PIXIE_THREADS");
-
-    let path = write_temp_config("host: 127.0.0.1\nport: 8080\nworkers: 0\n");
-    let _config = EnvGuard::set("PIXIE_CONFIG", path.to_str().expect("utf-8 path"));
-
-    let config = runtime_config().expect("runtime config should load");
-    assert_eq!(config.addr, "127.0.0.1:8080");
-    assert_eq!(config.workers, 4);
-
-    let _ = fs::remove_file(path);
-}
-
-#[test]
-fn supports_nb_worker_alias_from_yaml() {
-    let _lock = ENV_LOCK.lock().expect("env lock poisoned");
-
-    let path = write_temp_config("addr: 0.0.0.0:8080\nnb_worker: 6\n");
-    let _config = EnvGuard::set("PIXIE_CONFIG", path.to_str().expect("utf-8 path"));
-
-    let config = runtime_config().expect("runtime config should load");
-    assert_eq!(config.addr, "0.0.0.0:8080");
-    assert_eq!(config.workers, 6);
-
-    let _ = fs::remove_file(path);
-}
-
-#[test]
-fn uses_hardcoded_defaults_when_no_yaml_is_found() {
-    let _lock = ENV_LOCK.lock().expect("env lock poisoned");
-    let _addr = EnvGuard::set("PIXIE_ADDR", "0.0.0.0:9999");
-    let _threads = EnvGuard::set("PIXIE_THREADS", "32");
-
-    let nanos = SystemTime::now()
-        .duration_since(UNIX_EPOCH)
-        .expect("time should be monotonic")
-        .as_nanos();
-    let missing_path = env::temp_dir().join(format!("pixie-missing-config-{nanos}.yml"));
-    let _config = EnvGuard::set(
-        "PIXIE_CONFIG",
-        missing_path.to_str().expect("utf-8 path"),
-    );
-
-    let config = runtime_config().expect("runtime config should load");
-    assert_eq!(config.addr, "127.0.0.1:80");
-    assert_eq!(config.workers, 4);
-}
diff -ruN '--exclude=debian' '--exclude=.git' '--exclude=.pc' '--exclude=.codex' '--exclude=target' Pixie-1.1.0/pixie/tests/config_tests.rs new/pixie/tests/config_tests.rs
--- Pixie-1.1.0/pixie/tests/config_tests.rs	1970-01-01 01:00:00.000000000 +0100
+++ new/pixie/tests/config_tests.rs	2026-04-14 13:35:25.597440466 +0200
@@ -0,0 +1,99 @@
+mod common;
+
+use common::{EnvGuard, TempFile, env_lock, unique_path};
+use pixie::runtime_config;
+use std::fs;
+
+/// Vérifie le fallback complet quand aucun fichier n'est disponible.
+#[test]
+fn runtime_config_retourne_les_valeurs_par_defaut() {
+    let _lock = env_lock();
+    let missing = unique_path("pixie-missing-config", "yml");
+    let _config = EnvGuard::set(
+        "PIXIE_CONFIG",
+        missing.to_str().expect("chemin temporaire non UTF-8"),
+    );
+
+    let config = runtime_config().expect("configuration runtime invalide");
+    assert_eq!(config.addr, "127.0.0.1:80");
+    assert_eq!(config.workers, 4);
+}
+
+/// Vérifie la lecture d'un fichier YAML via `PIXIE_CONFIG`.
+#[test]
+fn runtime_config_lit_le_fichier_pointe_par_pixie_config() {
+    let _lock = env_lock();
+    let config_file = TempFile::new("pixie-config", "host: 0.0.0.0\nport: 9090\nworkers: 8\n");
+    let _config = EnvGuard::set("PIXIE_CONFIG", config_file.as_str());
+
+    let config = runtime_config().expect("configuration runtime invalide");
+    assert_eq!(config.addr, "0.0.0.0:9090");
+    assert_eq!(config.workers, 8);
+}
+
+/// Vérifie le fallback worker quand `workers` vaut 0.
+#[test]
+fn runtime_config_ignore_workers_zero() {
+    let _lock = env_lock();
+    let config_file = TempFile::new("pixie-config", "addr: 127.0.0.1:8080\nworkers: 0\n");
+    let _config = EnvGuard::set("PIXIE_CONFIG", config_file.as_str());
+
+    let config = runtime_config().expect("configuration runtime invalide");
+    assert_eq!(config.addr, "127.0.0.1:8080");
+    assert_eq!(config.workers, 4);
+}
+
+/// Vérifie les alias YAML `address` et `nb_worker`.
+#[test]
+fn runtime_config_supporte_les_alias_yaml() {
+    let _lock = env_lock();
+    let config_file = TempFile::new("pixie-config", "address: 10.0.0.1:8081\nnb_worker: 6\n");
+    let _config = EnvGuard::set("PIXIE_CONFIG", config_file.as_str());
+
+    let config = runtime_config().expect("configuration runtime invalide");
+    assert_eq!(config.addr, "10.0.0.1:8081");
+    assert_eq!(config.workers, 6);
+}
+
+/// Vérifie la remontée d'erreur quand le YAML est invalide.
+#[test]
+fn runtime_config_remonte_une_erreur_yaml() {
+    let _lock = env_lock();
+    let config_file = TempFile::new("pixie-config", ":\n  - not-valid");
+    let _config = EnvGuard::set("PIXIE_CONFIG", config_file.as_str());
+
+    let err = runtime_config().expect_err("une erreur YAML était attendue");
+    assert_eq!(err.kind(), std::io::ErrorKind::InvalidData);
+    assert!(err.to_string().contains("invalid YAML"));
+}
+
+/// Vérifie qu'une valeur vide de `PIXIE_CONFIG` est ignorée.
+#[test]
+fn runtime_config_ignore_pixie_config_vide() {
+    let _lock = env_lock();
+    let _config = EnvGuard::set("PIXIE_CONFIG", "");
+
+    let config = runtime_config().expect("configuration runtime invalide");
+    assert!(!config.addr.is_empty());
+    assert!(config.workers > 0);
+}
+
+/// Vérifie que le fichier local `config-pixie.yml` est pris en charge.
+#[test]
+fn runtime_config_lit_le_fichier_local() {
+    let _lock = env_lock();
+    let _config = EnvGuard::set("PIXIE_CONFIG", "");
+
+    let path = std::env::current_dir()
+        .expect("répertoire courant introuvable")
+        .join("config-pixie.yml");
+
+    fs::write(&path, "addr: 127.0.0.1:7070\nworkers: 3\n")
+        .expect("écriture du config-pixie.yml local impossible");
+
+    let config = runtime_config().expect("configuration runtime invalide");
+    assert_eq!(config.addr, "127.0.0.1:7070");
+    assert_eq!(config.workers, 3);
+
+    let _ = fs::remove_file(path);
+}
diff -ruN '--exclude=debian' '--exclude=.git' '--exclude=.pc' '--exclude=.codex' '--exclude=target' Pixie-1.1.0/pixie/tests/logger_tests.rs new/pixie/tests/logger_tests.rs
--- Pixie-1.1.0/pixie/tests/logger_tests.rs	1970-01-01 01:00:00.000000000 +0100
+++ new/pixie/tests/logger_tests.rs	2026-04-14 09:46:16.960597899 +0200
@@ -0,0 +1,9 @@
+use pixie::{log_error, log_info, log_warn};
+
+/// Vérifie que les helpers de log sont appelables sans panic.
+#[test]
+fn log_helpers_sont_sans_panic() {
+    log_info(format_args!("info"));
+    log_warn(format_args!("warn"));
+    log_error(format_args!("error"));
+}
diff -ruN '--exclude=debian' '--exclude=.git' '--exclude=.pc' '--exclude=.codex' '--exclude=target' Pixie-1.1.0/pixie/tests/router_tests.rs new/pixie/tests/router_tests.rs
--- Pixie-1.1.0/pixie/tests/router_tests.rs	1970-01-01 01:00:00.000000000 +0100
+++ new/pixie/tests/router_tests.rs	2026-04-14 13:35:25.601441281 +0200
@@ -0,0 +1,126 @@
+mod common;
+
+use std::path::Path;
+
+use common::{EnvGuard, TempDir, env_lock};
+use pixie::{resolve_route, resolve_web_root};
+
+/// Vérifie la résolution de la racine web via `PIXIE_WEB_ROOT`.
+#[test]
+fn resolve_web_root_utilise_pixie_web_root_si_valide() {
+    let _lock = env_lock();
+    let web = TempDir::new("pixie-web-root");
+    let _env = EnvGuard::set(
+        "PIXIE_WEB_ROOT",
+        web.path.to_str().expect("chemin non UTF-8"),
+    );
+
+    assert_eq!(resolve_web_root(), web.path);
+}
+
+/// Vérifie que la variable vide est ignorée et que le fallback dev est pris.
+#[test]
+fn resolve_web_root_ignore_pixie_web_root_vide() {
+    let _lock = env_lock();
+    let _env = EnvGuard::set("PIXIE_WEB_ROOT", "");
+
+    let expected = Path::new(env!("CARGO_MANIFEST_DIR")).join("../web");
+    assert_eq!(resolve_web_root(), expected);
+}
+
+/// Vérifie qu'une valeur invalide de `PIXIE_WEB_ROOT` est ignorée.
+#[test]
+fn resolve_web_root_ignore_pixie_web_root_non_dossier() {
+    let _lock = env_lock();
+    let invalid_file = TempDir::new("pixie-web-root-invalid");
+    invalid_file.write("file.txt", "not a dir");
+
+    let invalid_path = invalid_file.path.join("file.txt");
+    let _env = EnvGuard::set(
+        "PIXIE_WEB_ROOT",
+        invalid_path.to_str().expect("chemin non UTF-8"),
+    );
+
+    let expected = Path::new(env!("CARGO_MANIFEST_DIR")).join("../web");
+    assert_eq!(resolve_web_root(), expected);
+}
+
+/// Vérifie le fallback système quand le dossier dev `../web` est indisponible.
+#[test]
+fn resolve_web_root_fallback_systeme_si_dossier_dev_absent() {
+    let _lock = env_lock();
+    let _env = EnvGuard::set("PIXIE_WEB_ROOT", "");
+
+    let dev_root = Path::new(env!("CARGO_MANIFEST_DIR")).join("../web");
+    let backup = Path::new(env!("CARGO_MANIFEST_DIR")).join("../web-test-backup");
+
+    if backup.exists() {
+        let _ = std::fs::remove_dir_all(&backup);
+    }
+
+    std::fs::rename(&dev_root, &backup).expect("renommage temporaire du dossier web impossible");
+
+    let resolved = resolve_web_root();
+
+    std::fs::rename(&backup, &dev_root).expect("restauration du dossier web impossible");
+
+    assert_eq!(resolved, Path::new("/usr/share/pixie/web"));
+}
+
+/// Vérifie que `/` sert `hello.html` quand le fichier existe.
+#[test]
+fn resolve_route_sert_la_page_index() {
+    let web = TempDir::new("pixie-web-index");
+    web.write("hello.html", "ok");
+    web.write("404.html", "ko");
+
+    let (status, file) = resolve_route("GET / HTTP/1.1", &web.path);
+    assert_eq!(status, "HTTP/1.1 200 OK");
+    assert_eq!(file, web.path.join("hello.html"));
+}
+
+/// Vérifie que `/about` sert `about.html`.
+#[test]
+fn resolve_route_sert_une_page_nommee() {
+    let web = TempDir::new("pixie-web-about");
+    web.write("about.html", "about");
+    web.write("404.html", "ko");
+
+    let (status, file) = resolve_route("GET /about HTTP/1.1", &web.path);
+    assert_eq!(status, "HTTP/1.1 200 OK");
+    assert_eq!(file, web.path.join("about.html"));
+}
+
+/// Vérifie que la query string est ignorée dans la résolution du fichier.
+#[test]
+fn resolve_route_ignore_la_query_string() {
+    let web = TempDir::new("pixie-web-query");
+    web.write("hello.html", "ok");
+    web.write("404.html", "ko");
+
+    let (status, file) = resolve_route("GET /?name=pixie HTTP/1.1", &web.path);
+    assert_eq!(status, "HTTP/1.1 200 OK");
+    assert_eq!(file, web.path.join("hello.html"));
+}
+
+/// Vérifie le fallback 404 quand la route demandée n'existe pas.
+#[test]
+fn resolve_route_retourne_404_si_fichier_absent() {
+    let web = TempDir::new("pixie-web-missing");
+    web.write("404.html", "ko");
+
+    let (status, file) = resolve_route("GET /missing HTTP/1.1", &web.path);
+    assert_eq!(status, "HTTP/1.1 404 NOT FOUND");
+    assert_eq!(file, web.path.join("404.html"));
+}
+
+/// Vérifie qu'une méthode non GET retourne 404.
+#[test]
+fn resolve_route_refuse_les_methodes_non_get() {
+    let web = TempDir::new("pixie-web-method");
+    web.write("404.html", "ko");
+
+    let (status, file) = resolve_route("POST / HTTP/1.1", &web.path);
+    assert_eq!(status, "HTTP/1.1 404 NOT FOUND");
+    assert_eq!(file, web.path.join("404.html"));
+}
diff -ruN '--exclude=debian' '--exclude=.git' '--exclude=.pc' '--exclude=.codex' '--exclude=target' Pixie-1.1.0/pixie/tests/server_tests.rs new/pixie/tests/server_tests.rs
--- Pixie-1.1.0/pixie/tests/server_tests.rs	1970-01-01 01:00:00.000000000 +0100
+++ new/pixie/tests/server_tests.rs	2026-04-14 10:53:02.798846327 +0200
@@ -0,0 +1,79 @@
+mod common;
+
+use std::{thread, time::Duration};
+
+use common::{EnvGuard, TempDir, env_lock, reserve_local_port, send_http_request, wait_for_server};
+use pixie::run_server;
+
+/// Vérifie une réponse 200 sur `/` puis une réponse 404 sur une page absente.
+#[test]
+fn serveur_repond_200_et_404() {
+    let _lock = env_lock();
+
+    let port = reserve_local_port();
+    let addr = format!("127.0.0.1:{port}");
+
+    let web_root = TempDir::new("pixie-server-web");
+    web_root.write("hello.html", "Bonjour depuis test");
+    web_root.write("404.html", "Page absente");
+
+    let _web_env = EnvGuard::set(
+        "PIXIE_WEB_ROOT",
+        web_root.path.to_str().expect("chemin web non UTF-8"),
+    );
+
+    let addr_for_thread = addr.clone();
+    thread::spawn(move || {
+        let _ = run_server(&addr_for_thread, 1);
+    });
+
+    wait_for_server(&addr, Duration::from_secs(3));
+
+    let response_ok = send_http_request(&addr, "GET / HTTP/1.1\r\nHost: localhost\r\n\r\n")
+        .expect("requête 200 impossible");
+    assert!(response_ok.starts_with("HTTP/1.1 200 OK"));
+    assert!(response_ok.contains("Bonjour depuis test"));
+
+    let response_404 =
+        send_http_request(&addr, "GET /inconnue HTTP/1.1\r\nHost: localhost\r\n\r\n")
+            .expect("requête 404 impossible");
+    assert!(response_404.starts_with("HTTP/1.1 404 NOT FOUND"));
+    assert!(response_404.contains("Page absente"));
+}
+
+/// Vérifie que `run_server` remonte une erreur de bind sur adresse invalide.
+#[test]
+fn run_server_remonte_une_erreur_si_l_adresse_est_invalide() {
+    let err = run_server("256.0.0.1:80", 1).expect_err("une erreur de bind était attendue");
+    assert!(!err.to_string().is_empty());
+}
+
+/// Vérifie que le serveur gère l'erreur interne quand `404.html` est absent.
+#[test]
+fn serveur_journalise_une_erreur_si_404_est_absent() {
+    let _lock = env_lock();
+
+    let port = reserve_local_port();
+    let addr = format!("127.0.0.1:{port}");
+
+    let web_root = TempDir::new("pixie-server-web-no-404");
+    web_root.write("hello.html", "Bonjour depuis test");
+
+    let _web_env = EnvGuard::set(
+        "PIXIE_WEB_ROOT",
+        web_root.path.to_str().expect("chemin web non UTF-8"),
+    );
+
+    let addr_for_thread = addr.clone();
+    thread::spawn(move || {
+        let _ = run_server(&addr_for_thread, 1);
+    });
+
+    wait_for_server(&addr, Duration::from_secs(3));
+
+    let response_404 =
+        send_http_request(&addr, "GET /inconnue HTTP/1.1\r\nHost: localhost\r\n\r\n")
+            .expect("requête impossible");
+
+    assert!(response_404.is_empty());
+}
diff -ruN '--exclude=debian' '--exclude=.git' '--exclude=.pc' '--exclude=.codex' '--exclude=target' Pixie-1.1.0/pixie/tests/threadpool_tests.rs new/pixie/tests/threadpool_tests.rs
--- Pixie-1.1.0/pixie/tests/threadpool_tests.rs	1970-01-01 01:00:00.000000000 +0100
+++ new/pixie/tests/threadpool_tests.rs	2026-04-14 09:46:10.200071450 +0200
@@ -0,0 +1,55 @@
+use std::{
+    sync::{
+        Arc,
+        atomic::{AtomicUsize, Ordering},
+        mpsc,
+    },
+    thread,
+    time::Duration,
+};
+
+use pixie::ThreadPool;
+
+/// Vérifie que le pool exécute tous les jobs soumis.
+#[test]
+fn thread_pool_execute_tous_les_jobs() {
+    let pool = ThreadPool::new(2);
+    let done = Arc::new(AtomicUsize::new(0));
+    let (tx, rx) = mpsc::channel();
+
+    for _ in 0..8 {
+        let done = Arc::clone(&done);
+        let tx = tx.clone();
+        pool.execute(move || {
+            done.fetch_add(1, Ordering::SeqCst);
+            tx.send(()).expect("notification impossible");
+        });
+    }
+
+    drop(tx);
+
+    for _ in 0..8 {
+        rx.recv_timeout(Duration::from_secs(1))
+            .expect("job non exécuté dans le délai");
+    }
+
+    assert_eq!(done.load(Ordering::SeqCst), 8);
+}
+
+/// Vérifie que la création avec 0 worker panique.
+#[test]
+#[should_panic(expected = "thread pool size must be greater than zero")]
+fn thread_pool_new_panique_si_taille_zero() {
+    let _ = ThreadPool::new(0);
+}
+
+/// Vérifie qu'un envoi après panic worker ne panique pas l'appelant.
+#[test]
+fn thread_pool_execute_apres_panic_worker() {
+    let pool = ThreadPool::new(1);
+
+    pool.execute(|| panic!("panic volontaire"));
+
+    thread::sleep(Duration::from_millis(50));
+    pool.execute(|| {});
+}
diff -ruN '--exclude=debian' '--exclude=.git' '--exclude=.pc' '--exclude=.codex' '--exclude=target' Pixie-1.1.0/scripts/apt/configure-client.sh new/scripts/apt/configure-client.sh
--- Pixie-1.1.0/scripts/apt/configure-client.sh	2026-04-10 23:11:11.000000000 +0200
+++ new/scripts/apt/configure-client.sh	1970-01-01 01:00:00.000000000 +0100
@@ -1,81 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-
-if [[ $# -lt 1 || $# -gt 3 ]]; then
-  echo "Usage: $0 <repo-url> [distribution] [component]" >&2
-  echo "Example: $0 https://repo.example.org/pixie bookworm main" >&2
-  echo "Example: $0 file:/srv/pixie-apt bookworm main" >&2
-  exit 1
-fi
-
-REPO_URL="${1%/}"
-DIST="${2:-bookworm}"
-COMPONENT="${3:-main}"
-KEYRING_PATH="/usr/share/keyrings/pixie-archive-keyring.gpg"
-LIST_PATH="/etc/apt/sources.list.d/pixie.list"
-TMP_KEYRING="$(mktemp)"
-trap 'rm -f "$TMP_KEYRING"' EXIT
-
-if ! command -v sudo >/dev/null 2>&1; then
-  echo "sudo is required." >&2
-  exit 1
-fi
-
-if [[ "$REPO_URL" == "https://packages.example.com/pixie" ]]; then
-  echo "Replace packages.example.com with your real repository URL." >&2
-  exit 1
-fi
-
-case "$REPO_URL" in
-  http://*|https://*)
-    if ! command -v curl >/dev/null 2>&1; then
-      echo "curl is required for http(s) repositories." >&2
-      exit 1
-    fi
-    curl -fsSL "$REPO_URL/keyrings/pixie-archive-keyring.gpg" -o "$TMP_KEYRING"
-    ;;
-  file:*)
-    # file:/path/to/repo -> /path/to/repo
-    LOCAL_REPO_PATH="${REPO_URL#file:}"
-    cp "$LOCAL_REPO_PATH/keyrings/pixie-archive-keyring.gpg" "$TMP_KEYRING"
-    ;;
-  *)
-    echo "Unsupported repo URL: '$REPO_URL'" >&2
-    echo "Use an http(s) URL or file:/absolute/path URL." >&2
-    exit 1
-    ;;
-esac
-
-if [[ ! -s "$TMP_KEYRING" ]]; then
-  echo "Downloaded keyring is empty: $TMP_KEYRING" >&2
-  exit 1
-fi
-
-if command -v gpg >/dev/null 2>&1; then
-  if ! gpg --show-keys "$TMP_KEYRING" >/dev/null 2>&1; then
-    echo "Downloaded keyring is not a valid OpenPGP keyring." >&2
-    exit 1
-  fi
-fi
-
-sudo install -d -m 0755 /usr/share/keyrings
-sudo install -m 0644 "$TMP_KEYRING" "$KEYRING_PATH"
-
-# Remove stale pixie entries to avoid duplicate/broken sources.
-sudo rm -f /etc/apt/sources.list.d/pixie-local.list
-
-echo "deb [signed-by=$KEYRING_PATH] $REPO_URL $DIST $COMPONENT" | sudo tee "$LIST_PATH" >/dev/null
-
-sudo apt update
-sudo apt install -y pixie
-
-if command -v systemctl >/dev/null 2>&1; then
-  sudo systemctl enable --now pixie
-fi
-
-cat <<EOF
-Pixie installed.
-- Service status: sudo systemctl status pixie
-- Follow logs:   pixie log -f
-- Upgrade later: sudo apt update && sudo apt install --only-upgrade pixie
-EOF
diff -ruN '--exclude=debian' '--exclude=.git' '--exclude=.pc' '--exclude=.codex' '--exclude=target' Pixie-1.1.0/scripts/apt/publish-repo.sh new/scripts/apt/publish-repo.sh
--- Pixie-1.1.0/scripts/apt/publish-repo.sh	2026-04-10 23:11:11.000000000 +0200
+++ new/scripts/apt/publish-repo.sh	1970-01-01 01:00:00.000000000 +0100
@@ -1,88 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-
-ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
-REPO_DIR="${REPO_DIR:-$ROOT_DIR/apt-repo}"
-DIST="${DIST:-bookworm}"
-COMPONENT="${COMPONENT:-main}"
-ARCH="${ARCH:-amd64}"
-GPG_KEY_ID="${GPG_KEY_ID:-}"
-BUILD_DEB="${BUILD_DEB:-1}"
-
-if ! command -v dpkg-buildpackage >/dev/null 2>&1; then
-  echo "dpkg-buildpackage is required. Install devscripts/dpkg-dev." >&2
-  exit 1
-fi
-
-if ! command -v reprepro >/dev/null 2>&1; then
-  echo "reprepro is required. Install it with: sudo apt install reprepro" >&2
-  exit 1
-fi
-
-if [[ "$BUILD_DEB" == "1" ]]; then
-  (
-    cd "$ROOT_DIR"
-    dpkg-buildpackage -us -uc -b
-  )
-fi
-
-DEB_FILE="$(ls -t "$ROOT_DIR"/../pixie_*_"$ARCH".deb 2>/dev/null | head -n1 || true)"
-if [[ -z "$DEB_FILE" ]]; then
-  echo "No .deb package found for architecture '$ARCH' in $(dirname "$ROOT_DIR")." >&2
-  echo "Build one first with: dpkg-buildpackage -us -uc -b" >&2
-  exit 1
-fi
-
-mkdir -p "$REPO_DIR/conf"
-
-if [[ ! -f "$REPO_DIR/conf/distributions" ]]; then
-  {
-    echo "Origin: Pixie"
-    echo "Label: Pixie"
-    echo "Suite: stable"
-    echo "Codename: $DIST"
-    echo "Architectures: $ARCH"
-    echo "Components: $COMPONENT"
-    echo "Description: Pixie APT repository"
-    if [[ -n "$GPG_KEY_ID" ]]; then
-      echo "SignWith: $GPG_KEY_ID"
-    fi
-  } >"$REPO_DIR/conf/distributions"
-fi
-
-if [[ -n "$GPG_KEY_ID" ]]; then
-  if ! gpg --list-keys "$GPG_KEY_ID" >/dev/null 2>&1; then
-    echo "GPG key '$GPG_KEY_ID' not found in local keyring." >&2
-    exit 1
-  fi
-fi
-
-reprepro -b "$REPO_DIR" includedeb "$DIST" "$DEB_FILE"
-
-if [[ -n "$GPG_KEY_ID" ]]; then
-  mkdir -p "$REPO_DIR/keyrings"
-  TMP_ASC="$(mktemp)"
-  gpg --armor --export "$GPG_KEY_ID" >"$TMP_ASC"
-  cp "$TMP_ASC" "$REPO_DIR/keyrings/pixie-archive-keyring.asc"
-  gpg --dearmor --yes --output "$REPO_DIR/keyrings/pixie-archive-keyring.gpg" "$TMP_ASC"
-  rm -f "$TMP_ASC"
-fi
-
-if [[ "$REPO_DIR" == /home/* ]]; then
-  cat <<EOF
-Warning:
-  REPO_DIR is under /home ($REPO_DIR).
-  apt may show '_apt permission denied' warnings with file: repositories under /home.
-  Prefer hosting over HTTPS or moving the repo under /srv (example: /srv/pixie-apt).
-EOF
-fi
-
-cat <<EOF
-Repository updated in: $REPO_DIR
-Package added: $DEB_FILE
-
-Next steps:
-1. Host '$REPO_DIR' over HTTPS (recommended), or use file:$REPO_DIR locally.
-2. On client machine:
-   ./scripts/apt/configure-client.sh <REPO_URL> $DIST $COMPONENT
-EOF
diff -ruN '--exclude=debian' '--exclude=.git' '--exclude=.pc' '--exclude=.codex' '--exclude=target' Pixie-1.1.0/wikidoc/code_fonctionnement.md new/wikidoc/code_fonctionnement.md
--- Pixie-1.1.0/wikidoc/code_fonctionnement.md	1970-01-01 01:00:00.000000000 +0100
+++ new/wikidoc/code_fonctionnement.md	2026-04-21 08:24:54.000000000 +0200
@@ -0,0 +1,116 @@
+# Fonctionnement Du Code
+
+## Vue D'ensemble
+
+Pixie est un serveur HTTP statique en Rust base sur:
+
+- resolution de configuration YAML
+- ecoute TCP
+- routing de pages statiques HTML
+- thread pool pour traiter les connexions
+
+## Point D'entree
+
+Fichier: `/pixie/src/main.rs`
+
+Flux:
+
+1. charge la config runtime via `runtime_config()`
+2. lance le serveur via `run_server(addr, workers)`
+
+## Modules Principaux
+
+### Configuration
+
+Fichier: `/pixie/src/config.rs`
+
+Responsabilites:
+
+- lecture YAML depuis `PIXIE_CONFIG`, puis `./config-pixie.yml`, puis `/etc/pixie/config-pixie.yml`
+- fallback sur valeurs hardcodees (`127.0.0.1:80`, `workers=4`)
+- prise en charge des cles `addr`, `host`, `port`, `workers`, alias `address` et `nb_worker`
+
+### Serveur
+
+Fichier: `/pixie/src/server.rs`
+
+Responsabilites:
+
+- bind TCP sur l'adresse de config
+- creation du pool de workers
+- accept des connexions entrantes
+- lecture de la premiere ligne HTTP et construction de la reponse
+
+### Router
+
+Fichier: `/pixie/src/router.rs`
+
+Responsabilites:
+
+- resolution du repertoire web (`PIXIE_WEB_ROOT`, fallback local/dev/system)
+- mapping route -> fichier HTML (`/` -> `hello.html`, sinon `<route>.html`)
+- retour 404 avec `404.html` si la page n'existe pas
+
+### Thread Pool
+
+Fichiers:
+
+- `/pixie/src/threadpool/pool.rs`
+- `/pixie/src/threadpool/worker.rs`
+- `/pixie/src/threadpool/job.rs`
+
+Responsabilites:
+
+- file de jobs via canal mpsc
+- workers qui executent les connexions en parallele
+- fermeture propre des threads au drop
+
+### Logs
+
+Fichier: `/pixie/src/logger.rs`
+
+Responsabilites:
+
+- sorties `info`, `warn`, `error` standardisees
+
+## Flux D'une Requete
+
+1. un client ouvre une connexion TCP
+2. `run_server` soumet la connexion au thread pool
+3. `handle_connection` lit la request line
+4. `resolve_route` choisit le fichier HTML
+5. le serveur renvoie une reponse HTTP avec status + contenu
+
+## Tests
+
+Tous les tests sont regroupes dans:
+
+- `/pixie/tests/`
+
+Suites presentes:
+
+- `config_tests.rs`
+- `router_tests.rs`
+- `server_tests.rs`
+- `threadpool_tests.rs`
+- `logger_tests.rs`
+
+## Couverture
+
+Commande recommandee (instrumentation LLVM):
+
+```bash
+cd pixie
+cargo clean
+RUSTFLAGS='-Cinstrument-coverage' LLVM_PROFILE_FILE='pixie-%p-%m.profraw' cargo test
+TOOLBIN="$(rustc --print sysroot)/lib/rustlib/x86_64-unknown-linux-gnu/bin"
+"$TOOLBIN/llvm-profdata" merge -sparse pixie-*.profraw -o pixie.profdata
+"$TOOLBIN/llvm-cov" report \
+  --object target/debug/deps/config_tests-* \
+  --object target/debug/deps/threadpool_tests-* \
+  --object target/debug/deps/router_tests-* \
+  --object target/debug/deps/server_tests-* \
+  --object target/debug/deps/logger_tests-* \
+  --instr-profile pixie.profdata \
+  --ignore-filename-regex '/\\.cargo/registry|/tests/'
+```
diff -ruN '--exclude=debian' '--exclude=.git' '--exclude=.pc' '--exclude=.codex' '--exclude=target' Pixie-1.1.0/wikidoc/index.md new/wikidoc/index.md
--- Pixie-1.1.0/wikidoc/index.md	1970-01-01 01:00:00.000000000 +0100
+++ new/wikidoc/index.md	2026-04-14 15:38:48.641050395 +0200
@@ -0,0 +1,8 @@
+# Documentation Pixie
+
+- Installation Debian / Ubuntu: [install_debian.md](install_debian.md)
+- Installation Docker (CLI et Compose): [install_docker.md](install_docker.md)
+- Installation Arch: [install_arch.md](install_arch.md)
+- Publication Debian (procedure complete): [pixie_publish.md](pixie_publish.md)
+- Mise a jour rapide de version: [pixie_update.md](pixie_update.md)
+- Fonctionnement du code: [code_fonctionnement.md](code_fonctionnement.md)
diff -ruN '--exclude=debian' '--exclude=.git' '--exclude=.pc' '--exclude=.codex' '--exclude=target' Pixie-1.1.0/wikidoc/install_arch.md new/wikidoc/install_arch.md
--- Pixie-1.1.0/wikidoc/install_arch.md	1970-01-01 01:00:00.000000000 +0100
+++ new/wikidoc/install_arch.md	2026-04-14 11:43:50.793609174 +0200
@@ -0,0 +1,29 @@
+# Installation Arch Linux
+
+## Installation Utilisateur (AUR)
+
+Avec helper AUR:
+
+```bash
+yay -S pixie-git
+```
+
+Le service est gere automatiquement par le systeme d'installation.
+
+## Configuration
+
+Fichier principal:
+
+`/etc/pixie/config-pixie.yml`
+
+Exemple expose reseau:
+
+```yml
+addr: 0.0.0.0:8080
+workers: 4
+```
+
+Note:
+
+- `pixie-git` est le nom du paquet AUR.
+- Le binaire installe s'appelle `pixie`.
diff -ruN '--exclude=debian' '--exclude=.git' '--exclude=.pc' '--exclude=.codex' '--exclude=target' Pixie-1.1.0/wikidoc/install_debian.md new/wikidoc/install_debian.md
--- Pixie-1.1.0/wikidoc/install_debian.md	1970-01-01 01:00:00.000000000 +0100
+++ new/wikidoc/install_debian.md	2026-04-21 08:24:54.000000000 +0200
@@ -0,0 +1,43 @@
+# Installation Debian / Ubuntu
+
+## Etat actuel
+
+La publication officielle Debian est en cours (`ITP #1133770`, `RFS #1133771`).
+
+Tant que le paquet n est pas accepte dans les depots de la distribution cible,
+la commande `apt install pixie` ne fonctionnera pas partout par defaut.
+
+## Installation officielle (cible)
+
+Quand `pixie` est present dans les depots de ta distribution:
+
+```bash
+sudo apt update
+sudo apt install -y pixie
+```
+
+## Installation de test (avant disponibilite officielle)
+
+Depuis mentors.debian.net:
+
+```bash
+sudo apt install -y devscripts
+cd /tmp
+dget -x https://mentors.debian.net/debian/pool/main/p/pixie/pixie_1.1.0-3.dsc
+cd pixie-1.1.0
+dpkg-buildpackage -us -uc -b
+sudo apt install ../pixie_1.1.0-3_amd64.deb
+```
+
+## Configuration
+
+Fichier principal:
+
+`/etc/pixie/config-pixie.yml`
+
+Exemple:
+
+```yml
+addr: 0.0.0.0:8080
+workers: 4
+```
diff -ruN '--exclude=debian' '--exclude=.git' '--exclude=.pc' '--exclude=.codex' '--exclude=target' Pixie-1.1.0/wikidoc/install_docker.md new/wikidoc/install_docker.md
--- Pixie-1.1.0/wikidoc/install_docker.md	1970-01-01 01:00:00.000000000 +0100
+++ new/wikidoc/install_docker.md	2026-04-14 11:43:50.793609174 +0200
@@ -0,0 +1,57 @@
+# Installation Docker
+
+## Option 1 - Docker CLI
+
+Pull de l'image:
+
+```bash
+docker pull ghcr.io/laflut3/pixie:latest
+```
+
+Execution simple:
+
+```bash
+docker run --rm -p 8080:8080 ghcr.io/laflut3/pixie:latest
+```
+
+Execution avec fichier de configuration local:
+
+```bash
+docker run --rm -p 8080:8080 \
+  -v "$(pwd)/config-pixie.yml:/etc/pixie/config-pixie.yml:ro" \
+  ghcr.io/laflut3/pixie:latest
+```
+
+## Option 2 - Docker Compose
+
+Exemple `compose.yml`:
+
+```yaml
+services:
+  pixie:
+    image: ghcr.io/laflut3/pixie:latest
+    container_name: pixie
+    ports:
+      - "8080:8080"
+    volumes:
+      - ./config-pixie.yml:/etc/pixie/config-pixie.yml:ro
+    restart: unless-stopped
+```
+
+Lancer:
+
+```bash
+docker compose up -d
+```
+
+Arreter:
+
+```bash
+docker compose down
+```
+
+## Verification
+
+```bash
+curl http://localhost:8080/hello
+```
diff -ruN '--exclude=debian' '--exclude=.git' '--exclude=.pc' '--exclude=.codex' '--exclude=target' Pixie-1.1.0/wikidoc/pixie_publish.md new/wikidoc/pixie_publish.md
--- Pixie-1.1.0/wikidoc/pixie_publish.md	1970-01-01 01:00:00.000000000 +0100
+++ new/wikidoc/pixie_publish.md	2026-04-21 08:24:54.000000000 +0200
@@ -0,0 +1,74 @@
+# Publication Debian De Pixie
+
+## Objectif
+
+Publier `pixie` dans Debian via le workflow officiel:
+
+- ITP (WNPP)
+- upload mentors
+- RFS (sponsorship-requests)
+- sponsoring DD
+
+## Prerequis
+
+- Compte mentors.debian.net
+- Cle GPG locale (publique importee sur mentors)
+- Outils Debian:
+
+```bash
+sudo apt install -y reportbug devscripts dput-ng lintian
+```
+
+## Procedure
+
+1. Creer l ITP:
+
+```bash
+reportbug wnpp
+```
+
+Type: `ITP`
+
+2. Preparer le paquet source Debian:
+
+- format source non-native (`3.0 (quilt)`)
+- metadata Debian propres (`debian/control`, `debian/copyright`)
+
+3. Construire le source upload:
+
+```bash
+debuild -S -sa
+```
+
+4. Signer:
+
+```bash
+debsign -k<KEYID> ../pixie_<VERSION>_source.changes
+```
+
+5. Uploader sur mentors:
+
+```bash
+dput mentors ../pixie_<VERSION>_source.changes
+```
+
+6. Creer/mettre a jour le RFS:
+
+```bash
+reportbug sponsorship-requests
+```
+
+Puis suivre le bug RFS (ex: `#1133771`) avec le lien `dget -x` mentors.
+
+## Actions effectuees pour Pixie
+
+- ITP: `#1133770`
+- RFS: `#1133771`
+- Upload mentors:
+  `https://mentors.debian.net/debian/pool/main/p/pixie/pixie_1.1.0-3.dsc`
+
+## Notes importantes
+
+- Tant qu un DD ne sponsorise pas et que le paquet n est pas accepte,
+  `apt install pixie` global n est pas encore garanti.
+- Une fois accepte dans Debian, l installation devient standard via APT.
diff -ruN '--exclude=debian' '--exclude=.git' '--exclude=.pc' '--exclude=.codex' '--exclude=target' Pixie-1.1.0/wikidoc/pixie_update.md new/wikidoc/pixie_update.md
--- Pixie-1.1.0/wikidoc/pixie_update.md	1970-01-01 01:00:00.000000000 +0100
+++ new/wikidoc/pixie_update.md	2026-04-14 15:38:36.252603927 +0200
@@ -0,0 +1,48 @@
+# Mise A Jour Rapide De Pixie (Debian)
+
+## But
+
+Mettre a jour rapidement `pixie` vers une nouvelle version Debian.
+
+## Etapes minimales
+
+1. Mettre a jour la version applicative:
+
+- `pixie/Cargo.toml`
+
+2. Ajouter une entree changelog Debian:
+
+```bash
+dch -i
+```
+
+3. Construire le source package:
+
+```bash
+debuild -S -sa
+```
+
+4. Signer:
+
+```bash
+debsign -k<KEYID> ../pixie_<VERSION>_source.changes
+```
+
+5. Upload mentors:
+
+```bash
+dput mentors ../pixie_<VERSION>_source.changes
+```
+
+6. Mettre a jour le bug RFS (`#1133771`) avec:
+
+- nouvelle URL `dget -x`
+- resume des changements
+
+## Checklist ultra-courte
+
+- [ ] version code ok
+- [ ] `debian/changelog` incrementee
+- [ ] `debuild -S -sa` passe
+- [ ] `dput mentors` passe
+- [ ] follow-up poste sur `#1133771`
