seyr
Cookieless, no-PII web analytics with a sub-1KB tracker, a columnar event store, and multi-tenant billing — a self-hostable alternative to Google Analytics.
The problem
Site owners want per-site traffic insight without cookie banners, consent friction, or handing visitor data to a third party. seyr delivers fast aggregate dashboards while storing no cookies and no personal data — GDPR/CCPA-friendly by design — for small-to-mid operators and multi-site agencies.
Challenges
Identifying visitors without cookies or PII
Counting unique visitors normally means a cookie or a device fingerprint — both of which the privacy promise rules out, and both of which treat raw IP and user-agent as personal data. Instead, a visitor is identified by a one-way hash derived from their IP, user-agent, and the site domain, salted with a value that rotates every day. The IP and UA are used only long enough to compute location and device, then discarded. Because the salt changes daily the same person cannot be linked across days, and folding in the domain stops any visitor being tracked from one site to another.
High-volume events with instant aggregate reads
Analytics is write-heavy but read-latency-sensitive: millions of raw events must ingest cheaply, yet a dashboard has to answer "visitors this month" instantly. The event store is a columnar OLAP database that compresses repetitive fields aggressively and enforces a retention ceiling, and the counts that dashboards ask for most — pageviews and unique visitors per day — are continuously pre-aggregated into daily rollups so common date ranges never scan the raw table at all.
A sub-1KB tracker that still handles SPAs
A tracking script that bloats a customer's page defeats the "fast" selling point, so the embeddable snippet is held under 1KB by a build-time size budget that fails the build if it regresses. It sends data with the browser's beacon API (falling back to a keep-alive request), hooks into client-side navigation so single-page apps still register each route change as a pageview, respects Do Not Track, and posts to a deliberately neutral endpoint path so ad-blockers don't flag it.
Per-tenant quota enforcement at ingest speed
Every incoming event has to be attributed to a paying tenant and checked against a monthly plan limit — without a database round-trip slowing the hot path. Domain-to-tenant lookups and month-to-date counters are held in memory, seeded once from the transactional database and reconciled back as periodic deltas, so the limit check is effectively free per request. Over-limit traffic can be handled softly (still recorded) or blocked, depending on plan.
Implementation
Decoupled ingest path
The collection endpoint accepts and acknowledges every beacon immediately — it never reveals which requests were filtered as bots — then validates, attributes the event, and hands it to a background writer. That writer batches rows and flushes them by size or time interval, and deliberately drops (and counts) events if the store falls behind, so a slow or stalled database can never add latency to the visitor-facing request.
Safe, fast query layer
Dashboard filters are constrained to a known set of dimensions and every value is passed as a bound query parameter, so no user input is ever concatenated into SQL. Unfiltered day-level ranges are answered from the pre-aggregated rollups; hourly or filtered views fall back to the raw events, with gaps in the time series zero-filled so charts stay continuous.
Auth and tenancy
Login sessions keep a high-entropy token on the client but store only its hash on the server, so a database leak exposes no usable sessions; expiry slides on activity and passwords use a memory-hard hash. Signing up provisions the user, their first organization, and ownership in a single transaction, and every subsequent query is scoped to an organization — cross-tenant access simply returns "not found".
Provider-agnostic billing
Payments sit behind a single interface with a real gateway adapter (cards plus local mobile wallets) and a mock adapter for development. Return-from-gateway callbacks are handled idempotently, so a duplicated or replayed callback can't double-charge or double-activate, and a card can be tokenized for automatic renewal.
Why this stack
- Go
- Lightweight concurrency makes the write-heavy, drop-under-load ingest endpoint natural to build, and it ships as a single dependency-free binary that is trivial to self-host.
- ClickHouse
- A columnar analytics database: high-volume events compress well and aggregate queries over millions of rows stay fast, with continuously-maintained rollups for time series.
- PostgreSQL
- The transactional source of truth for users, organizations, sites, subscriptions, and usage — the data that needs referential integrity and correctness over raw speed.
- SvelteKit
- One server-rendered app spans the dashboard, marketing site, auth, billing, and read API, deployable as a Node service for self-hosting.
- esbuild
- Bundles and minifies the tracking script down to its sub-1KB budget, enforced automatically at build time.
- Turborepo
- A monorepo so one schema change can update the analytics store, the ingest code, and the typed query layer in a single commit.
What it does
- Cookieless, no-PII tracking with daily-rotating visitor hashing and a sub-1KB SPA-aware script
- Dashboards for visitors, pageviews, bounce rate, and duration with page/source/country/browser/OS/device breakdowns
- Custom event tracking with key/value props surfaced as ranked conversions
- Multi-tenant orgs with roles and public shareable dashboards via token
- Plan-based monthly event limits enforced at ingest (soft/block modes)
- SSLCommerz billing with idempotent callbacks, tokenized auto-renew, and bot/AI-scraper filtering