Telemetry is a privacy-focused, open-source analytics platform designed to provide meaningful insights without compromising user privacy. It's built for creators, developers, and anyone who believes in a more transparent and honest web.
- Privacy is Paramount: Telemetry is cookieless by design. It does not track individuals across the web and avoids collecting unnecessary personal data. The focus is on aggregated insights that respect visitor privacy.
- Clarity Over Clutter: The dashboard provides simple, actionable metrics like page views, unique visitors, and bounce rates, presented in a clean and intuitive interface.
- You Own Your Data: As a self-hosted solution, your website's data resides on your own infrastructure, giving you complete control.
- Built with Passion: The project is crafted using modern technologies like Fastify, React, and TypeScript, reflecting a commitment to quality and maintainability.
This section provides a more detailed look at the key libraries and frameworks used in the project.
- Framework: Fastify - A high-performance, low-overhead web framework for Node.js.
- Database ORM: Prisma - A next-generation ORM for Node.js and TypeScript that provides a type-safe database client.
- Validation: Zod - A TypeScript-first schema declaration and validation library used for validating API request bodies and environment variables.
- Logging: pino-pretty - A utility for formatting Pino logs in a human-readable way during development.
- Framework: React with Vite - A modern, fast build tool and development server for React applications.
- Language: TypeScript - Provides static typing for JavaScript, enhancing code quality and maintainability.
- Styling: Tailwind CSS with shadcn/ui - A utility-first CSS framework and a collection of pre-built, accessible React components.
- Server State Management: TanStack Query (React Query) - A powerful library for fetching, caching, and synchronizing server state in React applications.
- Client State Management: Zustand - A small, fast, and scalable state-management solution for React.
- Charting: Recharts - A composable charting library built on React components.
- Mapping: React jVectorMap - A library for creating interactive vector maps in React.
The core of the data collection is a lightweight JavaScript snippet that website owners embed on their pages.
- How it works:
- The script is loaded asynchronously to avoid blocking page rendering.
- It requires a
data-tenant-idattribute in the<script>tag to associate the data with the correct site. - It optionally reads a
data-api-keyattribute for authenticated event ingestion. - On page load, it automatically captures a
pageviewevent, collecting information like:- Hostname, path, and referrer
- Screen dimensions
- UTM parameters from the URL
- It uses
navigator.sendBeacon()to send this data to the backend API (/api/track) reliably, without impacting the subsequent page's load time. - A global
window.telemetry.goal(goalName)function is exposed, allowing website owners to track custom conversion events (e.g., newsletter sign-ups, button clicks).
User authentication is handled via GitHub OAuth2.
- Login Flow:
- A user clicks the "Sign in with GitHub" button on the
LoginPage. - They are redirected to
http://localhost:3000/login/github, which initiates the GitHub OAuth flow. - After authorizing the application, GitHub redirects the user back to
http://localhost:3000/login/github/callback. - The backend exchanges the authorization code for an access token.
- It then fetches the user's profile and primary verified email from the GitHub API.
- A
Userrecord is created in the database if one doesn't exist for that email. A default "tenant" (site) is also created for new users. - A signed,
httpOnlycookie (userId) is set in the user's browser to maintain the session. - The user is redirected to the
/dashboard.
- A user clicks the "Sign in with GitHub" button on the
The main dashboard is a single-page interface for viewing all analytics data.
- Features:
- Site (Tenant) Selection: Users can switch between different websites they've registered from a dropdown menu.
- Time Period Filter: Data can be filtered to show metrics for the last 24 hours, 7 days, or 30 days.
- Period Comparison: Summary cards show percentage change vs the previous period.
- Key Metrics: At-a-glance cards for Page Views, Unique Visitors, and Engagement (pages/session, new vs returning).
- Visualizations:
- Views Over Time: A line chart showing page view trends.
- Locations: A world map and a table showing the top countries by page views.
- Devices: Mobile/tablet/desktop breakdown with progress bars.
- Data Tables:
- Top Pages
- Top Referrers
- Top UTM Sources
- Top Cities
- Top UTM Mediums
- Top Campaigns
- Top Goal Completions
The settings page allows users to manage their sites.
- Features:
- Create New Site: Users can add a new website (tenant) to their account with optional allowed domains. A unique API key (
tlv_1_...) is automatically generated for each new site. - View Embed Script: For each site, the page displays the unique
<script>tag (includingdata-api-keywhen available) with a one-click copy button. - Manage Domains: Users can add or remove allowed domains for CORS. Only requests from registered domains are accepted.
- Delete Site: Users can permanently delete a site and all its associated analytics data.
- Create New Site: Users can add a new website (tenant) to their account with optional allowed domains. A unique API key (
The backend is a Fastify server. All API routes are defined in the src/routes/ directory.
- File:
src/routes/track.ts - Description: The main endpoint for collecting analytics data from the
analytics.jsscript. - Request Body: A JSON object that can be a
pageview,goal,outbound,performance, orscrollevent.pageview:{ type: "pageview", tenantId, apiKey?, hostname, path, browser, os, language, sessionId, ... }goal:{ type: "goal", tenantId, apiKey?, goalName, properties?, sessionId, ... }outbound:{ type: "outbound", tenantId, apiKey?, url, domain, path, sessionId }performance:{ type: "performance", tenantId, apiKey?, path, lcp, fid, cls, ttfb, fcp, sessionId }scroll:{ type: "scroll", tenantId, apiKey?, path, scrollDepth, sessionId }
- Processing:
- Rate Limiting: IP-based rate limit of 30 requests/minute. Returns
429when exceeded. - Bot Detection: Checks the
User-Agentheader against a list of ~40 known bot patterns (crawlers, scrapers, AI bots, monitoring tools). Returns403if matched. - Validates the incoming event against
createEventSchema. - Verifies that the
tenantIdexists. - API Key Validation: If the tenant has an
apiKeyset, the request must include a matchingapiKeyin the body. Returns403on mismatch. - Anonymizes the user by creating a unique
visitorIdhash from their IP address, User-Agent, and a server-side salt. This ensures privacy as the raw IP is not stored with the event. - Uses the
ip-api.comservice to perform a GeoIP lookup on the request IP to determine the country and city. - Saves the event to the
Eventtable in the database.
- Rate Limiting: IP-based rate limit of 30 requests/minute. Returns
- Response:
201 Createdon success.
-
File:
src/routes/auth.ts -
GET /login/github: Initiates the GitHub OAuth2 flow by redirecting the user to GitHub's authorization page. -
GET /login/github/callback: The callback URL after GitHub authorization. Handles user creation/login and sets the session cookie. -
GET /me: Returns the currently authenticated user's information based on theuserIdcookie. Used by the frontend to maintain session state. -
GET /logout: Clears theuserIdcookie, effectively logging the user out.
-
File:
src/routes/tenants.ts -
Authentication: All routes are protected by the
authHook, which verifies theuserIdcookie. -
GET /api/tenants: Returns a list of all tenants (sites) the authenticated user has access to. -
POST /api/tenants: Creates a new tenant for the authenticated user. -
PUT /api/tenants/:id: Renames a tenant. The user must be an 'ADMIN' of the tenant. -
DELETE /api/tenants/:id: Deletes a tenant and all associated data. The user must be an 'ADMIN'.
-
File:
src/routes/stats.ts -
Authentication: All routes are protected by the
authHook. -
Query Parameters: All routes require
tenantIdand acceptperiod(24h,7d,30d,90d), orstartDate/endDate(ISO strings) for custom date ranges. Segment filtering params:browser,os,country,language,device,referrer,utmSource. -
POST /api/track: The main endpoint for collecting analytics data from theanalytics.jsscript. -
GET /api/stats/summary: Returns the core metrics:pageViews,uniqueVisitors, andbounceRate. -
GET /api/stats/pages: Returns a list of the top 10 most viewed pages. -
GET /api/stats/referrers: Returns the top 10 referrers. -
GET /api/stats/views-over-time: Returns data points for the views-over-time line chart. -
GET /api/stats/sources: Returns the top 10 UTM sources. -
GET /api/stats/goals: Returns the top 10 completed goals. -
GET /api/stats/locations: Returns the top 20 countries by page views. -
GET /api/stats/devices: Returns mobile/tablet/desktop breakdown from screen width. -
GET /api/stats/engagement: Returns pages/session, new vs returning visitor split. -
GET /api/stats/campaigns: Returns top UTM mediums and campaigns. -
GET /api/stats/cities: Returns the top 20 cities by page views. -
GET /api/stats/compare: Compares current vs previous period with percentage change. -
GET /api/stats/browsers: Returns top browsers with view counts and percentages. -
GET /api/stats/os: Returns top operating systems with view counts and percentages. -
GET /api/stats/languages: Returns top languages with view counts and percentages. -
GET /api/stats/sessions: Returns total sessions and average session duration. -
GET /api/stats/scroll-depth: Returns average scroll depth and distribution (25%/50%/75%/100%). -
GET /api/stats/performance: Returns p50/p75/p90/p99 for LCP, INP, CLS, TTFB, FCP. -
GET /api/stats/outbound: Returns top 20 outbound links by click count. -
POST /api/stats/funnels: Accepts{ tenantId, steps: ["/page1", "/page2", ...], period }and returns conversion rates between steps. -
GET /api/stats/cohorts: Returns weekly cohort retention matrix. -
GET /api/stats/insights: Returns automated insights (trending pages, significant changes, top referrers). -
GET /api/export/events: Exports events as CSV or JSON. Params:tenantId,format(csv/json),startDate,endDate,limit.
The schema is defined in prisma/schema.prisma.
User: Stores user information (email, name, image).Account: Links aUserto an OAuth provider (e.g., GitHub).Tenant: Represents a website being tracked. Includes an optionalapiKeyfield for authenticated event ingestion.TenantUser: A join table linkingUserandTenant, defining roles (e.g., 'ADMIN', 'MEMBER').Event: The central table for all analytics data. It stores pageviews, goals, outbound clicks, performance metrics, scroll depth, location data, browser/OS/language info, session tracking, UTM parameters, custom event properties, and the anonymizedvisitorId.
- Bot Detection: The
/api/trackendpoint now filters ~40 known bot patterns (Googlebot, GPTBot, ClaudeBot, curl, scrapers, monitoring tools, etc.) and returns403for matches. - Rate Limiting: IP-based rate limit of 30 requests/minute on
/api/trackonly. Dashboard API endpoints are unaffected. - API Key Authentication: New
apiKeyfield on theTenantmodel (nullable, unique). Auto-generated (tlv_1_...format, 256-bit entropy) on tenant creation. When set,/api/trackvalidates the key from the request body. Legacy tenants without a key remain accessible. - Client Update:
analytics.jsreadsdata-api-keyfrom the script tag and includes it in all event payloads. - Dashboard Update: Settings page now shows the full embed script (with
data-api-key) and a one-click copy button. - SQL Injection Fix: Replaced
prisma.$queryRawUnsafe()with parameterized Prisma ORM queries in theviews-over-timeendpoint. All segment filter values are now properly escaped. - Dependency Swap: Replaced incompatible
fastify-rate-limitwith@fastify/rate-limitfor Fastify 5 compatibility.
- Enhanced Tracking Script:
analytics.jsnow captures browser/OS/version (client-side UA parsing), language, session ID (via sessionStorage), scroll depth (beforeunload beacon), outbound link clicks, and Core Web Vitals (LCP, INP, CLS, TTFB, FCP). - Custom Event Properties:
window.telemetry.goal("name", { key: "value" })now accepts an optional properties object stored as JSON. - New Event Types: Added
outbound,performance, andscrollevent types alongside existingpageviewandgoal. - Database Schema: Added 12 new columns to Event model (browser, browserVersion, os, osVersion, language, sessionId, scrollDepth, outboundUrl, outboundDomain, lcp, fid, cls, ttfb, fcp, properties) and 5 new indexes.
- Custom Date Ranges: All stats endpoints now accept
startDate/endDatequery params for arbitrary date ranges. Added90dperiod option. - Segment Filtering: All stats endpoints support filtering by
browser,os,country,language,device(mobile/tablet/desktop),referrer, andutmSource. - Browser/OS/Language Stats: New endpoints
/api/stats/browsers,/api/stats/os,/api/stats/languageswith percentage breakdowns. - Session Analytics: New endpoint
/api/stats/sessionsreturning total sessions, average duration (formatted). - Scroll Depth Analytics: New endpoint
/api/stats/scroll-depthwith average scroll depth and distribution (25%/50%/75%/100%). - Core Web Vitals: New endpoint
/api/stats/performancereturning p50/p75/p90/p99 for LCP, INP, CLS, TTFB, FCP. - Outbound Link Tracking: New endpoint
/api/stats/outboundshowing most-clicked external links. - Funnel Analysis: New
POST /api/stats/funnelsendpoint accepting page path steps, returning conversion rates between each step. - Cohort/Retention Analysis: New
GET /api/stats/cohortsendpoint grouping visitors by first-visit week with weekly retention matrix. - Automated Insights: New
GET /api/stats/insightsendpoint detecting significant metric changes, trending pages, and top referrers. - Data Export: New
GET /api/export/eventsendpoint supporting CSV and JSON formats with date range filtering. - Dashboard UI: Added custom date range picker, segment filter bar (browser/OS/country/language/device), automated insights cards, session metrics, scroll depth cards, browser/OS/language tables, outbound links table, Core Web Vitals panel with color-coded p75 values, and data export button.
- Advanced Analytics Endpoints: Added 5 new stats endpoints: devices, engagement, campaigns, cities, and period comparison.
- Dashboard UI: Added device breakdown bars, engagement card, city/medium/campaign tables, and period comparison badges on summary cards.
- Dynamic CORS: CORS origins are now managed per-tenant via a
domainsfield in the database. Dashboard URL is always allowed viaFRONTEND_URLenv var. - Responsive Dashboard: Fixed mobile overflow on dashboard header, added segmented period controls, responsive stat card sizing.
- Mobile Navigation: Added hamburger menu for mobile users on the landing page with dark mode toggle.
- Global Dark Mode: Dark mode now initializes on all pages, not just the landing page. Removed
next-themesdependency in favor of customuseDarkModehook. - Accurate Geolocation: Replaced outdated
geoip-litewithip-api.comfor city-level accuracy. - Trust Proxy: Enabled
trustProxyon Fastify to read real client IPs behind reverse proxies. - Build Script: Added
prisma generateand generated client copy to the build pipeline. - Tenant Domains: Added
domains String[]field to Tenant model for per-site CORS management.
- New Landing Page: Replaced the previous landing page with a new, modern, and more informative home page that better showcases the project's features and guiding principles.
- Documentation Section: Added a comprehensive documentation section with detailed guides on architecture, authentication, tracking, and more.
- Improved Logout: The logout process now includes a confirmation dialog to prevent accidental sessions termination.
- Enhanced Tracking: The
analytics.jsscript now exposes awindow.telemetry.pageview()function for manual pageview tracking. - UI & Routing Fixes: Updated application-wide routing to accommodate the new pages and improved the authentication error redirection logic.
- Location Tracking: Integrated IP-based geolocation to record the country and city for each analytics event.
- Dashboard Visualization: Added a world map and a "Top Countries" table to the dashboard to visualize visitor locations.
- API and Database: Implemented the necessary API endpoints and database schema changes to support location data.
- Developer Experience: Configured the Fastify logger to use
pino-prettyfor more human-readable output during development.