Cookie & Tracking Inventory
Inventory of every cookie, similar local-storage value, and tracking signal set by the three apps (Clinic, Portal, Console) and the sub-processors they call. Drives the cookie banner content and the privacy notice's cookie disclosure. Required artefact for ePrivacy compliance + GDPR transparency. v1 drafted 2026-05-15; reconciled against the codebase 2026-09-04, which added
org-timezoneanddemo-activeand corrected two rows.
Counsel-review status
Developer-drafted v1. Counsel reviews the cookie-banner UX (granular consent design, dismiss-equals-reject behaviour), confirms ePrivacy compliance under Romanian Law 506/2004, and validates the strictly-necessary classification of each cookie. See counsel brief.
Methodology
Three classifications matter under ePrivacy + GDPR:
| Class | Definition | Consent required? |
|---|---|---|
| Strictly necessary | Cookie / storage value without which the requested service cannot be provided | No — exempt under ePrivacy Art. 5(3) second sentence |
| Functional | Cookie that enhances functionality the user explicitly opted into (language, theme) | Borderline — depends on prior explicit user action |
| Analytics / measurement | Cookie / storage value used to measure usage | Yes — consent before set |
| Marketing / tracking | Cookie used for cross-site tracking, ad targeting, retargeting | Yes — consent before set; broader transparency expected |
The platform's launch posture: strictly necessary + functional only. No marketing cookies. Analytics is consent-gated (and at launch, deferred — the analytics provider is selected post-launch only).
Inventory
Portal (portal.restartix.pro, custom hostnames per clinic)
Strictly necessary
| Cookie | Set by | Purpose | TTL | Flags |
|---|---|---|---|---|
__session | Clerk | Active authentication session | Per Clerk's session config (~7 days at launch) | HttpOnly, Secure, SameSite=Lax |
__clerk_db_jwt | Clerk | Clerk's authentication state | Session | HttpOnly, Secure |
__clerk_* (varies) | Clerk | Clerk authentication framework cookies | Per Clerk | HttpOnly, Secure |
org-id | Platform (proxy.ts) | Tenant context bound to the hostname; used by every server-side fetch to scope requests to the correct organisation | Session | HttpOnly, SameSite=Lax |
org-slug | Platform (proxy.ts) | Tenant slug for URL building and display | Session | HttpOnly, SameSite=Lax |
org-name | Platform (proxy.ts) | Tenant display name for UI rendering | Session | HttpOnly, SameSite=Lax |
org-portal-self-signup | Platform (proxy.ts) | Whether the tenant's portal allows self-serve sign-up; gates the /onboard route | Session | HttpOnly, SameSite=Lax |
share-link-code | Platform (/join/[code]) | Carries the invite/share-link code through the onboarding flow when a patient lands via a share link | ≤ 1 hour | HttpOnly, Secure, SameSite=Lax |
org-timezone | Platform (proxy.ts) | Tenant's timezone; used to render dates and times in the clinic's local zone rather than the server's. Falls back to Europe/Bucharest when absent | Session | HttpOnly, SameSite=Lax |
demo-active | Platform (Portal /demo) | Marks that a patient entered a session through the demo flow, so the shared run-completion screen returns them to /demo instead of its default target. Set only for visitors who open /demo | 12 hours | HttpOnly, SameSite=Lax |
Functional
| Cookie | Set by | Purpose | TTL | Flags |
|---|---|---|---|---|
language | Platform (proxy.ts) | Tenant's default UI language; readable client-side for date / number formatting | Session | NOT HttpOnly (deliberate), SameSite=Lax |
language-user-override | Platform (server action setLanguage) | User's explicit language override (overrides tenant default) | 1 year | HttpOnly, SameSite=Lax |
Analytics
None at launch. Analytics provider (Plausible / PostHog / similar EU-hosted) selected post-launch. When added:
- Consent-gated via the cookie banner.
- Preferred provider's "cookie-less" mode used if available.
- If cookies are used, the inventory entry is added to this document in the same PR that integrates the provider.
Marketing / tracking
None. The platform does not run third-party ad pixels at launch and has no plan to.
Set by sub-processors that traverse the request path
| Cookie | Set by | Purpose | Class | Flags |
|---|---|---|---|---|
__cf_bm | Cloudflare (Bot Management) | Bot detection on the request path | Strictly necessary | HttpOnly, Secure, SameSite=None |
cf_clearance | Cloudflare (when a challenge fires) | Persists the result of a successful bot-challenge | Strictly necessary | HttpOnly, Secure, SameSite=None |
These are Cloudflare's standard CDN cookies. Documented at developers.cloudflare.com. They are operationally strictly-necessary for the CDN's security function and are exempt from consent.
Clinic app (clinic.restartix.pro, custom hostnames per clinic)
Same shape as Portal:
- Clerk authentication cookies (strictly necessary)
org-*proxy cookies (strictly necessary)language/language-user-override(functional)- Cloudflare cookies (strictly necessary)
Plus, post-launch, debug/operational cookies for clinic-admin tooling — not in launch scope.
No analytics or marketing.
Console (console.restartix.pro — platform-staff only)
Restricted to platform staff. Same auth + functional cookies. No analytics. The Console deliberately runs without any third-party tracking because Console-side telemetry could expose break-glass-elevation patterns or platform-internal operations.
| Cookie | Purpose |
|---|---|
| Clerk authentication cookies | Strictly necessary |
| Cloudflare cookies | Strictly necessary |
No org-id cookie in Console — superadmin context, not tenant-bound. No language or language-user-override either: the Console is English-only by design and ships no locale switcher, so there is no preference to persist.
Public landing page (restartix.pro root)
When the public landing page ships:
| Cookie | Purpose | Class |
|---|---|---|
cookie-consent | Records the user's cookie-banner choice ("strictly necessary only" / "with analytics") | Strictly necessary (recording the consent itself is exempt) |
| Cloudflare cookies | Strictly necessary | |
| (Future) analytics cookies | Functional / analytics — gated by cookie-consent value |
Similar non-cookie tracking surfaces
Local Storage / Session Storage
| Key | Set by | Purpose | App |
|---|---|---|---|
| Clerk-internal | Clerk | Client-side cache of auth state for the Clerk SDK | All three apps |
| Sentry-internal | Sentry SDK | Session ID for crash grouping; no PII — the SDK runs with sendDefaultPii: false and attaches no user identifier | Portal only. The Clinic and Console apps are not wired to error tracking |
Sentry's session-replay product is disabled at launch (it would capture user input — out of scope until the privacy posture is reviewed for replay).
IndexedDB
None at launch.
Web Workers / Service Workers
None at launch.
Pixels / web beacons
None.
Fingerprinting
The platform does not use fingerprinting libraries (FingerprintJS, etc.). Cloudflare may use TLS-handshake metadata for bot detection — this is part of Cloudflare's strictly-necessary CDN function, not a separate tracking layer the platform integrates.
Cookie banner design
Required behaviour under ePrivacy + ANSPDCP guidance (subject to counsel confirmation):
- Pre-consent default: only strictly-necessary cookies are set. Analytics cookies (when introduced) are not set until the user consents.
- Symmetry of choices: the banner offers "Accept all", "Reject all", and "Customise" with equally prominent styling. Hidden "Reject" buttons buried in a settings panel fail ANSPDCP review.
- Dismissing equals rejecting. Closing the banner without selection does not constitute consent.
- Per-category granularity: strictly necessary (always on, not toggleable), functional, analytics. Each toggleable category has a one-sentence purpose description.
- Withdrawal: the patient can revisit the banner choice at any time via a "Cookie preferences" link in the footer of all public pages.
- Record of consent: the choice is recorded with timestamp + IP + the version of the cookie-banner copy shown. Stored in the same versioned
consentsledger used for other consent purposes (or in a dedicatedcookie_consentstable if counsel recommends separation). - Privacy notice link: the banner links to the full privacy notice for detail.
Implementation note
At launch, with no analytics in scope, the cookie banner is structurally minimal — a single "Continue" acknowledgement of the strictly-necessary cookie notice is sufficient under ANSPDCP guidance (no consent needed for strictly-necessary cookies; only the transparency notice). When the analytics provider is added post-launch, the banner expands to the multi-category form.
The minimum-viable launch banner copy:
We use cookies that are strictly necessary for sign-in,
session management, and the security of this site.
We do not run tracking or advertising cookies.
[ Continue ] [ Privacy notice → ]When analytics is added, the copy expands:
We use cookies and similar technologies on this site.
Strictly necessary cookies (always active) keep you signed in
and the site secure.
Analytics cookies help us understand how the program works
for our patients. You can opt in or out below.
[ Strictly necessary ] ON (cannot disable)
[ Analytics ] ☐ Off by default
[ Accept selected ] [ Accept all ] [ Reject all ]
[ Privacy notice → ]Adding a new cookie / tracking surface
Engineering rule: a PR that introduces a new cookie, local-storage value, or tracking call must add the row in this inventory in the same PR. CI does not enforce this today (the inventory is markdown-only); the convention is enforced by code review until a CI guard is built (TODO: cmd/check-cookies similar to cmd/check-classification).
A new analytics or marketing surface additionally requires:
- An entry in the consent purpose catalog
- A cookie-banner expansion to allow opt-in / opt-out
- A privacy notice template update (1B.10) so the disclosure is current
- DPO approval
Change log
| Date | Change |
|---|---|
| 2026-05-15 | Initial v1 — strictly-necessary + functional inventory; analytics deferred; banner copy drafted |