Gitfed
bastien-mrq/gitfed / docs / HOW_IT_WORKS.md
HOW_IT_WORKS.md Code Preview
**Langues :** Français · [English](HOW_IT_WORKS.en.md)

# Comment fonctionne gitfed

Ce document explique le mécanisme central de gitfed : comment deux instances
qui ne se connaissent pas peuvent laisser leurs utilisateurs collaborer sans
qu'aucun compte ne soit jamais partagé entre elles. Pour le contexte plus
large (fichiers, code, déploiement), voir le [README](../README.fr.md) ; pour
le détail de chaque paquet Go, voir [`ARCHITECTURE.md`](ARCHITECTURE.md) ;
pour le mode d'emploi pratique (mettre en place une vraie collaboration
fédérée étape par étape), voir [`../FEDERATION.md`](../FEDERATION.md).

---

## 1. Le problème que ça résout

Sur GitHub ou GitLab, collaborer avec quelqu'un suppose soit que vous ayez
tous les deux un compte sur la même plateforme, soit que l'un des deux
migre. gitfed part d'un principe différent, plus proche du mail ou
d'ActivityPub : **chaque personne a un compte sur une seule instance — la
sienne — et cette instance peut prouver son identité auprès de n'importe
quelle autre instance qui accepte de lui faire confiance.**

Concrètement : alice a un compte sur `chez-moi.fr`. Bob a un compte sur
`ailleurs.net`. Bob peut pousser du code sur un dépôt d'alice sans jamais
créer de compte chez elle — à condition que l'instance d'alice ait choisi de
faire confiance à l'instance de Bob.

## 2. L'identité : un certificat plutôt qu'un mot de passe partagé

Chaque instance gitfed génère, à sa création, sa propre **autorité de
certification** (une paire de clés ed25519, stockée sur le volume de
données). C'est le socle de tout le modèle :

1. Un utilisateur local enregistre une clé SSH publique auprès de son
   instance (via `gitfed-tui` pour le tout premier compte, ou en
   autonomie ensuite dans `Paramètres → Clés SSH`).
2. Quand il en a besoin, il demande à son instance un certificat
   (`ssh git@instance gitfed-cert`, ou via l'outil `gitfed-renew-cert` en
   tâche planifiée). L'instance vérifie que la clé lui appartient bien, puis
   signe un **certificat SSH** au format OpenSSH.
3. Ce certificat embarque un **principal** — `bob@ailleurs.net`, jamais un
   simple nom d'utilisateur seul — et une durée de validité courte,
   **24 heures par défaut**.
4. Quand Bob se connecte à l'instance d'alice, il présente ce certificat au
   lieu de sa clé nue. L'instance d'alice vérifie deux choses : que la
   signature du certificat correspond bien à une autorité en laquelle elle a
   confiance, et que ce certificat n'a pas été révoqué (§6).

Aucune de ces deux instances n'a besoin de contacter l'autre en direct pour
cette vérification — la signature suffit, un peu comme un passeport n'a pas
besoin d'un appel téléphonique au pays émetteur pour être vérifié à la
frontière, tant qu'on connaît la clé publique de l'autorité qui l'a délivré.

## 3. La confiance entre instances : le magasin de confiance

