Add website analytics
Release / release (push) Successful in 3m49s
CI / test (push) Successful in 4m45s

A Tianji-style Websites section: a small tracking script, a public collection endpoint, and a
page per website with visitors, page views, visits, bounce rate, a chart and top lists for pages,
referrers, browsers, systems, devices, countries, languages and events. No cookies and no stored
IP address or user agent. SANDBAR_TRUST_PROXY makes the client address come from the proxy.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
VeillaxandClaude Sonnet 5.5 committed 2026-10-27 04:22:55 -06:00
1 parent 4ea4fe5289
commit b4fd31a182
13 files changed
+843 -22

No files matched your search

+34 -3
View File
@@ -3,12 +3,13 @@
Self-hosted notifications, monitoring, on-call and incident response in one small program.
One binary, one SQLite file, and everything is done in the web UI.
Sandbar covers the ground of four tools:
Sandbar covers the ground of five tools:
| Area | Like | What it does |
|---|---|---|
| **Topics** | ntfy | Publish a message to a topic with one HTTP request. Subscribe in the browser, with `curl`, or with the ntfy phone app |
| **Monitors** | Tianji, Uptime Kuma | Website, port, DNS and certificate checks, heartbeats for cron jobs, and a public status page |
| **Websites** | Tianji, Plausible, Umami | Page views, visitors, referrers, browsers, countries and custom events for your own sites, with no cookies and no personal data |
| **On-call** | Opsgenie | Rotating schedules, escalation policies, and alerts that are acknowledged, snoozed or resolved |
| **Incidents** | incident.io | Declare, assign, update and close an incident, with a timeline and a post-mortem |
@@ -112,7 +113,8 @@ new releases itself, in Docker and as a plain binary, on Linux and macOS.
### HTTPS and a reverse proxy
Sandbar serves plain HTTP, so put a reverse proxy in front of it and set `SANDBAR_URL` to the public address, since
links in notifications use it. Caddy needs one line:
links in notifications use it. Also set `SANDBAR_TRUST_PROXY=1`, so website analytics and the sign-in limit see the
real client address instead of the proxy's. Caddy needs one line:
```
sandbar.example.com {
@@ -200,6 +202,35 @@ in a small per-day table, so the bars go back 90 days even though raw check resu
Monitors cannot probe private or internal addresses unless you tick **Allow targets on private networks** on that
monitor, so a user who can add a monitor cannot use Sandbar to scan your network.
## Websites (analytics)
Add a website on the Websites page, then paste its one-line tracking code into the head of your pages:
```html
<script defer src="https://sandbar.example.com/script.js" data-website="WEBSITE_ID"></script>
```
The page for each website shows visitors, page views, visits, bounce rate and who is online now, a chart over the last
24 hours, 7, 30 or 90 days, and top lists for pages, referrers, browsers, operating systems, devices, countries,
languages and events.
- **Single-page apps** are counted as people move between pages. Add `data-hash` to count `#hash` changes as pages,
or `data-auto="false"` to send nothing until your code calls `sandbar.view()`.
- **Events:** call `sandbar.track("signup", { plan: "pro" })` from your code.
- **Private by design:** no cookies, nothing stored in the browser, no IP address or user agent kept (only the labels
"Chrome", "macOS", "Mobile" and so on), and nothing is sent when the visitor has Do Not Track on. A visitor is a hash
of the address, the browser and the website with a secret and the date, so the same person is one visitor for a day
and cannot be followed beyond it. Query strings are dropped from paths and only the referrer's host is kept.
- **Domain:** a website's optional domain makes Sandbar ignore visits from other hosts. Bots, and anything sending
more than 120 hits a minute from one address, are ignored.
- **Countries** come from the `CF-IPCountry` or `CloudFront-Viewer-Country` header, so they appear only behind
Cloudflare or CloudFront. Sandbar bundles no location database. Without the header, visits count as Unknown.
- **Behind a reverse proxy,** set `SANDBAR_TRUST_PROXY=1` so the client address comes from `X-Forwarded-For`. Without
it every visitor looks like the proxy. Only set it when the proxy is the only way to reach Sandbar.
- Hits are kept for `SANDBAR_ANALYTICS_DAYS` (default 365).
Not included: funnels, goals, session replay, a public share link, and comparison with the previous period.
## On-call
- A **schedule** rotates through people, handing over every N hours from a start time. **Overrides** cover a
@@ -256,7 +287,7 @@ than their retention are pruned hourly. Back up by copying the data folder while
- One instance, one organization. SQLite means no clustering.
- No ICMP ping monitor, no SMS or voice, and no two-factor or single sign-on yet.
- Sandbar serves plain HTTP. Put it behind HTTPS and set `-url`, or keep it on a private network.
- Sign-in attempts are limited to 10 per 10 minutes per address.
- Sign-in attempts are limited to 10 per 10 minutes per address (the client address, see `SANDBAR_TRUST_PROXY`).
- Tests: `go test ./...`. Style: `node scripts/lint-design.mjs` (see `docs/design.md`).
## License