takt

Vie privée

La protection de la vie privée est intégrée au cœur de Takt, pas ajoutée après coup. Aucun cookie, aucun identifiant persistant, aucune donnée personnelle ne quitte le navigateur. Les visiteurs n’ont rien à accepter.

Quand un event n’est pas envoyé

Avant tout envoi, Takt vérifie quatre conditions dans cet ordre. Si l’une est vraie, l’event est supprimé silencieusement :

1. Opt-out visiteur

Le visiteur a refusé la mesure : localStorage contient takt_ignore = '1'.

2. Do Not Track & GPC

Le navigateur signale Do Not Track (en-tête DNT) ou Global Privacy Control (en-tête Sec-GPC / navigator.globalPrivacyControl). Le serveur d'ingestion rejette toujours une requête qui porte l'en-tête DNT: 1 ou Sec-GPC: 1, quel que soit le réglage. respectDnt: false (ou data-respect-dnt="false") ne retire que le contrôle côté navigateur : ces visiteurs ne sont pas comptés pour autant.

3. Localhost / IP privée

L'hôte est local ou sur un réseau privé (localhost, ::1, 0.0.0.0, *.local, 127.*, 10.*, 192.168.*, 172.16.* à 172.31.*) et excludeLocalhost est actif (défaut).

4. Échantillonnage

sampleRate (ou data-sample-rate) est sous 1 et cet event tombe hors de la fraction conservée.
Les garde-fous opt-out, DNT/GPC et localhost sont actifs par défaut. En développement local, rien ne remonte : c'est volontaire, pas un bug.

Avec le SDK npm et les wrappers de framework, exclude ajoute un levier supplémentaire : toute page ou tout event nommé dont le chemin commence par un préfixe exclu (ex. ['/app','/account'], borné au segment, donc /app matche /app et /app/… mais pas /application) n’est jamais suivi. La vérification a lieu à l’envoi, elle tient donc à travers la navigation SPA. Le snippet CDN minimal n’expose pas cette option.

Donner le contrôle au visiteur

Tu peux exposer un bouton « Ne pas me suivre » qui pilote l’opt-out. Le choix s’écrit dans localStorage (clé takt_ignore), donc il persiste entre les visites sur ce navigateur.

En intégration npm, utilise les fonctions dédiées :

import { optOut, optIn, isOptedOut } from '@vskstudio/takt-core'

optOut()     // pose takt_ignore='1', plus aucun event n'est envoyé
optIn()      // retire le drapeau, reprend le tracking
isOptedOut() // true tant que le visiteur a refusé la mesure

Ces fonctions n’ont pas besoin qu’une instance existe : une bannière de consentement affichée avant init() peut enregistrer le refus, et toute instance créée ensuite le respecte. Les wrappers de framework réexportent les trois fonctions.

Côté snippet (CDN), le global window.takt n’expose que track. Pilote donc l’opt-out directement via localStorage. Le snippet lit cette clé avant chaque envoi :

function toggleTracking() {
  if (localStorage.getItem('takt_ignore') === '1') {
    localStorage.removeItem('takt_ignore') // opt-in
  } else {
    localStorage.setItem('takt_ignore', '1') // opt-out
  }
}

Nettoyage des URLs

Par défaut, Takt strippe la query string et le hash de toutes les URLs avant l’envoi : l’URL de la page, le referrer et la destination des liens sortants ne gardent que origine + chemin. Un token, un e-mail ou un identifiant glissé dans ?... ou #... n’atteint donc jamais l’analytics.

Tu peux ajuster ce comportement : trackQuery conserve la query entière, queryParams n’en garde qu’une allowlist, et scrubUrl remplace toute la logique par ta propre fonction (npm). Côté snippet, utilise data-track-query ou data-query-params pour le même effet.

scrubUrl s’applique à toutes les URLs qui partent : l’URL de la page, le referrer, et depuis @vskstudio/takt-core 0.9.0 la propriété url des events Outbound Link: Click et File Download émis par l’autocapture, puis depuis la 0.10.1 la propriété path des events 404. Ces liens arrivent déjà réduits à origine + chemin, ta fonction sert donc surtout à masquer un identifiant logé dans le chemin :

import { init } from '@vskstudio/takt-core'

const maskUserIds = (url: string) => url.replace(/\/users\/[^/]+/, '/users/:id')

