events table. All read queries live in pkg/db/query.go and share a common filtering pattern:
- Required:
domain = ? - Optional date range:
(? = '' OR timestamp >= ?)— passing an empty string skips the filter
Top-level KPIs
TheStatsCards component always renders these three cards at the top of every tab. They come from a single GET /api/stats call (GetStats in query.go), which filters on $pageview events only.
Pageviews
COUNT(*) of all $pageview events matching the domain and date range.Unique Visitors
COUNT(DISTINCT visitor_id) on $pageview events. Each distinct iris_vid value counts as one visitor.Sessions
COUNT(DISTINCT session_id) on $pageview events. Each distinct iris_sid value counts as one session.Top pages
API endpoint:GET /api/pagesComponent:
TopPages.tsxQuery:
GetTopPages in pkg/db/query.go
Groups $pageview events by url, counts occurrences, and returns the top 10 URLs ordered by pageview count descending.
Pagination is not implemented. The endpoint always returns a hard-coded maximum of 10 results.
Top referrers
API endpoint:GET /api/referrersComponent:
TopReferrers.tsxQuery:
GetTopReferrers in pkg/db/query.go
Groups $pageview events by referrer, counts distinct visitors per referrer, and excludes rows where referrer is empty (direct / no referrer traffic).
cleanReferrer() helper in TopReferrers.tsx strips the https://www. prefix from referrer URLs for display.
Like Top Pages, this endpoint is hard-coded to a maximum of 10 results with no pagination.
Web vitals
API endpoint:GET /api/vitalsComponent:
WebVitals.tsxQuery:
GetVitals in pkg/db/query.go
Filters on $web_vital events and computes the average value for each metric name using json_extract on the properties JSON column:
web-vitals library:
| Metric | Full name | Unit | Good | Needs improvement | Poor |
|---|---|---|---|---|---|
| LCP | Largest Contentful Paint | milliseconds | ≤ 2500 ms | ≤ 4000 ms | > 4000 ms |
| INP | Interaction to Next Paint | milliseconds | ≤ 200 ms | ≤ 500 ms | > 500 ms |
| CLS | Cumulative Layout Shift | unitless score | ≤ 0.1 | ≤ 0.25 | > 0.25 |
WebVitals component colour-codes each card based on the average value falling into one of these three bands.
Each $web_vital event stores the following properties (set by vitals.ts in the SDK):
| Property | Description |
|---|---|
$id | Unique identifier for the vital measurement |
$name | Metric name: LCP, INP, or CLS |
$val | Numeric measurement value |
$rating | String rating: good, needs-improvement, or poor |
Device breakdown
API endpoint:GET /api/devicesComponent:
DeviceBreakdown.tsxQuery:
GetDevices in pkg/db/query.go
Classifies each event by the screen_width value recorded at the time of the event using a SQL CASE expression:
| Category | Breakpoint |
|---|---|
| Mobile | screen_width < 768 px |
| Tablet | screen_width < 1024 px |
| Desktop | screen_width ≥ 1024 px |
Device classification is based on the
screen_width column which stores window.innerWidth (the viewport width) captured by the SDK at the time of the event — not User-Agent string parsing.Pageviews timeseries
API endpoint:GET /api/timeseriesComponent:
PageviewsChart.tsxQuery:
GetPageviewsTimeSeries in pkg/db/query.go
Buckets $pageview events by calendar day using SQLite’s strftime function:
PageviewsChart component renders a Recharts LineChart. When the API returns fewer days than the selected date window (e.g., days with zero traffic are absent from SQL results), buildEmptyBuckets() fills in the missing days with a count of 0 so the x-axis always covers the full selected range without gaps.