Privacy-first, cookie-free web analytics with a daily email digest. A lightweight, self-hostable alternative to Google Analytics — built entirely on the Cloudflare edge.
abacuslytics.com · Live demo · Report a bug · Request a feature
Most analytics tools bury the truth under dashboards you never open and cookies you have to apologize for. Abacus keeps it to the metrics that matter — visitors, pageviews, top pages, sources, countries — and brings them to you: drop in one tiny script and wake up to a clean summary of yesterday in your inbox.
- 📨 Daily email digest — the whole story in the time it takes to drink your coffee. No login required.
- 🍪 No cookies, no consent banners — nothing personal is stored, so there's nothing to consent to. GDPR / CCPA / PECR friendly by design.
- ⚡ Sub-1KB script — async, no layout shift, won't dent your Core Web Vitals.
- 🌍 Runs on the edge — fast ingestion everywhere, and your data stays yours.
- 📈 One screen, no maze — visitors, sources, pages, countries and goals, minus the GA4 labyrinth.
- 🔒 Privacy by design — visitor counts use a daily-rotating, irreversible hash. We literally can't follow anyone across sites or days.
┌────────────────────── one Cloudflare Worker ──────────────────────┐
your site │ │
<script>───┼──▶ /api/event ──▶ Workers Analytics Engine (raw events) │
│ │ │
dashboard ◀┼── Astro SSR ◀── D1 (sites, rollups, subscriptions) ◀── nightly cron│
│ │ │
inbox ◀───┼──────────────── Resend ◀── nightly cron (digest) ◀────────────────┘
└───────────────────────────────────────────────────────────────────┘
Raw pageview/event hits stream into Workers Analytics Engine (cheap, high-volume time series). A nightly cron rolls each day's data into D1 (SQLite) for historical ranges and emails every subscriber their digest via Resend. The live dashboard queries Analytics Engine directly.
| Layer | Choice |
|---|---|
| Framework | Astro 6 (SSR) → a single Cloudflare Worker |
| Auth | Clerk |
| Event store | Workers Analytics Engine |
| App data | D1 (SQLite) via Drizzle ORM |
| Resend | |
| Styling | Tailwind v4 + a hand-built editorial design system |
| Tooling | Bun workspaces monorepo |
apps/web Astro app → the single Worker (marketing + dashboard + /api/event + cron)
packages/tracker Embeddable, dependency-free tracking snippet → builds to apps/web/public/abacus.js
packages/db Shared D1 schema (Drizzle) + migrations
The Worker's custom entrypoint (apps/web/src/worker.ts) exposes both a fetch handler (Astro)
and a scheduled handler (the nightly digest), so everything ships as one deploy.
Prerequisites: Bun 1.3+, a free Clerk app, and (optionally) a Resend account for the email digest. Deploying needs a Cloudflare account.
git clone https://github.com/abacuslytics/abacus.git
cd abacus
bun installClerk reads its keys from Vite (import.meta.env), so they go in apps/web/.env:
cat > apps/web/.env <<'EOF'
PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_xxx
CLERK_SECRET_KEY=sk_test_xxx
# Optional: work on the dashboard/demo without signing in
PUBLIC_DISABLE_AUTH=true
EOFCloudflare runtime bindings (D1 / Analytics Engine / Resend / the visitor-hash salt) go in
apps/web/.dev.vars — copy the example and fill it in:
cp .dev.vars.example apps/web/.dev.varsℹ️
.envand.dev.varsare git-ignored — never commit real keys.
bun run tracker:build # builds apps/web/public/abacus.js
bun run db:generate # generate SQL from the Drizzle schema (already committed)
bun run db:migrate # apply migrations to a local D1 database
bun run db:demo # (optional) seed the public "live demo" site with databun run dev # http://localhost:4321Analytics Engine note: AE has no local query API —
writeDataPointruns locally but is a no-op for reads. WithPUBLIC_DISABLE_AUTH=trueand the seeded demo data, you can explore the full dashboard offline; live numbers require deploying withCF_ACCOUNT_ID+CF_AE_API_TOKEN.
<script defer data-domain="yourdomain.com" src="https://abacuslytics.com/abacus.js"></script>Track custom goals with the global helper:
<button onclick="window.abacus('signup')">Sign up</button>SPA route changes are counted automatically. Exclude yourself with
localStorage.abacus_ignore = 'true', or skip paths with data-exclude="/admin/*".
| Variable | Where | Purpose |
|---|---|---|
PUBLIC_CLERK_PUBLISHABLE_KEY |
.env |
Clerk frontend key |
CLERK_SECRET_KEY |
.env |
Clerk backend key |
PUBLIC_DISABLE_AUTH |
.env |
Dev-only: bypass Clerk and use a fixed dev user |
VISITOR_HASH_SALT |
.dev.vars / secret |
Salt for the daily-rotating visitor hash |
RESEND_API_KEY |
.dev.vars / secret |
Sends the daily digest + early-adopter signups |
RESEND_AUDIENCE_ID |
.dev.vars / secret |
Resend audience for early-adopter signups |
CF_ACCOUNT_ID / CF_AE_API_TOKEN |
.dev.vars / secret |
Query Analytics Engine via its SQL API |
# create the remote resources (one time), then paste their IDs into apps/web/wrangler.jsonc
wrangler d1 create abacus-db
wrangler kv namespace create SESSION
# create an "abacus_events" Analytics Engine dataset in the Cloudflare dashboard
# set production secrets
wrangler secret put CLERK_SECRET_KEY
wrangler secret put RESEND_API_KEY
wrangler secret put VISITOR_HASH_SALT
# …etc
bun run deploy # builds the tracker + Astro, then `wrangler deploy`- Weekly / monthly digest cadences
- Custom events & goal conversion funnels
- Team members & shared sites
- Public dashboards
- Import from Google Analytics / Plausible
- First-class WordPress / Next.js / Ghost plugins
Have an idea? Open an issue — we'd love to hear it.
Abacus is early and contributions of every size are welcome — code, docs, design, bug reports, or just telling a friend.
- Fork the repo and create a branch:
git checkout -b feat/my-thing bun install, thenbun run devto work locally (setPUBLIC_DISABLE_AUTH=trueto skip auth)- Before pushing:
bun run typecheckshould pass - Open a pull request describing the change
Good first contributions: new tracker plugins, dashboard widgets, digest email polish, docs, and
accessibility fixes. Look for issues tagged good first issue.
If Abacus is useful to you, the best ways to help:
- ⭐ Star the repo — it genuinely helps others find it
- 🐛 File issues and help triage
- 🛠️ Send a PR — even a typo fix counts
- 📣 Spread the word — a blog post or tweet goes a long way
Abacus is licensed under the GNU AGPL-3.0 — see LICENSE. In short: it's free and open source, and if you run a modified version as a network service, you must share your changes under the same license. (Same spirit as Plausible and PostHog.)
Built with Astro, the Cloudflare Developer Platform, Clerk, Drizzle ORM, Resend, and Chart.js. Inspired by the lovely simplicity of Plausible.
abacuslytics.com · No cookies were set reading this README.