Architecture
TideMeter uses a modern, modular architecture designed for privacy, performance, and flexibility.
Overview
TideMeter is organized as a Turborepo monorepo with four packages:
| Package | Path | Purpose |
|---|---|---|
@tidemeter/web | apps/web/ | Next.js 16 + PayloadCMS 3 application |
@tidemeter/tracker | packages/tracker/ | Lightweight tracking script (~1.5KB gzip) |
@tidemeter/analytics | packages/analytics/ | Analytics data layer (Drizzle ORM) |
@tidemeter/ui | packages/ui/ | Shared React UI components |
Dual Database Pattern
TideMeter uses a dual database architecture that separates application data from analytics data:
Application Database (PostgreSQL)
Managed by PayloadCMS, this stores:
- Users and authentication
- Teams and team memberships
- Website configurations
- API keys (hashed with SHA-256,
tm_prefix) - Funnels (multi-step conversion tracking)
Analytics Database (PostgreSQL, ClickHouse, or SQLite)
Managed by @tidemeter/analytics, this stores:
- Page view events and custom events
- Session data (with bounce detection)
- Visitor identity links (for user identification)
- Aggregated statistics
The analytics package uses an adapter pattern, allowing you to choose your storage backend:
// PostgreSQL adapter (default)
const repo = new PostgresAnalyticsRepository(db);
// ClickHouse adapter (high volume)
const repo = new ClickHouseAnalyticsRepository(client);Both adapters implement the same AnalyticsRepository interface, making them interchangeable.
Event Ingestion Pipeline
When a visitor loads a page with the TideMeter tracker, the following happens:
1. Event Collection
The tracker script sends a POST /api/collect request with:
- Page URL and title
- Referrer URL
- Screen dimensions
- Browser language
- Website ID
- Event name (defaults to
pageview) - Custom event data (optional)
- User ID (optional, for visitor identification)
No cookies, no personal data, no IP addresses are included in the request.
2. Event Processing
The processor.ts module:
- Filters bots using a comprehensive UA regex
- Parses the User-Agent string for browser, OS, and device type information
- Generates a visitor ID by hashing (SHA-256) a combination of the IP address, User-Agent, website ID, and a daily rotating salt
- Generates a session ID using a 30-minute window hash
- Extracts UTM parameters from the URL
- Extracts geographic data via optional GeoIP (MaxMind)
- Handles “identify” events to link anonymous visitors to known user IDs
The daily rotating salt ensures visitor IDs change every day, making cross-day tracking impossible.
3. Event Buffering
The buffer.ts module batches events for efficient database writes:
- Events are collected in memory
- Flushed when the buffer reaches 100 events
- Or when 5 seconds have passed since the last flush
- Whichever comes first
This reduces database write pressure while keeping data reasonably fresh.
4. Database Storage
The buffered events are written to the analytics database through the configured adapter (PostgreSQL or ClickHouse).
Authentication
TideMeter uses PayloadCMS built-in authentication:
- Cookie-based sessions using
payload-tokenHTTP-only cookie - Login via
POST /api/users/login - Logout via
POST /api/users/logout - Session check via
GET /api/users/me - Route protection via
proxy.ts(Next.js 16 pattern)
Note: TideMeter uses
proxy.ts, notmiddleware.ts, for request interception. This is the Next.js 16 pattern.
Dashboard Architecture
The dashboard is a React application using:
- Server Components for data fetching and initial rendering
- Client Components for interactive charts and real-time data
- Recharts for data visualization
- Tailwind CSS 4 for styling with CSS-first configuration
@tidemeter/uifor shared component library
Data Flow Diagram
┌─────────────┐ POST /api/collect ┌──────────────┐
│ Tracker │ ─────────────────────────▶│ Next.js │
│ (~1.5KB) │ │ API Route │
└─────────────┘ └──────┬───────┘
│
┌──────▼───────┐
│ Processor │
│ (SHA-256 ID, │
│ UA parsing) │
└──────┬───────┘
│
┌──────▼───────┐
│ Buffer │
│ (100 events │
│ or 5s flush)│
└──────┬───────┘
│
┌─────────────┼─────────────┐
│ │ │
┌──────▼──────┐ or ┌───────▼──────┐
│ PostgreSQL │ │ ClickHouse │
│ (default) │ │ (optional) │
└─────────────┘ └──────────────┘