Référence de l'API d'ingestion
Voici l'API HTTP neutre que chaque client intègre. Si vous construisez ou maintenez un package client, implémentez-le contre ce contrat. Toutes les routes sont en JSON sur HTTPS.
Chemin de base et versionnage
Les points d'entrée d'ingestion se trouvent sous /api/v1. Envoyez et acceptez application/json (le téléversement de sauvegarde est la seule exception en multipart/form-data).
Authentification
Chaque requête s'authentifie avec une clé d'API de projet (lm_...). Deux styles d'en-tête sont acceptés ; la forme bearer est recommandée :
La clé est mise en correspondance par son hachage SHA-256 et se résout vers un seul projet. Les échecs d'authentification renvoient 401 :
Limites de débit
- Ingestion (
/ingest,/logs,/dependencies) : 300 requêtes par minute, indexées par jeton de projet. - Heartbeats (
/heartbeats/*) : 300 requêtes par minute, indexées par jeton de projet, sur un budget séparé de celui de l'ingestion pour qu'une tempête d'exceptions ne retarde pas les heartbeats. - Sauvegardes (
/encryption-key,/backups*) : 60 requêtes par minute.
Dépasser une limite renvoie 429 Too Many Requests.
Quand l'ingestion est suspendue
Tant qu'un paiement reste dû sur l'abonnement de l'équipe, /ingest, /logs et /dependencies répondent 402 Payment Required et ne stockent rien :
Les pings de heartbeat continuent d'être enregistrés pendant cette période, pour qu'une équipe suspendue ne voie pas ses tâches planifiées passer pour cassées. Traitez un 402 comme un état à journaliser et à afficher, non comme une erreur à réessayer : il se lève de lui-même quand le paiement passe.
Un point d'entrée du coffre répond 402 dans la même période, avec son propre message : POST /backups. La lecture reste ouverte, matériel de clé compris, pour qu'une restauration de ce que vous avez déjà payé continue de fonctionner.
Lister, télécharger et supprimer une sauvegarde restent ouverts, comme le point d'entrée de clé, qui sert le matériel dont monitor:restore a besoin : une archive déjà stockée peut toujours être récupérée, et déchiffrée.
POST /api/v1/ingest, rapporter une exception
Rapporte une seule occurrence d'exception.
Corps de la requête
Règles des champs
| Champ | Requis | Type / limite |
|---|---|---|
exception.class | oui | chaîne, max 255 |
exception.message | non | chaîne, max 8192 |
exception.code | non | quelconque |
exception.file | non | chaîne, max 1024 |
exception.line | non | entier ≥ 0 |
exception.trace | non | tableau |
exception.trace[].code.start | avec code | entier ≥ 1, numéro de la première ligne de l'extrait |
exception.trace[].code.lines | avec code | tableau de chaînes, max 40 lignes de max 500 caractères |
context.environment | non | chaîne, max 100 |
context.release | non | chaîne, max 255 (un SHA hex active la corrélation de commit) |
context.occurred_at | non | chaîne, max 64 (date analysable) |
Le contexte. Le tableau ne nomme que les trois clés du contexte que le serveur valide et range dans des colonnes à part. Le contexte est pourtant stocké entier, avec toutes les clés que le client envoie (url, method, request, user...), et il n'entre pas en base tel quel :
- il est d'abord masqué à l'arrivée, avec la liste propre de Quiet Guard, qui reprend les secrets de la liste par défaut du SDK Laravel, et par la règle des clients : sous-chaîne sans casse, un tiret valant un souligné, un nom cherché aussi sans ses séparateurs intérieurs (
api_keymasqueapikeyetapiKey), chaîne JSON ouverte, ou masquée entière quand elle ne se décode pas et qu'un nom entre guillemets suivi d'un:y correspond, paramètres de requête et segments de chemin en forme de jeton d'une URL, et mot de passe d'unutilisateur:mot-de-passe@, celui d'un DSN (mysql://...) compris (voir le masquage du cœur). Un secret de cette liste qu'un client a retiré de la sienne y est masqué quand même, et ce qu'un client installé avant la version 0.3.1 ne savait pas masquer sous ces noms l'est aussi : il envoie encore l'URL sans la masquer, un instantané Livewire fermé et des en-têtes à tirets. Les en-têtes de cette liste qui portent l'adresse IP du visiteur (x-forwarded-for,x-real-ip...) ne sont pas repris : ce sont des données personnelles et non des secrets, et les garder ou les masquer est une décision du responsable de traitement. Le serveur les stocke tels qu'ils arrivent ; - il est ensuite borné à 65 535 octets de JSON encodé et à 30 niveaux d'imbrication, parce que MariaDB refuse un document JSON de 32 niveaux dans une colonne
json: au-delà de l'une ou l'autre borne, ou s'il ne peut pas être encodé, le contexte stocké est{"_truncated": true}.
environment, release et occurred_at sont lus avant cette borne et gardent leurs colonnes : un contexte trop gros ne les emporte pas. Chiffré ou en clair, c'est ce contexte masqué et borné qui est écrit, et une équipe dont le stockage chiffré est actif ne scelle jamais la valeur brute.
Les trames. Le serveur ne garde de chaque trame que file (coupé à 1024 caractères), line, function et class (255 caractères chacun), type (8), is_site et, de code, start et lines seulement. Une valeur d'un autre type que celui attendu est écartée plutôt que convertie, sauf pour line, qui accepte tout nombre, même écrit en chaîne ("88"), et le garde en entier, et pour code.start, qui accepte un entier écrit en chaîne ("40", la validation refusant tout autre nombre) et le garde en entier aussi ; toute autre clé, les arguments d'un appel en premier lieu, n'entre jamais en base.
Trame zéro et extrait de code. À partir de la version 0.3, les clients placent le site de levée en tête de trace : une trame réduite au file et à la line de l'exception elle-même, puisque la trace de PHP commence à l'appelant et n'a jamais contenu la ligne fautive. Une trace_limit compte cette trame (1 = le site seul). Chaque trame peut porter un objet code optionnel : start, le numéro de sa première ligne, et lines, les lignes de source qui entourent la ligne de la trame. Les clients envoient les cinq lignes de part et d'autre (onze au plus), chacune coupée à 500 caractères, sur au plus dix trames par rapport, jamais pour un chemin contenant /vendor/, et remplacent entière par [scrubbed] toute ligne qui nomme une clé de leur liste de masquage et lui donne une valeur. Le serveur ne repasse pas sur ces lignes, seul le contexte est masqué à l'arrivée ; il refuse un extrait au-delà de 40 lignes ou de 500 caractères par ligne, et scelle les extraits au repos avec le reste de la trace quand le stockage chiffré de l'équipe est actif.
Réponses
202 Accepted: stockée :
``json { "message": "accepted", "stored": true, "issue_id": "01kw6tkrd8wjbfv2pktr7ssw43" } ``
202 Accepted: quota dépassé, accusée mais non stockée :
``json { "message": "accepted", "stored": false } ``
422 Unprocessable Entity: échec de validation.
issue_id est l'identifiant public de l'issue, un ULID. C'est la valeur que porte l'URL du tableau de bord, vous pouvez donc la journaliser et la coller dans la barre d'adresse. Ce n'est pas un numéro de ligne, et la réponse ne contient aucun compteur d'occurrences : ces nombres auraient indiqué à chaque projet combien d'exceptions la plateforme entière avait stockées.
POST /api/v1/logs, rapporter des logs applicatifs (lot)
Stocke un lot d'entrées de log. Conditionné à la fonctionnalité de plan logs.
Corps de la requête
Règles des champs
| Champ | Requis | Type / limite |
|---|---|---|
logs | oui | tableau, 1 à 500 entrées |
logs.*.level | non | chaîne, max 20 |
logs.*.message | oui | chaîne, max 8192 |
logs.*.context | non | tableau |
logs.*.channel | non | chaîne, max 100 |
logs.*.environment | non | chaîne, max 100 |
logs.*.release | non | chaîne, max 255 |
logs.*.logged_at | non | chaîne, max 64 |
Le contexte d'un log. Le serveur ne le masque pas : c'est ce que votre application a choisi d'écrire dans son propre log, et c'est la liste de masquage de votre client qui s'y applique, avant l'envoi. Il le borne par la même règle que celui d'une exception : au-delà de 65 535 octets de JSON encodé, il est remplacé par {"_truncated": true}, et l'entrée est stockée avec le reste du lot.
Réponses
202 Accepted:
``json { "message": "accepted", "stored": 137 } ``
stored indique combien d'entrées ont réellement été enregistrées (un lot partiel est stocké lorsque le quota est presque plein).
403 Forbidden: le plan n'inclut pas les logs :
``json { "message": "Application logs are not available on your current plan." } ``
POST /api/v1/dependencies, rapporter le composer.lock
Remplace l'instantané de dépendances du projet et déclenche un scan de vulnérabilités en arrière-plan.
Corps de la requête
Règles des champs
| Champ | Requis | Type / limite |
|---|---|---|
packages | oui | tableau, 1 à 5000 entrées |
packages.*.name | oui | chaîne, max 255 |
packages.*.version | oui | chaîne, max 100 |
packages.*.is_dev | non | booléen |
Réponse
202 Accepted:
``json { "message": "accepted", "dependencies": 142 } ``
POST /api/v1/heartbeats/{slug}, pinguer une tâche planifiée
Signale qu'une tâche planifiée vient de s'exécuter. Le corps est facultatif : sans corps, le ping se comporte comme il l'a toujours fait. Le SDK Laravel envoie, pour ses battements automatiques :
auto doit valoir true pour être pris en compte, tout autre valeur laisse le ping se comporter comme s'il n'y avait pas de corps. kind vaut scheduler ou queue. queue porte jusqu'à 100 caractères. Un heartbeat créé par un ping qui porte auto, kind, period (1 à 1440) et grace (0 à 1440) naît armé ; un heartbeat existant n'est jamais réarmé. lag_ms (0 ou plus ; au-delà de 86 400 000, une journée, il est conservé comme une journée) n'est enregistré que pour un heartbeat de file, et un ping de file sans lag_ms efface la valeur précédente. Un champ illisible est ignoré, jamais refusé : le ping est toujours enregistré.
{slug} est normalisé en minuscules, chiffres et tirets. Le premier ping sur un slug inconnu enregistre le heartbeat, armé quand le corps ci-dessus suffit à l'armer, non armé sinon : consigné dans les deux cas, il n'alerte jamais tant qu'une période attendue n'a pas été définie. Voir Heartbeats.
Réponses
202 Accepted:
``json { "message": "pong", "armed": true } ``
armed indique si une période attendue est définie, pour qu'un client distingue un ping consigné d'un ping surveillé.
422 Unprocessable Entity: le slug se normalise en chaîne vide, ou le projet détient déjà le nombre maximal de heartbeats.
Points d'entrée du coffre de sauvegardes
Ceux-ci alimentent le coffre de sauvegardes chiffrées. Tous requièrent la fonctionnalité de plan backups. Tant qu'un paiement reste dû, POST /backups répond 402 Payment Required ; récupérer le matériel de clé, lister, télécharger et supprimer restent ouverts, pour que monitor:restore continue de fonctionner sur les archives déjà en place.
GET /api/v1/encryption-key
Sert le matériel de clé de l'équipe pour que le client puisse sceller et (avec la phrase secrète, en local) désenvelopper.
403si le plan n'inclut pas les sauvegardes.404si l'équipe n'a pas configuré de clés de chiffrement.
GET /api/v1/backups
POST /api/v1/backups
Téléversement multipart/form-data d'un blob déjà chiffré.
| Champ | Requis | Remarques |
|---|---|---|
type | oui | database ou files |
name | non | chaîne, max 255 |
file | oui | l'archive chiffrée |
201 Createdrenvoie les métadonnées de la sauvegarde stockée (même forme qu'une entrée de liste).403si le plan n'inclut pas les sauvegardes.413 Request Entity Too Largesi le téléversement dépasserait le quota de stockage :
``json { "message": "Backup storage quota exceeded.", "used_bytes": 5012345678, "limit_bytes": 5000000000 } ``
GET /api/v1/backups/{uuid}
Diffuse le blob chiffré en téléchargement (<nom>.enc).
DELETE /api/v1/backups/{uuid}
Supprime la sauvegarde. Renvoie 204 No Content.
Récapitulatif des erreurs
| Statut | Signification |
|---|---|
202 Accepted | Ingéré (consultez stored pour savoir ce qui a été conservé). |
201 Created | Sauvegarde téléversée. |
204 No Content | Sauvegarde supprimée. |
401 Unauthorized | Clé d'API manquante ou invalide. |
402 Payment Required | Ingestion et téléversements de sauvegarde suspendus tant qu'un paiement reste dû. |
403 Forbidden | Fonctionnalité absente du plan de l'équipe. |
404 Not Found | Clés de chiffrement non configurées (point d'entrée de clé). |
413 Payload Too Large | Quota de stockage des sauvegardes dépassé. |
422 Unprocessable Entity | Échec de validation. |
429 Too Many Requests | Limite de débit dépassée. |
Vous lisez la documentation Quiet Guard v1.0.