Files
parenthese/README.fr.md
T
IliesAllaliandClaude Opus 5.5 e596887a44 Invitations à créer son arbre, coupées en auto-hébergement (SIGNUP_PROMPTS) (#202)
- La famille sans compte trouve « Faire le mien » en bas du menu Ajouter ; la ligne quitte le menu du
  compte. Après l'inscription, l'arbre de la famille est rattaché et l'assistant de création s'ouvre.
- Démo : les messages des liens et la bulle après « Enregistrer et quitter » proposent l'inscription
  (lien cliquable, message gardé 6 s). La démo lit aussi les options de l'instance.
- Message d'invitation de Noël du 1er décembre au 6 janvier.
- SIGNUP_PROMPTS (API, /auth/options.signupPrompts) : éteint par défaut, allumé pour l'instance hébergée
  dans ecosystem.config.cjs, toujours coupé quand l'inscription est fermée. README, .env.example, OpenAPI.
- Mesures : via sur create_account_clicked et account_screen_viewed, source arbre-partage ou demo
  quand l'adresse n'en porte pas.
- « Quitter l'édition ? » dans la démo ne dit plus que les modifications restent sur l'appareil :
  la démo n'enregistre rien et tout disparaît au rechargement.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 18:17:59 +01:00

16 KiB

Parenthèse

CI

Un arbre de famille qu'on explore comme une carte, avec les photos, les vidéos et les voix de chacun. Il se partage par un lien et un mot de passe : les proches le parcourent sans compte et ajoutent ce qu'ils savent.

Site et démo : parenthese.io · English version

Ce que fait l'application

  • Un arbre par famille : personnes, unions, liens parent-enfant, mise en page automatique.
  • Des médias sur chaque personne : photos, vidéos (fichier ou lien YouTube), enregistrements audio pour garder une voix, documents, traces GPS.
  • Des annotations libres sur l'arbre : dessins, stickers, textes, photos posées où l'on veut.
  • Un partage par lien : chaque arbre a une adresse /arbre/<nom> protégée par un mot de passe de partage, qui permet de regarder et de proposer des ajouts.
  • Une consultation sans compte : un proche qui reçoit le lien et le mot de passe ouvre l'arbre directement.
  • Des contributions validées : le propriétaire choisit si les modifications des contributeurs s'appliquent tout de suite ou passent par une file de relecture, où il approuve ou rejette chaque changement.
  • Import et export GEDCOM pour venir d'un autre logiciel ou en repartir (l'export contient les personnes et leurs liens, pas les fichiers médias).

La promesse

  • Gratuit, et le restera.
  • Vos données restent les vôtres : pas de publicité, pas de revente. Ce que l'application enregistre et pourquoi est détaillé sur la page Données et vie privée.
  • Auto-hébergeable : le code est ouvert, une famille peut faire tourner sa propre instance sur son serveur ou son NAS sans dépendre de personne.

Auto-héberger

Prérequis : Docker avec le plugin Compose (docker compose version doit répondre).

git clone https://github.com/IliesAllali/parenthese.git
cd parenthese
cp .env.example .env

Le clone est facultatif. Les images de l'API et du front sont publiées sur le registre de GitHub (ghcr.io/iliesallali/parenthese-api et ghcr.io/iliesallali/parenthese-web, en amd64 et arm64), deux fichiers suffisent donc :

mkdir parenthese && cd parenthese
curl -fsSLO https://raw.githubusercontent.com/IliesAllali/parenthese/main/docker-compose.yml
curl -fsSL https://raw.githubusercontent.com/IliesAllali/parenthese/main/.env.example -o .env

Ouvrez .env et remplissez les deux valeurs vides :

  • POSTGRES_PASSWORD : un mot de passe pour la base (lettres et chiffres).
  • JWT_SECRET : une clé longue et aléatoire. Pour la générer : openssl rand -base64 48 ou node -e "console.log(require('crypto').randomBytes(48).toString('base64'))".

Puis :

docker compose up -d

Le premier démarrage télécharge les images (ou les construit depuis les sources si elles sont introuvables), applique les migrations de la base, puis sert l'application sur http://localhost. Créez votre compte depuis l'écran d'accueil : le premier compte est un compte comme les autres, il n'y a pas d'administrateur global.

