From c9ac8e1e21853ff1f0c0c0a32bb4e9cd2031ef6f Mon Sep 17 00:00:00 2001 From: msa46 Date: Sat, 15 Aug 2026 14:30:39 +0200 Subject: [PATCH] docs: design consent-gated Matomo tracking --- .../2026-08-15-gdpr-matomo-tracking-design.md | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 docs/superpowers/specs/2026-08-15-gdpr-matomo-tracking-design.md diff --git a/docs/superpowers/specs/2026-08-15-gdpr-matomo-tracking-design.md b/docs/superpowers/specs/2026-08-15-gdpr-matomo-tracking-design.md new file mode 100644 index 0000000..ad55140 --- /dev/null +++ b/docs/superpowers/specs/2026-08-15-gdpr-matomo-tracking-design.md @@ -0,0 +1,82 @@ +# GDPR-Gated Matomo Tracking Design + +## Goal + +Add Matomo analytics to the Astro Starlight wiki without sending analytics requests or loading the remote Matomo client before a visitor explicitly consents. Visitors must be able to decline without losing site functionality and withdraw consent later. + +This implementation supports privacy compliance but does not, by itself, guarantee legal compliance. The Matomo server configuration and the association's privacy documentation must match the behavior described here. + +## Chosen approach + +Use Starlight's global `head` configuration to load a small local consent controller on every page. The controller owns the consent interface and loads `https://analytics.msvincognito.nl/matomo.js` only after affirmative consent. + +This follows Starlight's supported analytics integration point without overriding framework components. It also avoids making a request to the analytics host before the visitor chooses to participate. + +## Visitor experience + +- On a first visit, show an accessible consent banner that briefly explains first-party, cookieless analytics and links to `https://msvincognito.nl/privacy-policy`. +- Show equally visible **Accept analytics** and **Decline** actions. The wiki remains fully usable after either choice. +- Remember the choice locally so the banner does not reappear on each page. +- Keep a visible **Privacy settings** control available after a choice. It reopens the banner so consent can be changed or withdrawn. +- If browser storage is unavailable, fail privately: do not track automatically, and ask again on a later page load. + +## Tracking behavior + +The local controller uses a versioned, wiki-specific storage key with three states: no decision, accepted, or declined. + +When the state is accepted, initialize `window._paq` and queue Matomo commands in privacy-first order: + +1. require tracking consent as defense in depth; +2. disable analytics cookies; +3. grant consent for the current page load based on the stored choice; +4. set the HTTPS tracker URL to `https://analytics.msvincognito.nl/matomo.php`; +5. set site ID `1`; +6. track the page view and enable link tracking; +7. asynchronously load `https://analytics.msvincognito.nl/matomo.js` once. + +When the state is declined, do not initialize Matomo or load any resource from the analytics origin. + +When an accepted choice is withdrawn, queue Matomo's consent-revocation command if the tracker has already loaded, remove any Matomo cookies defensively, persist the declined state, and stop future tracking. The current page is not reloaded. + +The current Starlight site uses normal document navigation, not Astro's `ClientRouter`, so each page load initializes at most one page view. The controller must still guard against duplicate initialization. + +## Project structure + +- Add one focused client script for consent state, UI behavior, and conditional Matomo loading. +- Register that script globally through `astro.config.mjs`, using the configured Astro base path so subpath deployments continue to work. +- Add the banner and settings-control styles to the existing `src/styles/incognito.css` file. +- Add focused Node tests alongside the existing tests. Tests should exercise observable behavior with a lightweight DOM rather than duplicate the implementation. + +No third-party consent-management dependency is added. + +## Accessibility and privacy requirements + +- Use a labelled dialog/banner region with keyboard-operable native buttons. +- Move focus into the banner when privacy settings are reopened and restore a sensible focus target after a decision. +- Do not preselect consent, treat inactivity as consent, or make acceptance visually easier than refusal. +- Use clear language that identifies MSV Incognito, the analytics purpose, the cookieless configuration, and the withdrawal route. +- Do not record the consent choice in Matomo. + +## Verification + +Automated tests must verify that: + +- no Matomo script or queue is created before consent; +- acceptance persists the choice and loads the expected HTTPS Matomo endpoint once; +- decline persists the choice without loading Matomo; +- withdrawal changes the stored choice, invokes revocation when possible, and prevents later initialization; +- the global Starlight configuration includes the local consent controller with base-path support; +- a production build contains the controller and consent UI styling. + +Run the project's full `npm run verify` command after the focused red/green test cycle. + +## Required operational follow-up + +Before deployment, the Matomo administrator should confirm server-side IP anonymization, appropriate log and analytics retention periods, restricted administrator access, and that data is not repurposed for advertising or cross-site profiling. The privacy page must describe the analytics purpose, data categories, retention, controller identity, consent withdrawal, and data-subject rights. + +## References + +- [Starlight global head configuration](https://starlight.astro.build/reference/configuration/#head) +- [Matomo tracking consent API](https://developer.matomo.org/guides/tracking-consent) +- [Matomo JavaScript tracking API](https://developer.matomo.org/guides/tracking-javascript) +- [Dutch DPA cookie-banner guidance](https://autoriteitpersoonsgegevens.nl/actueel/ap-pakt-misleidende-cookiebanners-aan)