Gitfed
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é ?