Trois conteneurs tournent : db (PostgreSQL 16), api (l'API Node) et web (nginx qui sert le front et relaie l'API). Seul web est exposé, sur le port choisi par WEB_PORT (80 par défaut). Si ce port est déjà pris, changez WEB_PORT dans .env et adaptez CORS_ORIGIN en conséquence. Le conteneur web trouve l'API à api:4000 ; si vous lancez les images sous d'autres noms de service, donnez API_UPSTREAM à web (par exemple API_UPSTREAM=monapi:4000).

Pour ouvrir l'instance à la famille depuis Internet, placez un reverse proxy HTTPS devant le port web (Caddy, Traefik, nginx) et mettez l'adresse publique dans CORS_ORIGIN, par exemple https://arbre.votre-domaine.fr.

Pour qu'une personne qui tombe sur l'adresse ne puisse pas s'inscrire et y créer ses arbres, mettez ALLOW_REGISTRATION=false dans .env, puis lancez docker compose up -d. Les comptes existants se connectent et créent leurs arbres comme avant, la famille ouvre toujours les arbres partagés avec le lien et le mot de passe, et l'application ne propose plus de créer de compte. Une instance sans aucun compte accepte quand même le premier, la variable peut donc être posée avant le tout premier démarrage : créez votre compte tout de suite, avant de donner l'adresse.

