The legal moat

Designed so that identity cannot persist.

Every analytics platform asks you to balance insight against intrusion. Observer is architected so the balance is unnecessary: the default mode stores nothing on the visitor's device, identity rotates daily, and small samples are suppressed at query time. Privacy here is not a policy — it is math.

This website

What this website does with your visit.

Scope. The rest of this page describes the Observer product: what it does when a customer runs it on their own server. This section describes what this website, observer.castomo.com, does.

Analytics: Observer. We count visits with Observer, our own analytics, run by Castomo on its own server in Frankfurt. It sets no cookies and stores nothing on your device. For each page you open it records: the page address (of any query only campaign tags such as utm_source) and title, the site you came from, your browser, device type and operating system, your browser language, a rough screen-size bracket, how long the page was actively open, how fast it loaded, any script error, and — as totals — where people click and how far they scroll. It takes no recording of your screen and reads no keystrokes.

Your IP address is used for a moment, together with your browser, to make a code that changes every day and is different on every site. The IP address itself is never stored, and the server that delivers Observer keeps no access log. The code lets us count one visitor once a day, and it cannot be used to link a visit to visits on other days or other sites. Raw records are deleted after 24 hours; only totals are kept, for 25 months. If your browser sends Do Not Track or Global Privacy Control, Observer sends no visit data.

We do this because we have a legitimate interest in knowing how our pages are used (Article 6(1)(f) GDPR). You can object at any time: hello@castomo.com.

Chat assistant. Every page has a chat button run by Castomo (chat.castomapi.com, servers in Amsterdam). Until you open the chat, the button stores nothing on your device and starts no conversation; loading the page only fetches the button's settings from our chat server, which, like any web server, records the request, including your IP address, in a rotating log that is overwritten as it fills.

When you open the chat, the chat window saves a visitor code and a few settings in your browser's local storage, and our server creates a visitor record: the code (kept only as a hash), a hash of your IP address, your browser type, language and time zone, the page you were on and where you came from, and everything you type — plus your name, e-mail or phone number only if you give them.

The assistant is an AI, and it says so. Its answers are written by AI models run by OpenRouter (Google Gemini) and OpenAI, which receive what you type in order to reply; after a conversation ends the same text is sent to a model again to write a short summary. A person on our team can read the conversation and take it over. These providers may process data outside the European Economic Area.

We keep a visitor's record and conversations until 180 days after their last visit, then delete them. You can ask us to delete them sooner: hello@castomo.com. Please do not type anything you do not want us to have.

The claims — tested, not aspirational

Every line below is asserted by a test.

These are not values we intend. They are invariants the CI suite checks on every build — including a 6-spec browser privacy suite that runs the tracker in a real browser with storage proxies and network recording.

Zero cookies. Zero browser storage.

In default mode the tracker writes nothing and reads nothing on the visitor's device — no cookies, no localStorage, no sessionStorage, no IndexedDB. A proxy-sealed storage asserts zero accessor hits.

Daily-rotated identity

Identity is derived server-side from what the browser already sends (IP + user agent), hashed with an HKDF key that rotates at midnight UTC. Yesterday's hash cannot be joined to today's.

Per-site namespaces

Each site gets its own salt. Your blog cannot correlate with your shop — enforced by construction, not by policy.

No raw IP storage

The IP exists for milliseconds in collector memory — geo lookup and HMAC input — then it is gone. Never logged, never serialized; a CI invariant checks the collector never marshals it.

k-anonymity ≥ 5

Every dashboard row, export and share link suppresses data below 5 sessions. Small-sample re-identification is blocked at query time, not by convention.

No fingerprinting

No canvas, WebGL, audio or font probes. A static audit fails the build on any violation — the SDK physically cannot grow a fingerprinting bone.

All data on your hardware

Self-hosted on your infrastructure. No third-party endpoint, no subprocessor chain, no international transfers — the data never leaves the machines you own.

Append-only audit log

Logins, config changes, key creation, alert firings — every action is append-only logged and visible on the Privacy Posture page.

DNT, GPC and opt-out honored

Do Not Track, Global Privacy Control and observer('optOut') silence the SDK unconditionally — verified by tests asserting zero beacons and zero storage.