Faire confiance à une autorité, ça se construit. La première fois que
l'instance d'alice croise le domaine `ailleurs.net` (typiquement parce
qu'alice a accordé un accès à `bob@ailleurs.net` sur un de ses dépôts), elle
déclenche une **découverte fédérée** :

1. Elle récupère `https://ailleurs.net/.well-known/gitfed.json`, un document
   public qui contient la clé publique de l'autorité de certification de
   cette instance.
2. Elle vérifie que ce document est cohérent (le domaine déclaré correspond
   bien, une clé est présente) et que la requête ne pointe pas vers une
   adresse interne ou privée (protection anti-SSRF : IP littérales,
   `127.0.0.0/8`, `10.0.0.0/8`, `169.254.0.0/16`/métadonnées cloud, etc. sont
   toutes refusées, y compris après résolution DNS).
3. Selon la politique configurée sur l'instance d'alice :
   - **liste blanche (par défaut)** — le domaine est enregistré comme **en
     attente** ; un administrateur doit l'approuver explicitement dans
     `Administration → Confiance fédérée` avant que le moindre accès ne soit
     accordé.
   - **auto-confiance (TOFU — "trust on first use")** — le domaine est
     approuvé immédiatement à la première découverte, à activer uniquement
     si on accepte ce niveau de risque.
4. La découverte est limitée à **10 nouveaux domaines par minute** pour
   toute l'instance, pour empêcher qu'un compte compromis ne s'en serve
   comme sonde réseau à grande échelle.

Une fois un domaine approuvé, la vérification d'un certificat qui en
provient est **entièrement locale** : aucune requête réseau n'est refaite à
chaque `git push`.

## 4. L'autorisation : qui peut faire quoi sur quel dépôt

L'identité fédérée dit *qui* on est ; elle ne dit rien de ce qu'on a le
droit de faire. Ça, c'est le rôle du modèle d'ACL, entièrement local à
chaque dépôt :

- Chaque dépôt a un **propriétaire** (toujours admin) et, optionnellement,
  des **collaborateurs**, chacun avec un rôle explicite :
  **lecture**, **écriture**, ou **admin**.
- Un dépôt est en plus **public** ou **privé**. Un dépôt public accorde la
  **lecture** à n'importe quel principal authentifié, de n'importe quelle
  instance de confiance, sans qu'il soit besoin de l'ajouter comme
  collaborateur. L'**écriture** n'est en revanche jamais implicite — même
  sur un dépôt public, il faut un rôle explicite pour pousser du code.
- Octroyer un accès à un principal sur un domaine encore inconnu de
  l'instance est justement ce qui déclenche la découverte fédérée décrite
  au §3.

## 5. Le cycle complet, étape par étape

Reprenons l'exemple : alice veut donner à Bob un accès en écriture sur
`alice/mon-projet`.

1. **Sur l'instance d'alice** — alice va dans les paramètres du dépôt et
   accorde le rôle *écriture* à `bob@ailleurs.net`.
2. Si `ailleurs.net` est un domaine encore inconnu, l'instance d'alice
   déclenche la découverte (§3) et le domaine reste **en attente** tant
   qu'un admin ne l'a pas approuvé.
3. **Sur l'instance de Bob** — Bob demande un certificat à sa propre
   instance (`gitfed-cert` ou renouvellement automatique). Son instance
   signe un certificat portant le principal `bob@ailleurs.net`.
4. Bob exécute `git push ssh://git@chez-moi.fr:2222/alice/mon-projet.git`,
   en s'authentifiant avec son certificat.
5. L'instance d'alice vérifie la signature du certificat contre la clé
   d'autorité qu'elle a approuvée pour `ailleurs.net`, vérifie qu'il n'est
   pas révoqué, puis vérifie dans son ACL locale que `bob@ailleurs.net` a
   bien le rôle *écriture* sur `alice/mon-projet`.
6. Si tout est en ordre, `git-receive-pack` s'exécute normalement, exactement
   comme pour un utilisateur local.

À aucun moment Bob n'a eu besoin d'un compte, d'un mot de passe ou d'une
clé enregistrée chez alice.

## 6. Expiration et révocation

Un certificat de 24 h qui traîne après la suppression d'un compte reste, en
théorie, un accès résiduel. gitfed traite ça de deux façons complémentaires :

- **Le TTL est court** (24 h par défaut, configurable) — la fenêtre
  d'exposition d'une clé compromise est bornée même sans action de
  l'administrateur.
- **La révocation est immédiate.** Supprimer un utilisateur ou retirer une
  de ses clés SSH enregistre la révocation correspondante (par principal
  et/ou par empreinte de clé) ; chaque authentification SSH consulte cette
  liste avant d'accepter un certificat, même s'il est encore
  cryptographiquement valide.

## 7. Ce qui n'est *pas* fédéré

Pour être clair sur les limites du modèle : **un dépôt vit sur une seule
instance.** Il n'y a ni réplication, ni miroir automatique entre instances.
La fédération ne concerne que l'identité — qui a le droit de pousser ou de
lire — jamais les données elles-mêmes. Si l'instance d'alice tombe, son
dépôt n'est disponible nulle part ailleurs tant qu'il n'a pas été cloné en
dehors.

## 8. Cas particulier : cloner un dépôt public sans compte

Tout ce qui précède décrit l'accès *authentifié* — nécessaire pour tout
dépôt privé, et pour toute écriture, même sur un dépôt public. Mais un
dépôt **public** peut aussi être cloné en HTTPS, sans certificat, sans
clé SSH, sans compte du tout :

```sh
git clone https://chez-moi.fr/alice/mon-projet.git
```

C'est strictement un raccourci de lecture, pas une deuxième voie
d'authentification : aucune écriture n'est possible par ce chemin (il n'y
a pas de route `git-receive-pack` en HTTP, littéralement aucune), et un
dépôt qui redevient privé cesse instantanément d'être accessible ainsi.
Voir [`ARCHITECTURE.md`](ARCHITECTURE.md) §7 pour le détail
d'implémentation.

## 9. Cas particulier : fusionner une branche depuis le web

Les merge requests permettent à un collaborateur en écriture de fusionner
une branche dans une autre depuis l'interface web — une « écriture » qui
ne passe pas par SSH, ce qui ressemble à une deuxième exception au modèle
d'identité du §2. Ça n'en est pas une : le navigateur n'envoie jamais de
donnée git, il dit seulement au serveur de combiner deux branches qu'il a
déjà, et chaque commit que l'une ou l'autre pointe n'est arrivé dans le
dépôt que via un vrai push authentifié par SSH au départ. L'accès passe
par exactement le même contrôle de rôle en écriture qu'un `git push`
traverse déjà — le bouton web n'ouvre aucune capacité qu'un collaborateur
en écriture n'aurait pas déjà en clonant, fusionnant en local, puis en
repoussant. Voir [`ARCHITECTURE.md`](ARCHITECTURE.md) §8 pour comment la
fusion elle-même est isolée de l'état réel d'une branche (worktree
jetable, détection de conflit avant toute écriture, mise à jour de ref en
compare-and-swap).