Loopar Introduction
loopar-webpage

Analytics

Loopar includes privacy-friendly, first-party web analytics — no external scripts, no advertising cookies.


How It Works

Every public web page automatically renders a TrafficTracker: on page load it registers a view, then periodically reports how long the visitor actually engaged with the page.

PieceRole
TrafficTrackerClient component, auto-injected on the web workspace
Page View entityOne row per page view
publicActionTrack / publicActionPingPublic endpoints receiving the data
Analytics DashboardKPIs at Desk → Analytics

Nothing to configure — it works out of the box on every tenant.

Opt-out

Visiting any URL with ?notrack=1 sets a persistent opt-out for that browser. Logged-in users are recorded as internal traffic and excluded from the dashboard KPIs.

What Gets Tracked

On page load — track

The tracker POSTs to /api/Page View/track with a fresh viewId. The server enriches and stores a Page View row:

FieldSource
document, referrer, language, viewportSent by the client
session_id30-minute rolling session id (localStorage)
utm_source / utm_medium / utm_campaignTaken from the URL
browser, os, device_typeParsed server-side from the User-Agent
country, region, city, timezone, ispGeo lookup by IP (cached, 2s timeout)
ip_hashSalted hash — the raw IP is never stored
is_internal1 when the visitor is logged in

While reading — ping

Every 15 seconds (and when the tab is hidden or closed, via sendBeacon) the tracker updates the same row through /api/Page View/ping:

  • active_ms — time the page was visible AND the user was active (mouse/keyboard/scroll within the last 30s)
  • scroll_depth — deepest scroll position reached (0–100%)

Abuse protection

  • Bots filtered: requests with bot user-agents (crawler lists + headless/lighthouse patterns) are acknowledged but not stored.
  • Rate limits per IP: 30 track and 120 ping per minute — beyond that the endpoint answers 429.
  • activeMs is clamped to 24h and scrollDepth to 0–100, so a hostile client can't poison the metrics.

Engagement Model

Classic analytics count a "bounce" as a single-page visit — useless for landing pages. Loopar measures real engagement:

A view is engaged when the visitor was actively on the page for at least 10 seconds (active_ms ≥ 10000).

Derived KPIs:

KPIDefinition
Engaged rateengaged views / measured views
Bounce rate100 − engaged rate
Avg. active secondsMean active_ms across views
Avg. scroll depthMean deepest scroll %

Because activity pauses when the tab is hidden or the user goes idle (30s without input), a tab left open in the background does not inflate engagement.

The Dashboard

Open Analytics Dashboard in the desk. It reads the last N days (1–365, default 30), always excluding internal (logged-in) traffic.

BlockContents
KPIsTotal views, unique visitors, sessions, pages/session, engaged rate, bounce rate, avg. active seconds, avg. scroll depth — each with % change vs the previous period
Visits over timeDaily views/visitors series
Top pagesMost viewed documents
AudienceCountries, devices, browsers
AcquisitionReferrers and UTM campaigns
HourlyTraffic distribution by hour

Unique visitors are counted by distinct ip_hash, sessions by distinct session_id.

Privacy

  • No raw IPs stored. The IP is hashed as sha256(ip + salt) truncated to 16 characters. The salt comes from the TRACKING_SALT env var, or is generated once and persisted at sites/.tracking-salt (never committed).
  • No third-party scripts or cookies. The only client-side state is a session id in localStorage.
  • Geo lookup is coarse — country/region/city via IP, private/LAN IPs skipped.
  • Opt-out honored (?notrack=1), bots discarded, internal traffic flagged.

This keeps the data useful for product decisions without profiling individual visitors.