init({ domain: 'exemple.fr', outbound: true, files: true, scrubUrl: maskUserIds })

Masquage de routes

Le nettoyage des URLs retire la query et le hash, mais le chemin part en clair. Une route qui porte un secret dans un segment le transmet donc tel quel : /verify/abc123, /reset/7f3c9a ou /invoices/4812.pdf arrivent dans l’analytics avec leur jeton. Depuis @vskstudio/takt-core 0.10.0 (wrappers de framework 0.8.0), deux options remplacent ces chemins par le motif de la route avant l’envoi. Le chemin réel ne quitte jamais le navigateur.

redactRoutes : masquer les routes sensibles

Tu listes les routes qui portent un secret. Un chemin qui correspond à l’un des motifs est envoyé sous la forme du motif, tous les autres gardent leur chemin réel :

import { init } from '@vskstudio/takt-core'

init({
  domain: 'exemple.fr',
  redactRoutes: ['/verify/[token]', '/reset/:code', '/invoices/[id].pdf']
})

/verify/abc123 part alors comme /verify/[token], et /blog/mon-article reste /blog/mon-article. Les statistiques par page sont intactes partout ailleurs.

  • Syntaxe des motifs : celle de SvelteKit, Astro et Next ([param], [[optionnel]], [...reste], groupes (groupe) ignorés) et celle de Vue Router, React Router, Angular et Solid (:param, :param?, *, **). Un segment peut mêler texte et paramètre ([id].pdf).
  • Portée : l’URL de la page, le referrer quand il vient du même site, la destination des liens sortants et des téléchargements, et le chemin des events 404.
  • Le motif envoyé est normalisé : les groupes disparaissent (/(app)/verify/[token] devient /verify/[token]) et les contraintes de paramètre aussi (:id(\d+) devient :id).
  • Contraintes : {id<\d+>} ne correspond qu’à un segment entier qui satisfait la regex, sans franchir /. La regex suit une grammaire restreinte, détaillée sur la page PHP. Une route qui en sort, ou dont le degré d’ambiguïté dépasse 1, est inutilisable : tout chemin de même forme part alors en /[redacted], les autres ne sont pas touchés.

Chemins illisibles

Un chemin qui ne se décode pas (échappement % mal formé, comme %ZZ, ou UTF-8 invalide une fois décodé) part en /[redacted] dès qu’une route est configurée : un chemin qui ne peut pas être contrôlé ne part jamais en clair. Sans route, il part tel quel.

L’option redactUndecodablePaths (défaut true) assouplit ce comportement quand elle vaut false : le chemin est alors comparé aux routes comme les autres, il part sous son gabarit s’il en correspond un, et brut sinon.

  • Côté serveur : le client Takt de PHP, les bridges Laravel et Symfony et le client serveur Node (@vskstudio/takt-core/server) acceptent l’option. Elle ne règle que les événements envoyés par le serveur. Le snippet navigateur rendu par PHP, Laravel ou Symfony garde le défaut, masqué.
  • Côté navigateur : le SDK JS accepte l’option dans init() et createTakt(), et takt.auto.js la lit dans l’attribut data-redact-undecodable-paths="false". Les wrappers de framework et l’élément <takt-analytics> ne l’exposent pas : pour la régler, utilise directement le SDK core.

routeTemplates : envoyer toutes les pages en gabarit

routeTemplates: true envoie chaque page sous la forme de son gabarit de route : /blog/bonjour devient /blog/[slug], /users/42 devient /users/:id. Aucun chemin réel ne part.

Le cœur ne connaît pas ton routeur : il lui faut un résolveur routeTemplate qui renvoie le gabarit courant. Les wrappers de framework le branchent pour toi (voir plus bas). Quand le résolveur ne renvoie rien (aucune route ne correspond), redactRoutes s’applique quand même, sinon le chemin réel part. Dans ce mode, le referrer interne est réduit à l’origine du site, puisque son gabarit n’est pas connu.

Le compromis : statistiques par page

RéglageCe qui partClassement Pages
DéfautChemin réel, sans query ni hashUne ligne par URL
redactRoutesMotif pour les routes listées, chemin réel ailleursIntact, sauf les routes masquées regroupées
routeTemplatesGabarit pour toutes les pagesUne ligne par route : tous les articles d’un blog fusionnent

