Conception

Fournisseurs : capacités et limites vérifiées

Matrice établie par essai sur les versions épinglées, pas d'après la documentation commerciale. Date des vérifications : 25/10/2026.

RybbitUmamiMatomo
Version essayéeghcr.io/rybbit-io/rybbit-backend:latestv2.19.05.3.2
Navigateurouiouioui
Serveur (HTTP)ouiouioui
Mobile natifnonouioui
Vues de pageouiouioui
Événements personnalisésouiouioui
identifyouinonoui (uid)
Enregistrement de sessionouinonextension payante
Entonnoirsouiouiplugin
Authentification d'ingestionclé d'API (serveur)aucunejeton (serveur)

Contrats d'ingestion

RybbitUmamiMatomo
EndpointPOST /api/trackPOST /api/sendPOST /matomo.php
CorpsJSONJSONformulaire URL-encodé
En-têtesAuthorization: Bearer, Content-TypeContent-Type, User-AgentContent-Type
Champs d'événementsite_id, type, event_name, pathname, propertieswebsite, name, data, urlidsite, rec=1, apiv=1, e_c, e_a

Pièges constatés à l'essai

Détection de robots : les deux répondent « succès » en jetant l'événement

C'est le piège principal du POC. Umami et Rybbit filtrent les robots d'après le User-Agent, et répondent tout de même en succès.

  • Umami : sans User-Agent de navigateur crédible, la requête renvoie 200 et rien n'est stocké. Un User-Agent contenant +http — convention classique des robots — suffit à déclencher le filtre. Constaté : aucune ligne dans website_event.
  • Rybbit : la requête renvoie {"success":true} et l'événement part dans la table bot_events au lieu de events. Le journal du backend l'indique (botDetectionTotals: {ua_pattern: 1}). Constaté : events = 0, bot_events = 2.

Le même filtre frappe un navigateur réel piloté : le User-Agent par défaut de Chromium en mode sans interface contient HeadlessChrome, et Umami répond alors 200 avec le corps littéral {"beep":"boop"} sans rien stocker (constaté le 26/10/2026). Un test de bout en bout qui ne remplace pas ce User-Agent mesure le filtre antirobot, pas la chaîne d'envoi de l'application. smoke-app.ts remplace donc HeadlessChrome par Chrome dans le contexte Playwright. Les visiteurs réels ne sont pas concernés.

Conséquence pour le code : les deux adaptateurs exigent un userAgent côté serveur et refusent de s'initialiser sans lui. Vérifier un code HTTP ne prouve rien ; c'est pourquoi le test de fumée interroge les données.

Matomo

  • core:install n'existe pas en Matomo 5 : l'installation passe par l'installeur web. Le script provision-matomo.mjs déroule le formulaire étape par étape.
  • Détecter l'installation par la présence d'un bouton de connexion donne un faux positif : l'installeur en affiche un. La détection porte sur le titre de la page.
  • e_v n'accepte qu'une valeur numérique ; les autres propriétés passent en dimension.
  • Un jeton d'API s'obtient sans jeton préalable via UsersManager.createAppSpecificTokenAuth, authentifié par mot de passe.
  • Live.getLastVisitsDetails lit les journaux bruts : la vérification n'attend pas l'archivage, qui est différé.

Umami

  • /api/send n'exige aucune authentification : l'endpoint doit être protégé par le proxy (limitation de débit), sinon n'importe qui peut injecter des événements.
  • L'API de lecture exige un jeton d'administration (/api/auth/login).
  • Les noms d'événements se lisent par metrics?type=event ; les valeurs de propriétés par event-data/values. Vérifier le seul nom ne prouve pas que la charge utile est arrivée.
  • Pas d'identify : les sessions sont dérivées d'une empreinte serveur.

Rybbit

  • properties est une chaîne JSON de 2048 caractères maximum, pas un objet. L'adaptateur sérialise et retire les clés les plus longues jusqu'à tenir dans la limite.
  • Seuls les chaînes et les nombres sont acceptés comme valeurs.
  • event_name est limité à 256 caractères.
  • L'API d'administration passe par better-auth : cookie de session et en-tête Origin, sans quoi elle répond MISSING_OR_NULL_ORIGIN.
  • Dépendances : ClickHouse, PostgreSQL et Redis. C'est le fournisseur le plus lourd.
  • La création de site est POST /api/organizations/{organizationId}/sites — chemin absent de la documentation publique, trouvé dans openapi.json.
  • GET /api/sites/{id}/events/count renvoie un tableau de tranches horaires ([{ time, pageview_count, custom_event_count, event_count, … }]), pas un total. Lire data comme un nombre donne zéro en permanence, sans erreur. Les vues de page se comptent par pageview_count, pas par event_count.

Limite de conception connue

Les files d'attente sont indexées par nom de fournisseur : une seule instance par type de fournisseur est gérée. Deux instances Matomo simultanées se télescoperaient. Ce n'est pas un besoin identifié ; le lever demanderait une clé composite.

Ce qui n'est pas couvert

  • screen() : refusé par les trois adaptateurs, faute de SDK natif dans ce POC.
  • identify() : implémenté dans l'interface pour la portabilité, mais refusé par configuration dans ce dépôt (ADR 006).
  • Enregistrement de session : jamais activé, quelle que soit la capacité du fournisseur.