Rapport technique d’ingénierie : écosystème gghstats v1.5.1

1. Contexte et philosophie de conception
Les données de trafic GitHub (vues et clones) ne sont conservées que dans une fenêtre glissante de 14 jours. Sans capture propre, cette télémétrie disparaît. gghstats persiste l’historique de trafic dans SQLite, avec tableau de bord, API, CLI et métriques Prometheus, au-delà de la limite de l’API.

La conception privilégie la souveraineté des données et le minimalisme opérationnel : un binaire Go + SQLite, pas de base externe obligatoire, auto-hébergement plutôt qu’un SaaS qui exige de confier un PAT à un tiers.
Séparation des responsabilités
| Dépôt | Rôle |
|---|---|
| gghstats (core) | Application : sync GitHub → SQLite, serve, API/UI, CLI, Prometheus, alertes opt-in, image GHCR / binaires. Variables d’app telles que GGHSTATS_DB, GGHSTATS_TRUSTED_PROXIES, GGHSTATS_GITHUB_TOKEN, etc. |
| gghstats-selfhosted (déploiement) | IaC opérateur : Compose (minimal / Traefik TLS / observabilité), Helm, layout GGHSTATS_HOST_DATA, pin d’image, docs VPS. Pas le code du moteur. |

Traefik, Helm, stacks Grafana/Prometheus/Loki et la convention GGHSTATS_HOST_DATA appartiennent à selfhosted. Le core ne livre que le binaire/image et la sémantique des variables d’application (ex. faire confiance à X-Forwarded-* via GGHSTATS_TRUSTED_PROXIES).
2. Installation et déploiement
Évaluation rapide (core)
| Méthode | Commande / outil | Usage |
|---|---|---|
| Script | curl -fsSL https://get.gghstats.com/install.sh | sh |
Install rapide Linux/macOS (pin : VERSION=v1.5.1 …) |
| Homebrew | brew install hrodrig/gghstats/gghstats |
Postes de travail |
| Go | go install github.com/hrodrig/gghstats/cmd/gghstats@latest |
Source / bleeding-edge |
Mode démo : gghstats serve --demo (ou GGHSTATS_DEMO=true) — UI avec données synthétiques, sans PAT ni sync réelle.
Production : qui vit où
Core (application) :
- Persistance SQLite :
GGHSTATS_DB/--db(en conteneur souvent/data/gghstats.dbavec volume sur/data). - Derrière un reverse proxy :
GGHSTATS_TRUSTED_PROXIES(CIDR/IP du pair qui parle à gghstats) pour que rate-limit / whitelist / access log utilisent le vrai client viaX-Forwarded-For/X-Real-IP. Liste vide = ignorer ces en-têtes (défaut sûr). - Image :
ghcr.io/hrodrig/gghstatssurgcr.io/distroless/static-debian13:nonroot.
Selfhosted (manifestes) : version repo ~0.1.58, pin app GGHSTATS_VERSION=v1.5.1.
GGHSTATS_HOST_DATA: répertoire hôte (ex./home/gghstats/gghstats-data) avec.env, SQLite monté en/data, et éventuellement.env.observability/ CSS de thème. Compose avec--env-file "${GGHSTATS_HOST_DATA}/.env".- Compose minimal : un service, port hôte (ex. 8080).
- Compose Traefik : HTTPS (80/443), Let’s Encrypt (
GGHSTATS_HOSTNAME,ACME_EMAIL). Rate-limit bord + app ;/metricshors routeur public (scrape interne). SSO Authelia optionnel surgghstats_edge. - Observability (optionnel, besoin Traefik /
gghstats_edge) : Prometheus, Grafana, Loki ; projet Compose-p gghstats-obs; overlay Traefik pour Grafana sur son FQDN. - Helm : chart
gghstatsdepuishttps://hrodrig.github.io/gghstats-selfhosted(ou clone./run/kubernetes/helm/gghstats) ; token dans un Secret ; PVC sur/data. - Upgrade d’image : changer
GGHSTATS_VERSION→pull+up -d(restartne suffit pas). Helper :./run/scripts/compose-stack.sh.
Les alertes (Slack / webhook / Loki / SMTP) sont du core (GGHSTATS_ALERTS_*) ; selfhosted ne fait que câbler ces vars dans Compose/Helm et documente gghstats alert test.
3. Trajectoire : v1.1.0 → v1.5.1
v1.1.0 — Pins + Featured (migration v6)
- CLI
gghstats repo/gghstats featured: les pins s’unissent à l’ensemble trafic (FILTER ∪ pins) ; Featured est une vitrine éditoriale sans trafic sur/. - Tables
pinsetfeatured(migration v6). - Page
/featured(grille néo-brutaliste) ; lien de nav seulement si le catalogue n’est pas vide.
v1.2.0 — UX Featured + nombres
- Formatage des compteurs (séparateurs /
GGHSTATS_COMPACT_NUMBERS). /featured: pagination, recherche et tri (page,per_page,q,sort,dir) viaFilterFeatured.
v1.3.0 — Statistiques d’index + JSONL
- Panneau de stats clones quotidiennes, rang/
# - %, ligne Unique sur le graphique d’index. - Export JSONL depuis l’index (résumé + historique + referrers/paths/stars par repo filtré). Né ici ; schéma API/SQLite trafic inchangé.
v1.4.0 — UX uniques + JSON Featured + horodatage UTC
- U1 : uniques en chiffre principal ; événements clones/vues en secondaire.
GET /api/v1/featured+/featureddans le sitemap (si entrées).- JSONL :
Content-Dispositionavec stamp UTCgghstats-export-YYYYMMDD-HHMM.jsonl(#23). Correctifs Carlok #22–#25 (charts locale, tooltips, Rank).
v1.5.0 — Cinq tranches de reporting
| Tranche | Contenu |
|---|---|
| F-fresh | Fraîcheur / couverture par métrique (views / clones). Statuts : fresh | delayed | missing | failed | never. Graphiques avec trous null (zéro explicite ≠ jour omis). Fetch views/clones indépendant. Migration v7 (traffic_metric_state, traffic_metric_coverage). |
| V-vis | github_visibility + report_policy (exclude > include > inherit) ; GGHSTATS_REPORT_PRIVATE ; CLI repo report ls/set. Surfaces de rapport fail-closed (404 indistinguable). Collection (filter/pins/INCLUDE_PRIVATE) séparée du reporting. Migration v8. |
| V-json | gghstats repo report ls --json (+ filtres --visibility / --policy) pour scripts post-upgrade. |
| U-legend | Légende i18n sur les charts détail : trou/null = non rapporté par GitHub ; 0 = zéro confirmé. |
| X-chart | Téléchargement JSON aligné chart (/{owner}/{repo}/traffic.json) ; dogfood …/traffic?dense=1 (sparse par défaut inchangé). |
Opérateurs : après 1.5.0, lignes existantes → unknown + inherit jusqu’au prochain sync (ou repo report set … include). L’historique SQLite n’est pas effacé ; dashboard/APIs/exports/badges peuvent paraître vides jusque-là.
v1.5.1 — Correctif Featured (#47)
En 1.5.0, Featured a été traité à tort comme surface de rapport : /featured, API, nav et sitemap exigeaient une ligne repos collectée et reportVisible. Cela casse la vitrine éditoriale (entrée Featured ≠ repo à trafic).
1.5.1 restaure le catalogue : à nouveau FilterFeatured / FeaturedCount. La visibilité de rapport reste limitée aux routes trafic/dashboard. Pas de sync nécessaire pour la vitrine. Ce n’est pas un assouplissement du fail-closed du dashboard ; le bug était le mauvais scope de Featured.
4. Données, sécurité et chaîne d’approvisionnement
Migrations SQLite pertinentes
| Version | Contenu |
|---|---|
| v6 | pins, featured |
| v7 | Fraîcheur / couverture du trafic |
| v8 | repos.github_visibility, repos.report_policy |
Ouverture en WAL pour lectures concurrentes (UI/API) pendant que le sync écrit.
Défense en profondeur (core)
- Rate limiting token-bucket par IP ; whitelist IP optionnelle ;
GGHSTATS_API_ONLY. GGHSTATS_TRUSTED_PROXIES(voir §2).- Cosign + SBOM sur les releases ; couverture de tests ≥ 80 % en release-check.
- Image distroless nonroot.
Alertes
Opt-in (GGHSTATS_ALERTS_ENABLED) : sinks Slack / webhook / Loki / SMTP ; règles après sync ; validation avec gghstats alert test. Les stacks Grafana/Loki d’observability Compose sont du déploiement selfhosted, distincts du sink Loki de l’app.
5. Clôture
v1.5.1 consolide la ligne 1.5 : fraîcheur honnête (cinq statuts + trous null), visibilité de rapport fail-closed avec migrations v7/v8, et le correctif que Featured est un catalogue éditorial, pas un scope de rapport. Le moteur reste dans gghstats ; Traefik, Helm, GGHSTATS_HOST_DATA et l’observabilité vivent dans gghstats-selfhosted, avec pin d’image v1.5.1.
Références : CHANGELOG · SPEC · README core · gghstats-selfhosted · install https://get.gghstats.com/install.sh