vue-ensemble-architecture.md 6999 octets

Vue d'ensemble de l'architecture

Gitrust est une plateforme d'hébergement Git auto-hébergée construite sur le framework rustwarden-core. L'application expose deux points d'entrée réseau (HTTP + SSH) et persiste ses données dans PostgreSQL.

Vue d'ensemble

graph TB
    subgraph Clients
        Browser["Navigateur Web"]
        GitCLI["Git CLI (ssh/https)"]
    end

    subgraph "Gitrust Binary (src/main.rs)"
        Main["main()"]
    end

    subgraph "Crates Gitrust"
        Web["gitrust-web<br/><i>Routes HTTP, templates,<br/>handlers Axum</i>"]
        SSH["gitrust-ssh<br/><i>Serveur SSH (russh),<br/>auth par clé, sessions</i>"]
        SshGuard["gitrust-ssh-guard<br/><i>SecureListener, détecteurs,<br/>BanManager, AuthTracker</i>"]
        Hooks["gitrust-hooks<br/><i>impl RustwardenHooks<br/>(on_user_registered, ...)</i>"]
        Core["gitrust-core<br/><i>Models, services, migrations,<br/>rôles, types, DTOs</i>"]
        Git["gitrust-git<br/><i>Bare repos, tree browser,<br/>pack protocol (git2)</i>"]
    end

    subgraph "Framework"
        RW["rustwarden-core<br/><i>Auth, users, JWT, sessions,<br/>ResourceService, i18n</i>"]
    end

    subgraph "Stockage"
        PG[(PostgreSQL)]
        FS[("Système de fichiers<br/>bare repos .git")]
    end

    subgraph "CI/CD (optionnel)"
        CiWorker["CiWorker<br/><i>tokio::spawn, mpsc,<br/>sous-processus Dagger</i>"]
        Dagger["Dagger Engine<br/><i>Containers isolés,<br/>cache, exécution</i>"]
        CiEngine["ci-engine<br/><i>Module Dagger Python<br/>(Easy Mode)</i>"]
        Syft["Syft (optionnel)<br/><i>Génération SBOM<br/>CycloneDX</i>"]
        DTrack["Dependency-Track<br/>(optionnel)<br/><i>Analyse vulnérabilités</i>"]
    end

    Browser -->|HTTP :4000| Web
    GitCLI -->|SSH :2222| SshGuard
    SshGuard -->|"AcceptOutcome::Accepted"| SSH
    GitCLI -->|HTTPS :4000| Web

    Main --> Web
    Main --> SSH
    Main --> SshGuard
    Main --> Hooks
    Main -.->|"si CI_ENABLED"| CiWorker

    Web --> Core
    Web --> Git
    Web -.->|"admin ACL/ban"| SshGuard
    SSH --> Core
    SSH --> Git
    SSH --> SshGuard
    SshGuard --> Core
    Hooks --> Core

    Core --> RW
    Core --> PG
    Git --> FS

    CiWorker --> Core
    CiWorker -->|"dagger call"| Dagger
    Dagger -->|"Easy Mode"| CiEngine
    Dagger -->|"Power Mode"| FS
    CiWorker -.->|"si CI_SBOM_ENABLED"| Syft
    Syft -.->|"si CI_DTRACK_ENABLED"| DTrack

Démarrage de l'application

Le diagramme suivant détaille la séquence d'initialisation depuis main() jusqu'au démarrage des serveurs HTTP et SSH.

sequenceDiagram
    participant Main as main()
    participant Builder as RustwardenBuilder
    participant App as RustwardenApp
    participant Mig as AppMigrator
    participant DB as PostgreSQL
    participant SMTP as Serveur SMTP

    Main->>Main: GitrustConfig::from_env()
    Main->>Main: Arc<GitrustHooks> (hooks FS)

    Main->>Builder: builder().from_env()
    Note over Builder: Charge .env, init tracing
    Main->>Builder: .headless().auto_migrate(false)
    Main->>Builder: .merge_routes(app_routes(hooks))
    Note over Builder: Routes = framework API<br/>+ pages SSR gitrust<br/>+ Extension(hooks)
    Main->>Builder: .build().await
    Builder->>DB: Connexion pool
    Builder->>DB: Migrations rustwarden-core
    Builder->>SMTP: EmailQueueProcessor.start()
    Note over SMTP: Worker de fond : dépile<br/>email_queue toutes les 30s
    Builder-->>Main: RustwardenApp

    Main->>Mig: run_migrations(app.database())
    Mig->>DB: core_migrations() + gitrust_migrations()
    Note over DB: Tables: users, resources,<br/>repositories, ssh_keys,<br/>teams, team_members,<br/>team_repository_access
    Mig-->>Main: Ok

    Main->>Main: tokio::spawn(SSH server :2222)

    alt CI_ENABLED=true
        Main->>Main: CiWorker::start(config.ci)
        Note over Main: Vérifie dagger dans PATH<br/>Si CI_SBOM_ENABLED: vérifie syft<br/>Si CI_DTRACK_ENABLED: vérifie API DT
        Main->>Main: tokio::spawn(CiWorker loop)
        Note over Main: Écoute mpsc channel<br/>pour les jobs CI
    end

    Main->>App: app.run().await
    Note over App: HTTP :4000 démarre

Principes de conception

Séparation framework / métier gitrust

Gitrust est construit au-dessus de rustwarden-core, un framework Rust d'authentification et de gestion d'utilisateurs. Cette séparation est intentionnelle :

  • rustwarden-core gère : auth JWT, sessions, users, roles génériques, i18n, SMTP, OAuth, audit log générique.
  • gitrust-core gère : tout ce qui est spécifique à la forge Git — dépôts, clés SSH, équipes, permissions RBAC à 4 niveaux, CI, import de dépôts.

La règle fondamentale : ne jamais modifier crates/rustwarden-core/. Toute extension passe par des wrappers, des hooks (RustwardenHooks) ou des traits implémentés côté gitrust.

Mode headless

gitrust-web opère en mode headless() : les pages UI standard de rustwarden (login, register, settings) sont désactivées et réimplémentées entièrement dans gitrust-web/templates/. Cela permet un design cohérent (sidebar Git, DaisyUI) sans compromis avec l'UI générique du framework.

SSR + HTMX, zéro CDN

L'interface est rendue côté serveur via Askama (templates Rust compilés). HTMX gère les interactions dynamiques (mises à jour partielles de page, SSE pour les notifications et les logs CI). Aucun asset externe : tout CSS/JS est servi depuis static/ — imposé par la Content Security Policy du framework.

Bare repos sur le système de fichiers

gitrust-git est découplé de la base de données. Les opérations Git (navigation d'arbre, lecture de blobs, pack protocol) travaillent directement sur les bare repos ({GIT_REPOS_BASE_PATH}/{owner}/{slug}.git/) via libgit2. La base ne stocke que les métadonnées (nom, description, visibilité, ownership). Ce découplage permet de remplacer ou tester gitrust-git indépendamment.

CI hybride : Dagger comme abstraction d'exécution

Plutôt que d'implémenter un runner CI from scratch, gitrust délègue l'exécution à Dagger, qui garantit l'isolation (containers), la reproductibilité (cache) et la portabilité (local ou runner distant via SSH+rsync). Le CiWorker est une tâche Tokio dans le processus principal — pas un daemon séparé — ce qui simplifie le déploiement (un seul binaire).


Pour aller plus loin