# Privacy and analytics

Kartotek's optional, cookie-free visit analytics — what it measures, why it's designed the way it is, and how a site owner turns it on.

## For visitors

If a Kartotek site has this feature enabled, viewing a Post or Category page counts as one aggregate **page view** — nothing more. There is no analytics cookie, tracking pixel, browser fingerprinting, or persistent identifier of any kind, on this or any Kartotek site. No IP address, User-Agent, referrer, or session identifier is ever stored — the only thing written down is a daily count of page views, per page, with nothing that could identify who made them.

Because nothing is read from or written to your own device, there's no consent banner — there's no cookie to consent to. The live, legally-binding notice for the specific site you're on (with its actual controller details filled in) is at that site's own `/_privacy` address, linked from this doc's own site's [/_privacy](/_privacy).

## For administrators

The admin sidebar's **Analytics** section shows: site-wide summary cards (total Post page views, total direct Category page views) for a selectable range (7/30/90/365 days, or all time) and Domain (all, a specific configured Domain, or the unconfigured/default bucket); a daily trend chart for each, with an accessible data table alongside it; a ranked table of the most-viewed Posts; and a ranked table of Categories showing direct and contained page views side by side. A Post's own admin page and a Category's own admin page each get a compact Analytics card with today/7-day/30-day/all-time totals and a link to the full overview. A collapsed **Privacy and data** panel explains what is and isn't stored and provides two irreversible deletion actions — delete everything, or delete everything before a chosen UTC date.

**What counts as a page view:** one successful, public (non-admin), Stage-eligible resolution of a Post or Category. A PrePub or Hidden Post, a request from a logged-in admin session, and a 404 never count. A reload counts again — there's deliberately no attempt to deduplicate repeat views. Known automated traffic (search crawlers, social/link-preview bots, uptime monitors, common headless-automation and HTTP-library User-Agents, and Kartotek's own PDF/JSON export generator) is excluded via a small, deterministic classifier — unknown automation may still be counted, which is why the dashboard describes page views as approximate rather than exact.

A Post's view rolls up into its directly-assigned Category's own counter and every ancestor Category above it, as the Category hierarchy is structured **at the moment of that view** — moving a Post or Category later only changes future counts, it never rewrites a day that's already been recorded. A Category's own page is counted separately from — and never combined with — the Posts assigned beneath it, so "direct Category page views" and "contained Post page views" always stay two distinct numbers.

## For site owners: enabling and configuring analytics

Analytics defaults to **disabled**. Turning it on needs every one of the following set — any one missing keeps the feature off, and the admin Analytics page states exactly which are missing. Each is a database-backed setting managed from **Admin → Settings** (`/_admin/settings`) rather than only a deployment environment variable — the environment variable named alongside each one still works as a fallback/emergency override until an admin sets the field directly:

- **Enable visit analytics** (`ANALYTICS_ENABLED`) — the master toggle. Turning it on from Settings requires typing an exact confirmation phrase, shown on the page, acknowledging you've reviewed this doc's legitimate-interest reasoning below — the same commitment the `ANALYTICS_LIA_ACKNOWLEDGED` environment variable used to represent on its own, now recorded automatically the moment you confirm.
- **Controller name** (`ANALYTICS_CONTROLLER_NAME`) — the controller's name, shown on the public `/_privacy` notice.
- **Controller email** (`ANALYTICS_CONTROLLER_EMAIL`) — a contact address, also shown publicly.
- **Controller country** (`ANALYTICS_CONTROLLER_COUNTRY`) — the controller's jurisdiction.
- **Supervisory authority URL** (`ANALYTICS_SUPERVISORY_AUTHORITY_URL`) — a link to the relevant data-protection supervisory authority.
- **Privacy contact** (`ANALYTICS_PRIVACY_CONTACT`) — optional, an additional contact/address line shown on the public notice.

**Legal basis:** this design's lawful basis is the controller's legitimate interest (GDPR Article 6(1)(f)) in understanding aggregate readership of its own published content, not consent — which is what makes the no-consent-banner design above correct, not an oversight. This is a technical privacy design, not a claim of legal certification for any jurisdiction: review the reasoning here and get your own jurisdiction-specific legal advice before enabling it in production.

**Making the live notice discoverable:** the visitor chrome doesn't carry a direct link to `/_privacy` in its footer — this doc, listed plainly in the `/_docs/` sidebar, is the one deliberate route left to it. You remain responsible for judging whether that's sufficiently accessible for your own jurisdiction, and for adding a more prominent route (e.g. back in the page chrome) if your situation calls for it.

## See also

[Posts and publishing](/_docs/posts) for the Post/Category model this measures, [Domains, UIDs and addresses](/_docs/addressing) for Domains and UIDs, and [Dashboard and maintenance](/_docs/dashboard) for the admin dashboard generally.
