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. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
122 lines
5.2 KiB
Desktop File
122 lines
5.2 KiB
Desktop File
[Unit]
|
|
Description=MaBibli — gestion de bibliothèque personnelle
|
|
Documentation=https://git.akbar.nohost.me/mathieu/mabibli
|
|
After=network.target
|
|
|
|
[Service]
|
|
Type=simple
|
|
User=__APP__
|
|
Group=__APP__
|
|
|
|
# WorkingDirectory n'est PAS décoratif ici. ASP.NET Core déduit son *content root*
|
|
# du répertoire courant, et c'est sous ce content root qu'il cherche `wwwroot/`,
|
|
# c'est-à-dire tout le client Blazor WebAssembly. Vérifié en local : lancé depuis un
|
|
# autre répertoire, le service démarre, répond 200 sur `/`… et journalise
|
|
# « The WebRootPath was not found », l'application n'étant jamais servie.
|
|
WorkingDirectory=__INSTALL_DIR__
|
|
ExecStart=__INSTALL_DIR__/MaBibli.Api
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# CONTRAINTE DURE : écoute sur 127.0.0.1 uniquement
|
|
# ---------------------------------------------------------------------------
|
|
# L'application fait confiance à l'en-tête `YNH_USER` pour savoir qui la consulte.
|
|
# Cet en-tête n'est digne de confiance que parce que SSOwat l'écrase à chaque
|
|
# requête traversant nginx. Un service qui écouterait sur 0.0.0.0 serait joignable
|
|
# en direct sur le port interne, sans passer par nginx : n'importe qui sur le réseau
|
|
# pourrait alors forger `YNH_USER: mathieu` et se faire passer pour un membre du
|
|
# foyer. L'adresse ci-dessous est donc une frontière de sécurité, pas un réglage.
|
|
#
|
|
# Ne jamais remplacer par `http://*:__PORT__`, `http://0.0.0.0:__PORT__`
|
|
# ni `http://+:__PORT__`.
|
|
Environment=ASPNETCORE_URLS=http://127.0.0.1:__PORT__
|
|
|
|
Environment=ASPNETCORE_ENVIRONMENT=Production
|
|
|
|
# La base vit dans le répertoire de données, pas à côté du binaire : c'est ce qui
|
|
# permet à `upgrade` de remplacer tout `install_dir` sans perdre la bibliothèque.
|
|
# `ConnectionStrings__MaBibli` surcharge la valeur d'`appsettings.json`
|
|
# (`Data Source=mabibli.db`, relative au content root).
|
|
#
|
|
# ⚠️ LES GUILLEMETS SONT OBLIGATOIRES — ne pas les retirer.
|
|
# `Environment=` découpe sur les espaces. Sans guillemets, cette ligne ne définit pas
|
|
# une variable mais DEUX : `ConnectionStrings__MaBibli=Data` et
|
|
# `Source=__DATA_DIR__/mabibli.db`. L'application reçoit alors la chaîne de connexion
|
|
# `Data` et meurt au démarrage sur :
|
|
# ArgumentException: Format of the initialization string does not conform to
|
|
# specification starting at index 0.
|
|
# Constaté à la première installation réelle (2026-08-18), et reproduit isolément sur
|
|
# une unité de test : sans guillemets `[Data]`, avec `[Data Source=/tmp/x.db]`.
|
|
Environment="ConnectionStrings__MaBibli=Data Source=__DATA_DIR__/mabibli.db"
|
|
|
|
# Pas de socket de diagnostic .NET, pas de télémétrie sortante.
|
|
Environment=DOTNET_EnableDiagnostics=0
|
|
Environment=DOTNET_CLI_TELEMETRY_OPTOUT=1
|
|
|
|
# Certains chemins .NET cherchent un HOME ; le pointer vers le répertoire de données
|
|
# évite toute tentative d'écriture dans un home inexistant.
|
|
Environment=HOME=__DATA_DIR__
|
|
|
|
Restart=on-failure
|
|
RestartSec=5
|
|
TimeoutStopSec=20
|
|
|
|
StandardOutput=journal
|
|
StandardError=journal
|
|
SyslogIdentifier=__APP__
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Durcissement
|
|
# ---------------------------------------------------------------------------
|
|
NoNewPrivileges=yes
|
|
PrivateTmp=yes
|
|
PrivateDevices=yes
|
|
DevicePolicy=closed
|
|
# ⚠️ `yes` ne convient PAS ici, et c'est contre-intuitif.
|
|
# Le `data_dir` de YunoHost vit sous `/home/yunohost.app/__APP__`. `ProtectHome=yes`
|
|
# rend `/home` inaccessible et vide dans le namespace du service : la base devient
|
|
# introuvable, et le démarrage meurt sur « SQLite Error 14: unable to open database
|
|
# file ». `ReadWritePaths=` ne suffit pas à percer cette protection — mesuré sur une
|
|
# unité de test : `yes` + `ReadWritePaths` échoue, `tmpfs` + `BindPaths` réussit.
|
|
#
|
|
# `tmpfs` conserve l'essentiel du bénéfice : les répertoires personnels du serveur
|
|
# restent invisibles au service, seul son propre répertoire de données est remonté.
|
|
ProtectHome=tmpfs
|
|
BindPaths=__DATA_DIR__
|
|
ProtectHostname=yes
|
|
ProtectClock=yes
|
|
ProtectControlGroups=yes
|
|
ProtectKernelModules=yes
|
|
ProtectKernelTunables=yes
|
|
ProtectKernelLogs=yes
|
|
ProtectProc=invisible
|
|
RestrictNamespaces=yes
|
|
RestrictRealtime=yes
|
|
RestrictSUIDSGID=yes
|
|
LockPersonality=yes
|
|
RemoveIPC=yes
|
|
SystemCallArchitectures=native
|
|
|
|
# Tout le système en lecture seule, sauf le répertoire de données. `install_dir`
|
|
# appartient à root : le service ne peut donc pas réécrire ses propres binaires.
|
|
ProtectSystem=strict
|
|
ReadWritePaths=__DATA_DIR__
|
|
|
|
# AF_INET/AF_INET6 restent nécessaires : le service écoute en TCP local *et* appelle
|
|
# les API BnF et OpenLibrary en sortie. Pour cette raison, pas de `IPAddressDeny=any`.
|
|
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
|
|
|
|
# Aucune capacité : le port alloué par YunoHost est > 1024, donc même
|
|
# CAP_NET_BIND_SERVICE est inutile.
|
|
CapabilityBoundingSet=
|
|
AmbientCapabilities=
|
|
|
|
# Liste reprise de radarr_ynh, autre application .NET packagée sans Docker, donc
|
|
# éprouvée sur ce runtime.
|
|
SystemCallFilter=~@clock @debug @module @mount @obsolete @reboot @setuid @swap
|
|
|
|
# ⚠️ Ne PAS ajouter MemoryDenyWriteExecute=yes : le JIT de .NET a besoin de pages
|
|
# exécutables inscriptibles, le service ne démarrerait pas.
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|