Files
Mathieu LimonierandClaude Opus 5 6a6d745af4 MaBibli 1.0.0
Gestion de bibliothèque personnelle auto-hébergée : catalogue, prêts,
scan de code-barres, consultation hors-ligne.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-22 22:36:16 +02:00

17 KiB

Installer MaBibli

Deux voies. Seule la première apporte une authentification — voir l'avertissement de la section « Ailleurs que sur YunoHost ».

Le paquet YunoHost · Contraintes ce qu'il contient, ce qui ne se négocie pas
Première mise en production pas à pas, sur un serveur neuf
Désinstaller avec ou sans les données
Ailleurs que sur YunoHost Synology, NAS, VPS — sans authentification

Pour monter de version, voir publier-une-version.md. Pour comprendre le code, voir architecture.md.

Le paquet YunoHost

MaBibli se déploie via un second dépôt, mabibli_ynh, qui ne contient aucun code C# : il porte le manifeste, les configurations nginx et systemd, et les scripts d'installation. Toute la documentation, elle, vit ici — ce fichier est le point d'entrée unique du projet.

Contenu

Fichier Rôle
manifest.toml Identité, version, URL de l'archive et son sha256, ressources (utilisateur système, répertoires, port, permissions)
conf/systemd.service Unité du service — écoute sur 127.0.0.1, base dans le répertoire de données, durcissement
conf/nginx.conf Reverse proxy et intégration SSOwat
scripts/_common.sh Variables partagées et sauvegarde/restauration cohérente de la base SQLite
scripts/install remove upgrade backup restore Cycle de vie de l'application
build/publier.sh Seul script de publication : version, une archive par architecture, manifeste, commit et push du paquet, tag et push du code. --archive-seule compile et archive sans rien publier, pour reprendre après un échec
doc/DESCRIPTION.md doc/ADMIN.md Textes affichés par YunoHost lui-même (catalogue et interface d'administration) — ils doivent rester dans ce dépôt

Points de conception

Le service n'écoute que sur 127.0.0.1. L'application déduit l'identité de l'en-tête YNH_USER injecté par SSOwat ; cet en-tête n'est digne de confiance que si nginx est le seul chemin d'accès. Un service exposé sur le réseau permettrait de forger YNH_USER et de contourner le portail. La contrainte est écrite dans mabibli_ynh/conf/systemd.service (ASPNETCORE_URLS) et le port n'est pas ouvert au pare-feu.

Le packaging s'inspire de radarr_ynh, autre application .NET packagée sans Docker : c'est de là que vient le déploiement self-contained, qui évite d'installer dotnet-runtime côté système.

Aucune compilation sur le serveur. Le publish est self-contained : il embarque son propre runtime .NET, donc aucun paquet dotnet-runtime n'est nécessaire. ynh_setup_source vérifie le sha256 de l'archive avant de la déployer.

Les données survivent aux mises à jour. La base SQLite vit dans le répertoire de données, pas à côté du binaire ; upgrade ne remplace que le répertoire d'installation.

La sauvegarde passe par l'API de sauvegarde en ligne de SQLite. La base est en mode WAL : copier le seul fichier .db d'une base active peut ne rien sauvegarder du tout. Voir mabibli_ynh/doc/ADMIN.md.

Contraintes permanentes du paquet — ce ne sont pas des tâches

Le dépôt du code doit rester public

ynh_setup_source télécharge sans jeton. Sur un dépôt privé, Gitea répond 404 et non 403 à un anonyme : le symptôme est identique à « la release n'existe pas », ce qui envoie chercher au mauvais endroit. Ne pas mettre de jeton dans l'URL du manifeste — il serait lisible sur le serveur.

L'application exige un domaine entier, pas un sous-chemin

MaBibli s'installe sur mabibli.mondomaine.tld, pas sur mondomaine.tld/mabibli.

Deux éléments sont figés à la compilation du client Blazor WebAssembly : la balise <base href="/"> de index.html, et les empreintes d'intégrité de service-worker-assets.js. Les réécrire sur le serveur casserait le service worker, donc le mode hors-ligne — et rien n'est recompilé sur le serveur, c'est tout l'intérêt du self-contained. Le paquet déclare donc l'application en full_domain.

Lever cette contrainte demanderait une archive par chemin d'installation, ou une compilation sur le serveur. Les deux annulent le bénéfice du self-contained.

install_dir finit par appartenir à l'application, pas à root

Le manifeste demande owner = "root:rwx" pour que le service ne puisse pas réécrire ses binaires, mais le helper _ynh_apply_default_permissions repasse derrière avec un chown -R mabibli:mabibli. L'intention tient quand même, portée par ProtectSystem=strict dans l'unité systemd : tout est en lecture seule sauf ReadWritePaths=, qui ne liste que le data_dir.

⚠️ Ne pas retirer ProtectSystem=strict en croyant que la propriété des fichiers protège encore.


Mettre en production

La première mise en production, pas à pas. Pour la montée de version, voir « Monter de version » dans publier-une-version.md. publier.sh enchaîne les étapes courantes ; ce qui suit les détaille une à une, pour les comprendre et pour les cas qu'il ne couvre pas.

machine de dev                    Gitea                        serveur YunoHost
──────────────                    ─────                        ────────────────
publier.sh
  → archive .tar.gz   ────────►  release du dépôt `mabibli`
  → manifest.toml     ────────►  dépôt `mabibli_ynh`  ──────►  yunohost app install
     (url + sha256)                                            (vérifie le sha256)

Première mise en production

1. Préalables, une seule fois

  • Le dépôt du code (mabibli) est public sur Gitea. ynh_setup_source télécharge sans jeton ; sur un dépôt privé Gitea répond 404 — indiscernable d'une release absente.
  • Le domaine dédié existe côté YunoHost. MaBibli s'installe sur un domaine entier (mabibli.mondomaine.tld), jamais sur un sous-chemin :
sudo yunohost domain add mabibli.mondomaine.tld
  • Le SDK .NET est installé sur la machine de développement (dotnet --version).

2. Produire l'archive, le manifeste et le tag

Depuis mabibli_ynh, avec le dépôt du code à côté (../mabibli) :

./build/publier.sh --version 1.0.0

⚠️ C'est le seul cas où --version s'impose. Le script propose normalement d'incrémenter la version précédente, qu'il lit dans les tags ou dans manifest.toml ; à la toute première publication il n'y en a aucune, et il refuse plutôt que de deviner. Ensuite, ./build/publier.sh sans option suffit.

Le script compile en Release self-contained, vérifie le publish (binaire présent, wwwroot/ embarqué, toutes les ressources d'index.html réellement sur disque, aucun placeholder d'empreinte non substitué), produit l'archive de façon reproductible, en calcule le sha256, réécrit version, amd64.url et amd64.sha256 dans manifest.toml, commite et pousse le paquet, puis tague et pousse le code.

À contrôler dans sa sortie : la ligne binaire, wwwroot et ressources d'index.html : OK, le sha256 affiché — c'est celui que le manifeste porte désormais — et les deux de la vérification côté distant.

Il ne reste alors que l'étape 3. Les étapes 4 et suivantes décrivent ce que le script vient de faire, pour le cas où l'on reprend à la main après un échec en cours de route (--archive-seule s'arrête après l'archive, sans toucher au manifeste ni à git).

3. Déposer la release

Le tag v1.0.0 est déjà poussé par l'étape 2 (git tag v1.0.0 && git push origin main --tags si l'on reprend à la main).

