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>
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_sourceté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__UtilisateurSimuleen 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_USERinjecté 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 sonRemote-UserversYNH_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
Dockerfilecompile 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-chemin —
mabibli.xxx.synology.me, jamaisnas.xxx.synology.me/mabibli. La raison est la même que pour lefull_domainde 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 parsqlite3 … ".backup"— c'est ce que fait le paquet YunoHost, et la raison est écrite dansmabibli_ynh/doc/ADMIN.md. - Il n'y a pas de mécanisme de mise à jour. Ni
yunohost app upgrade, ni release, ni vérification desha256: à chaque version,git pullpuisdocker 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=…audotnet publishduDockerfile.
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.