https://github.com/tidemeter/tidemeter
Privacy-first self-hosted web analytics
https://github.com/tidemeter/tidemeter
privacy-first-analytics self-hosted-analytics web-analytics
Last synced: 3 months ago
JSON representation
Privacy-first self-hosted web analytics
- Host: GitHub
- URL: https://github.com/tidemeter/tidemeter
- Owner: tidemeter
- License: mit
- Created: 2026-04-04T23:37:24.000Z (3 months ago)
- Default Branch: main
- Last Pushed: 2026-04-07T22:16:53.000Z (3 months ago)
- Last Synced: 2026-04-08T16:03:17.099Z (3 months ago)
- Topics: privacy-first-analytics, self-hosted-analytics, web-analytics
- Language: TypeScript
- Homepage: https://tidemeter.com
- Size: 379 KB
- Stars: 2
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
- Agents: AGENTS.md
Awesome Lists containing this project
README
# TideMeter
**Self-hosted, privacy-focused web analytics**
[](LICENSE)
[](https://nextjs.org)
[](https://payloadcms.com)
---
## Features
- **Privacy-focused** — no cookies, no fingerprinting, GDPR-friendly by design
- **Lightweight tracker** — ~1.5 KB gzipped, zero dependencies
- **Real-time dashboard** — interactive charts powered by Recharts
- **Multiple database support** — PostgreSQL, ClickHouse, or SQLite for analytics storage
- **Built on PayloadCMS 3 + Next.js 16** — modern, extensible full-stack architecture
- **Docker Compose** — one-command self-hosting
- **SPA support** — automatic history API interception for single-page apps
- **Custom event tracking** — track signups, clicks, purchases, anything
- **Team collaboration** — multi-user with role-based access
- **API for programmatic access** — query your analytics data from anywhere
## Quick Start (Docker)
```bash
git clone https://github.com/your-org/tidemeter.git
cd tidemeter
cp .env.example .env
# Edit .env with your settings (at minimum, change PAYLOAD_SECRET and SESSION_SALT_SECRET)
docker compose -f docker/docker-compose.yml up -d
```
Visit **http://localhost:3700** to create your admin account and add your first website.
> **Documentation**: Full docs available at [tidemeter.com/docs](https://tidemeter.com/docs)
## Development Setup
### Prerequisites
- [Node.js](https://nodejs.org) 22+
- [pnpm](https://pnpm.io) 9+
- [PostgreSQL](https://www.postgresql.org) 16+ (or use Docker for the database)
### Install & Run
```bash
# Install dependencies
pnpm install
# Create your environment file
cp .env.example .env
# Edit .env — point DATABASE_URL / ANALYTICS_DATABASE_URL to your PostgreSQL instance
# Start all packages in dev mode
pnpm dev
# Build for production
pnpm build
# Run tests
pnpm test
# Lint
pnpm lint
# Run analytics database migrations
pnpm db:migrate
```
The dev server starts at **http://localhost:3700** with Turbopack HMR.
## Adding the Tracker
Add the tracking script to any website you want to monitor. The `data-website-id` comes from your TideMeter dashboard after adding a site.
```html
```
### Script Attributes
| Attribute | Description | Default |
| ------------------ | --------------------------------------------- | ------------- |
| `data-website-id` | **(required)** Website ID from your dashboard | — |
| `data-host-url` | Override the analytics endpoint URL | Script origin |
| `data-auto-track` | Auto-track pageviews | `true` |
| `data-respect-dnt` | Respect the Do-Not-Track header | `true` |
| `data-domains` | Comma-separated list of allowed domains | All domains |
### Custom Events
```javascript
// Track a named event
tidemeter.track("signup", { plan: "pro" });
// Track a pageview manually (when auto-track is disabled)
tidemeter.track();
```
## Architecture
TideMeter is a **Turborepo monorepo** with a clear separation between the application layer and the analytics data layer.
```
┌─────────────────────────────────────────────┐
│ apps/web │
│ Next.js 16 + PayloadCMS 3 │
│ (dashboard, admin, API routes) │
├──────────────┬──────────────┬───────────────┤
│ @tidemeter/ │ @tidemeter/ │ @tidemeter/ │
│ tracker │ analytics │ ui │
│ (t.js) │ (Drizzle ORM)│ (components) │
└──────────────┴──────┬───────┴───────────────┘
│
┌───────────┼───────────┐
│ │ │
PostgreSQL ClickHouse SQLite
```
- **Dual database design** — PayloadCMS manages application data (users, sites, settings) in PostgreSQL; analytics events are stored in a separate database that can be PostgreSQL, ClickHouse, or SQLite.
- **Repository pattern** — the `@tidemeter/analytics` package defines an `AnalyticsRepository` interface with swappable adapters (`postgres`, `clickhouse`), selected at runtime via the `ANALYTICS_DB_TYPE` env var.
- **Tracker** — `@tidemeter/tracker` compiles to a single `t.js` file via Rollup, served as a static asset.
## Configuration
All configuration is done through environment variables. Copy `.env.example` to `.env` and adjust:
| Variable | Description | Default |
| ------------------------ | -------------------------------------------------------------- | ----------------------------------------------------------- |
| `DATABASE_URL` | PostgreSQL connection string for PayloadCMS | `postgresql://tidemeter:tidemeter@localhost:5432/tidemeter` |
| `PAYLOAD_SECRET` | Secret for PayloadCMS auth (min 32 chars) | — |
| `ANALYTICS_DB_TYPE` | Analytics storage engine: `postgresql`, `clickhouse`, `sqlite` | `postgresql` |
| `ANALYTICS_DATABASE_URL` | Connection string for analytics DB (PostgreSQL) | Same as `DATABASE_URL` |
| `CLICKHOUSE_URL` | ClickHouse HTTP endpoint | `http://localhost:8123` |
| `CLICKHOUSE_DATABASE` | ClickHouse database name | `tidemeter_analytics` |
| `ANALYTICS_SQLITE_PATH` | Path to SQLite file (when using SQLite) | `./data/analytics.db` |
| `NEXT_PUBLIC_APP_URL` | Public URL of the application | `http://localhost:3000` |
| `SESSION_SALT_SECRET` | Secret for hashing visitor IDs (rotated daily) | — |
| `GEOIP_DB_PATH` | Path to MaxMind GeoLite2-City.mmdb (optional) | — |
See [`.env.example`](.env.example) for the full annotated reference.
## ClickHouse Mode
For high-traffic sites, use ClickHouse as the analytics storage engine. The override compose file adds a ClickHouse container and reconfigures the app:
```bash
docker compose -f docker/docker-compose.yml -f docker/docker-compose.ch.yml up -d
```
This starts PostgreSQL (for PayloadCMS) + ClickHouse (for analytics) + the TideMeter app.
## Tech Stack
| Layer | Technology |
| ------------- | -------------------------------- |
| Framework | Next.js 16 |
| CMS | PayloadCMS 3 |
| Styling | Tailwind CSS 4 |
| Charts | Recharts |
| State | React Hooks |
| Analytics ORM | Drizzle ORM |
| Build | Turborepo + pnpm |
| Runtime | Node.js 22 (Alpine) |
| Docker | Multi-stage Dockerfile + Compose |
## Project Structure
```
tidemeter/
├── apps/
│ └── web/ # Next.js 16 + PayloadCMS application
├── packages/
│ ├── analytics/ # Analytics data layer (Drizzle ORM, adapters)
│ │ └── src/
│ │ ├── adapters/ # postgres, clickhouse implementations
│ │ ├── schema/ # Drizzle table definitions
│ │ ├── types.ts # Core interfaces (AnalyticsRepository, etc.)
│ │ └── factory.ts # Adapter factory
│ ├── tracker/ # Lightweight tracking script (Rollup → t.js)
│ ├── ui/ # Shared UI components
│ └── tsconfig/ # Shared TypeScript configs
├── docker/
│ ├── Dockerfile # Multi-stage production build
│ ├── docker-compose.yml # Default stack (PostgreSQL)
│ ├── docker-compose.ch.yml # ClickHouse override
│ └── clickhouse/ # ClickHouse init scripts
├── turbo.json # Turborepo pipeline config
├── pnpm-workspace.yaml # pnpm workspace definition
└── package.json # Root scripts (dev, build, test, lint)
```
## License
[MIT](LICENSE) © 2026 TideMeter