About this project
goatdash is a lightweight, privacy-focused dashboard for [GoatCounter](https://www.goatcounter.com/) analytics. It runs entirely client-side as vanilla JavaScript with zero dependencies, no build tooling, and no backend server of its own. Deployment is simply copying a handful of static files to any web host.
## What it does
The dashboard connects to one or more GoatCounter instances via their public v0 API over HTTPS. It displays analytics data including:
- **Five KPI cards**: unique visitors (with trend), pageviews, top page, tracked paths, and total events.
- **Referrer breakdown**: traffic grouped by channel (direct, search engines, campaigns, other sites) with drill-down into individual referrers and the pages they brought.
- **Choropleth world map**: countries shaded by visit count using a square-root scale, with hover tooltips, zoom, pan, and reset.
- **Drill-down everywhere**: click any page to see its referrers, any referrer to see the pages it drove, browsers/systems/devices to their versions, countries to regions, and campaigns to their source URLs.
- **Flexible date ranges**: today, 7 days, 30 days, 90 days, or a custom start/end period.
## Multi-site support
goatdash is designed around multi-site setups where each site lives at its own domain but shares a single GoatCounter account. GoatCounter resolves the correct site from the `Host` header, so the dashboard queries each site cross-origin at its own domain. The sidebar lists all sites from `/api/v0/sites`, scoped to the API key's permissions. Site switching is fast thanks to background precaching of inactive sites.
## Architecture and stack
- **Vanilla JS only**: no React, no bundler, no CDN calls. Seven static files total.
- **No backend**: the browser speaks directly to the GoatCounter API. There is no server to patch, database to back up, or service to keep alive.
- **Service worker**: caches the app shell and versioned assets for instant reloads; API responses are cached with stale-while-revalidate.
- **Theme**: dark, light, or auto mode, switched via topbar buttons and applied before paint by an external `theme.js` script compatible with strict CSP (`default-src 'self'`).
- **Language**: Spanish, English, or auto-detect, persisted in `localStorage`.
- **Demo mode**: loads realistic sample data without an API key for exploration.
## Installation
There is no install script and nothing to compile. Serve the static files from any HTTP server:
```sh
python3 -m http.server 8000
```
Requirements: a static web server and a GoatCounter instance whose v0 API is reachable over HTTPS from the browser. No Docker, Node, or build tools needed.
For production multi-site deployments, configure your web server to serve the files from a dedicated domain (e.g., `stats.example.com`) and ensure `Cache-Control: no-store` is set on the index HTML. Asset files use version query strings (e.g., `app.js?v=3`) and must be bumped on each deploy to avoid stale cache issues.
An optional systemd-based weekly self-updater is provided (`deploy/goatdash-update.sh`) that downloads the latest GitHub release, verifies its SHA256 checksum, backs up the current installation, and swaps in the new version.
## Configuration
On first load the connect screen asks for:
- The **GoatCounter URL** of your site(s) (e.g., `https://stats.cloudless.club`).
- An **API key** created in GoatCounter under Settings > API, with at least Count and Read statistics permissions.
Both values are stored in the browser's `localStorage` and transmitted only over HTTPS to your GoatCounter instance. Theme, language, selected site, and date range are also persisted locally.
For multi-site setups, each site must have its own domain pointing to the same GoatCounter installation. GoatCounter sends `Access-Control-Allow-Origin: *`, enabling cross-origin requests without a proxy. Note that every authenticated request triggers an `OPTIONS` preflight, resulting in two round trips per API call.
## Usage
Open the page and enter your GoatCounter URL and API key, or click **Try Demo** to explore with sample data. Use the segmented control to switch date ranges, the gear menu to change theme/language or disconnect, and click any metric card to drill down into related data. The refresh menu clears caches and re-fetches everything.
## Development
The project consists of plain HTML, CSS, and JavaScript across `index.html`, `styles.css`, `theme.js`, `app.js`, `fixtures.js`, and `sw.js`. There is no `package.json`, bundler, or test harness. Local development:
```sh
python3 -m http.server 8000
```
Demo fixture data in `fixtures.js` mirrors the real API response shape.
## License
AGPL-3.0. The world map asset (`assets/world-map.js`) is retained verbatim from Abhishekh Singh's MIT-licensed goatcounter-dashboard and remains MIT.
Comments
0 Rating appears after 10 ratings
Sign in to join the discussion.