routeTemplates convient à une application privée (tableau de bord, espace client) où aucun chemin ne mérite d’être lu tel quel. Sur un site public, il fusionne tous tes articles en une seule ligne : préfère redactRoutes et ne liste que les routes qui portent un secret.

Le masquage de routes agit dans le navigateur, avant l'envoi : le chemin réel n'est jamais stocké. Le regroupement d'URL du tableau de bord, lui, ne change que l'affichage des chemins déjà reçus.

Où le régler

IntégrationComment
@vskstudio/takt-coreOptions redactRoutes, routeTemplates, routeTemplate et redactUndecodablePaths de init() / createTakt()
takt.auto.js (snippet)Attributs data-redact-routes (liste séparée par des virgules) et data-redact-undecodable-paths="false", pas de routeTemplates
SvelteKitrouteTemplate lu depuis page.route.id
VueOption router du plugin ou du composant, ou vueRouterTemplate(router)
ReactreactRouterTemplate(router) avec un data router ; redactRoutes recommandé pour Next.js
SolidsolidRouterTemplate(useCurrentMatches())
AngularrouteTemplateFromSnapshot() dans le contexte d’injection de provideTakt
AstroAstro.routePattern, transmis par <TaktRoute />
Node (serveur)redactRoutes et option route par appel
Élément <takt-analytics>Attribut redact-routes (liste séparée par des virgules), pas de routeTemplates
PHP (takt-core-php 0.6.0)redactRoutes, routeTemplates et routeTemplate d’Options en Mode::Sdk ; redactRoutes, route, withRoute() et redactUndecodablePaths (depuis 0.7.0) du client serveur Takt
Laravel (0.6.0)TAKT_REDACT_ROUTES et TAKT_ROUTE_TEMPLATES, gabarit lu depuis la route Laravel
Symfony (0.6.0)Nœuds redact_routes et route_templates, chemin lu depuis la route Symfony

Côté PHP, les motifs acceptent aussi la syntaxe {param} de Laravel et Symfony : voir les pages PHP, Laravel et Symfony. Le plugin WordPress n’expose pas ces options.

Le snippet minimal (/takt.js, 1 kB) ne gère pas ces options. Pour masquer des routes sans build, charge takt.auto.js avec data-redact-routes ; routeTemplates demande le paquet npm ou un wrapper de framework.

Supprimer ton compte (RGPD)

Tu peux supprimer ton compte toi-même, sans contacter le support. C’est ton droit à l’effacement (RGPD, article 17).

Où : Réglages → onglet Profil → zone de danger → bouton « Supprimer mon compte ».

Le déroulé

  1. Le bouton ouvre une boîte de dialogue de confirmation.
  2. Takt t’envoie un e-mail contenant un lien sécurisé. Aucun mot de passe n’est demandé : la confirmation passe uniquement par ce lien.
  3. Tu confirmes en cliquant sur le lien reçu. Le compte est alors désactivé immédiatement.
La confirmation se fait toujours par e-mail. Si tu ne reçois rien, vérifie tes spams. Rien n'est supprimé tant que le lien n'a pas été ouvert.

Délai de grâce de 90 jours

La désactivation est réversible pendant 90 jours. Pendant cette fenêtre, le compte n’est pas effacé : il te suffit de te reconnecter pour le réactiver, ainsi que les organisations dont tu es le seul membre.

Passé ce délai, la suppression devient définitive et irréversible : compte, données personnelles, avatar et statistiques d’audience des sites de tes organisations solo sont purgés sans retour possible.

Cas bloqué : seul propriétaire d’une organisation à plusieurs

Si tu es l’unique propriétaire d’une organisation qui compte d’autres membres, la suppression est bloquée. Tu dois d’abord transférer la propriété de cette organisation à un autre membre (depuis la section Équipe) avant de pouvoir supprimer ton compte.

Les organisations dont tu es le seul membre sont, elles, supprimées en même temps que ton compte.

Ce que Takt ne collecte pas

  • Pas de cookie ni de stockage d’identifiant de session.
  • Pas d’adresse IP conservée côté client, pas de fingerprinting du navigateur.
  • Pas de query string ni de hash dans les URLs (strippés par défaut).
  • Pas de donnée personnelle : garde tes props anonymes et à faible cardinalité.
Parce qu'aucune donnée personnelle n'est traitée, Takt s'intègre sans bannière de consentement dans la plupart des juridictions. Vérifie toujours ton contexte légal propre.