gghstats
Self-hosted GitHub traffic dashboard that stores clones and views in SQLite beyond the 14-day API window
About gghstats
This large README is shown as a plain-text preview. Read the full README at the source
# gghstats [](https://github.com/hrodrig/gghstats/releases) [](https://github.com/hrodrig/gghstats/releases) [](https://github.com/hrodrig/gghstats/actions) [](https://codecov.io/gh/hrodrig/gghstats) [](https://gghstats.hermesrodriguez.com/hrodrig/gghstats) [](https://go.dev/) [](https://opensource.org/licenses/MIT) [](https://pkg.go.dev/github.com/hrodrig/gghstats) [](https://deps.dev/go/github.com%2Fhrodrig%2Fgghstats) **Repo:** [github.com/hrodrig/gghstats](https://github.com/hrodrig/gghstats) · **Releases:** [GitHub Releases](https://github.com/hrodrig/gghstats/releases) · **Spec:** [SPEC.md](SPEC.md) · **Operator:** [gghstats-selfhosted](https://github.com/hrodrig/gghstats-selfhosted) · **Live:** [gghstats.hermesrodriguez.com](https://gghstats.hermesrodriguez.com) · **Changelog:** [CHANGELOG.md](CHANGELOG.md) · **Roadmap:** [ROADMAP.md](ROADMAP.md)  Self-hosted dashboard and CLI for GitHub repository traffic stats. GitHub only keeps traffic for 14 days; `gghstats` keeps historical data indefinitely in SQLite. If you want your **own self-hosted** deployment (Docker Compose, Traefik with TLS, Helm, optional Prometheus/Grafana/Loki), use the companion repo **[gghstats-selfhosted](https://github.com/hrodrig/gghstats-selfhosted)** — it lists the supported options and example manifests. **Related tools (same maintainer):** - **[pgwd](https://github.com/hrodrig/pgwd)** — PostgreSQL connection watchdog ([live traffic](https://gghstats.hermesrodriguez.com/hrodrig/pgwd); deploy: [pgwd-selfhosted](https://github.com/hrodrig/pgwd-selfhosted)) - **[gghstats](https://github.com/hrodrig/gghstats)** — GitHub repo traffic beyond 14 days ([live demo](https://gghstats.hermesrodriguez.com); deploy: [gghstats-selfhosted](https://github.com/hrodrig/gghstats-selfhosted)) - **[kzero](https://github.com/hrodrig/kzero)** — bastion-first declarative workload reset ([live traffic](https://gghstats.hermesrodriguez.com/hrodrig/kzero); deploy: [kzero-selfhosted](https://github.com/hrodrig/kzero-selfhosted)) - **[groot](https://github.com/hrodrig/groot)** — Kubernetes diagnostics archive ([live traffic](https://gghstats.hermesrodriguez.com/hrodrig/groot); deploy: [groot-selfhosted](https://github.com/hrodrig/groot-selfhosted)) **Releases:** [GitHub Releases](https://github.com/hrodrig/gghstats/releases) ship binaries (tarballs/zip + checksums). **Multi-arch** container images (`linux/amd64`, `linux/arm64`) are on [GHCR](https://github.com/hrodrig/gghstats/pkgs/container/gghstats) as `ghcr.io/hrodrig/gghstats:v<version>` (same `v` prefix as the Git tag, e.g. `v0.6.0`) and `:latest`. Pushing a `v*` tag on `main` triggers the [Release workflow](.github/workflows/release.yml) (GoReleaser). Day-to-day work happens on `develop` (see [Release workflow](#release-workflow)). ## Demo **Live:** [gghstats.hermesrodriguez.com](https://gghstats.hermesrodriguez.com) CLI walkthrough (Charm [VHS](https://github.com/charmbracelet/vhs)):  Regenerate: `make install && PATH="$(go env GOPATH)/bin:$PATH" bash -c "vhs docs/demo.tape"` — see [docs/README.md](docs/README.md). ### Beyond GitHub’s 14-day Traffic tab GitHub **Insights → Traffic** shows clones and views for a **rolling 14 days** only. Older daily data disappears from the UI unless you archive it yourself. **gghstats** syncs the same GitHub API into **SQLite** so you keep daily charts and totals for as long as you run it. Same repository ([`hrodrig/gghstats`](https://github.com/hrodrig/gghstats)):  *GitHub Insights → Traffic — rolling 14-day window (May 2026).*  *Same repo on the [live demo](https://gghstats.hermesrodriguez.com/hrodrig/gghstats) — daily clones/views from first sync (Mar–May 2026). Totals differ from GitHub’s 14d rollup; see [Repository page charts](#repository-page-charts-clones--views).*  ## Table of contents - [Demo](#demo) - [Beyond GitHub’s 14-day Traffic tab](#beyond-githubs-14-day-traffic-tab) - [Features](#features) - [Compared to similar tools](#compared-to-similar-tools) - [Repository page charts](#repository-page-charts-clones--views) - [Quick start](#quick-start) - [Install](#install) - [Build](#build) - [Web UI assets (developers)](#web-ui-assets-developers) - [Usage](#usage) - [Examples](#examples) - [Configuration](#configuration) - [Data directory (SQLite paths)](#data-directory-sqlite-paths) - [Opt-in alerts](#opt-in-alerts) - [Web UI languages (i18n)](#web-ui-languages-i18n) - [Environment file](#environment-file) - [Custom UI theme (optional)](#custom-ui-theme-optional) - [HTTP API (JSON)](#http-api-json) · **[API consumer guide](docs/api.md)** (auth, examples, dogfood map) - [Typical scenarios](#typical-scenarios) - [Troubleshooting](#troubleshooting) - [Release workflow](#release-workflow) - [Security and quality](#security-and-quality) - [Database](#database) - [Community standards](#community-standards) - [Acknowledgments](#acknowledgments) - [License](#license) - [Roadmap](ROADMAP.md) · [Spec (API & sync)](SPEC.md) · [API consumer guide](docs/api.md) ## Features - **History beyond GitHub’s 14-day Traffic tab** — keep daily clones/views (and more) in SQLite for months/years instead of losing the window every two weeks - **Collects** views, clones, referrers, popular paths, and star history from the GitHub API - **Auto-discovers** repositories (or filter by org/repo rules) so you do not hand-list every repo - **Scheduled + on-demand sync** — background interval plus Sync in the UI/API so the dashboard stays fresh without manual fetches - **Web dashboard** with Chart.js graphs (stacked unique/count on the repo page) - **Clone momentum (7d / 30d)** on each repo page — see velocity vs the previous window of the same length (same formula as H2H) - **Head to Head (H2H)** at `/h2h` — compare two repos with weighted share scores (0–100, sum to 100); open *How the H2H score is calculated* on that page for the formula - **SVG badges** — embed clones/views/stars in READMEs (`/api/v1/badge/…`); copy Markdown from the repo page - **Web UI languages (i18n):** English (default), Spanish, German, French, Brazilian Portuguese, Bulgarian, and Russian — sidebar language selector, cookie `gghstats_locale`, env defaults (see [Web UI languages](#web-ui-languages-i18n)) - **Light/dark/midnight themes** in the sidebar, plus optional **custom CSS** (`GGHSTATS_CUSTOM_CSS` / [contrib/themes](contrib/themes/)) when you want to customize the dashboard further — built-in palettes and token reference in [docs/themes.md](docs/themes.md) - **JSON API** for scripts and external UIs — start with **[docs/api.md](docs/api.md)** (examples + dogfood map); contracts in [SPEC.md](SPEC.md); optional **`GGHSTATS_API_ONLY`** - **CLI** for one-shot `fetch` / `report` / `export` without running the full dashboard - **Prometheus metrics** at `GET /metrics` — scrape sync health and HTTP traffic from Grafana/Alloy - **Per-IP rate limit + optional IP whitelist** — blunt abuse on public demos without a reverse-proxy rewrite - **Single binary + SQLite** — no external database; packages for Homebrew, `.deb`/`.rpm`, FreeBSD/OpenBSD (see [Install](#install)) - **Docker** on GHCR; production Compose / Helm / observability in **[gghstats-selfhosted](https://github.com/hrodrig/gghstats-selfhosted)** - **Demo mode:** `gghstats serve --demo` (or `GGHSTATS_DEMO=true`) — try the UI with sample data, no GitHub token (sync, update check, and collector stay off) - **Opt-in alerts:** Slack / webhook / Loki sinks + traffic, ops, and **star milestone** rules after sync; smoke-test with `gghstats alert test` (see [Opt-in alerts](#opt-in-alerts)) - **Backup / restore:** `gghstats backup --output …` / `gghstats restore --input …` — snapshot SQLite before upgrades or moves ### Compared to similar tools Niche peers that **archive GitHub traffic beyond the 14-day window**. Not a full market survey (SaaS dashboards and one-off scripts omitted). **Fine-grained comparison** below focuses on the three closest traffic-archive peers: **[ghstats](https://github.com/vladkens/ghstats)**, **[git-clone-stats](https://github.com/taylorwilsdon/git-clone-stats)**, and **gghstats**. Also in the niche: **[gh-tracker](https://github.com/rayketcham-lab/gh-tracker)** (broader GitHub metrics, React + FastAPI) and **GitHub Traffic** (14-day UI only, no self-hosted archive). All labels are a **snapshot (checked 2026-07-27)** — re-verify upstream stars, releases, and defaults before you depend on a peer. #### Snapshot | | **gghstats** | [ghstats](https://github.com/vladkens/ghstats) | [git-clone-stats](https://github.com/taylorwilsdon/git-clone-stats) | |--|--------------|------------------------------------------------|---------------------------------------------------------------------| | Runtime | Go 1.26, single binary | Rust, single binary | Python (PyPI / uv) | | Release | **1.1.0** | ~0.9.0 (Jun 2026) | ~1.0.6 (~Aug 2025) | | GitHub stars (approx.) | ~5 | ~180 | ~24 | | Storage | SQLite | SQLite | SQLite or Firestore | | Container image | distroless nonroot, multi-arch | ~20 MB, non-root | ~150 MB, non-root (check upstream) | | License | MIT | MIT | MIT | | Maintenance | Very active (`0.7`→`1.0` in ~4 months) | Occasional (community PRs) | Quiet (last release ~2025) | #### What each measures | Metric | **gghstats** | **ghstats** | **git-clone-stats** | |--------|--------------|-------------|---------------------| | Clones (count + uniques) | Yes | Yes | Yes | | Views (count + uniques) | Yes | Yes | Yes | | Referrers / popular paths | Yes | Yes | No | | Star history (time series) | Yes (incremental) | Yes | Current count only | | Issues / PRs / forks on dashboard | Partial (repo metadata) | Yes (issues + PRs) | Basic stars | | Momentum 7d / 30d | Yes | — | — | | Head-to-head (compare two repos) | Yes | — | — | **ghstats** is the widest on repo metrics (issues/PRs). **gghstats** focuses on traffic + stars + comparison/momentum. **git-clone-stats** is the narrowest: clones/views/stars and badges. #### Product and UX | Capability | **gghstats** | **ghstats** | **git-clone-stats** | |------------|--------------|-------------|---------------------| | Web dashboard | Chart.js, neo-brutalist, light/dark | Own UI, tables + charts | Minimal HTML/JS, cards + charts | | i18n | EN / ES / DE / FR / PT | — | — | | README SVG badges | Yes (native) | — | Yes (shields.io style) | | Demo without token | Yes (`--demo`) | — | — | | Repo management in UI | Auto-discover + filter | Auto-discover + filter | **Add/remove in UI** | | JSON API | Rich (repos, traffic, stars, popular, H2H, charts, sync) + **API-only mode** | `/api/repos` only | `/api/stats`, chart-data, badge | | Prometheus | Yes (`/metrics`) | — | — | | Alerts (Slack / webhook / Loki / SMTP) | Yes | — | — | | Backup / restore CLI | Yes | — (volume only) | DB export/import | | One-shot CLI | `fetch` / `report` / `export` / `backup` | Limited | `sync` + `server` | #### Sync and filters | | **gghstats** | **ghstats** | **git-clone-stats** | |--|--------------|-------------|---------------------| | Default interval | `1h` | Hourly cron (`0 59 * * * *`) | daily / weekly / biweekly | | Parallel workers | Yes (pool, default 4) | Serial (simple design) | CLI sync / background | | Filter syntax | `owner/*`, `!fork`, `!archived` | Very similar (`GHS_FILTER`) | Username + org + manual add in UI | | Star history | Incremental (skip when count unchanged) | History sync | No historical series | | Retries / rate limit | Backoff + jitter + `X-RateLimit-Reset` | Basic read timeout | Python implementation | | Private repos | `GGHSTATS_INCLUDE_PRIVATE` | `GHS_INCLUDE_PRIVATE` | Token `repo` scope | **gghstats** and **ghstats** filters are almost siblings (same problem, same `!fork` / `!archived` rules). #### Security and ops | | **gghstats** | **ghstats** | **git-clone-stats** | |--|--------------|-------------|---------------------| | Default bind | `127.0.0.1` (since 0.7) | `0.0.0.0` | Typical `0.0.0.0` / port flag | | Trusted proxies | Yes (`GGHSTATS_TRUSTED_PROXIES`) | Not documented | No | | Rate limit + IP whitelist | Yes | — | — | | Security headers + CSP | Yes | Basic | Basic | | HTTP server timeouts | Yes (configured) | API read timeout | — | | Supply chain | Cosign + SBOM + coverage ≥80% | Small image | Multi-stage Docker | | Native packaging | `.deb` / `.rpm` / Homebrew / FreeBSD / OpenBSD | Docker-first | PyPI + Docker + **App Engine** | | Ops companion | **[gghstats-selfhosted](https://github.com/hrodrig/gghstats-selfhosted)** (Compose, Traefik, Helm, Grafana) | Compose in repo | Compose + GAE guide | **gghstats** leads on hardening and server-grade packaging. **git-clone-stats** wins if you want **Firestore + Google App Engine** without running a VPS. #### When to choose **Choose gghstats if** you want a **1.0-stable** contract, full CLI, Prometheus, alerts, i18n, H2H/momentum, badges, and demo mode; you deploy on a VPS or behind Traefik and care about trusted proxies, rate limits, distroless images, and Cosign; you prefer **Go** (single binary, no Python/Rust toolchain on the host); you want native packaging (Homebrew, deb/rpm, BSD ports) in addition to Docker. **Choose ghstats if** you want the **lightest, most popular** niche option (~180 stars, ~20 MB image); dashboard + `/api/repos` + solid filters are enough; you prefer **Rust** and a deliberately minimal setup; you do not need badges, alerts, Prometheus, i18n, or repo comparisons. **Choose git-clone-stats if** you live in **Python / PyPI / uv** and want `pip install` plus a `sync` / `server` CLI; you care about **Firestore** or **Google App Engine**; you want **repo management in the UI** (add/remove) rather than aggressive auto-discovery; shields.io badges and a minimal HTML front end without a build step are enough; you do not need referrers/paths, H2H, Prometheus, or gghstats-level hardening. **Also consider:** **[gh-tracker](https://github.com/rayketcham-lab/gh-tracker)** for people/commits/releases breadth with a React UI; **GitHub Traffic** when 14 days in the GitHub UI is enough. #### Relationship (honest) - **gghstats** acknowledges **[ghstats](https://github.com/vladkens/ghstats)** as prior art in the same space (Rust, SQLite, traffic >14d) and **[git-clone-stats](https://github.com/taylorwilsdon/git-clone-stats)** for badges and a minimal dashboard idea. **gghstats** is not a fork — independent **Go** code. - **ghstats** is the adoption and minimalism benchmark in this niche. - **gghstats** has invested most in **1.x product + ops + security** in a short window. - **git-clone-stats** is the most **scriptable / cloud-friendly** of the three. #### One-line summary | Project | In one sentence | |---------|-----------------| | **gghstats** | Full Go dashboard/ops stack, self-hosted production-ready with a frozen **1.0** API contract. | | **ghstats** | Minimal, popular **Rust** option: small container, traffic archive, little else. | | **git-clone-stats** | **Python/PyPI** path with badges and a route to Firestore / App Engine. | ### Repository page charts (Clones & Views) On each repository’s detail page, the **Clones** and **Views** bar charts are **stacked** from GitHub’s daily traffic API: | Segment | Meaning | GitHub field | |--------|---------|--------------| | **Lower** (theme primary color) | Unique visitors or cloners that day | `uniques` | | **Upper** (theme info color) | Total views or total clones that day | `count` | Exact colors depend on light/dark theme (Bootstrap `--bs-primary` / `--bs-info`, overridden in the app’s neo-brutalist CSS). Chart **legends** (e.g. Unique / Count) follow the active UI locale. Use the tooltip on each bar for values. ### Traffic freshness and coverage GitHub Traffic is a rolling response and can be late or temporarily incomplete. For each repository and each independent metric (**views** and **clones**), gghstats persists the last successful fetch time, latest observed UTC date, latest response coverage, status, and last error in SQLite. The repository page and traffic API therefore show one of: **Fresh**, **Delayed**, **Missing completed days**, **Fetch failed**, or **Never synced**. - A row explicitly returned by GitHub with `count: 0` is a **confirmed zero**. - An absent date is **unknown**, never inferred as zero. Detail-chart payloads retain every UTC calendar date and encode unknown count/unique values as `null`, so Chart.js renders a gap rather than compressing or zero-filling it. - Freshness and missing-day checks use completed **UTC** days only. The current UTC day is deliberately not considered missing because GitHub commonly publishes it late. - A later GitHub response can revise a daily count; the successful response replaces that day’s stored traffic values and coverage atomically. A newer partial response cannot make an older cached row look confirmed inside its coverage window. - Views and clones are fetched independently. If one fails, the successful sibling data remains stored, the failed metric exposes its error state, and the repo/run is not reported as fully successful. ### Why doesn’t the index chart show per-repo coverage? The index aggregate chart is intentionally narrower: it can show totals from repositories with different coverage and does **not** expose a per-repository coverage matrix. Use an individual repository traffic chart and its freshness state when completeness matters. Some established calculations (H2H momentum and rolling aggregate counters) intentionally treat absent days as zero; that is calculation semantics, not a statement that GitHub reported zero traffic. [Back to top](#gghstats) ## Quick start The steps below are a **minimal path** to install gghstats, run the dashboard once, and **try the UI** (repository list, charts, H2H, languages). They are **not** a production or server deployment. ### Demo mode (no GitHub token) ```bash gghstats serve --demo --open # or: GGHSTATS_DEMO=true gghstats run --open ``` Uses a separate sample dataset (`demo/alpha`, `demo/beta`, `demo/gamma`) when the DB is empty. Sync and update-check are disabled. For real traffic, unset demo and set `GGHSTATS_GITHUB_TOKEN`. **Running gghstats on a VPS, with TLS, Traefik, Compose stacks, Helm, or observability** — use **[gghstats-selfhosted](https://github.com/hrodrig/gghstats-selfhosted)**. That repository owns production manifests, env layout (`GGHSTATS_HOST_DATA`), and operator docs; this repo ships the application binary and image only.
Frequently Asked Questions
What is gghstats?
gghstats is a Third-party APIs library for the Go programming language. Self-hosted GitHub traffic dashboard that stores clones and views in SQLite beyond the 14-day API window
How do I install gghstats?
Install gghstats with the Go module system using `go get hrodrig/gghstats`. Check the repository for the current installation instructions.
What category does gghstats belong to?
gghstats is listed under Third-party APIs, specifically Third-party APIs.