Votre instance ne pousse personne à y créer un compte. Sur app.parenthese.io, l'instance hébergée, l'application invite à créer son propre arbre : la famille qui ouvre un arbre partagé trouve « Faire le mien » dans le menu Ajouter, les messages de la démo proposent l'inscription, et du 1er décembre au 6 janvier le message d'invitation parle de Noël. Ces invitations sont éteintes par défaut. Pour les allumer, mettez SIGNUP_PROMPTS=true dans .env (elles restent coupées si l'inscription est fermée).

Chercher quelqu'un dans des sources publiques

Le menu « + » propose « Quelqu'un ailleurs » : un panneau à droite de l'arbre où l'on tape un nom pour voir ce que les sources publiques disent de cette personne. Les liens vers des sites sans recherche interrogeable d'ici (presse ancienne sur Gallica, registres matricules sur FranceArchives, Geneanet, FamilySearch et quelques autres, la France d'abord) marchent toujours : votre navigateur ouvre le site avec le nom déjà tapé, et rien ne passe par le serveur.

Le serveur peut aussi interroger lui-même deux sources et afficher leurs résultats dans le panneau. Elles restent éteintes tant que vous ne les nommez pas dans .env :

# deces : décès en France depuis 1970 (fichier de l'INSEE, cherché par matchID)
# wikitree : profils publics de WikiTree
SEARCH_SOURCES=deces,wikitree
# Facultatif, gratuit sur deces.matchid.io : sans clé, les recherches s'arrêtent après une centaine
MATCHID_TOKEN=

Seuls le prénom, le nom et l'année de naissance quittent le serveur, et le panneau l'écrit à côté de chaque source. Un résultat ajouté à l'arbre ne remplit que les champs vides, passe la personne « à vérifier » et garde le lien de la source dans sa note. Le propriétaire de l'arbre ajoute directement ; la famille voit les résultats et les liens. Une clé matchID vaut 30 jours : renouvelez-la depuis votre compte matchID.

Connexion par votre fournisseur d'identité (OIDC)

Expérimental dans la 1.2.0 : testé contre un fournisseur OpenID certifié, vos retours sur de vraies installations sont les bienvenus dans les issues.

Parenthèse peut confier la connexion à un fournisseur OpenID Connect que vous faites déjà tourner (Authentik, Authelia, Keycloak, Pocket ID, Zitadel…) : la famille garde un seul compte et sa double authentification. Créez chez lui une application OIDC (client confidentiel, flux « authorization code ») dont l'adresse de retour est l'adresse publique de Parenthèse suivie de /auth/oidc/callback, puis renseignez dans .env :

OIDC_ISSUER=https://auth.votre-domaine.fr/application/o/parenthese/   # exactement comme le fournisseur l'annonce
OIDC_CLIENT_ID=parenthese
OIDC_CLIENT_SECRET=...                                                # vide pour un client public (PKCE seul)
OIDC_REDIRECT_URI=https://arbre.votre-domaine.fr/auth/oidc/callback
OIDC_PROVIDER_NAME=Authentik                                          # nom affiché sur le bouton

L'écran de connexion affiche alors « Se connecter avec Authentik ». À la première connexion, Parenthèse relie le compte du fournisseur au compte Parenthèse qui a le même email, ou en crée un si ALLOW_REGISTRATION le permet. Inscription fermée, le fournisseur ne connecte que les personnes qui ont déjà un compte (le premier compte d'une instance vide reste accepté). Un email n'est pris en compte que si le fournisseur le dit vérifié (email_verified) ; OIDC_REQUIRE_VERIFIED_EMAIL=false lève ce contrôle, pour un fournisseur qui ne l'envoie pas et où personne ne choisit lui-même son adresse. Une fois relié, un compte suit l'identifiant de l'utilisateur chez le fournisseur, pas son email.

PASSWORD_LOGIN=false retire les mots de passe Parenthèse : la connexion passe par le fournisseur seulement (l'API refuse de démarrer sans OIDC_ISSUER). Un compte créé par le fournisseur n'a pas de mot de passe ; pour le supprimer, on retape son email. OIDC_SCOPES (par défaut openid email profile) et OIDC_APP_URL (où l'app s'ouvre après la connexion, par défaut l'origine de OIDC_REDIRECT_URI) se changent rarement.

Une instance auto-hébergée n'envoie aucune mesure d'audience : le suivi d'usage de parenthese.io ne s'active que si une clé PostHog est fournie au build, ce que cette configuration ne fait pas.

Les liens d'aide et de vie privée de l'application mènent à parenthese.io. Pour pointer vers vos propres pages, renseignez VITE_HELP_URL et VITE_PRIVACY_URL quand vous construisez vous-même le front (voir frontend/.env.example).

Commandes utiles :

docker compose logs -f api     # journal de l'API
docker compose ps              # état des conteneurs
docker compose down            # arrêt, les données sont conservées

Pour vérifier qu'une installation fonctionne de bout en bout, lancez bash tools/docker-smoke.sh http://localhost (avec le port si WEB_PORT n'est pas 80). Le script a besoin de curl et jq. Il passe par le port web comme un navigateur : page d'accueil, santé de l'API, création d'un compte, connexion, création puis suppression d'un arbre de test. Le compte de test (smoke-...@example.com) reste en base. Avec ALLOW_REGISTRATION=false, le script s'arrête à la création du compte, c'est le refus attendu.

Un seul conteneur (Unraid, NAS)

Pour les systèmes qui installent un conteneur par application, ghcr.io/iliesallali/parenthese-aio fait tourner la base, l'API et le front ensemble :

docker run -d --name parenthese --restart unless-stopped -p 8080:80 -v /chemin/vers/appdata/parenthese:/data ghcr.io/iliesallali/parenthese-aio

Rien à remplir. Au premier démarrage, le conteneur crée dans /data la base, son mot de passe et la clé des sessions, puis les réutilise. Les photos et vidéos vont dans /data/media. Pour fermer la création de compte, ajoutez -e ALLOW_REGISTRATION=false (sur Unraid, « Allow registration » à false). Sauvegardez tout le dossier /data conteneur arrêté, ou exportez la base pendant qu'il tourne avec docker exec parenthese runuser -u postgres -- pg_dump parenthese > parenthese.sql. La base est ici PostgreSQL 15, l'installation compose utilise la 16.

Où sont les données

Deux volumes Docker, conservés entre les redémarrages et les mises à jour :

  • parenthese_db-data : la base PostgreSQL (personnes, liens, comptes, contributions).
  • parenthese_media : les fichiers envoyés (photos, vidéos, audio, documents).

Sauvegardes

Avec Docker, une sauvegarde tient en deux commandes, à lancer depuis le dossier du dépôt :

docker compose exec -T db pg_dump -U parenthese -Fc parenthese > parenthese-db-$(date +%F).dump
docker run --rm -v parenthese_media:/data -v "$PWD":/backup alpine tar -czf /backup/parenthese-media-$(date +%F).tar.gz -C /data .

La première commande sauvegarde la base. Elle contient les personnes, leurs liens, les comptes et les contributions, et elle sait à qui appartient chaque photo, vidéo ou enregistrement. La seconde sauvegarde les fichiers eux-mêmes. Il faut les deux. Restaurez la base seule et les souvenirs pointent vers des fichiers disparus. Restaurez l'archive seule et vous avez un dossier de fichiers rattachés à personne. L'export GEDCOM n'est pas une sauvegarde non plus. Il sert à emmener l'arbre vers un autre logiciel de généalogie et laisse de côté les médias et les annotations.

Adaptez -U parenthese et le nom de base si vous avez changé POSTGRES_USER ou POSTGRES_DB. Pour restaurer la base : docker compose exec -T db pg_restore -U parenthese -d parenthese --clean --if-exists < parenthese-db-AAAA-MM-JJ.dump, puis décompressez l'archive des médias dans le volume avec la même commande docker run, tar -xzf à la place de -czf.

Le script deploy/backup.sh fait la même chose (dump PostgreSQL, archive des médias, sommes de contrôle, rotation, envoi S3 optionnel) pour une installation sans Docker : il lit backend/.env et a besoin de pg_dump sur la machine. Ses réglages passent par les variables BACKUP_ROOT, BACKUP_RETENTION_DAYS et BACKUP_S3_BUCKET.

Mettre à jour

docker compose pull && docker compose up -d

latest suit la branche main. Pour rester sur une version, renseignez PARENTHESE_VERSION dans .env (par exemple 1.0). Depuis un clone, git pull && docker compose up -d --build reconstruit depuis les sources.

Les migrations de base de données s'appliquent toutes seules au démarrage de l'API, et une migration ne se défait pas. Lancez les deux commandes de sauvegarde ci-dessus avant chaque mise à jour. L'image tout-en-un le fait seule : avant d'appliquer une migration, elle copie la base dans /data/backups et garde les cinq dernières copies.

Développer

Le dépôt contient trois projets Node : backend/ (Fastify, Prisma, PostgreSQL, TypeScript), frontend/ (React, Vite) et landing/ (le site vitrine, inutile pour faire tourner l'application). Les images Docker utilisent Node 24.

Backend

npm --prefix backend install
npm --prefix backend run db:bootstrap
npm --prefix backend run dev

db:bootstrap démarre un PostgreSQL portable sous Windows (base genealogy, utilisateur postgres, port 5432) : les binaires doivent avoir été extraits dans backend/.local/postgresql_full/pgsql, le script ne les télécharge pas. Sur un autre système, n'importe quel PostgreSQL local convient : renseignez DATABASE_URL dans backend/.env (modèle dans backend/.env.example) et créez le schéma avec npm --prefix backend run prisma:migrate.

Sur n'importe quel système avec Docker, une base jetable remplace db:bootstrap :

docker run -d --name parenthese-dev-db -p 5432:5432 -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=genealogy postgres:16
npm --prefix backend run prisma:deploy
npm --prefix backend run dev

Avant prisma:deploy, backend/.env doit exister (copie de backend/.env.example) avec DATABASE_URL="postgresql://postgres:postgres@localhost:5432/genealogy", qui correspond à ces identifiants. docker stop parenthese-dev-db arrête la base, docker start parenthese-dev-db la relance avec ses données.

L'API écoute sur http://localhost:4000 et répond { "ok": true } sur /health.

Frontend

Créez frontend/.env.development.local :

VITE_API_BASE_URL=
VITE_DEV_PROXY_TARGET=http://127.0.0.1:4000
VITE_DEV_PROXY_STRIP_API=1

Puis :

npm --prefix frontend install
npm --prefix frontend run dev

Le serveur Vite relaie /api, /trees, /auth et /health vers l'API locale en retirant le préfixe /api, comme le fait nginx en production. Sans ce fichier, le front appelle http://localhost:4000 en direct et les liens partagés /api/arbre/... répondent 404.

Tests

npm --prefix frontend test
npm --prefix backend test

Contribuer

Issues et petites pull requests ciblées sont les bienvenues : la marche à suivre, les tests à faire passer et les conventions sont dans CONTRIBUTING.fr.md. Pour une faille de sécurité, pas d'issue publique : voir SECURITY.fr.md.

Licence

Le code est publié sous PolyForm Noncommercial 1.0.0. En clair :

  • Vous pouvez lire le code, le modifier et l'auto-héberger pour un usage non commercial, pour votre famille ou une association par exemple.
  • Vous ne pouvez pas monter un service payant à partir de ce code.
  • Le nom et le logo Parenthèse restent la propriété d'Ilies Allali.

Le texte de la licence fait foi.