Dans l'interface Gitea : créer la release v1.0.0 sur le dépôt mabibli, et y téléverser les deux archives :

build/dist/mabibli-1.0.0-linux-x64.tar.gz
build/dist/mabibli-1.0.0-linux-arm64.tar.gz

⚠️ Les deux, sans exception. Le manifeste déclare architectures = ["amd64", "arm64"] et YunoHost choisit la ligne correspondant à la machine : une archive manquante n'échoue pas ici, elle échoue chez celui qui installe, dans ynh_setup_source.

Vérification, à faire sans être authentifié (autre navigateur, ou curl comme ci-dessous) :

curl -fsSLI "https://forge.example.org/mabibli/releases/download/v1.0.0/mabibli-1.0.0-linux-x64.tar.gz" | head -1
curl -fsSLI "https://forge.example.org/mabibli/releases/download/v1.0.0/mabibli-1.0.0-linux-arm64.tar.gz" | head -1

Attendu : HTTP/2 200. Un 404 signifie soit que la release n'est pas déposée, soit que le dépôt est privé — les deux se ressemblent, commencer par vérifier la visibilité.

4. Le paquet, sur Gitea

⚠️ L'étape la plus facile à oublier quand elle est manuelle yunohost app install <url> lit le manifeste depuis Gitea, jamais la copie locale : un manifeste corrigé mais non poussé n'existe pas pour le serveur. L'étape 2 s'en charge et vérifie l'arrivée par git ls-remote ; en reprise à la main :

