Gitfed
bastien-mrq/gitfed/ Commits/ 3796f22

Add ROADMAP.md and gitfed-tui -set-public/-set-private for v1.0.0

ROADMAP.md separates what's rejected outright (SSO, PAT auth, a federation directory) from what's deferred on complexity/priority grounds (in-browser conflict resolution, inline diff comments, alternate merge strategies, scheduled backups, enforced NetworkPolicy) from open candidates for later — grounded in decisions already made during development, not new speculation. Linked from both READMEs. gitfed-tui gains -set-public/-set-private <repo>: flips visibility non-interactively over `kubectl exec`, so that one operation is scriptable without driving the TUI's keystrokes against a live instance.

bastien-mrq 2026-07-28 23:23 commit 3796f22627c0961a550b310482e73b1786e16045 parent ebb2dbc58c6c7a1e5482df54e7955e8b7c040d60
5 files changed +115 −0
M CHANGELOG.md +6 −0
M README.fr.md +1 −0
M README.md +1 −0
A ROADMAP.md +84 −0
M cmd/gitfed-tui/main.go +23 −0
CHANGELOG.md
diff --git a/CHANGELOG.md b/CHANGELOG.md index d2b09f8..4c0249f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 1.0.0 + +First stable release. Federated SSH-certificate identity, per-repo/per-role ACLs, a self-service web UI (repo browser, commit history and detail, merge requests with review and a real server-side merge, pinned repos, federated notifications), anonymous read-only HTTPS clone for public repos, and a documented backup/restore procedure — see `README.md` for the full picture and `ROADMAP.md` for what was deliberately left out and why. + +Also adds `gitfed-tui -set-public <repo>` / `-set-private <repo>`: a non-interactive way to flip a repo's visibility over `kubectl exec`, without driving the interactive TUI's keystrokes against a live instance. + ## 0.10.3 - A file path outside the repo tree (e.g. `?path=../../../etc/passwd`) returned a raw 500 instead of the usual 404 — git itself already rejects it (`relative path syntax can't be used outside working tree`), so nothing was ever exposed, but gitfed didn't recognize that error as "not found" like it does every other one. Found while re-checking the older file-browsing code for the same bug class as 0.10.1's fix.
README.fr.md
diff --git a/README.fr.md b/README.fr.md index 43221a4..77d933d 100644 --- a/README.fr.md +++ b/README.fr.md @@ -68,6 +68,7 @@ instance en fonctionnement. | [`docs/security/AUDIT-2026-07-29.md`](docs/security/AUDIT-2026-07-29.md) | Audit de suivi couvrant le clone HTTPS anonyme, les notifications fédérées et les dépôts épinglés ajoutés depuis. | | [`docs/security/AUDIT-2026-07-29b.md`](docs/security/AUDIT-2026-07-29b.md) | Audit des merge requests — a trouvé et corrigé une faille critique non authentifiée (écriture de fichier arbitraire via des hash de commit / noms de branche forgés), plus deux correctifs de moindre sévérité. | | [`DESIGN.md`](DESIGN.md) | Le document de conception d'origine — le « pourquoi » derrière l'architecture, écrit avant le début de l'implémentation. | +| [`ROADMAP.md`](ROADMAP.md) | Ce qui a été volontairement laissé de côté pour la V1 et pourquoi, ce qui est rejeté purement et simplement, et ce qui pourrait venir ensuite. | | [`CHANGELOG.md`](CHANGELOG.md) | Historique des versions (également servi sur `/changelog` dans l'interface web). | | [`THIRD_PARTY_LICENSES.md`](THIRD_PARTY_LICENSES.md) | Chaque dépendance Go et sa licence. |
README.md
diff --git a/README.md b/README.md index 473e9ba..a58fba0 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,7 @@ instance. | [`docs/security/AUDIT-2026-07-29.md`](docs/security/AUDIT-2026-07-29.md) *(French)* | Follow-up audit covering the anonymous HTTPS clone, federated notifications and pinned repos added afterward. | | [`docs/security/AUDIT-2026-07-29b.md`](docs/security/AUDIT-2026-07-29b.md) *(French)* | Audit of merge requests — found and fixed a critical, unauthenticated argument-injection bug (arbitrary file write via crafted commit hashes/branch names), plus two lower-severity fixes. | | [`DESIGN.md`](DESIGN.md) *(French)* | The original design rationale — the "why" behind the architecture, written before implementation started. | +| [`ROADMAP.md`](ROADMAP.md) *(French)* | What was deliberately left out of V1 and why, versus what's rejected outright versus what might come later. | | [`CHANGELOG.md`](CHANGELOG.md) | Version history (also served at `/changelog` in the web UI). | | [`THIRD_PARTY_LICENSES.md`](THIRD_PARTY_LICENSES.md) | Every Go dependency and its license. |
ROADMAP.md
diff --git a/ROADMAP.md b/ROADMAP.md new file mode 100644 index 0000000..f01c182 --- /dev/null +++ b/ROADMAP.md @@ -0,0 +1,84 @@ +# Feuille de route + +Ce document liste ce qui a été volontairement laissé de côté pour la V1, +pourquoi, et ce qui pourrait venir ensuite — pas un engagement de +calendrier, juste une trace de l'état des décisions pour ne pas avoir à +re-débattre les mêmes questions plus tard. Voir [`DESIGN.md`](DESIGN.md) +pour le rationnel d'origine, et les entrées `## X.Y.Z` de +[`CHANGELOG.md`](CHANGELOG.md) pour ce qui a réellement été livré. + +--- + +## 1. Rejeté, pas juste reporté + +Ces points ont été explicitement discutés et écartés — les rouvrir +demande une vraie raison nouvelle, pas juste "ce serait bien d'avoir". + +- **SSO / connexion fédérée pour l'interface web.** Le modèle d'identité + de gitfed (certificats SSH courte durée, voir + [`docs/HOW_IT_WORKS.md`](docs/HOW_IT_WORKS.md) §2) couvre déjà l'accès + git ; ajouter du SSO pour le web ouvrirait une deuxième surface + d'authentification fédérée à maintenir et à faire confiance, pour un + gain limité puisque le mot de passe web n'ouvre déjà qu'une session + opaque sans droit git (§4 du même document). +- **Authentification par jeton (PAT) pour le clone HTTPS.** Le clone HTTPS + anonyme couvre déjà le seul cas légitime (lecture publique sans + compte) ; un jeton ajouterait un deuxième mécanisme d'écriture par HTTP + à côté de SSH, contraire au principe "une seule porte à surveiller" + ([`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) §6-7). +- **Annuaire / découverte publique d'instances fédérées.** Le modèle + retenu est que chaque personne ajoute elle-même l'URL des instances + auxquelles elle a accès — un annuaire centralisé recréerait exactement + le point de couplage fort que la fédération pair-à-pair est censée + éviter. + +## 2. Reporté — complexité ou priorité, pas un refus de principe + +- **Résolution de conflit dans le navigateur.** Les merge requests + détectent les conflits et bloquent la fusion avec la liste précise des + fichiers concernés, mais la résolution se fait en local (`git pull` + + fusion + push) — un éditeur de conflit web est un morceau nettement + plus gros, à faire seulement si le besoin se confirme à l'usage. +- **Commentaires de diff en ligne (par ligne).** Les merge requests n'ont + qu'un fil de discussion général pour l'instant — des commentaires + ancrés sur une ligne précise demandent de gérer leur position au fil + des nouveaux push (rebase, force-push, etc.), volontairement pas fait + dans la première version. +- **Stratégies de fusion alternatives (squash, rebase, fast-forward + seul).** Le bouton "Fusionner" ne fait qu'un commit de fusion classique + (`--no-ff`) systématiquement. Réévaluer si l'historique en devient trop + bruyant en pratique. +- **Sauvegardes planifiées automatiquement.** `deploy/backup.sh` existe + et fonctionne, mais rien ne le déclenche tout seul — c'est à + l'opérateur de le brancher sur un cron/timer. Pas de stockage hors-site + (S3 ou équivalent) prévu non plus pour l'instant. +- **`NetworkPolicy` réellement appliquée.** Le manifeste existe + (`deploy/k8s/networkpolicy.yaml`) mais k3s + flannel (la configuration + par défaut de ce cluster) ne l'applique pas — nécessiterait de changer + de CNI (Cilium, Calico...), pas fait tant que ce cluster reste + mono-tenant. +- **Minuterie de renouvellement pour `gitfed-renew-cert`.** L'outil existe + côté client, mais aucun exemple de timer systemd/cron n'est fourni — + chacun le met en place lui-même pour l'instant. + +## 3. Candidats pour plus tard, sans priorité arrêtée + +Des idées qui reviendraient naturellement si l'usage le justifie, mais +qui ne sont pas des manques identifiés aujourd'hui : + +- Recherche de code (pas seulement par nom de dépôt/sujet). +- Intégration CI / webhooks sortants sur push ou fusion. +- ACL par équipe/organisation, en plus de l'ACL par personne actuelle. +- Import/miroir depuis un dépôt externe (GitHub, etc.). + +## 4. Ce qui ne changera probablement jamais + +Rappel du principe directeur (voir les deux README) : **gitfed n'essaie +pas de rivaliser avec GitHub ou GitLab sur le nombre de fonctionnalités.** +Le modèle d'identité fédérée est le sujet ; l'interface web existe pour le +rendre utilisable au quotidien, pas pour cocher toutes les cases d'un +forge complet. Un fork inter-dépôts (pull request cross-repo façon +GitHub) n'est notamment pas prévu : le modèle actuel est un dépôt partagé +avec des collaborateurs à accès explicite, pas un graphe de forks — ça +resterait vrai même si des organisations/équipes étaient ajoutées un +jour.
cmd/gitfed-tui/main.go
diff --git a/cmd/gitfed-tui/main.go b/cmd/gitfed-tui/main.go index 4e3e6fe..895627f 100644 --- a/cmd/gitfed-tui/main.go +++ b/cmd/gitfed-tui/main.go @@ -21,6 +21,8 @@ import ( func main() { configPath := flag.String("config", "gitfed.json", "path to instance config file") + setPublic := flag.String("set-public", "", "non-interactive: make <repo> public and exit, skipping the TUI") + setPrivate := flag.String("set-private", "", "non-interactive: make <repo> private and exit, skipping the TUI") flag.Parse() cfg, err := config.Load(*configPath) @@ -37,6 +39,27 @@ func main() { } defer closeFn() + // -set-public/-set-private exist so this one operation is scriptable + // (e.g. over `kubectl exec`) without driving the interactive TUI's + // keystrokes against a live instance — every other admin action still + // goes through the TUI on purpose. + if *setPublic != "" || *setPrivate != "" { + if *setPublic != "" && *setPrivate != "" { + fmt.Fprintln(os.Stderr, "gitfed-tui: pass only one of -set-public / -set-private") + os.Exit(1) + } + repo, public := *setPublic, true + if *setPrivate != "" { + repo, public = *setPrivate, false + } + if err := ops.SetRepoPublic(repo, public); err != nil { + fmt.Fprintf(os.Stderr, "gitfed-tui: set-public %s: %v\n", repo, err) + os.Exit(1) + } + fmt.Printf("%s is now %s\n", repo, map[bool]string{true: "public", false: "private"}[public]) + return + } + p := tea.NewProgram(newModel(ops, cfg.Domain, mode), tea.WithAltScreen()) if _, err := p.Run(); err != nil { fmt.Fprintf(os.Stderr, "gitfed-tui: %v\n", err)