# 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.

|                              |                  Rybbit                   |   Umami    |      Matomo       |
| ---------------------------- | :---------------------------------------: | :--------: | :---------------: |
| Version essayée              | `ghcr.io/rybbit-io/rybbit-backend:latest` | `v2.19.0`  |      `5.3.2`      |
| Navigateur                   |                    oui                    |    oui     |        oui        |
| Serveur (HTTP)               |                    oui                    |    oui     |        oui        |
| Mobile natif                 |                    non                    |    oui     |        oui        |
| Vues de page                 |                    oui                    |    oui     |        oui        |
| Événements personnalisés     |                    oui                    |    oui     |        oui        |
| `identify`                   |                    oui                    |  **non**   |    oui (`uid`)    |
| Enregistrement de session    |                    oui                    |    non     | extension payante |
| Entonnoirs                   |                    oui                    |    oui     |      plugin       |
| Authentification d'ingestion |            clé d'API (serveur)            | **aucune** |  jeton (serveur)  |

## Contrats d'ingestion

|                    | Rybbit                                                    | Umami                            | Matomo                                    |
| ------------------ | --------------------------------------------------------- | -------------------------------- | ----------------------------------------- |
| Endpoint           | `POST /api/track`                                         | `POST /api/send`                 | `POST /matomo.php`                        |
| Corps              | JSON                                                      | JSON                             | formulaire URL-encodé                     |
| En-têtes           | `Authorization: Bearer`, `Content-Type`                   | `Content-Type`, **`User-Agent`** | `Content-Type`                            |
| Champs d'événement | `site_id`, `type`, `event_name`, `pathname`, `properties` | `website`, `name`, `data`, `url` | `idsite`, `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](/decisions/analytics)).
- Enregistrement de session : jamais activé, quelle que soit la capacité du fournisseur.