cd ../mabibli_ynh && git add -A && git commit -m "Publier la version 1.0.0" && git push

Le contrôle qui tranche, quel que soit le chemin suivi — c'est le manifeste tel que le serveur le lira :

curl -fsSL "https://forge.example.org/mabibli_ynh/raw/branch/main/manifest.toml" | grep amd64

5. Installer

sudo yunohost app install https://forge.example.org/mabibli_ynh --debug

YunoHost demande le domaine (celui créé à l'étape 1) et le groupe autorisé (all_users).

Ce qu'il faut voir passer : Prefetching asset main sur la bonne URL, puis l'installation des fichiers, nginx, systemd, et enfin The service mabibli has correctly executed the action start — le script attend la ligne Application started du journal, ce qui fait échouer franchement l'installation si les migrations EF Core ne passent pas.

6. Vérifier

sudo ss -tlnp | grep -i mabibli

Doit montrer 127.0.0.1:<port> uniquement. C'est une frontière de sécurité : sur 0.0.0.0, n'importe qui sur le réseau pourrait forger l'en-tête YNH_USER et se faire passer pour un membre du foyer.

sudo ls -l /home/yunohost.app/mabibli

mabibli.db doit exister : les migrations se sont appliquées seules au premier démarrage. (Pas de -wal ni -shm au repos, c'est normal — SQLite fait un checkpoint à la fermeture de la dernière connexion.)

Enfin, dans un navigateur sur https://mabibli.mondomaine.tld : le portail authentifie, et l'application affiche ton nom d'utilisateur YunoHost — pas « anonyme ». C'est le seul contrôle qui éprouve réellement l'intégration SSO.


Désinstaller

sudo yunohost app remove mabibli

Retire le service, la conf nginx, la permission SSO, l'utilisateur système, le port et /var/www/mabibli. /home/yunohost.app/mabibli survit, donc la bibliothèque aussi : une désinstallation faite trop vite ne doit pas être irréversible.

⚠️ Corollaire à connaître en phase d'essai : réinstaller après un remove sans purge retrouve l'ancienne base. Ce n'est pas une installation vierge, même si tout le reste est neuf.

Pour tout effacer, données comprises :

sudo yunohost app remove mabibli --purge

Contrôle qu'il ne reste rien :

systemctl status mabibli; sudo ls -d /var/www/mabibli /home/yunohost.app/mabibli 2>&1; getent passwd mabibli

Les trois doivent être négatifs. Le domaine, lui, reste déclaré dans YunoHost.


Installer ailleurs que sur YunoHost (Synology, NAS, VPS)

Le déploiement de référence reste le paquet mabibli_ynh : c'est lui qui est éprouvé, et c'est le seul qui apporte une authentification. Ce qui suit sert aux hébergements qui n'ont pas de portail SSO. Dockerfile, .dockerignore et compose.yaml, à la racine de ce dépôt, existent pour ça.

L'application n'est qu'un processus et un fichier SQLite : MaBibli.Api sert lui-même le client Blazor. Il n'y a donc rien à orchestrer.

⚠️ Hors YunoHost, il n'y a AUCUNE authentification

C'est le point à comprendre avant tout le reste, et il ne se contourne pas par la configuration. FournisseurUtilisateurSsowat lit l'en-tête YNH_USER que le portail injecte ; sans portail, il n'y a pas d'identité, et l'application ne s'en invente pas.

Conséquence exacte, vérifiée en exécution :

Sans identité
Catalogue, auteurs, séries, revues, prêts (communs au foyer) fonctionnent
Statuts de lecture, listes d'envies (personnels) refusés — 400 « Impossible d'ajouter une envie sans savoir à qui elle appartient. »

Deux façons de rendre une identité, aucune des deux n'authentifie qui que ce soit :

  • Identite__UtilisateurSimule en variable d'environnement. C'est le mécanisme prévu pour le développement, mais c'est de la configuration ordinaire : elle vaut aussi en production. Mono-utilisateur.
  • l'en-tête YNH_USER injecté par le proxy inversé (DSM : Portail des applications > Proxy inversé > En-tête personnalisé). Même niveau de sécurité, mais si un proxy authentifiant est ajouté un jour (Authelia, authentik), il n'y a plus qu'à lui faire recopier son Remote-User vers YNH_USER : l'application n'a rien à changer.

⚠️ Dans les deux cas, quiconque atteint le port EST cet utilisateur. Le port ne doit donc être publié que sur la boucle locale (127.0.0.1:8080:8080), le proxy inversé restant le seul chemin d'accès. C'est la transposition exacte de la contrainte d'écoute que le paquet YunoHost pose comme dure.

Trois prérequis qui ne se négocient pas

  • Une machine x86_64 ou arm64. Contrairement au paquet YunoHost, cette voie ne télécharge aucune archive : le Dockerfile compile depuis les sources, pour l'architecture de l'hôte, sur des images .NET qui existent pour les deux. ⚠️ Rien n'a été essayé sur un NAS ARM réel — ni Container Manager, ni le proxy inversé de DSM.
  • HTTPS, sans quoi le scan du code-barres ne s'ouvrira jamais : la caméra exige un contexte sécurisé. Sur Synology, cela veut dire proxy inversé + certificat Let's Encrypt sur un nom DDNS (mabibli.xxx.synology.me).
  • Un nom d'hôte entier, pas un sous-cheminmabibli.xxx.synology.me, jamais nas.xxx.synology.me/mabibli. La raison est la même que pour le full_domain de YunoHost, et elle est développée plus bas : <base href="/"> et les empreintes du service worker sont figés à la compilation.

La marche à suivre

git clone <ce dépôt> mabibli && cd mabibli
# éditer compose.yaml : nom d'utilisateur, chemin du volume
docker compose up -d --build

Sur Synology, le même compose.yaml se colle dans Container Manager > Projet, en remplaçant ./donnees par un chemin réel (/volume1/docker/mabibli).

Les migrations EF Core s'appliquent seules au premier démarrage : la base se crée dans le volume, et c'est le volume seul qui la fait survivre au remplacement de l'image.

Ce qu'il faut savoir avant de s'y mettre

  • La sauvegarde ne se fait pas en copiant le .db. SQLite tourne en mode WAL : copier le seul fichier d'une base active peut ne rien sauvegarder du tout. Il faut arrêter le conteneur, ou passer par sqlite3 … ".backup" — c'est ce que fait le paquet YunoHost, et la raison est écrite dans mabibli_ynh/doc/ADMIN.md.
  • Il n'y a pas de mécanisme de mise à jour. Ni yunohost app upgrade, ni release, ni vérification de sha256 : à chaque version, git pull puis docker compose up -d --build. Les données ne bougent pas, elles sont dans le volume.
  • L'image est construite depuis les sources, pas depuis une release. Elle ne porte donc aucun numéro de version, et « À propos » affiche honnêtement « version de développement ». Pour un vrai numéro, passer -p:Version=X.Y.Z -p:MaBibliDateBuild=… au dotnet publish du Dockerfile.

Vérifié en exécution le 2026-08-21

Image construite et démarrée localement, base neuve dans un volume monté :

Cas Résultat
Migrations au premier démarrage appliquées, mabibli.db créé dans le volume
GET / et _framework/blazor.webassembly.js 200, <base href="/"> intact
GET /api/moi avec Identite__UtilisateurSimule {"identifiant":"prenom","simule":true}
GET /api/moi sans rien {"identifiant":null,"affichage":"inconnu"}
GET /api/moi avec en-tête YNH_USER: camille {"identifiant":"camille","simule":false}
POST /api/souhaits sans identité 400, message lisible

⚠️ Ce qui n'a PAS été vérifié : l'installation sur un Synology réel, le proxy inversé de DSM et le certificat. Ce sont des gestes de DSM, pas du code — mais personne ne les a éprouvés ici.