Privacy model
Your numbers are not our product. Kymo sells analytics; it does not build visitor profiles, sell data, or set anything on your visitors' machines. The privacy model is not a policy bolted on afterward — it is how the system works.
Cookieless identity
Kymo does not set a cookie or read one. It has no cross-site identifier and no persistent visitor ID. It writes one first-party localStorage key on the site owner's own domain, for first-touch attribution, and that key holds no identifier — see First-touch storage below.
To tell two pageviews apart within a single day, Kymo derives a hash server-side from data the request already carries:
visitor = sha256( HASH_SALT | UTC-date | IP | user-agent | site ) // first 16 hex charsThe UTC-date term is the important one: it changes at midnight UTC, so the same person on the same device gets a different hash tomorrow. The hash cannot be reversed to an IP or a user-agent, and it cannot be linked across days or across sites.
Because identity rotates daily, "unique visitors" means unique per UTC day. Someone who visits Monday and Tuesday is two uniques. This is a deliberate trade-off — see Cookieless identity for the detail.
Raw IPs are never stored
The IP address is used for exactly two things, both in-request and in-memory:
- As one ingredient in the hash above.
- To check the per-site excluded-IP list, so you can drop your own office traffic.
After that it is discarded. The raw IP is never written to the database. The same is true of a human visitor's raw user-agent: Kymo derives a browser name and a device class from it, then drops it.
AI crawlers are the one exception, and they are not people. A crawler's user-agent is retained on its own event row so a classification stays auditable and can be corrected later. No human's user-agent is retained.
First-touch storage
beat.js writes one first-party localStorage key, kymo_ft, on the site owner's own domain. It is on by default and holds the traffic source of the visit that first brought that browser to the site:
utm_source,utm_medium,utm_campaign,utm_term,utm_contentfrom that first visit- the referrer of that first visit
- the landing path, with no query string and no fragment
- the timestamp of that first visit
That is the whole contents. It holds no identifier — no visitor id, no random id, nothing that identifies a person, and nothing that describes them. Two people arriving from the same campaign store the same value.
It is localStorage, not a cookie, so it is never attached to a request. It is bound to the site owner's own origin, so it is never read across domains and Kymo cannot correlate one site's key with another's. It expires after 13 months, the CNIL limit for audience measurement exempt from consent, after which it is discarded and written again.
The daily hash rotation is unchanged. Visitor identity is still sha256( HASH_SALT | UTC-date | IP | user-agent | site ), still rotates at UTC midnight, and still cannot be linked across days. First touch persists a traffic source, never a person.
A site owner can switch it off entirely with data-attribution="off" on the script tag, after which beat.js reads no storage and writes none:
<script defer src="https://kymo.in/beat.js"
data-site="yoursite.com"
data-attribution="off"></script>Why no cookie banner is required
Consent banners exist because of cookies and similar identifiers stored on a user's device. Kymo sets no cookie and stores no identifier on the device. It writes two first-party localStorage keys, neither of which identifies anyone: kymo_ft, the first-touch traffic source described above, kept within the 13-month CNIL audience measurement window; and kymo_ignore, written only when a site owner deliberately opts their own browser out.
No cookies, no identifier on the device, no cross-site identifier means no consent prompt to show. This is the design intent, stated plainly — it is not legal advice, and a site owner who prefers to store nothing at all can switch first touch off.
What is stored, what is not
| Stored | Not stored |
|---|---|
| Path, referrer hostname | Full referrer URL with query |
| Country, city, coordinates (from edge headers) | Raw IP address |
| Device class, browser name | Raw user-agent string (human visitors) |
| A daily-rotating visitor hash | Any cross-day or cross-site identifier |
| UTM source / medium / campaign | Names, emails, or form contents |
| First-touch source, medium, campaign, term, content, referrer, landing path and time | Cookies, on your site or ours |
| Goal names and revenue you send | Any identifier in the first-touch key |
The full field list is on What we store.
GDPR posture
Kymo stores no cookies, no raw IP addresses, and no identifier that persists beyond a UTC day or reaches across sites. There is no personal profile to export, rectify, or erase, because none is built. This is the design intent, stated plainly — it is not legal advice, and you should confirm your own obligations for your jurisdiction and use case.