Documentation développeur
Tout ce que fait SnagSpy, et comment y accéder
SnagSpy reçoit l’OpenTelemetry de votre application, regroupe les erreurs en incidents, les corrèle avec les déploiements, les requêtes en base, la topologie des services et les journaux, puis vous dit pourquoi quelque chose a cassé. Commencez par un guide ci-dessous ; la référence de l’API a sa propre page, et chaque réglage, chaque limite et la liste honnête de ce qu’elle ne fait pas se trouvent plus bas.
Cette page est une traduction. La version anglaise fait foi : en cas de divergence sur un détail technique, c’est elle qui décrit le comportement réel. La traduction n’a pas encore été relue par un locuteur natif.
Tout ce qui suit a été vérifié dans le code en cours d’exécution plutôt qu’écrit de mémoire. Si une fonctionnalité manque ici, considérez qu’elle manque à la plateforme et dites-le — la section finale énumère délibérément les manques.
Vue d’ensemble
La plateforme est un monolithe modulaire en Go avec un tableau de bord Nuxt. Deux processus vous concernent : ingest, qui accepte la télémétrie, et l’API, qui sert tout ce que vous lisez. Ils sont séparés pour qu’un afflux de télémétrie n’emporte pas le tableau de bord avec lui.
De l’OTLP standard en entrée
Traces, journaux et métriques arrivent en OpenTelemetry. Rien n’est propriétaire : une application qui fait déjà tourner un SDK OTel peut le pointer ici et se passer entièrement de nos SDK — et le pointer ailleurs plus tard.
Corrélé, pas seulement regroupé
Un incident porte le déploiement qui l’a précédé, les requêtes lentes autour de lui, les services en amont et les journaux à côté.
Expliqué, avec le raisonnement
Les investigations IA sélectionnent et formulent ; la plateforme calcule. Chaque chiffre d’une réponse est vérifiable, et une réponse qui ne peut être étayée n’est pas donnée.
Démarrage rapide
- Créez un compte et un projet. Inscrivez-vous, puis connectez-vous — l’inscription ne renvoie volontairement pas de session. Les projets se créent depuis le tableau de bord, ou depuis l’API si vous préférez le scripter.
- Émettez un DSN. Un par projet, depuis la page Projets du tableau de bord. Un DSN s’écrit scheme://publicKey@host/projectID et peut être livré sans risque dans un bundle navigateur — il peut écrire de la télémétrie et ne rien lire.
- Envoyez quelque chose. L’un des SDK ci-dessous, ou de l’OTLP brut vers le point d’ingestion.
- Regardez-le arriver. L’incident apparaît dans le tableau de bord, sous Erreurs.
JavaScript et le navigateur
import { init, captureException } from "@snagspy/browser"
init({ dsn: globalThis._importMeta_.env.VITE_SNAGSPY_DSN, environment: "production" })
try { risky() } catch (error) { captureException(error) }
Les deux SDK masquent les valeurs qui ressemblent à des secrets avant que quoi que ce soit ne quitte le processus. Ce n’est pas une politesse côté serveur : un mot de passe qui nous est envoyé puis masqué à l’arrivée a déjà traversé le réseau.
Concepts
- Organisation
- La frontière de facturation et d’appartenance. Chaque donnée appartient à exactement une organisation, et l’isolation entre locataires est une condition de publication plutôt qu’une convention.
- Projet
- Une application. Possède ses DSN, son consentement à la relecture et sa limite d’ingestion.
- DSN
- Un identifiant d’écriture par projet, scheme://publicKey@host/projectID. Public par conception : il peut écrire de la télémétrie et ne rien lire.
- Clé d’API
- Un identifiant de lecture pour l’API du tableau de bord, limité à l’organisation. Affiché une seule fois à la création.
- Événement / occurrence
- Une erreur telle qu’elle s’est produite, avec sa pile, son contexte et les signaux corrélés.
- Incident
- Des occurrences regroupées par empreinte. Une pile minifiée est regroupée sur le message plus le nom de chunk de chaque frame, hash de contenu retiré, pour qu’un redéploiement ne scinde pas un incident en deux.
- Trace et span
- OTel standard. La liste des traces filtre sur les spans et agrège des traces entières, et indique lesquels de ses chiffres sont des estimations.
- Unité de relecture
- L’unité de facturation de la relecture de session : une unité par tranche de 64 Kio envoyée.
- Investigation
- Un appel au modèle, et l’unité dans laquelle toutes les fonctionnalités IA sont décomptées : une analyse de cause racine, ou une revue de code. Facturée au moment de l’appel, avant d’en connaître le résultat — un appel en échec a quand même coûté un appel.
SDK
Les deux sont de fines couches d’ergonomie au-dessus d’OpenTelemetry plutôt que des bibliothèques d’instrumentation à part entière. Ce qu’elles ajoutent est la partie facile à rater : la gestion du DSN, l’enregistrement des exceptions dans la forme sur laquelle la plateforme regroupe, et le masquage avant envoi.
| SDK | Paquet | Apporte |
|---|
| Go | sdk/go/snagspy | Analyse du DSN, CaptureException, masquage, middleware HTTP. |
| Navigateur et Node | sdk/js | Analyse du DSN, capture des erreurs et des rejets non gérés, normalisation des piles vers la forme de V8, relecture de session, retours utilisateurs, masquage, un plugin Vite pour les source maps. |
Le SDK navigateur normalise la pile de chaque moteur vers la forme de V8 avant l’envoi, et transporte les identifiants de débogage dans un attribut de span. Un identifiant de débogage fait 32 caractères hexadécimaux sans séparateurs — le SDK retire pour vous les tirets d’un UUID canonique.
Relecture de session
La relecture est enregistrée avec rrweb, et le masquage est inversé par rapport aux réglages par défaut de rrweb : le texte est masqué sauf autorisation explicite, plutôt qu’exposé sauf masquage explicite. Le masquage a lieu dans le navigateur avant la capture, car rien côté serveur ne peut défaire un enregistrement non masqué déjà arrivé.
Les en-têtes d’un envoi de fragment
Content-Type: application/octet-stream
X-Replay-Session: <session id>
X-Replay-Seq: <0, 1, 2, …>
X-Replay-Encoding: json-zstd
Le SDK navigateur les envoie pour vous. Le corps est constitué d’octets compressés opaques que cette plateforme s’engage à ne pas lire. Une horloge client absente ou illisible n’est pas une erreur — le serveur substitue la sienne, car un enregistrement horodaté à tort vaut toujours la peine d’être conservé.
Trois choses qui vous seront refusées
- Le projet n’a pas donné son consentement. La capture de relecture est par projet et reste désactivée tant qu’elle n’est pas activée pour ce projet sur la page Projets.
- Votre offre ne vend pas la relecture. L’offre gratuite ne la comprend pas, et l’envoi est refusé avec plan_excludes_replay en nommant votre offre. Ce n’est pas un dépassement — rien ici ne compte d’unités face à un volume inclus.
- Le fragment est trop volumineux. Le plafond s’applique aux octets réellement lus, pas à ce que Content-Length annonçait.
Regarder un enregistrement exige la portée propriétaire, revérifie le consentement du projet au moment de la lecture, et est inscrit au journal d’audit avant que les octets ne soient lus.
Source maps
Les artefacts s’envoient par projet depuis votre build — le SDK JavaScript fournit un plugin Vite qui s’en charge. La ligne vit dans PostgreSQL et les octets dans un magasin d’objets, adressés par contenu : envoyer deux fois la même source map ne coûte donc qu’une copie.
Les frames sont résolues à la lecture, pas à l’ingestion. Une frame qui ne peut pas être résolue dit pourquoi au lieu de faire échouer la page : un envoi manquant se dégrade en frame minifiée accompagnée d’une explication, plutôt qu’en erreur.
Offres, usage et rétention
L’usage est mesuré et rapporté. Il n’entraîne jamais d’action sur le chemin d’écriture. Dépasser un volume inclus ne vous ralentit pas, ne rejette pas votre télémétrie et ne déclenche aucun prélèvement — il n’y a aucune facturation à l’usage ici. Vous êtes prévenu une fois par signal et par période, et le message dit clairement que rien n’a été ralenti. La seule chose que décide une offre, c’est si un signal est vendu : une offre qui ne comprend pas la relecture de session refuse les envois de relecture, ce qui est un acte différent de la destruction de télémétrie déjà payée.
Rétention
Une offre restreint le plafond de la plateforme et ne l’élargit jamais. Les plafonds sont de 90 jours pour les erreurs et événements et de 30 jours pour les spans de trace ; les lignes de journal suivent le réglage des traces. Une organisation peut restreindre sa propre rétention dans ses réglages. Le journal d’audit n’est pas configurable par le client.
Une offre expirée conserve ses données quatorze jours de plus. Les limites, les sièges et les fonctionnalités reviennent à l’offre gratuite dès la fin de la période payée, parce qu’un paiement les rétablit ; la fenêtre de rétention, non, parce qu’un historique d’erreurs supprimé ne revient pas.
Alertes et intégrations
Les destinations se configurent par organisation. Le type d’une destination choisit le notificateur, et un type sans notificateur derrière lui échoue bruyamment plutôt que d’être ignoré.
| Type | Nécessite | Notes |
|---|
| Slack | Une URL de webhook entrant | Porte un champ text à côté de l’alerte structurée. |
| Discord | Une URL de webhook | Porte un champ content. |
| Webhook générique | Une URL | Les requêtes sortantes passent par un composeur qui refuse les adresses privées et lien-local. |
| Courriel | SMTP_* configuré | Une destination n’est pas confirmée tant que l’adresse ne s’est pas prouvée. |
Déploiements
Enregistrez les déploiements depuis votre pipeline, ou connectez GitHub ou GitLab projet par projet. Chaque fournisseur a son propre adaptateur de webhook, et un déploiement enregistre quel fournisseur l’a attesté, et pas seulement qu’un webhook l’a fait.
Synthèses
Un résumé planifié, réglé par organisation. Vous pouvez prévisualiser le prochain sans l’envoyer à qui que ce soit.
Surveillance des tâches planifiées et disponibilité
Créez un contrôle, puis faites appeler par votre tâche son URL de pointage — le tableau de bord en émet une par contrôle et vous fournit une ligne de crontab prête à l’emploi. Le jeton dans l’URL est l’identifiant, donc l’appel n’est volontairement pas authentifié : une tâche cron sur une machine sans secrets peut quand même signaler son passage.
# at the end of your job
curl -fsS "$SNAGSPY_CHECKIN_URL"
Un pointage qui n’arrive pas dans son délai de grâce déclenche une alerte. Les moniteurs de disponibilité vont dans l’autre sens : la plateforme sonde une URL que vous indiquez et enregistre chaque résultat sur le moniteur.
Investigations IA
Une investigation lit un incident, une alerte ou un projet et explique ce qui s’est passé. La répartition des rôles est délibérée et mérite d’être connue avant de faire confiance à une réponse : le modèle sélectionne et formule ; la plateforme calcule. Chaque chiffre d’une réponse provient d’une requête, pas du modèle, et une réponse qui ne peut être étayée par une requête n’est pas donnée.
- Entièrement désactivées sauf si le déploiement dispose d’une clé de fournisseur IA — désactivées, et non en échec à chaque requête.
- Facturées par appel au modèle, avant d’en connaître le résultat. Un appel en échec a quand même coûté un appel : ne facturer qu’en cas de succès masquerait une boucle emballée à l’endroit précis où vous la chercheriez.
- La revue d’une modification est une fonctionnalité distincte, derrière une autorisation distincte, désactivée tant qu’une organisation ne l’active pas. Votre pipeline d’intégration envoie un diff et reçoit des constats — rien n’est lu dans vos dépôts, car la plateforme ne détient aucun identifiant qui le permettrait, et le diff n’est pas conservé. Une revue vaut un appel au modèle et se décompte sur le même volume inclus qu’une investigation.
- Refusables par organisation. Les données envoyées sont un sous-ensemble volontairement minimal d’un seul incident : type d’exception, message, pile, coupable, décomptes et la version corrélée. Jamais d’identifiants d’utilisateurs finaux, jamais de sacs d’attributs bruts, jamais d’événements entiers.
Auto-hébergement
La pile est en Docker Compose : PostgreSQL, Redis, ClickHouse, l’API, ingest, un worker et un service migrate à usage unique. Tout se configure par l’environnement.
Variables de configuration
DATABASE_URL- Chaîne de connexion PostgreSQL. Aucune valeur par défaut.
REDIS_URL- Redis, utilisé pour la limitation de débit. Aucune valeur par défaut.
ENV- development (par défaut) ou production. En production, les livraisons de paiement de bac à sable sont refusées.
LOG_LEVEL- info par défaut.
DASHBOARD_ORIGIN- Où le tableau de bord est servi. Les requêtes authentifiées par cookie doivent envoyer un Origin correspondant, sinon origin_rejected.
API_PUBLIC_URL- Active l’authentification unique. Non défini, le SSO répond sso_unavailable.
INGEST_PUBLIC_URL- Le point d’entrée publié dans les DSN.
SESSION_COOKIE_NAME- slk_session par défaut.
SESSION_TTL- 720h par défaut.
SECURE_COOKIES- À définir pour les déploiements HTTPS.
SHUTDOWN_TIMEOUT- 15s par défaut.
INGEST_REQUESTS_PER_MINUTE- 600 par défaut, par projet.
INGEST_EVENTS_PER_MINUTE- 30000 par défaut, par organisation.
AUTH_ATTEMPTS_PER_MINUTE- 10 par défaut.
INVESTIGATIONS_PER_HOUR- 100 par défaut.
CLICKHOUSE_URL- Active le magasin d’événements ClickHouse. Sa présence bascule aussi les lectures vers lui.
READ_EVENTS_FROM_CLICKHOUSE- Dérivé de CLICKHOUSE_URL. Lancez le backfill avant de basculer sur une base avec un historique.
SECRET_ENCRYPTION_KEY- Requis par tout ce qui stocke un secret chiffré, sinon la réponse est secrets_not_durable.
ARTIFACT_DIR / ARTIFACT_S3_*- Où vivent les artefacts de source maps. Un service pointé vers le mauvais magasin échoue silencieusement.
BACKUP_ENCRYPTION_KEY- Requis par chaque commande de sauvegarde. Une valeur différente rend illisibles les artefacts existants.
BACKUP_DIR / BACKUP_S3_* / BACKUP_WAL_SPOOL- Destination des sauvegardes et tampon d’écriture anticipée.
BACKUP_RETENTION_DAYS / BACKUP_MIN_ARTIFACTS- Quelle quantité d’historique conserver.
ANTHROPIC_API_KEY / ANTHROPIC_MODEL- Investigations IA. Entièrement désactivées si non défini.
GENIUSPAY_API_KEY / _API_SECRET / _WEBHOOK_SECRET- Achat d’une offre. La clé est l’interrupteur : non définie, ni le paiement ni la route de webhook ne sont montés.
GENIUSPAY_BASE_URL- Par défaut, la base de l’API marchand.
SMTP_HOST / _PORT / _USERNAME / _PASSWORD / _FROM- Envoi des alertes par courriel.
OPERATOR_WEBHOOK_URL- Alertes d’exploitation sur ce déploiement. Volontairement non défini par défaut : rien n’est envoyé.
ACCOUNT_ALERT_WEBHOOK_URL- Alertes commerciales sur les clients. Repli sur OPERATOR_WEBHOOK_URL.
OTLP_ENDPOINT- Où cette plateforme envoie sa propre télémétrie.
Commandes
cmd/api- L’API du tableau de bord.
cmd/ingest- Le service d’ingestion de télémétrie. Un processus séparé, à dessein.
cmd/worker- Les tâches planifiées — rétention, purge, synthèses, évaluation des alertes.
cmd/alerts- Évaluer les alertes une fois, à la main.
cmd/migrate- Appliquer les migrations. Embarquées dans le binaire : un nouveau fichier .sql ne fait rien tant que l’image n’est pas reconstruite.
cmd/seed- Construire l’organisation et les projets de test locaux.
cmd/plan- Attribuer une offre à une organisation. Une action d’exploitation, et elle est auditée.
cmd/retention- Appliquer la rétention maintenant.
cmd/purge- Supprimer la télémétrie des organisations qui n’existent plus.
cmd/erase- Effacer les données d’une personne sur demande.
cmd/refingerprint- Recalculer le regroupement des incidents sur tout l’historique.
cmd/backfill- Copier les événements existants dans ClickHouse avant d’y basculer les lectures.
cmd/backup- Prendre une sauvegarde chiffrée.
Les migrations sont embarquées dans le binaire. Un nouveau fichier .sql ne fait rien tant que l’image migrate n’est pas reconstruite, et le conteneur journalise « migrations applied » dans les deux cas. Vérifiez la version du schéma plutôt que la ligne de journal.
Sécurité et confidentialité
- Le masquage a lieu deux fois. Les SDK masquent les valeurs qui ressemblent à des secrets avant l’envoi, et le chemin d’ingestion masque à nouveau à l’arrivée — clés, jetons, en-têtes d’autorisation, JWT, identifiants cloud, et affectations clé=valeur dans le texte libre.
- La relecture est masquée avant la capture, dans le navigateur, avec les réglages par défaut de rrweb inversés.
- Les locataires sont isolés comme condition de publication, pas par convention. La suite inter-locataires fait partie des contrôles qui doivent passer.
- Les requêtes sortantes sont contrôlées. Les destinations d’alertes passent par un composeur qui refuse les adresses privées et lien-local : un webhook ne peut donc pas être pointé vers l’intérieur du réseau.
- Les changements d’offre sont audités. Qui a accordé quoi est le seul historique que garde cette attribution.
- Le site vitrine ne stocke rien et n’appelle personne — pas d’analytique, pas de CDN, pas de polices chargées à l’exécution — c’est pourquoi il ne porte aucune bannière de cookies. Un test l’impose.
Le registre des sous-traitants et les plafonds de rétention sont publiés séparément et relèvent du même engagement : voir les pages sécurité et confidentialité.
Ce que cette plateforme ne fait pas
Énuméré plutôt qu’omis. Un développeur qui ne trouve pas une fonctionnalité suppose qu’il l’a manquée, et l’apprend de la manière coûteuse.
- Elle n’agit pas sur ce que comprend une offre. Dépasser un volume inclus ne vous ralentit jamais, ne rejette jamais de télémétrie et ne déclenche aucun prélèvement — il n’y a aucune facturation au dépassement.
- Elle ne prédit pas les pannes. Elle signale une métrique qui monte vers une ligne qu’elle trace déjà, avec la pente et le trafic derrière, et dit ce qui invaliderait la projection. Il n’y a ni probabilité ni heure de décès.
- Elle ne suggère pas de mises à jour de dépendances. Une frame de pile nomme un paquet et jamais une version : une suggestion serait une instruction assurée à propos d’un logiciel qu’elle n’a jamais vu.
- Elle n’exécute aucune remédiation. Une suggestion est une phrase.
- Il n’y a pas de facturation annuelle. Le catalogue ne porte qu’un prix mensuel et le paiement vend des mois à ce prix.
- Il n’y a pas encore de factures. Rien n’est émis à personne.
- L’ingestion de points de métrique n’est pas conditionnée à l’offre, contrairement à la relecture. Une offre sans volume de points de métrique peut quand même en envoyer et les voir mesurés.
- Le tableau de bord est uniquement en anglais. Les pages vitrine, légales et de documentation sont en anglais et en français ; le produit, non.