Le manifeste de la 0.5.0, deja publiee, s'est mis a pointer une archive
introuvable — l'installation echouait sur ynh_setup_source, sans que le
symptome designe la cause.
Le remplacement global des URL avait reecrit amd64.url, c'est-a-dire
precisement le champ que la documentation ecrite le meme jour designait comme
« a ne jamais changer a la main », puisque publier.sh le regenere a chaque
publication.
⚠ L'erreur de conception est plus large : le placeholder avait sa place dans la
COPIE publique, pas dans le depot de travail, qui sert au deploiement reel et
doit rester operationnel. Anonymiser un depot qui sert encore, c'est le casser.
Ce qui reste, et qui vaut : depot_code commande toujours toutes les URL
derivees, et le garde-fou refuse toujours de publier avec une URL en
example.org — il ne se declenche simplement plus ici, puisqu'il sert a la copie
publique.
sha256 inchange : seule l'URL avait bouge, le manifeste retrouve exactement son
etat de publication.
--- L'installation ne demande plus qui a acces
init_main_permission quitte [install] au profit de
resources.permissions.main.allowed = "all_users". Verifie dans le coeur
YunoHost (src/utils/resources.py) :
init_allowed = infos["allowed"] or self.get_setting(f"init_{perm}_permission") or []
allowed L'EMPORTE sur la question. La garder l'aurait donc posee pour rien, en
laissant croire que la reponse comptait.
⚠ Ce n'est pas un reglage de confort : visitors casserait deux invariants du
code. L'application n'a aucune authentification propre — elle fait confiance a
YNH_USER parce que SSOwat l'ecrase a chaque requete, exactement comme elle
n'ecoute que sur 127.0.0.1 — et toute personne autorisee peut SUPPRIMER
n'importe quel livre du catalogue commun, sans role ni lecture seule.
⚠ Pas de protected = true, delibere : restreindre a un groupe dedie reste
legitime, et c'est l'affaire de l'administrateur. C'est l'elargissement qui est
un contresens, pas le reglage.
⚠ allowed n'agit qu'a la CREATION de la permission (if perm not in
existing_perms) : une montee de version ne realigne rien. Un commentaire de
manifeste n'atteignant aucun administrateur, l'avertissement vit dans
doc/DESCRIPTION.md (page d'installation) et doc/ADMIN.md (interface
d'administration) — les deux seuls fichiers du paquet que YunoHost lit lui-meme.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
175 lines
8.6 KiB
TOML
175 lines
8.6 KiB
TOML
#:schema https://raw.githubusercontent.com/YunoHost/apps/master/schemas/manifest.v2.schema.json
|
|
|
|
packaging_format = 2
|
|
|
|
id = "mabibli"
|
|
name = "MaBibli"
|
|
description.fr = "Gestion de bibliothèque personnelle : catalogue, prêts et scan ISBN"
|
|
description.en = "Personal library manager: catalogue, loans and ISBN scanning"
|
|
|
|
version = "0.5.0~ynh1"
|
|
|
|
maintainers = ["mathieu"]
|
|
|
|
[upstream]
|
|
license = "AGPL-3.0-or-later"
|
|
code = "https://git.akbar.nohost.me/mathieu/mabibli"
|
|
|
|
[integration]
|
|
yunohost = ">= 12.1.38"
|
|
helpers_version = "2.1"
|
|
|
|
# x86_64 uniquement : CLAUDE.md acte un serveur PC/VPS, et la chaîne de publication
|
|
# ne produit qu'un binaire `linux-x64`. Déclarer d'autres architectures promettrait
|
|
# des archives qui n'existent pas.
|
|
architectures = ["amd64"]
|
|
|
|
# Une seule instance : la bibliothèque est commune au foyer (CLAUDE.md, « Portée des
|
|
# données »). Deux instances voudraient dire deux collections étanches, ce qui est
|
|
# exactement ce que le projet a écarté.
|
|
multi_instance = false
|
|
|
|
# L'application ne parle pas à LDAP : elle lit l'identité dans les en-têtes que
|
|
# SSOwat injecte. C'est bien une intégration SSO, mais pas une intégration LDAP.
|
|
ldap = false
|
|
sso = true
|
|
|
|
# ~170 Mo de publish self-contained (runtime .NET embarqué), plus la base et la marge
|
|
# de décompression de l'archive.
|
|
disk = "500M"
|
|
|
|
# ⚠️ `ram.build` ne désigne AUCUNE compilation : rien n'est compilé sur le serveur, c'est
|
|
# tout l'intérêt du self-contained. Il couvre le téléchargement et la décompression de
|
|
# l'archive de 69 Mo. Ne pas aller chercher la valeur du côté d'un build .NET.
|
|
ram.build = "50M"
|
|
|
|
# Mesuré sur le serveur le 2026-08-22, service en marche :
|
|
# cat /sys/fs/cgroup/system.slice/mabibli.service/memory.peak -> 208 793 600 o
|
|
# soit 199,1 Mio de pic, contre 200M déclarés jusqu'ici : 0,4 % de marge, c'est-à-dire
|
|
# aucune. Porté à 256M.
|
|
#
|
|
# ⚠️ `systemctl show -p MemoryPeak` ne renvoie RIEN sur ce serveur — la commande réussit
|
|
# en silence et l'on croit lire un pic là où l'on ne lit rien. Passer par le cgroup.
|
|
#
|
|
# ⚠️ Le chiffre MAJORE le besoin : `memory.peak` compte le cache de fichiers, donc une
|
|
# part des 69 Mo de binaire mappé, récupérable sous pression — et le GC de .NET se serre
|
|
# sur une machine étroite. Mais YunoHost compare une déclaration statique à la RAM
|
|
# disponible, sans faire cette nuance. L'asymétrie tranche : un refus d'installation est
|
|
# visible et se contourne, un OOM survient plus tard, sous une opération lourde, sans que
|
|
# rien ne désigne la cause.
|
|
#
|
|
# ⚠️ Le pic n'est PAS le lookup ISBN, contrairement à l'intuition — une notice. C'est
|
|
# « Nouveautés » sur un auteur très réédité : jusqu'à dix pages de 100 notices SRU en
|
|
# parallèle, plus le catalogue entier chargé pour le rapprochement. Ce cas-là n'a pas
|
|
# encore été mesuré, et il croît avec la taille de la bibliothèque : à reprendre le jour
|
|
# où le fonds aura beaucoup grossi.
|
|
ram.runtime = "256M"
|
|
|
|
[install]
|
|
|
|
[install.domain]
|
|
type = "domain"
|
|
|
|
# Pas de question `path` : MaBibli s'installe sur un domaine entier.
|
|
#
|
|
# Ce n'est pas un oubli. Le client Blazor WebAssembly fige son `<base href="/">`
|
|
# et les empreintes de `service-worker-assets.js` **à la compilation**. Servir
|
|
# l'application sous un sous-chemin demanderait de réécrire `index.html` sur le
|
|
# serveur, ce qui invaliderait les empreintes d'intégrité du service worker et
|
|
# casserait le mode hors-ligne. Comme le paquet ne compile rien sur le serveur,
|
|
# la seule option honnête est le domaine entier.
|
|
#
|
|
# Conséquence côté YunoHost : `resources.permissions.main.url` étant une chaîne,
|
|
# le cœur classe l'app en « full_domain » et refusera un changement d'URL vers
|
|
# un sous-chemin.
|
|
|
|
# Pas de question `init_main_permission` : l'accès est fixé à `all_users` par la
|
|
# ressource `permissions` ci-dessous, et l'installation ne demande donc rien.
|
|
#
|
|
# Vérifié dans le cœur YunoHost (`src/utils/resources.py`) :
|
|
#
|
|
# init_allowed = infos["allowed"] or self.get_setting(f"init_{perm}_permission") or []
|
|
#
|
|
# `allowed` l'emporte sur la question ; garder la question l'aurait donc posée pour
|
|
# rien, en laissant croire que la réponse comptait.
|
|
#
|
|
# ⚠️ Ce code ne s'exécute qu'à la CRÉATION de la permission. Une montée de version ne
|
|
# réaligne rien : un accès élargi à la main le reste.
|
|
|
|
[resources]
|
|
|
|
[resources.sources.main]
|
|
# Archive **déjà compilée** (publish self-contained linux-x64), produite par
|
|
# `build/publier.sh` et déposée en release sur le Gitea de l'utilisateur.
|
|
# Le SDK .NET n'est jamais installé sur le serveur : c'est tout l'intérêt du
|
|
# self-contained (CLAUDE.md, « Ne jamais compiler sur le serveur »).
|
|
#
|
|
# Ces deux valeurs sont réécrites par `build/publier.sh`.
|
|
amd64.url = "https://git.akbar.nohost.me/mathieu/mabibli/releases/download/v0.5.0/mabibli-0.5.0-linux-x64.tar.gz"
|
|
amd64.sha256 = "15fb2ee67fb8f3a77a4c4734229e74d2605231713e100da38972d4dcf29e225d"
|
|
|
|
# L'archive dépose ses fichiers à la racine, sans dossier intermédiaire.
|
|
in_subdir = false
|
|
|
|
# Pas d'`autoupdate` : les releases sont déposées à la main sur un Gitea privé,
|
|
# aucune stratégie amont connue de YunoHost ne s'y applique.
|
|
|
|
[resources.system_user]
|
|
|
|
[resources.install_dir]
|
|
# Le service tourne sous `mabibli` mais ne doit **pas** pouvoir réécrire ses
|
|
# propres binaires : propriétaire root, application en lecture seule. Cela va de
|
|
# pair avec `ProtectSystem=strict` dans l'unité systemd.
|
|
owner = "root:rwx"
|
|
group = "__APP__:rx"
|
|
|
|
[resources.data_dir]
|
|
# La base SQLite vit ici, et **pas** à côté du binaire : c'est ce qui permet à une
|
|
# mise à jour de remplacer intégralement `install_dir` sans toucher aux données.
|
|
owner = "__APP__:rwx"
|
|
group = "__APP__:rx"
|
|
|
|
[resources.permissions]
|
|
# Toute l'application derrière le SSO, `/api` compris : la bibliothèque est
|
|
# familiale, rien n'a vocation à être public. `auth_header` vaut `true` par
|
|
# défaut, c'est lui qui autorise SSOwat à injecter `YNH_USER` — sans quoi
|
|
# l'application ne saurait jamais qui la consulte.
|
|
main.url = "/"
|
|
main.auth_header = true
|
|
|
|
# ╔══════════════════════════════════════════════════════════════════════════════╗
|
|
# ║ ⚠️ MaBibli EST UNE APPLICATION PRIVÉE. NE JAMAIS L'OUVRIR AUX `visitors`. ║
|
|
# ╚══════════════════════════════════════════════════════════════════════════════╝
|
|
#
|
|
# Ce n'est pas une préférence de confidentialité, c'est une propriété du code, et
|
|
# elle vaut sur les DEUX moitiés de l'application :
|
|
#
|
|
# • L'application n'a AUCUNE authentification propre. Elle lit `YNH_USER`, un
|
|
# en-tête que SSOwat injecte, et fait confiance à ce qu'il contient. Ouverte aux
|
|
# visiteurs, il n'y a plus personne pour l'écraser : tout ce qui est PERSONNEL
|
|
# (statuts de lecture, liste d'envies) devient lisible et modifiable par le
|
|
# premier venu. C'est la même frontière que « le service n'écoute que sur
|
|
# 127.0.0.1 » — SSOwat n'est pas un confort, c'est la seule porte.
|
|
#
|
|
# • Il n'existe AUCUNE notion d'écriture réservée. Toute personne autorisée peut
|
|
# ajouter, modifier et SUPPRIMER n'importe quel livre du catalogue commun, ainsi
|
|
# que noter un prêt ou son retour. C'est délibéré — un foyer, pas un service
|
|
# public — et cela suppose que les personnes autorisées se connaissent.
|
|
#
|
|
# `all_users` est donc le plus large qui ait du sens : les comptes du serveur.
|
|
# Restreindre davantage (un groupe dédié) est légitime ; élargir ne l'est pas.
|
|
#
|
|
# ⚠️ Volontairement PAS `protected = true` : l'administrateur du serveur reste
|
|
# maître de ses permissions, et peut vouloir restreindre à un groupe. C'est
|
|
# l'élargissement qui est un contresens, pas le réglage lui-même.
|
|
main.allowed = "all_users"
|
|
|
|
[resources.ports]
|
|
# Port interne uniquement. `exposed` vaut `false` par défaut : le pare-feu reste
|
|
# fermé, et l'unité systemd n'écoute que sur 127.0.0.1.
|
|
|
|
[resources.apt]
|
|
# `sqlite3` sert aux sauvegardes : la base est en mode WAL (vérifié), un simple
|
|
# `cp` du fichier `.db` ne suffit pas. Voir `scripts/backup`.
|
|
packages = "sqlite3"
|