How identity works

A new key every midnight.
Yesterday is another country.

Most analytics tools give a visitor a persistent ID. Observer derives an anonymized device-day pseudonym server-side: HMAC-SHA256 over what the browser already sends, keyed by an HKDF key that rotates at midnight UTC, salted per site. A visitor is "someone, on this device, today" — and nothing more.

Not a cookie

Nothing is stored on the device. Nothing to clear, nothing to refuse, nothing to consent to.

Not fingerprinting

The input is what every HTTP request already carries — IP and user agent — used for milliseconds and discarded. No probes.

Not joinable

Key rotation is cryptographic, not administrative: joining Monday's visitor to Tuesday's requires the old key, which no longer exists anywhere.

Query-time protection

k-anonymity ≥ 5 — everywhere, no exceptions.

Dashboards, CSV exports and public share links all pass through the same post-filter: any row describing fewer than 5 sessions is suppressed. A quietly-read internal page cannot be re-identified by filtering your way down to one visitor.

Pages report · /internal/roadmap-2026suppressed
PathViewsDevicesEngagedStatus
/9,3426,1182:58exact
/pricing4,1873,2063:24exact
/blog/cookieless-analytics2,9052,1444:02exact
/internal/roadmap-2026n = 3 — below the k-anonymity floor of 5suppressed
shown (n ≥ 5)suppressed (n < 5)

The same floor applies to every export, every API response and every public share snapshot. "Estimated" device counts carry an honest badge — NAT collisions undercount, IPv6 churn fragments, so devices are estimated device-days, never "unique users".

The consent-free claim, worded legally

What we say — and what we refuse to say.

Based on our self-assessment, Observer meets the criteria established by the CNIL and can be implemented without requiring user consent when correctly configured.

CNIL self-assessment wording — the template we ship for your privacy policy
We say

Self-hosted · cookie-less · no consent required when correctly configured (CNIL self-assessment wording) · sessions are pseudonymous · aggregate statistics are anonymous.

We never say

"GDPR compliant", "certified", "CNIL-approved", "anonymous users", "100% anonymous". We show you the architecture and hand you the tests — you verify, you decide.

You show your DPO

The per-site Privacy Posture screen: exactly what is collected, stored where, for how long, under which tier and legal basis — plus the live audit feed.

Per-site, server-enforced

Four privacy tiers. The server holds the key.

Tiers are enforced by the collector, not the snippet — the tracker cannot upgrade itself. DNT and GPC silence the SDK unconditionally at every tier.

TierBrowser storageWhat it unlocksConsentIdentity model
T0 — Strict (default)Nothing — no write, no readEverything: sessions, heatmaps, frustration, CWV, funnelsNo bannerDaily-rotated HMAC
T1 — SessionsessionStorage only (tab-scoped)Stable sessions across reloads; reliable funnels and pathsDocumented legal basisPer-tab sid + HMAC
T2 — Consented+ localStorage visitor id (13 months, never auto-extended)Cross-visit continuity; session replay (sampled, masked)Explicit opt-inVisitor vid + HMAC
T3 — DisabledNothingNothing — SDK inert, collector rejectsKill-switchNone
13-month TTL, never extended

The T2 visitor id expires 13 months after creation. Visits do not extend it — that would defeat the purpose, and CNIL guidance is explicit about it.

Pre-T2: zero of everything

Before consent, T2 collection is zero beacons, zero storage, zero cookies — asserted by the consent-gate test in the browser suite.

Recorder masking at source

T2 replay applies maskAllInputs in the browser, honors obs-mask / obs-block / obs-ignore, and disables canvas and font capture.

The page you show a lawyer

The Privacy Posture screen — live, per site.

Every site in your registry carries a live posture view: tier, captured fields, retention state, consent requirement, legal-basis reference and the recent audit entries. Not a PDF from the sales team — the actual configuration, read from the control plane, for the site in question, right now.

Point your DPO at it. It answers the only three questions that matter: what is collected, for whom, and for how long.

The refusals

What Observer never collects.

Deleting these from a roadmap is easy. Observer never had them on one — they contradict the architecture, and the static audit keeps them out of the SDK.

