bastien-mrq/gitfed / DESIGN.md
DESIGN.md
Code Preview
# GitFed — Serveur Git fédéré (nom de code)
## 1. Résumé
Serveur git auto-hébergé, écrit en Go, accessible en SSH (HTTP non prioritaire),
capable de **fédérer l'identité et les autorisations** entre instances indépendantes.
Un utilisateur créé sur une instance peut pousser/tirer sur un repo hébergé par
une autre instance, sans y recréer de compte, via un système de **certificats SSH
signés par l'instance d'origine**.
La donnée d'un repo ne vit qu'à un seul endroit (pas de réplication). La
fédération ne porte que sur l'**identité** et l'**autorisation**, pas sur le
transfert d'objets git (qui reste le protocole git standard).
## 2. Objectifs
- Auth SSH obligatoire pour push (clé/certificat). HTTP: lecture seule, optionnel, non prioritaire.
- Fédération d'identité inter-instances via un protocole custom (pas ForgeFed/ActivityPub).
- Gestion des repos/utilisateurs/ACL via TUI dans un premier temps.
- Pas de réécriture du protocole git : on wrappe `git-receive-pack` / `git-upload-pack`.
- UI web plus tard, branchée sur le même noyau identité/ACL.
## 3. Non-objectifs (pour l'instant)
- Pas de réplication/mirroring automatique de repos entre instances.
- Pas de fork fédéré façon Forgejo/ActivityPub.
- Pas de gestion fine de webhooks/CI dans le MVP.
- Pas de HTTP smart-protocol complet (juste lecture simple si besoin).
## 4. Vue d'ensemble de l'architecture
```
┌─────────────────────────────┐ ┌─────────────────────────────┐
│ Instance A │ │ Instance B │
│ ┌───────────────────────┐ │ │ ┌───────────────────────┐ │
│ │ CA locale (ed25519) │ │ │ │ CA locale (ed25519) │ │
│ │ signe les certs users │ │ │ │ signe les certs users │ │
│ └───────────────────────┘ │ │ └───────────────────────┘ │
│ ┌───────────────────────┐ │ HTTPS │ ┌───────────────────────┐ │
│ │ /.well-known/gitfed.json│◄┼────────┼─►│/.well-known/gitfed.json│ │
│ └───────────────────────┘ │ (trust)│ └───────────────────────┘ │
│ ┌───────────────────────┐ │ │ │
│ │ Serveur SSH │◄─────────┼── alice@instanceB.example │
│ │ - vérifie cert │ │ SSH │ (push avec certificat) │
│ │ - résout principal │ │ direct │ │
│ │ - check ACL repo │ │ │ │
│ │ - exec git-receive-pack│ │ │ │
│ └───────────────────────┘ │ │ │
│ ┌───────────────────────┐ │ │ │
│ │ Store: repos + ACL + │ │ │ │
│ │ trust store CA distantes│ │ │ │
│ └───────────────────────┘ │ │ │
└─────────────────────────────┘ └─────────────────────────────┘
```
## 5. Modèle d'identité et de confiance
### 5.1 Principe
- Chaque instance possède une paire de clés **CA** (ed25519), générée au setup.
- Chaque utilisateur local reçoit, à la connexion/login initial, un **certificat
SSH** (format OpenSSH `ssh-keygen -s`) signé par la CA de son instance.
- `principal` = `<username>@<domaine-instance>` (ex: `alice@instanceb.example`)
- `valid_after` / `valid_before` : TTL court (24–72h, configurable)
- `key_id` : identifiant unique du certificat (pour logs/audit)
- Le client présente ce certificat (pas la clé brute) à toute instance à laquelle
il se connecte.
### 5.2 Découverte et confiance inter-instances
- Chaque instance expose un endpoint HTTPS statique :
`https://<domaine>/.well-known/gitfed.json`
```json
{
"version": 1,
"domain": "instanceb.example",
"ca_public_key": "ssh-ed25519 AAAA...",
"software": "gitfed/0.1.0",
"contact": "admin@instanceb.example"
}
```
- Quand instance A voit apparaître un principal `xxx@instanceb.example` (ex :
ajouté comme collaborateur sur un repo), elle va chercher ce endpoint pour
récupérer la CA publique de `instanceb.example`, **si elle ne la connaît pas déjà**.
- Politique de confiance configurable par l'admin de l'instance :
- `whitelist` (par défaut) : l'admin doit approuver manuellement chaque nouveau
domaine avant que la CA soit ajoutée au trust store.
- `auto-trust` (opt-in) : la CA est ajoutée automatiquement dès la première
rencontre (TOFU — trust on first use).
- Une fois la CA en trust store, la vérification d'un certificat est **100%
locale** (pas d'appel réseau à chaque connexion SSH) : robustesse et rapidité.
- Révocation : gérée par le TTL court du certificat + renouvellement périodique
côté client auprès de son instance d'origine. Pas de CRL dans le MVP.
### 5.3 Pourquoi ce choix
- Évite un appel réseau "live" à chaque push (contrairement à une vérification
à la volée), donc pas de dépendance de disponibilité de l'instance d'origine
à chaque opération git.
- Décentralisé : pas de registre central unique, chaque instance décide qui
elle trust.
- Réutilise un mécanisme SSH standard et éprouvé (certificats OpenSSH), pas de
crypto maison.
## 6. Modèle d'autorisation (ACL)
- Chaque repo a une ACL locale à l'instance qui l'héberge :
```json
{
"repo": "alice/mon-projet",
"owner": "alice@instancea.example",
"collaborators": [
{ "principal": "bob@instanceb.example", "role": "write" },
{ "principal": "carol@instancec.example", "role": "read" }
]
}
```
- `role` : `read` | `write` | `admin`.
- Ajouter un collaborateur distant déclenche la résolution de confiance (§5.2)
si le domaine n'est pas encore connu.
## 7. Flux d'une opération git distante (push)
1. Bob (compte sur `instanceb.example`) est ajouté comme collaborateur `write`
sur `alice/mon-projet` hébergé sur `instancea.example`.
2. `instancea.example` résout la confiance vers `instanceb.example` (si pas déjà fait).
3. Bob se connecte en SSH à `instancea.example`, présente son certificat
signé par la CA de `instanceb.example`.
4. `instancea.example` :
- vérifie la signature du certificat contre la CA connue de `instanceb.example`
- vérifie le TTL du certificat
- extrait le principal `bob@instanceb.example`
- vérifie l'ACL du repo demandé
5. Si autorisé → exec direct de `git-receive-pack` (ou `git-upload-pack` pour un
pull) comme pour un utilisateur local. Aucune réplication, la donnée reste
sur `instancea.example`.
## 8. Structure du projet (Go)
```
gitfed/
├── cmd/
│ ├── gitfed-server/ # binaire serveur (SSH + endpoint well-known)
│ └── gitfed-tui/ # TUI admin (users, repos, ACL, trust store)
├── internal/
│ ├── ca/ # génération/signature de certificats
│ ├── ssh/ # serveur SSH (golang.org/x/crypto/ssh), handlers
│ ├── federation/ # découverte, trust store, client well-known
│ ├── acl/ # modèle ACL + persistance
│ ├── gitexec/ # wrapping git-receive-pack / git-upload-pack
│ ├── store/ # stockage repos bare + métadonnées (bbolt ou sqlite)
│ └── tui/ # composants Bubble Tea
├── DESIGN.md
└── go.mod
```
Dépendances clés pressenties :
- `golang.org/x/crypto/ssh` — serveur SSH + certificats natifs
- `charmbracelet/wish` — framework SSH-app (bâti sur x/crypto/ssh)
- `charmbracelet/bubbletea` + `lipgloss` — TUI
- `modernc.org/sqlite` ou `etcd-io/bbolt` — stockage users/ACL/trust store
- git : appelé en sous-processus (`os/exec`), pas de réimplémentation du protocole
## 9. Roadmap par phases
**Phase 0 — Squelette**
- Serveur SSH minimal, auth par clé publique brute (sans certificat encore)
- Stockage bare repos sur disque, exec direct de git
**Phase 1 — Instance locale complète**
- CA locale : génération + émission de certificats utilisateurs
- Gestion users/clés/repos via TUI
- ACL locale par repo (read/write/admin)
**Phase 2 — Fédération v0**
- Endpoint `/.well-known/gitfed.json`
- Trust store des CA distantes + politique whitelist/auto-trust
- ACL étendue aux principals distants
- Commande TUI : ajouter un collaborateur fédéré, approuver un domaine
**Phase 3 — Durcissement**
- Renouvellement de certificat (TTL court côté client)
- Audit log des accès cross-instance
- Rate limiting / anti-abus sur la découverte de domaines inconnus
**Phase 4 — UI web**
- Réutilise le même noyau identité/ACL/fédération
## 10. Questions ouvertes / à trancher plus tard
- Faut-il un mécanisme de révocation explicite (CRL-like) au-delà du TTL court ?
- Comment gérer le renouvellement de certificat si l'instance d'origine de
l'utilisateur est temporairement indisponible ?
- Format exact de nommage des repos distants côté client (`git clone
gitfed://instancea.example/alice/mon-projet` ?).
- Politique par défaut : whitelist ou auto-trust au premier lancement ?
- Multi-clé par utilisateur (plusieurs devices) : un certificat par device ou
un certificat "identité" réutilisé ?