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

  1. 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.
  2. É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.
  3. Envoyez quelque chose. L’un des SDK ci-dessous, ou de l’OTLP brut vers le point d’ingestion.
  4. 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.

SDKPaquetApporte
Gosdk/go/snagspyAnalyse du DSN, CaptureException, masquage, middleware HTTP.
Navigateur et Nodesdk/jsAnalyse 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.

Envoyer de la télémétrie

Le service d’ingestion est distinct de l’API et écoute sur son propre port (:8081 par défaut ; publiez-le via INGEST_PUBLIC_URL). Il accepte l’OTLP sur HTTP, et chaque route s’authentifie avec un DSN, pas avec une session.

Deux compteurs s’appliquent à chaque envoi : une limite de requêtes par projet et une limite d’événements par organisation. Les deux sont décrites sur la page de référence de l’API.

Les points d’entrée, les en-têtes et une configuration de collecteur se trouvent dans le guide OpenTelemetry. →

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

TypeNécessiteNotes
SlackUne URL de webhook entrantPorte un champ text à côté de l’alerte structurée.
DiscordUne URL de webhookPorte un champ content.
Webhook génériqueUne URLLes requêtes sortantes passent par un composeur qui refuse les adresses privées et lien-local.
CourrielSMTP_* 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.