Cross-site identity stitching

No global visitor graph. Per-site namespaces make correlation between your properties impossible — even for you.

Fingerprinting surfaces

No canvas, WebGL, audio or font probes. The mouse-move heatmap is deliberately excluded too — movement profiling is the least privacy-friendly heatmap type.

Ad-platform integrations

No audience exports, no pixel syncs, no campaign matching. If a feature would require sending visitor data to a third party, it cannot exist in Observer.

Keystroke capture

Form analytics counts focus and fill events. Field labels are hashed; values are never read, never sent, never stored.

Raw IP in durable storage

Not in the event store, not in logs, not in exports. A CI invariant fails the build if the collector ever marshals the IP into a durable form.

Long-horizon raw events

Raw events TTL out at 24 hours by schema. What persists is 25 months of k-anonymized rollups — aggregates, not behavior traces.

Honest limitations — part of the trust story

What Observer cannot tell you.

We publish these because every analytics tool has limits, and most hide them. Ours are the direct consequence of the architecture — and we think that trade is worth it.

  • "Devices" are estimated device-days — never "unique users". NAT collisions undercount; IPv6 churn fragments.
  • Sessions live within the 24-hour event horizon. Cross-day journeys do not exist in T0/T1 — by design.
  • New-vs-returning requires T2 consent. Where others fake it, the UI says "requires consent".
  • Form analytics counts fields, never values; recordings mask inputs at source.
  • No cross-site identity, no ad-platform integrations, no fingerprinting — the refusals are why the banner-free posture is defensible.
  • Session replay ships with a sandboxed player — but recording requires T2, so most sites will never record most visitors. That is the point.

FAQ

Asked and answered — precisely.

Do I need a consent banner?

No — in the default mode (T0), Observer stores nothing on the visitor's device and reads nothing from it. The CNIL self-assessment confirms this posture. If you enable T1 or T2 (session storage or visitor id), you need a documented legal basis or explicit consent.

How does it count visitors without cookies?

Observer derives an anonymous identity server-side from what the browser already sends (IP + user agent), hashed with a key that rotates at midnight UTC. Each day gets a fresh key, so yesterday's identity cannot be linked to today's. This is not a cookie, not fingerprinting, and not persistent.

Can I see individual users?

No. Observer shows anonymized sessions — device-day pseudonyms. Timelines show what was done, never who did it. There are no user profiles, no cross-site identity, and no persistent visitor tracking in the default mode.

Is it really self-hosted?

Yes. Observer runs on your hardware — ClickHouse, collector, API and dashboard, all in Docker or bare Node.js. No third-party endpoint, no subprocessor, no data leaves your infrastructure.

How is it different from Plausible, Umami or Fathom?

Those are traffic counters. Observer adds the entire behavior layer — session timelines with tab-split lanes, heatmaps, frustration detection, form drop-off, CWV RUM — plus the privacy architecture none of them ship: k-anonymity, an audit log, a privacy-posture page and rotating identity namespaces.

What about GDPR?

Observer is designed for GDPR compliance by architecture — data minimisation, purpose limitation, storage limitation. In the default mode, no personal data is stored. We provide CNIL-compliant self-assessment wording for your privacy policy. We never claim "GDPR certified" — we show you the architecture and let you verify.

How much does it cost?

Observer is self-hosted. The only cost is your server — roughly €60–90/month for about 60 million events per month. No per-traffic, per-site, per-seat or per-feature charges. Competitors charge $1,200–$2,950+/month at the same volume.

Why can't the snippet "upgrade" itself to collect more?

Because the tier is enforced by the collector, server-side. A modified or misconfigured snippet cannot enable storage, extend retention or start recording — the collector rejects events that the site's tier does not allow.

Does it respect Do Not Track?

Yes — and Global Privacy Control, and the explicit observer('optOut') call. All three silence the SDK unconditionally: zero beacons, zero storage. This is asserted by automated browser tests.

Don't trust the claims.
Run the tests.

The browser privacy suite — cookies, storage, PII scrubbing, consent gate, GPC silence — runs against your deployment during onboarding, and the report goes to your DevOps team.