{"id":18842044,"url":"https://github.com/overshard/analytics","last_synced_at":"2026-05-08T17:36:10.938Z","repository":{"id":38890094,"uuid":"503966699","full_name":"overshard/analytics","owner":"overshard","description":"Single-binary self-hosted website analytics. Collector API, dashboards, world map, PDF reports.","archived":false,"fork":false,"pushed_at":"2026-05-07T22:59:03.000Z","size":1379,"stargazers_count":1,"open_issues_count":6,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2026-05-08T00:34:35.762Z","etag":null,"topics":["alpine","analytics","axum","docker","rust","self-hosted","sqlite","vite"],"latest_commit_sha":null,"homepage":"https://analytics.bythewood.me","language":"Rust","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-2-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/overshard.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2022-06-16T00:59:24.000Z","updated_at":"2026-05-07T22:59:07.000Z","dependencies_parsed_at":"2024-10-26T22:56:04.660Z","dependency_job_id":null,"html_url":"https://github.com/overshard/analytics","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/overshard/analytics","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/overshard%2Fanalytics","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/overshard%2Fanalytics/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/overshard%2Fanalytics/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/overshard%2Fanalytics/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/overshard","download_url":"https://codeload.github.com/overshard/analytics/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/overshard%2Fanalytics/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32790646,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-08T08:22:46.396Z","status":"ssl_error","status_checked_at":"2026-05-08T08:22:45.650Z","response_time":54,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["alpine","analytics","axum","docker","rust","self-hosted","sqlite","vite"],"created_at":"2024-11-08T02:53:31.159Z","updated_at":"2026-05-08T17:36:10.921Z","avatar_url":"https://github.com/overshard.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Analytics\n\nSelf-hosted website analytics, single-operator. One axum binary with sqlx + SQLite, minijinja templates, and a Vite + Bootstrap frontend. Tracks page views, clicks, scrolls, sessions, and custom events; renders metric cards, time-series charts, a world map with admin-1 drill-down, and PDF/markdown reports for any date range.\n\n## Features\n\n- Embed a small collector script on any number of properties (tracked sites)\n- Dashboard with 16 metric/chart/list aggregations and user-defined custom cards\n- GeoIP enrichment (auto-downloads DB-IP City Lite) and UA parsing (auto-downloads ua-parser regexes)\n- Bot traffic routed to a separate table so human dashboards never have to filter it\n- Public/private toggle per property; signed-cookie auth for the operator\n- PDF and markdown export of any dashboard view (Chromium-rendered PDF)\n- Single-binary deploy via `git push server master`\n\n## System dependencies\n\nLocal dev needs all of these on your `PATH`:\n\n| Tool | Why | Version |\n|---|---|---|\n| `rustc` / `cargo` | Build the axum binary | 2021 edition, current stable is fine (1.70+) |\n| `bun` | Frontend deps + Vite + map builder | 1.x |\n| `make` | Run the dev/build targets | any |\n| `pkg-config` + OpenSSL headers | Linked at build time on Linux | distro packages: `pkg-config`, `libssl-dev` (Debian/Ubuntu), `openssl-dev` (Alpine) |\n| `chromium` | PDF report export only; everything else works without it | any recent build |\n\nInstall hints:\n\n```sh\n# Rust toolchain (recommended via rustup)\ncurl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh\n\n# Bun\ncurl -fsSL https://bun.sh/install | bash\n\n# System libs (Debian/Ubuntu)\nsudo apt install -y build-essential pkg-config libssl-dev chromium\n\n# System libs (Alpine)\nsudo apk add musl-dev pkgconfig openssl-dev chromium\n```\n\nThe Docker build (see `Dockerfile`) reproduces this on `rust:alpine` + `alpine:3.23`. If you only care about Docker, you do not need any of the above on the host.\n\nTwo more files are downloaded into `data/` at runtime, no setup required:\n\n- `data/db.mmdb`: DB-IP City Lite (CC-BY-4.0). Refreshed if older than 30 days.\n- `data/regexes.yaml`: canonical ua-parser regexes (Apache-2.0).\n\nIf either download fails the server still boots: UA parsing falls back to a substring heuristic and GeoIP enrichment is skipped.\n\n## Quickstart\n\n```sh\ncp samplefiles/env.sample .env\n# edit .env to set ANALYTICS_PASSWORD and BASE_URL\nmake\n```\n\n`make` (alias `make run`) installs frontend deps if needed, then runs Vite watch and `cargo run` concurrently on port 8000. Visit http://localhost:8000/login.\n\nFirst boot creates a \"Proprium\" property and starts tracking the dashboard's own usage.\n\n## Configuration\n\nAll config comes from `.env` (loaded via `dotenvy`). The full set:\n\n| Variable | Required | Purpose |\n|---|---|---|\n| `ANALYTICS_PASSWORD` | yes | Single operator password |\n| `BASE_URL` | yes for prod | Used in absolute URLs (sitemap, og tags, embed snippet) |\n| `PORT` | no (default `8000`) | HTTP listen port |\n| `ANALYTICS_COOKIE_SECRET` | no | 32+ bytes for signing the session cookie. Falls back to a SHA-512 of the password, so rotating the password invalidates sessions |\n| `ANALYTICS_DATA_DIR` | no (default `./data`) | Where the SQLite db, mmdb, and regexes live. Production sets this to `/data` |\n| `ANALYTICS_ROOT` | no | Override the project root (where `templates/`, `dist/`, `migrations/`, `static_maps/` are read from) |\n| `CHROMIUM_BIN` | no | Path to chromium for PDF export. Falls back to `PATH` lookup, then a `/opt/playwright-browsers/` glob |\n\n## Make targets\n\n| Target | What it does |\n|---|---|\n| `make run` (default) | Vite watch + `cargo run` on port 8000 |\n| `make build` | Vite assets + topojson maps + release binary (`target/release/analytics`) |\n| `make start` | Run the release binary (after `make build`) |\n| `make maps` | Rebuild per-country topojson under `static_maps/` from Natural Earth |\n| `make seed` | Create or refresh a \"Seed Test\" property with realistic fake events. Override with `SESSIONS=2000 DAYS=60` |\n| `make migrate FROM=\u003cpath-to-django.sqlite3\u003e` | One-shot import of an existing Django analytics database, preserving property UUIDs so embedded snippets keep working. Add `FORCE=1` to wipe first |\n| `make pull` | rsync the production db + geoip from `git remote server` into `data/` |\n| `make push` | `git push` to every configured remote |\n| `make clean` | `cargo clean` plus removing `dist/`, `node_modules/`, the SQLite db, and the mmdb |\n\nThere are no tests or linters configured.\n\n## Deploy\n\nProduction runs on Docker. The standard flow is `git push server master` to a remote whose post-receive hook runs `docker compose up --build --detach`. Sample files in `samplefiles/`:\n\n- `Caddyfile.sample`: reverse proxy with TLS\n- `env.sample`: the same `.env` shown above\n- `post-receive.sample`: the git hook\n\nData persists to `/srv/data/analytics/` on the host (mounted into the container at `/data`).\n\n## Stack\n\n- **Backend:** axum 0.8, sqlx 0.8 against SQLite (WAL, `synchronous=NORMAL`, `busy_timeout=5s`), tower-cookies for signed sessions\n- **Templates:** minijinja 2 with a Jinja2-faithful HTML formatter\n- **Frontend:** Vite 6, Bootstrap 5 SCSS, Chart.js, d3-geo + topojson, monaspace argon font (self-hosted via `@fontsource`)\n- **Enrichment:** maxminddb (GeoIP), uaparser (UA), moka (5-minute dashboard cache)\n- **PDF:** chrome-headless-shell via `--print-to-pdf` against a temp html file\n\nSee [CLAUDE.md](CLAUDE.md) for the full architecture rundown, route table, and data model.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fovershard%2Fanalytics","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fovershard%2Fanalytics","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fovershard%2Fanalytics/lists"}