partiduo-admin
= partiduo-admin :toc: left :toc-title: Table des matières :icons: font
Administration du parc de dossiers Partiduo (ADR-008 de partiduo-docs) : une application Marten séparée, avec sa propre base, qui gère le cycle de vie des dossiers sans jamais entrer dans leurs données comptables (ADR-008 D3), et partiduo-agent, l'exécutant installé sur chaque serveur d'hébergement (ADR-008 D4).
Licence : AGPL-3.0-or-later (voir LICENSE).
== Contenu
[cols="1,3",options="header"] |=== |Binaire |Rôle
|partiduo-admin (src/server.cr) |Interface HTMX + Bulma (charte et theme.css de la maquette), fr/en/nl, servie sur admin.<domaine> ; API de l'exécutant /api/agent/v1/….
|partiduo-admin-manage (manage.cr) |Ligne de commande Marten : migrate, bootstrap (premier super-admin par invitation), schedule (planification, toutes les 15 minutes).
|partiduo-agent (src/agent.cr) |Exécutant : tire ses tâches en HTTPS avec le jeton du serveur, liste fermée de types traduits en appels à partiduo-provision et à partiduo-manage instance (contrat doc/api/instance-cli.adoc de partiduo-app), tâches idempotentes et reprenables, journal renvoyé.
|partiduo-connect (src/connect.cr) |Fournisseur d'identité commun aux dossiers et à l'administration (ADR-012), servi sur connect.<domaine> avec sa propre base : un compte, une passkey, un jeu de codes de récupération par personne ; OpenID Connect.
|partiduo-connect-manage (connect_manage.cr) |Ligne de commande de connect : migrate, client (déclarer un dossier ou l'administration), invite, signing-key, account. |===
== Modèle
- Structures : cabinet (plusieurs collaborateurs), gestionnaire indépendant (une seule personne, admin de sa structure, qui gère tous ses dossiers) ou parc sans cabinet (un seul, périmètre propre du super-admin). Utilisateurs d'administration et trois rôles (ADR-008 D2) : super-admin (tout le parc, passkey exigée), admin de cabinet (sa structure), gestionnaire de dossiers (dossiers confiés) ; affectations.
- Donneurs d'ordre : qui commande et paie un dossier (cabinet, la société elle-même, autre payeur) — identité, SIREN, adresse de facturation, contact. La facturation (tarifs, périodes, factures) est hors périmètre ; la vue « dossiers par donneur d'ordre » s'exporte en CSV.
- Dossiers (inventaire) : sous-domaine, type (entreprise ou foyer, choisi à la création et transmis à
partiduo-provision --kind, A-PER-5 ; un foyer n'a ni module d'entreprise, ni SIREN ni TVA), régime, modules, extensions, serveur, paquet, version relevée sur l'instance, état, cabinet gestionnaire, donneur d'ordre obligatoire. Modules et extensions se choisissent par familles (tenue des comptes, gestion, télédéclarations, facture électronique, banque et paiements, foyer, outils), d'après le catalogue de la console (src/admin/services/catalog.cr, D-ADM-016, D-ADM-019) : dépendances cochées, incompatibilités désactivées, micro-entreprise ou profession libérale en boutons radio, pièces exclues du type du dossier (foyer ou entreprise) ou de son régime fiscal écartées, mêmes règles contrôlées par le serveur ;spec/admin/catalog_spec.crcompare ce catalogue aux manifestes des dépôts voisins. - Serveurs (jeton d'exécutant en empreinte), tâches, sauvegardes (mode de chiffrement, empreinte de la clé), demandes d'opérations sensibles (mode à une ou deux personnes), alertes, certificats émis (quota Let's Encrypt) et journal d'audit en ajout seul (déclencheur PostgreSQL).
Les droits par rôle sont tous dans src/admin/services/access.cr.
== Actions (ADR-008 D5)
Créer ; modifier modules et extensions ; suspendre ; réactiver ; archiver (sauvegarde figée vérifiée, conservation dix ans) ; restaurer l'archive ; supprimer définitivement après la durée légale (à une ou deux personnes, voir ci-dessous) ; sauvegardes planifiées, rétention, restauration à une date (instance neuve ou remplacement), restauration test ; supervision (service, base, certificat et échéance, quota Let's Encrypt, disque, dernière sauvegarde, exécutant silencieux) avec alertes à l'écran et par courriel ; recours d'accès par réémission de l'invitation d'administrateur (à une ou deux personnes).
== Paquet d'un dossier
Les serveurs d'hébergement sont sous FreeBSD ; les instances Partiduo y sont installées par l'un de deux paquets exclusifs, aux mêmes emplacements : partiduo-app (production) ou partiduo-app-devel (développement, sur le serveur de test) ; une seule version par serveur. Chaque dossier porte le paquet attendu sur son serveur, son paquet (app ou devel), celui de l'administration qui le crée (D-ADM-014) : partiduo-admin-devel, compilé avec -Dpartiduo_devel, ne crée que des dossiers devel, partiduo-admin que des dossiers app. Il est affiché sur la fiche et la liste des dossiers ; une restauration dans une instance neuve garde le paquet du dossier restauré. Chaque tâche d'un dossier transmet son paquet à l'exécutant (paramètre package), qui le compare au paquet installé pour une instance pas encore déclarée sur le serveur (création, instance neuve) et refuse la tâche s'il diffère : ensuite, c'est le paquet du serveur qui la sert.
La mise à jour des paquets n'est pas l'affaire de l'administration : beryl fait le pkg upgrade sur tous les serveurs, et le script rc.d de chaque instance applique ses migrations à son démarrage. L'administration ne publie donc aucune version et ne monte aucun dossier ; la version affichée d'un dossier est celle que rend l'instance (création, restauration, supervision), et chaque sauvegarde retient celle de l'instance au moment de la prise.
API de l'exécutant 2.0.0 : type instance.upgrade et état de tâche waiting retirés, paramètre version des tâches remplacé par package.
== Validation à deux au choix
Les opérations sensibles — suppression définitive et recours d'accès — passent par une demande. Chaque structure règle si elles exigent deux personnes (DECISIONS D-VAL2-001 à D-VAL2-008 de partiduo-app) : écran « Validation à deux » (menu de l'admin de la structure ; colonne de la liste des structures et menu du parc sans cabinet pour le super-admin).
[cols="1,3",options="header"] |=== |Mode |Effet
|Une personne (défaut) |Le demandeur, s'il est habilité à valider (admin de la structure ou super-admin), confirme seul sur la page « Confirmer seul » : ré-authentification forte de moins de cinq minutes (passkey, ou code TOTP pour les rôles au niveau 2 ; la connexion elle-même compte), puis confirmation explicite — sous-domaine du dossier retapé pour une suppression définitive, case cochée pour un recours d'accès. La demande d'un gestionnaire de dossiers attend toujours un admin.
|Deux personnes |Une autre personne habilitée (admin de la structure ou super-admin) valide, comme avant. Le demandeur ne peut pas confirmer seul. |===
- Qui règle : l'admin de la structure (cabinet, gestionnaire indépendant) ; le super-admin règle le parc sans cabinet et consulte les autres sans les changer.
- Défaut : une personne pour toute nouvelle structure ; proposée (bandeau) à un cabinet d'au moins deux personnes habilitées, jamais imposée. Reprise des données : un cabinet qui comptait déjà deux admins actifs reste à deux personnes.
- Personnes habilitées : admins actifs de la structure ; pour le parc sans cabinet, les super-admins actifs. Le mode « deux personnes » est refusé sous deux ; si l'équipe y retombe (compte désactivé, départ), il se désactive de lui-même (
dual_approval.auto_offau journal d'audit, à la désactivation et à chaque planification). - Bascule (
dual_approval.change) : une demande en attente reste en attente si l'on passe à deux personnes ; si l'on revient à une personne, son demandeur peut la confirmer seul après ré-authentification. - Ce qui ne change pas : durée légale et état « archivé » revérifiés par l'exécutant (D-CRA-007), tâche annulable tant qu'elle attend, jamais rejouée après un échec (D-AFN-010), journal d'audit (
approval.request,approval.confirm_alone,approval.approve,auth.reauth). - Exécutant (API 1.2.0) : chaque tâche porte
approval_mode(singleoudual) etapprovers; l'exécutant exige un nom en mode une personne et deux noms distincts en mode deux personnes (absent : deux), l'écrit au journal de la tâche, et pour un recours d'accès passe à l'instance--approval-mode single|dualavec le ou les noms (contrat d'instance 1.1.0, B-VAL2-001 levé). Instance encore au contrat 1.0.0 : la validation à deux lui est transmise sans l'option (repli consigné au journal de la tâche), celle à une personne est refusée avant tout geste — mettre l'instance à jour.
=== Actions propres à une structure (D-ADM-023)
Le même réglage décide des changements d'admins d'une structure, qui passent par une demande sans dossier dès que la structure a une équipe : inviter un admin (écran des admins de la structure, écran des utilisateurs, assistant « Inviter un utilisateur »), nommer un gestionnaire admin, retirer un admin (il devient gestionnaire), le remplacer, désactiver ou réactiver un compte d'admin (un compte de super-admin : décidé par le parc sans cabinet, validé par un super-admin seulement), et repasser de deux personnes à une personne. Passer à deux personnes reste immédiat. Les demandes figurent dans « Opérations sensibles » (colonne « Cible » : la structure) ; l'écran des admins marque le compte visé « En attente de validation ».
- Autres actions sensibles (D-ADM-026) : la réinvitation d'un admin (elle révoque ses sessions et ses moyens d'authentification ; écran des utilisateurs), la désactivation, la réactivation et le changement de nature (cabinet, gestionnaire indépendant) d'une structure, gestes du super-admin sur la fiche de la structure (
/firms/<id>/settings, lien du nom dans la liste des structures). Le parc sans cabinet ne se désactive pas et ne change pas de nature. - Une personne : confirmation seule, ré-authentification forte de moins de cinq minutes et case cochée, puis l'action est faite aussitôt. Quand le demandeur est le super-admin, sur un cabinet ou un gestionnaire indépendant qui a des admins actifs (D-ADM-028), ceux-ci sont prévenus par courriel dès la demande et peuvent la valider ou la refuser pendant le délai du dernier recours ; le super-admin ne la confirme seul qu'ensuite (échéance affichée, trace
approval.recourse). - Deux personnes : un autre admin actif de la structure ou un super-admin valide ; jamais le demandeur (refusé aussi par la base, contrainte
admin_approval_other_person). - Recontrôles à la décision : demandeur encore habilité, compte visé inchangé (rôle, état, structure), règles du service (jamais zéro admin, adresse libre) ; sinon la demande est close (
approval.stale) et rien n'est fait. Jamais rejouée. - Anti-blocage : deux personnes exigent toujours deux personnes habilitées et se désactivent d'elles-mêmes sous deux ; le super-admin valide en dernier recours la demande d'un cabinet dont l'autre admin est injoignable, mais seulement après un délai sans décision d'un admin de la structure (D-ADM-024 : 48 heures,
PARTIDUO_ADMIN_RECOURSE_DELAY_HOURSde 1 à 144 ; heure affichée dans « Opérations sensibles », traceapproval.recourse, admins de la structure avisés par courriel) ; pour le parc sans cabinet, qui a la main sur le serveur repasse la structure à une personne :
[source,sh]
partiduo-admin-manage approval-mode-single --firm= --reason="second super-admin injoignable"
=== Archivage, déplacement et transfert d'un dossier (D-ADM-027)
- Archivage et déplacement : dans une structure réglée sur deux personnes, ce sont des opérations sensibles — la fiche du dossier et les assistants enregistrent une demande, qu'une autre personne habilitée valide (recontrôle à la décision ; le déplacement bascule alors le nom et prépare l'assistant de restauration du demandeur). À une personne, geste direct. La restauration reste hors de la validation à deux (la clé d'une sauvegarde chiffrée n'est jamais conservée).
- Transfert vers une autre structure (assistant « Transférer un dossier », super-admin) : demande qui vise le dossier et la structure d'accueil, avec le donneur d'ordre choisi dans l'accueil ; chaque structure donne son accord sur la page du transfert (
/approvals/<id>/transfer), selon son réglage (une personne, ou deux personnes distinctes), après ré-authentification forte ; jamais le demandeur, jamais de dernier recours. Au dernier accord : rattachement et donneur d'ordre changés, gestionnaires de l'ancienne structure retirés, administrateurs du dossier inchangés, demandes en attente closes, chiffrement jamais affaibli (clé de l'accueil exigée pour « clé du cabinet »), sauvegardes passées laissées telles quelles (pas de re-chiffrement), sauvegarde prise aussitôt, traces dans les journaux des deux structures. Exécutant et instance inchangés.
== Sauvegardes et chiffrement
Chaque sauvegarde d'un dossier est un pg_dump -Fc de sa base et une archive tar.gz de ses pièces jointes. Elle est préparée sur le serveur qui l'héberge, relue en entier, décrite par un manifeste signé par ce serveur, puis envoyée au stockage, hors du serveur de production (quantas, lui-même copié sur des NAS) : rien d'elle ne reste sur le serveur. Le chiffrement est un choix réglé dans l'administration (DECISIONS D-CHF-001 à D-CHF-024 de partiduo-app) :
[cols="1,3",options="header"] |=== |Mode |Effet
|Aucun |Fichiers en clair (.dump, .media.tar.gz) dans le stockage.
|Clé d'exploitation (défaut) |Fichiers age post-quantiques. Le serveur n'a que les clés publiques d'exploitation : il ne relit pas les sauvegardes passées, pas même les siennes, et aucune copie n'est lisible sans une clé privée d'exploitation, gardée hors ligne. Restaurer, sur n'importe quel serveur, exige d'ouvrir la capsule de la sauvegarde sur le poste de l'exploitant.
|Clé du cabinet |Fichiers age post-quantiques dont la clé est chiffrée pour la clé publique RSA du cabinet : ni le serveur, ni l'exploitant ne peuvent les lire. Restaurer exige la clé privée de l'admin du cabinet, utilisée dans son navigateur. |===
- Clé de sauvegarde et capsule (D-CHF-014) : chaque sauvegarde est chiffrée pour une clé
agetirée pour elle seule (age-keygen -pq,AGE-SECRET-KEY-PQ-1…), jamais écrite en clair, effacée à la fin de la tâche. Elle est gardée dans une capsule :….key.age, fichieragepour les clés d'exploitation du serveur ;….key.rsa, RSA-OAEP (SHA-256) pour la clé du cabinet. La capsule est déposée au stockage à côté des fichiers et gardée par l'administration. - Affaiblissement soumis à validation (D-ADM-025). Ordre de protection, du plus au moins protecteur : clé du cabinet, clé d'exploitation, clé du serveur (retirée, D-CHF-015), aucun. Passer à un mode de rang inférieur — réglage du cabinet, ou chiffrement effectif d'un dossier (surcharge quittée pour un réglage du cabinet plus faible compris) — est une demande
backup_weaken, décidée par la structure selon son réglage (une ou deux personnes), dès qu'elle a une équipe ; le service refuse le geste direct. Le super-admin peut la demander, jamais la confirmer seul ; il ne la valide qu'en dernier recours (D-ADM-024), et jamais pour quitter la clé du cabinet (D-CHF-008). Le renforcement et le changement de la clé du cabinet (rotation après compromission) restent immédiats ; chaque nouvelle clé est annoncée par courriel aux autres admins actifs du cabinet — auteur, heure, ancienne et nouvelle empreinte, marche à suivre si le changement n'est pas légitime — et les adresses avisées sont inscrites au journal d'audit (D-ADM-029). - Clés d'exploitation (D-CHF-015) : paires
agepost-quantiques produites hors ligne, la clé privée chiffrée par une phrase de passe, en deux exemplaires au moins tenus par deux personnes. Seules les clés publiques (age1pq1…) vont sur les serveurs, par beryl (/usr/local/etc/partiduo-agent/exploitation.recipients, une par ligne). Les clés non post-quantiques sont refusées.
[source,sh]
age-keygen -pq | age -p > exploitation-2026.age age -d exploitation-2026.age | age-keygen -y
La première commande demande la phrase de passe ; la seconde affiche la clé publique, à confier à beryl.
- Restauration (D-CHF-016) : la page « Clé de la sauvegarde requise » donne la capsule et la commande à lancer sur le poste de l'exploitant (
age -d -i exploitation-2026.age backup-….key.age). L'exploitant colle la clé de sauvegarde affichée. L'administration la contrôle (son destinataire est celui de la sauvegarde), la remet une seule fois à l'exécutant, qui la garde en mémoire. Pour la clé du cabinet, la page ouvre la capsule dans le navigateur, la clé privée ne quittant pas la page. Une restauration interrompue est à redemander avec la clé. - Signature (D-CHF-018) : chaque serveur signe le manifeste de ses sauvegardes avec sa clé Ed25519 (
ssh-keygen -Y sign -n partiduo-backup, clé/usr/local/etc/partiduo-agent/backup-signing.keyposée par beryl). Le super-admin déclare la clé publique de chaque serveur (écran « Serveurs ») ; une sauvegarde n'est relue que si la signature de son manifeste est vérifiée par une clé de son serveur d'origine, valable à sa date de prise, et si ses fichiers ont les tailles et empreintes du manifeste et de l'administration (D-CHF-011). Une clé révoquée ne vaut plus pour les sauvegardes prises après sa révocation. - Restauration test (D-CHF-017) : toute sauvegarde est relue à sa prise (
pg_restore --list,tar -t, après déchiffrement). Ensuite, sans la clé de la sauvegarde : signature, empreintes et structure des fichiersage(« enveloppe seule ») ; avec elle (« Tester avec la clé ») : restauration complète dans une base temporaire. - Rotation (D-CHF-019) : les gros fichiers ne changent jamais ; seules les capsules sont re-chiffrées, avec
age(B-CHF-003).
[source,sh]
age -d -i exploitation-2025.age backup-….key.age | age -R nouvelles.recipients > backup-….key.age.nouvelle
Un attaquant qui a déjà copié une sauvegarde et sa capsule, et qui obtient la clé privée qui ouvrait cette capsule, lit ce qu'il a copié : aucune rotation n'y peut rien.
- Réglage : par cabinet, surchargeable par dossier. Déposer la clé du cabinet et quitter le mode « clé du cabinet » sont réservés à l'admin du cabinet (D-CHF-008). Clé du cabinet : paire RSA-OAEP de 3072 bits produite dans le navigateur de l'admin du cabinet, ou par une commande locale :
[source,sh]
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:3072 -aes-256-cbc -out cabinet-prive.pem openssl pkey -in cabinet-prive.pem -pubout -out cabinet-public.pem openssl pkey -pubin -in cabinet-public.pem -outform DER | openssl dgst -sha256
Seule la clé publique (cabinet-public.pem) est déposée ; la dernière commande affiche son empreinte.
- Clair jamais sur le disque :
pg_dump,taretpg_restoreécrivent et lisent par un tube relié àage(-des gestesdump,restore,media-archive,media-restorede l'enveloppe de sudo). Une restauration authentifie d'abord tout le fichier : rien n'est remplacé par une sauvegarde altérée ou avec une mauvaise clé. - Journal d'audit :
backup_encryption.firm,backup_encryption.dossier,backup_key.deposit,backup.restoreetbackup.test_restore(mode, clé fournie ou non),backup.capsule(capsule téléchargée),signing_key.addetsigning_key.revoke; jamais une clé de sauvegarde.
=== Stockage des sauvegardes
Fichiers d'une sauvegarde, sous <serveur>/<dossier>/ (serveur qui l'a prise) :
[cols="2,3",options="header"] |=== |Fichier |Contenu
|backup-<horodatage>.dump.age |base (pg_dump -Fc), chiffrée |backup-<horodatage>.media.tar.gz.age |pièces jointes, chiffrées |backup-<horodatage>.key.age ou .key.rsa |capsule |backup-<horodatage>.manifest |manifeste (texte) |backup-<horodatage>.manifest.sig |signature du manifeste (sshsig) |===
Préfixes : backup-, archive- (archive de fin de vie), pre-restore- (sauvegarde de sûreté d'une restauration) ; sans chiffrement, ni .age ni capsule.
Deux façons de stocker, au choix (en production, l'une des deux est obligatoire) :
- serveur de sauvegarde (D-CHF-024, conseillé) :
--store-command, une commande sans shell qui parle le protocole departiduo-agent store-serve, en généralsshvers le serveur de sauvegarde ; - sur le serveur même (D-CHF-025), quand il n'y a qu'un serveur :
--store-dir /var/db/partiduo-store, répertoire de l'exécutant (partiduo-agent, 0700), distinct de--backup-dir. Les mêmes règles s'appliquent (écriture une seule fois, archives gardées). Les sauvegardes restent chiffrées et signées, mais disparaissent avec le serveur : copiez ce répertoire ailleurs (NAS,zfs send), tel quel, il ne contient aucune clé. Restaurer sur un autre serveur demande d'y recopier d'abord le répertoire<serveur>/<dossier>/.
Côté serveur de sauvegarde, authorized_keys force la commande :
[source,sh]
command="/usr/local/bin/partiduo-agent store-serve --root /backups/partiduo --client loulou",restrict ssh-ed25519 AAAA… partiduo-agent@loulou
Le serveur loulou n'écrit que sous loulou/, sans jamais écraser un fichier, et lit tout (restauration d'une sauvegarde d'un autre serveur). Il n'efface que sous loulou/ ; une archive dans sa durée légale n'est jamais effacée (D-CRA-007). L'élagage, par un autre serveur, des sauvegardes d'un dossier déplacé les garde (kept).
Dossier d'essai (paquet devel, serveur de test) : sa suppression définitive se demande dans tout état sauf « en création » et « supprimé », sans archive ni durée légale, avec la même validation à une ou deux personnes. L'exécutant ne s'en remet pas au paquet annoncé par la tâche : il saute le contrôle de la durée légale seulement si partiduo-app-devel est installé sur son serveur (pkg info). Le stockage, lui, garde toujours une archive dans sa durée légale : elle est laissée, dite au journal et rendue (kept).
=== Format des fichiers (format 2)
- Base et pièces jointes : fichiers
age(C2SPage-encryption.org/v1), un seul destinatairemlkem768x25519(la clé de sauvegarde), segments de 64 Kio ChaCha20-Poly1305 authentifiés.age-inspect --jsonen donne la structure ; l'exécutant lit lui-même l'en-tête (partiduo-backup), sansage-inspect. - Capsule :
age(destinatairesmlkem768x25519des clés d'exploitation) ou RSA-OAEP (SHA-256, MGF1-SHA-256, sans étiquette) de la clé de sauvegarde, en texte. - Manifeste :
partiduo-backup/2 server loulou dossier backup backup- taken_at 2026-10-09T10:15:00Z mode exploitation recipient age1pq1… capsule-key <SHA-256 de la clé d'exploitation> file backup-.dump.age file backup-.media.tar.gz.age
Le cabinet est nommé par cabinet-key <empreinte> ; sans chiffrement, ni recipient ni capsule.
=== Restauration hors de Partiduo
Avec les fichiers d'une sauvegarde (copiés du stockage ou d'un NAS), sans administration ni réseau, avec les seuls outils age, openssl et ssh-keygen :
[source,sh]
age -d -i exploitation-2026.age backup-….key.age > backup.key age -d -i backup.key -o backup.dump backup-….dump.age age -d -i backup.key -o media.tar.gz backup-….media.tar.gz.age
Ces trois commandes valent pour la clé d'exploitation. Pour la clé du cabinet, la première commande devient :
[source,sh]
openssl pkeyutl -decrypt -inkey cabinet-prive.pem -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 -pkeyopt rsa_mgf1_md:sha256 -in backup-….key.rsa -out backup.key
Authenticité, avec le fichier allowed_signers des clés de signature (<serveur> namespaces="partiduo-backup" ssh-ed25519 AAAA…) :
[source,sh]
ssh-keygen -Y verify -f allowed_signers -I loulou -n partiduo-backup -s backup-….manifest.sig < backup-….manifest sha256 backup-….dump.age backup-….media.tar.gz.age
La seconde commande donne les empreintes, à comparer à celles du manifeste. On restaure ensuite comme une sauvegarde en clair :
[source,sh]
createdb --encoding=UTF8 dossier_restaure && pg_restore --no-owner -d dossier_restaure backup.dump tar -xzf media.tar.gz -C <stockage des pièces jointes>
partiduo-agent decrypt (--key, ou --capsule avec --identity ou --private-key) et partiduo-agent inspect (fichier age, ou manifeste avec --signers) font de même.
== Modes de l'exécutant
dry-run (à blanc):: Rien n'est exécuté : état simulé, chaque geste écrit au journal. Specs et répétitions. --fail-on TEXTE (à blanc seulement) fait échouer la première opération qui contient TEXTE : répétition d'un échec et de sa reprise, par exemple --fail-on "tar" pour une sauvegarde.
local:: Machine de développement : vraies bases partiduo_adm_* (toute autre base est refusée), vrais partiduo-provision, pg_dump, pg_restore, tar ; ni vhost, ni service rc.d, ni Let's Encrypt — les fichiers de service sont produits dans --work-dir, l'arrêt d'un service est un fichier témoin. --manage et --provision désignent les outils du paquet app (défaut : /usr/local/bin/partiduo-manage et partiduo-provision, liens du paquet partiduo-app ou partiduo-app-devel, installés aux mêmes emplacements), --manage-devel et --provision-devel ceux du paquet devel (défaut : vides, ceux de app ; à fournir seulement pour essayer deux versions côte à côte hors paquet) ; le paquet d'une instance est retenu dans le fichier PACKAGE de son répertoire.
production:: Serveur d'hébergement FreeBSD : /usr/local/bin/sudo -n limité par deploy/partiduo-agent.sudoers à deux scripts enveloppes possédés par root (deploy/libexec/), qui valident leurs arguments et lisent /usr/local/etc/partiduo-agent/helpers.conf (deploy/helpers.conf.example) : partiduo-agent-root (installation et retrait d'une instance, service par service(8), certificat) et partiduo-agent-instance (interface d'instance, partiduo-provision, bases, sauvegardes, sous le compte des instances, propriétaire des bases). Une seule version par serveur : le paquet qui sert les instances est celui qui y est installé (partiduo-app ou partiduo-app-devel, exclusifs, aux mêmes emplacements), et les instances sont déclarées dans partiduo_app_instances (rc.conf, script rc.d partiduo_app ; partiduo_instances et partiduo sur un serveur dont le paquet est antérieur au renommage, D-ADM-032) ; les enveloppes refusent un paquet absent du serveur, ou une instance non déclarée quand la tâche ne précise pas de paquet. --domain est obligatoire : l'hôte et la base de chaque tâche sont recalculés depuis le sous-domaine (D-AFN-002, D-AFN-004, D-AFN-005).
Invitations remises par le serveur (tous modes, recommandé en production) : --smtp-url-file /usr/local/etc/partiduo-agent/smtp.url --mail-from notifications@partiduo.app. Le fichier (partiduo-agent 0600, posé par beryl depuis le coffre) contient l'URL du compte SMTP de Partiduo (smtp://notifications%40partiduo.app:secret@hôte:587, shard marten-mail) : jamais le relais SMTP du serveur, réservé aux alertes du système. L'exécutant envoie lui-même le lien d'invitation de l'administrateur d'un dossier (création, recours d'accès) ; l'administration n'en reçoit que la trace invitation_delivered. Sans cette option, le lien lui est rendu et elle l'envoie (D-ADM-009) : une faille de l'administration le révélerait (D-CRA-003).
Ce que l'exécutant refuse d'une administration compromise : une sauvegarde hors du répertoire du dossier (<backup-dir>/<sous-domaine>/, D-CRA-001), une valeur prise pour une option de manage instance (D-CRA-002), une pièce jointe hors du stockage de l'instance (D-CRA-004) ; les lignes de PostgreSQL qui citent des valeurs de la base ne remontent pas dans le journal des tâches (D-CRA-005). Une sauvegarde chiffrée par la clé du cabinet ne peut pas être lue sur le serveur (voir « Sauvegardes et chiffrement »).
== Développement
[source,sh]
SKIP_MARTEN_CLI_PRECOMPILATION=1 shards install createdb --encoding=UTF8 partiduo_admin crystal run manage.cr -- migrate crystal run manage.cr -- bootstrap --email=vous@exemple.fr # affiche le lien d'invitation MARTEN_ENV=development crystal run src/server.cr # http://127.0.0.1:8200
Exécutant local (jeton affiché à la création du serveur dans l'interface)
crystal build src/agent.cr -o bin/partiduo-agent bin/partiduo-agent --admin-url http://127.0.0.1:8200 --token-file jeton.txt --mode local
--manage ../partiduo-app/bin/partiduo-manage --provision ../partiduo-app/bin/partiduo-provision
--pg-socket /tmp --backup-dir /tmp/partiduo-backups --state-dir /tmp/partiduo-agent
--server-name local --store-dir /tmp/partiduo-store
Hors production, le stockage est un répertoire (--store-dir, défaut <state-dir>/store), la clé de signature est créée au premier usage dans l'état de l'exécutant, et les clés d'exploitation sont lues dans <state-dir>/exploitation.recipients. age 1.3 ou suivant (age, age-keygen) et ssh-keygen sont requis, par l'exécutant comme par l'administration (contrôle d'une clé de sauvegarde remise).
Specs (base propre, dont le nom contient test) ; celles du chiffrement et du stockage tournent seules, sans base ni réseau (crystal spec spec/agent/backup_crypto_spec.cr) :
[source,sh]
createdb --encoding=UTF8 partiduo_admin_test DATABASE_URL='postgres:///partiduo_admin_test?host=/tmp' crystal spec --order random crystal tool format --check src spec spec-connect config scripts manage.cr connect_manage.cr crystal build lib/ameba/bin/ameba.cr -o bin/ameba && bin/ameba
== connect (ADR-012)
Exploitation (commandes du serveur, récupération d'un compte dont la clé d'accès et les codes sont perdus, clients, registraires, clés) : fiche partiduo-docs/exploitation/connect.adoc.
connect est un troisième programme du dépôt, déployé à part : son sous-domaine (connect.<domaine>), sa base (partiduo_connect), sa configuration Marten (config/connect/), son application (src/connect/, tables connect_…). Il ne charge rien de l'administration et n'a aucun pouvoir sur le parc. Il dit qui est la personne ; chaque dossier décide seul si elle entre (ADR-002 D3).
[cols="2,3",options="header"] |=== |Adresse |Rôle
|/.well-known/openid-configuration, /jwks.json |Découverte et clés publiques (ES256).
|/authorize, /token, /userinfo |Flux « code d'autorisation » ; PKCE S256 obligatoire, clients confidentiels (client_secret_basic ou _post), acr 1 à 3 (ADR-002 D2), acr_values=3 fait passer par la passkey. Claims : sub, email, email_verified, name, et locale (langue d'affichage du compte) seulement si le compte en a une (D-ADM-015).
|/logout |Déconnexion unique : la session fermée avise par canal arrière chaque client qu'elle avait ouvert (ADR-012 D8) ; idem à la suspension d'un compte et à l'usage d'un code de récupération.
|/api/v1/signup-tickets |Invitation à créer un compte, demandée par un client : rend un lien (à envoyer par courriel) et un code (à transmettre par un autre canal) ; {"account": true} si l'adresse a déjà un compte (ADR-012 D6).
|/signup/<jeton>, /account |Création du compte (lien + code), enrôlement, langue d'affichage (l'une des sept langues, ou celle du navigateur ; vaut pour connect, la console et les dossiers, D-ADM-015), facteurs, codes, changement d'adresse, journal personnel.
|/api/v1/clients |Déclaration du client d'un dossier par l'exécutant de son serveur (registraire, Authorization: Bearer …) : client <dossier>.<domaine>, adresses fixées par connect (/login/federated/connect/… de partiduo-ui-bulma), nouveau secret à chaque déclaration ; action=disable à la suppression du dossier. |===
Réglages (environnement) : PARTIDUO_DOMAIN, PARTIDUO_CONNECT_HOST (défaut connect.<domaine>), PARTIDUO_CONNECT_URL (émetteur), PORT (8300), DATABASE_URL, MARTEN_SECRET_KEY, PARTIDUO_CONNECT_SMTP_URL (avis, marten-mail), PARTIDUO_CONNECT_MAIL_FROM, PARTIDUO_CONNECT_SIGNING_KEYS (fichier des JWK privées, la première signe, toutes sont publiées ; exigé en production). La passkey a pour RP ID le domaine commun (ADR-002 D5).
[source,sh]
createdb --encoding=UTF8 partiduo_connect crystal run connect_manage.cr -- migrate crystal run connect_manage.cr -- client add --client-id=pipo --name=pipo.partiduo.localhost
--redirect-uri=http://pipo.partiduo.localhost:3000/auth/callback
--post-logout-uri=http://pipo.partiduo.localhost:3000/
--backchannel-uri=http://pipo.partiduo.localhost:3000/auth/backchannel-logout crystal run connect_manage.cr -- invite --email=vous@exemple.fr # lien + code MARTEN_ENV=development crystal run src/connect.cr # http://connect.partiduo.localhost:8300
createdb --encoding=UTF8 partiduo_connect_test DATABASE_URL='postgres:///partiduo_connect_test?host=/tmp' crystal spec spec-connect --order random
Dossiers clients de connect : l'exécutant de chaque serveur est registraire (partiduo-connect-manage registrar add --name=loulou, jeton au coffre) ; réglé par --connect-url et --connect-token-file, il déclare le client de chaque dossier qu'il crée juste avant partiduo-provision --connect-*. Le secret ne passe ni par l'administration ni en argument : fichier 0600 effacé aussitôt (mode local), entrée standard de partiduo-agent-instance (production), qui prend l'émetteur dans helpers.conf (CONNECT_URL). Le code de création du compte de l'administrateur du dossier s'affiche sur la page de la tâche, à lui transmettre de vive voix (ADR-012 D6).
L'administration est elle aussi cliente (ADR-012 D4) : réglée par PARTIDUO_ADMIN_CONNECT_URL, PARTIDUO_ADMIN_CONNECT_CLIENT_ID et PARTIDUO_ADMIN_CONNECT_CLIENT_SECRET (ou _SECRET_FILE), elle n'accepte plus ni mot de passe, ni TOTP, ni passkey locaux : la connexion passe par /login/connect (niveau attesté par connect, passkey redemandée au super-admin), la déconnexion par connect, l'invitation d'un utilisateur lie son compte connect (même adresse) et rend le code de création du compte. La langue de la console est alors celle du compte connect (claim locale, relu à chaque connexion), sinon celle du navigateur ; le sélecteur de langue des écrans connectés devient un lien vers la page du compte connect (D-ADM-015, DECISIONS.adoc). Client à déclarer chez connect :
[source,sh]
partiduo-connect-manage client add --client-id=admin --name="Partiduo administration"
--redirect-uri=https://admin.partiduo.dev/login/connect/callback
--post-logout-uri=https://admin.partiduo.dev/login
--backchannel-uri=https://admin.partiduo.dev/login/connect/backchannel-logout
Relais de l'authentification d'un cabinet (ADR-012 D9). Un cabinet qui a son fournisseur OpenID Connect est déclaré par l'exploitation :
[source,sh]
partiduo-connect-manage upstream add --code=cabinet-x --name="Cabinet X"
--issuer=https://login.cabinet-x.example --client-id=partiduo-connect
--client-secret-file=secret.txt --domains=cabinet-x.example --ceiling=2
Les adresses de ses domaines se connectent alors chez lui (« Continuer chez mon cabinet »), jamais par un mot de passe connect. Le compte connect reste (même sub) ; le niveau est 2 si le cabinet atteste une authentification à plusieurs facteurs (amr), plafonné à --ceiling ; sans elle, la connexion est refusée. Un compte n'est créé que pour une adresse invitée. Le cabinet peut annoncer la fin d'une session (/login/sso/<code>/backchannel-logout) : connect ferme celles de la personne et avise les dossiers.
Duplication (ADR-012 D5). connect ne garde rien sur la machine : sessions, comptes, défis et codes sont en base ; les clés de signature et MARTEN_SECRET_KEY viennent du coffre et sont les mêmes partout. Plusieurs serveurs connect servent donc le même nom, sur une base commune :
. serveur A : recettes postgresql-primary (réplication en flux vers B, par le réseau privé) et partiduo-connect ; . serveur B, dans un autre centre de données : postgresql-standby (copie à chaud, lecture seule) et partiduo-connect service arrêté (une base en lecture seule refuse les sessions) ; . bascule : promotion de la base de B (pg_ctl promote), démarrage de connect sur B, connect.<domaine> pointé vers B (DNS à durée de vie courte, ou mandataire). L'ancien primaire revient en secours, jamais en primaire. Bascule à exercer, et à chronométrer, avant la mise en production.
Les sessions ouvertes restent valables après la bascule (même base, mêmes clés) ; seules les connexions en cours au moment de la panne sont à refaire.
Clé de signature pour le coffre (une ligne, rien sur le disque) : partiduo-connect-manage signing-key generate. Rotation : nouvelle clé en seconde position du fichier (publiée), puis en première une heure plus tard au moins (les clients relisent /jwks.json toutes les heures).
== DNS des dossiers (ADR-013)
L'administration seule tient le nom de chaque dossier : <sous-domaine>.<domaine>. CNAME <hostname du serveur>., TTL 300 s. Aucun exécutant n'a de clé DNS ; les adresses des serveurs restent dans les enregistrements que beryl tient pour eux.
- Réglage :
PARTIDUO_ADMIN_DNS_CREDENTIALS_FILE(clé OVH propre à l'administration, JSONapplication_key,application_secret,consumer_key,endpointfacultatif),PARTIDUO_ADMIN_DNS_ZONES(zones gérées, séparées par des virgules). Sans fichier de clé, ou pour une zone absente de la liste (joker*.partiduo.devde la zone de test), rien n'est modifié etdns_statereste vide. - Création : le nom est posé avant la tâche ; la tâche porte
dns_target(le serveur), et l'exécutant attend jusqu'à six minutes que le nom désigne les mêmes adresses avant de demander le certificat ; au-delà, la tâche échoue sans appel à Let's Encrypt et se relance. - Suppression : le nom est effacé quand
instance.deletea réussi. - Garde-fous : seul l'enregistrement créé par l'administration (
dns_record_id) est modifié ou effacé ; un autre enregistrement pour ce nom bloque (étaterror, alertedns), rien n'est écrasé ; la cible est un serveur actif. Journal d'audit :dns.create,dns.update,dns.delete. Les noms en attente ou en erreur sont repris parmanage schedule. - Noms réservés (
Protocol::RESERVED_SLUGS) :admin,autoconfig,autodiscover,connect,mail,www. - Déplacement (manuel, au cas par cas) : un super-admin rattache le dossier suspendu à un autre serveur (le nom bascule aussitôt), puis lance la restauration « Déplacement » sur le nouveau serveur (instance installée sous le même sous-domaine, refusée si la base y existe déjà) ; le dossier redevient actif. L'ancienne instance se retire à la main. Procédure :
partiduo-docs/exploitation/dns.adoc.
== Provenance du code (AGPL-3.0, article 13)
L'administration et connect affichent en pied de page leur version, la licence et un lien vers ce dépôt (src/shared/licences.cr), et offrent à /licences la liste des composants tiers avec leur licence, leur provenance et leur texte. Ce texte, THIRD-PARTY-LICENSES.txt, est assemblé par scripts/third-party-licenses.sh (Crystal, shards du verrou, Bulma, htmx, Lucide, IBM Plex, encodeur QR code de Project Nayuki ; textes vendus dans licenses/) : à relancer après tout changement du verrou ; la CI le vérifie.
== Pages d'accès communes avec partiduo-ui-bulma
Connexion, second facteur, invitation, codes de récupération, erreurs et /licences ont la même mise en page que celles de l'application (partiduo-ui-bulma, interface de référence) : langue, bandeau, carte, blocs avec icônes, séparateurs, boutons tactiles, pied de page. Seules changent la mention sous le nom (« administration des dossiers ») et les blocs propres (connexion par connect). access.css, theme.css, bulma.min.css, fonts.css, icons/sprite.svg, brand/logo.svg et brand/aloli-logo.svg sous src/admin/assets/admin/ sont des copies octet pour octet de ceux de partiduo-ui-bulma : ne pas les modifier ici, mais là-bas, puis les recopier et mettre à jour l'empreinte dans les deux specs (spec/admin/shared_assets_spec.cr ici, spec/ui/shared_assets_spec.cr là-bas).
L'enrôlement de l'application d'authentification (page du compte de l'administration et de connect) affiche un QR code de l'URI otpauth://, puis le secret groupé par quatre caractères, comme l'application (classes pd-totp-grid, pd-qr-box, pd-qr, pd-secret, dans admin.css et connect.css ; noir sur blanc dans les deux thèmes). L'encodeur src/shared/qr_code.cr (PartiduoAdmin::Qr::QrCode) est une copie de partiduo-app/src/auth/services/qr_code.cr : son bloc class QrCode … end est identique à la source, ce que vérifie spec/admin/qr_code_spec.cr (empreinte SHA-256 du bloc, matrice d'une URI réelle, et comparaison à la copie de travail voisine ../partiduo-app quand elle est là). Ne pas le modifier ici : modifier partiduo-app, recopier, puis mettre à jour les empreintes de la spec. Le rendu SVG (src/shared/qr_svg.cr) reprend celui de PartiduoUi::QrSvg.
== Développement local
shard.yml et shard.lock référencent les shards maison sur GitHub (aloli-crystal/* et aloli-partiduo/*), suivis par branche. Le verrou versionné ne contient aucune dépendance path: : les ports FreeBSD et la CI l'exigent.
Pour travailler sur les copies de travail voisines (../../prod-crystal/<nom> et ../partiduo-<dépôt>), lancer :
[source,sh]
scripts/override-local.sh
Le script génère un shard.override.yml local et non versionné (ignoré par git), installe les shards, puis remet shard.lock à son état versionné. Une copie de travail absente reste prise sur GitHub.
Pour régénérer le verrou versionné, supprimer d'abord l'override local :
[source,sh]
rm -f shard.override.yml SKIP_MARTEN_CLI_PRECOMPILATION=1 shards update scripts/override-local.sh # retour au mode local
== Exploitation (FreeBSD)
Les serveurs tournent sous FreeBSD 15. Tout s'installe par paquets, depuis l'arbre de ports aloli-ports :
finance/partiduo-admin: l'application (partiduo-admin,partiduo-admin-manage, fichiers statiques, gabarits et traductions rangés dansshare/partiduo-admin/à côté des programmes, voirconfig/settings/production.cr), son script rc.dpartiduo_adminet la planification/usr/local/etc/cron.d/partiduo-admin(partiduo-admin-manage scheduletoutes les quinze minutes) ;sysutils/partiduo-agent: l'exécutant (partiduo-agent), les deux scripts enveloppes (deploy/libexec/) dans/usr/local/libexec/partiduo-agent/, les règles sudoers (deploy/partiduo-agent.sudoers) dans/usr/local/etc/sudoers.d/partiduo-agentet son script rc.dpartiduo_agent;finance/partiduo-appetfinance/partiduo-app-devel: les instances (l'un ou l'autre, exclusifs :/usr/local/lib/partiduo, script rc.dpartiduo), que l'exécutant installe avec leurpartiduo-provision, leurs gabarits (deploy/templates/) et leurpartiduo-manage. Les paquets sont mis à jour par beryl, pas par l'exécutant : le script rc.d d'une instance passe ses migrations avant chaque démarrage.
deploy/ ne contient plus que ce que les ports reprennent : les règles sudoers, les scripts enveloppes et l'exemple de leur configuration. Les paquets créent les comptes système (partiduo par partiduo-app, partiduo-agent et partiduo-admin par les leurs) ; sans eux : pw useradd partiduo-agent -c "Partiduo agent" -d /nonexistent -s /usr/sbin/nologin (de même pour partiduo-admin).
Serveur d'hébergement, après pkg install partiduo-agent partiduo-app (partiduo-agent-devel partiduo-app-devel sur le serveur de test), en root :
[source,sh]
Configuration des enveloppes (root 0644 : elles refusent de démarrer sinon)
install -d -o root -g wheel -m 755 /usr/local/etc/partiduo-agent install -o root -g wheel -m 644 deploy/helpers.conf.example /usr/local/etc/partiduo-agent/helpers.conf # à adapter visudo -c # règles de /usr/local/etc/sudoers.d/partiduo-agent (paquet)
Répertoires : sauvegardes (les deux comptes y écrivent ; les répertoires des
dossiers héritent du groupe, règle BSD), fichiers de partiduo-provision,
état de l'exécutant. Jamais sous /var/backups : root:wheel 0750 (mtree de
base), les comptes de service ne peuvent pas le traverser (D-CHF-026).
install -d -o partiduo -g partiduo-agent -m 770 /var/db/partiduo-backups install -d -o partiduo -g partiduo -m 750 /var/db/partiduo-stage install -d -o partiduo-agent -g partiduo-agent -m 700 /var/db/partiduo-agent
Rôle PostgreSQL des instances (socket /tmp, authentification peer)
su -m postgres -c 'createuser --createdb --no-superuser partiduo'
nginx lit les vhosts des instances : dans le bloc http de
/usr/local/etc/nginx/nginx.conf, « include partiduo/*.conf; »
Jeton du serveur (affiché à sa création dans l'administration)
install -o partiduo-agent -g partiduo-agent -m 600 jeton.txt /usr/local/etc/partiduo-agent/agent.token sysrc partiduo_agent_enable=YES sysrc partiduo_agent_flags="--mode production --admin-url https://admin.partiduo.app
--token-file /usr/local/etc/partiduo-agent/agent.token --domain partiduo.app --server-name loulou
--store-command '/usr/bin/ssh -T -i /usr/local/etc/partiduo-agent/store.key -o BatchMode=yes partiduo-store@quantas'
--smtp-url-file /usr/local/etc/partiduo-agent/smtp.url --mail-from notifications@partiduo.app" service partiduo_agent start
Posés par la recette beryl partiduo-agent (D-CHF-022) : le paquet age, les clés publiques d'exploitation (/usr/local/etc/partiduo-agent/exploitation.recipients, 0644), la clé de signature du serveur (backup-signing.key, 0600, partiduo-agent, et sa .pub), la clé ssh du stockage (store.key, 0600). Ces clés privées sont propres à chaque serveur et viennent du coffre de beryl. La clé publique de signature est déclarée dans l'administration (écran « Serveurs »). La clé privée d'exploitation n'est sur aucun serveur.
Serveur de sauvegarde (quantas), après pkg install partiduo-agent : un compte partiduo-store, propriétaire de la racine du stockage, et une ligne authorized_keys par serveur de production (section « Stockage hors du serveur »). Ce serveur n'a aucune clé de chiffrement ; ses copies sur les NAS se font telles quelles.
--domain : même valeur que DOMAIN de helpers.conf. --server-name : nom du serveur dans l'administration. --store-command (ou --store-dir sans serveur de sauvegarde, D-CHF-025) est obligatoire en production. Défauts de l'exécutant : --state-dir /var/db/partiduo-agent, --backup-dir /var/db/partiduo-backups, --pg-socket /tmp, --helpers-dir /usr/local/libexec/partiduo-agent. --smtp-url-file exige --mail-from (D-CRA-003).
L'administration envoie ses propres courriels (invitations des utilisateurs, alertes) par le même type de compte : PARTIDUO_ADMIN_SMTP_URL dans son environnement (admin.env). Sans elle, tout envoi est refusé avec la cause.
Pour chaque instance, l'enveloppe racine fait les gestes de l'INSTALL.txt de partiduo-provision : environnement /usr/local/etc/partiduo/<dossier>.env (root:partiduo 0640, relu et validé clé par clé), données /var/db/partiduo/<dossier> (pièces jointes dans media/), journal /var/log/partiduo/<dossier>.log, déclaration dans rc.conf (partiduo_app_instances, partiduo_app_enable=YES), service partiduo_app start <dossier>, tâche quotidienne /usr/local/etc/cron.d/partiduo-<dossier>, vhost /usr/local/etc/nginx/partiduo/<dossier>.conf et certificat Let's Encrypt par acme.sh, comme sur le parc beryl et selon les conventions du port FreeBSD security/acme.sh : acme.sh sous l'utilisateur acme (su -m), jamais en root, home /var/db/acme/.acme.sh, certificat laissé dans /var/db/acme/certs/<hôte>_ecc/ (fullchain.cer, <hôte>.key), défi HTTP-01 sur /usr/local/www/letsencrypt (possédé par acme), --reloadcmd '/usr/local/bin/sudo /usr/sbin/service nginx reload' rejoué par le cron de renouvellement /usr/local/etc/cron.d/acme ; un certificat encore valide n'est pas réémis. Prérequis : acme.sh (/usr/local/sbin/acme.sh), l'utilisateur acme, son cron et la règle /usr/local/etc/sudoers.d/acme, posés par la recette beryl acme ; l'enveloppe refuse l'installation, avant toute écriture, s'il en manque un.
Serveur de l'administration, après pkg install partiduo-admin age (age-keygen contrôle une clé de sauvegarde remise) :
[source,sh]
su -m postgres -c 'createuser partiduo-admin && createdb --owner=partiduo-admin --encoding=UTF8 partiduo_admin'
Avec l'environnement de l'application (ci-dessous), sous partiduo-admin :
partiduo-admin-manage migrate
partiduo-admin-manage bootstrap --email=vous@exemple.fr # lien d'invitation
sysrc partiduo_admin_enable=YES service partiduo_admin start
Variables de l'application (fichier d'environnement lu par son script rc.d, voir le port) : DATABASE_URL, MARTEN_SECRET_KEY, PARTIDUO_DOMAIN (partiduo.app), PARTIDUO_ADMIN_HOST, PARTIDUO_ADMIN_URL, PARTIDUO_ADMIN_MAIL_FROM, PARTIDUO_ADMIN_ALERTS_TO, PARTIDUO_ADMIN_SUPPORT_EMAIL (adresse de support, seul contact donné aux admins des cabinets dans les courriels de la console ; à défaut, l'adresse d'expédition, jamais celle d'un super-admin ni les destinataires des alertes, D-ADM-031) ; au besoin PARTIDUO_ADMIN_RECOURSE_DELAY_HOURS (délai du dernier recours du super-admin, et de sa confirmation seule sur un cabinet, 48 heures par défaut, D-ADM-024, D-ADM-028), PARTIDUO_ADMIN_ASSETS_ROOT et PARTIDUO_ADMIN_ARTIFACTS_ROOT, prioritaires sur les fichiers du paquet.
Décisions et blocages : ../partiduo-app/DECISIONS.adoc (D-ADM-…) et ../partiduo-app/BLOCAGES.adoc (B-ADM-…).
partiduo-admin
- 0
- 0
- 0
- 0
- 11
- 7 days ago
- September 28, 2026
GNU Affero General Public License v3.0
Tue, 27 Oct 2026 07:52:24 GMT