Architecture
Updated
On this page
browser ──► POST /api/event ──► Collector ──► EventSink ──► analytics DB (visits, events, event_props)
│ ├─ DatabaseSink (default: write in-request, stitches visits)
│ └─ QueueSink (buffer → worker → DatabaseSink)
dashboard (Preact, no build) ──► JSON API ──► Stats engine (Period + Filters) ──► analytics DB
└─ metadata DB (users, sites, goals, shared links, shields)
Design choices
- Visits, not sessions. One row per visit, stitched on write with a 30-minute inactivity timeout.
- Two logical databases. Hot, append-only tables use
TIDEMARK_ANALYTICS_DB. Everything else uses the default connection. Tables are keyed bysite_idand indexed on time. - Stateless app tier. Sessions and cache can move to Redis or the database, and any number of PHP workers can serve requests.
- Swappable seams.
EventSink(writes) andStats\Stats(reads) are the only places that know about the storage engine. A columnar implementation can replace them without touching controllers or the UI. - No build step. The dashboard is Preact loaded as plain modules.
Growing a self-hosted installation
Was this helpful?
Ready to try Tidemark?
Free plan, no credit card. No cookies, no footprints.