Architecture overview

Gitrust is a self-hosted Git hosting platform built on the rustwarden-core framework. The application exposes two network entry points (HTTP + SSH) and persists its data in PostgreSQL.

Overview

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
Routes HTTP, templates,
handlers Axum
"] SSH["gitrust-ssh
Serveur SSH (russh),
auth par clé, sessions
"] SshGuard["gitrust-ssh-guard
SecureListener, détecteurs,
BanManager, AuthTracker
"] Hooks["gitrust-hooks
impl RustwardenHooks
(on_user_registered, ...)
"] Core["gitrust-core
Models, services, migrations,
rôles, types, DTOs
"] Git["gitrust-git
Bare repos, tree browser,
pack protocol (git2)
"] end subgraph "Framework" RW["rustwarden-core
Auth, users, JWT, sessions,
ResourceService, i18n
"] end subgraph "Stockage" PG[(PostgreSQL)] FS[("Système de fichiers
bare repos .git")] end subgraph "CI/CD (optionnel)" CiWorker["CiWorker
tokio::spawn, mpsc,
sous-processus Dagger
"] Dagger["Dagger Engine
Containers isolés,
cache, exécution
"] CiEngine["ci-engine
Module Dagger Python
(Easy Mode)
"] Syft["Syft (optionnel)
Génération SBOM
CycloneDX
"] DTrack["Dependency-Track
(optionnel)
Analyse vulnérabilités"] 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

Starting the application

The following diagram details the initialization sequence from main() to starting the HTTP and SSH servers.

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 (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
+ pages SSR gitrust
+ 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
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,
repositories, ssh_keys,
teams, team_members,
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
Si CI_SBOM_ENABLED: vérifie syft
Si CI_DTRACK_ENABLED: vérifie API DT Main->>Main: tokio::spawn(CiWorker loop) Note over Main: Écoute mpsc channel
pour les jobs CI end Main->>App: app.run().await Note over App: HTTP :4000 démarre

Design principles

Framework/business separation gitrust

Gitrust is built on top of rustwarden-core, a Rust authentication and user management framework. This separation is intentional:

  • rustwarden-core manages: JWT auth, sessions, users, generic roles, i18n, SMTP, OAuth, generic audit log.
  • gitrust-core manages: everything specific to the Git forge — repositories, SSH keys, teams, 4-level RBAC permissions, CI, repository import.

The fundamental rule: never modify crates/rustwarden-core/. Any extension goes through wrappers, hooks (RustwardenHooks) or features implemented on the gitrust side.

Headless mode

gitrust-web operates in headless() mode: the standard rustwarden UI pages (login, register, settings) are deactivated and reimplemented entirely in gitrust-web/templates/. This allows a coherent design (Git sidebar, DaisyUI) without compromising with the generic UI of the framework.

SSR + HTMX, zero CDN

The interface is rendered server-side via Askama (compiled Rust templates). HTMX manages dynamic interactions (partial page updates, SSE for notifications and CI logs). No external assets: all CSS/JS is served from static/ — imposed by the framework's Content Security Policy.

Bare rest on file system

gitrust-git is decoupled from the database. Git operations (tree navigation, blob reading, pack protocol) work directly on bare repos ({GIT_REPOS_BASE_PATH}/{owner}/{slug}.git/) via libgit2. The database only stores metadata (name, description, visibility, ownership). This decoupling allows gitrust-git to be replaced or tested independently.

Hybrid CI: Dagger as execution abstraction

Rather than implementing a CI runner from scratch, gitrust delegates execution to Dagger, which guarantees isolation (containers), reproducibility (cache) and portability (local or remote runner via SSH+rsync). The CiWorker is a Tokio task in the main process — not a separate daemon — which simplifies deployment (a single binary).


To go further