Referencia de la API de ingesta
Esta es la API HTTP neutral que integra cada cliente. Si construye o mantiene un paquete cliente, impleméntelo contra este contrato. Todas las rutas son JSON sobre HTTPS.
Ruta base y versionado
Los puntos de entrada de ingesta se encuentran bajo /api/v1. Envíe y acepte application/json (la subida de copias de seguridad es la única excepción, en multipart/form-data).
Autenticación
Cada petición se autentica con una clave de API de proyecto (lm_...). Se aceptan dos estilos de encabezado; la forma bearer es la recomendada:
La clave se coteja por su hash SHA-256 y se resuelve hacia un único proyecto. Los fallos de autenticación devuelven 401:
Límites de tasa
- Ingesta (
/ingest,/logs,/dependencies): 300 peticiones por minuto, indexadas por token de proyecto. - Heartbeats (
/heartbeats/*): 300 peticiones por minuto, indexadas por token de proyecto, con un presupuesto separado del de la ingesta para que una tormenta de excepciones no retrase los heartbeats. - Copias de seguridad (
/encryption-key,/backups*): 60 peticiones por minuto.
Superar un límite devuelve 429 Too Many Requests.
Cuando la ingesta está suspendida
Mientras quede un pago pendiente en la suscripción del equipo, /ingest, /logs y /dependencies responden 402 Payment Required y no almacenan nada:
Los pings de heartbeat se siguen registrando durante ese periodo, para que las tareas programadas de un equipo suspendido no parezcan rotas. Trate un 402 como un estado que registrar y mostrar, no como un error que reintentar: se levanta solo cuando el pago pasa.
Un punto de entrada de la bóveda responde 402 en el mismo periodo, con su propio mensaje: POST /backups. La lectura sigue abierta, incluido el material de clave, para que una restauración de lo que ya ha pagado siga funcionando.
Listar, descargar y eliminar una copia siguen abiertos, igual que el punto de entrada de clave, que sirve el material que necesita monitor:restore: un archivo ya almacenado puede recuperarse, y descifrarse.
POST /api/v1/ingest, reportar una excepción
Reporta una única ocurrencia de excepción.
Cuerpo de la petición
Reglas de los campos
| Campo | Requerido | Tipo / límite |
|---|---|---|
exception.class | sí | cadena, máx. 255 |
exception.message | no | cadena, máx. 8192 |
exception.code | no | cualquiera |
exception.file | no | cadena, máx. 1024 |
exception.line | no | entero ≥ 0 |
exception.trace | no | array |
exception.trace[].code.start | con code | entero ≥ 1, número de la primera línea del fragmento |
exception.trace[].code.lines | con code | array de cadenas, máx. 40 líneas de máx. 500 caracteres |
context.environment | no | cadena, máx. 100 |
context.release | no | cadena, máx. 255 (un SHA hex activa la correlación de commit) |
context.occurred_at | no | cadena, máx. 64 (fecha analizable) |
El contexto. La tabla solo nombra las tres claves del contexto que el servidor valida y guarda en columnas propias. El contexto se almacena sin embargo entero, con todas las claves que envía el cliente (url, method, request, user...), y no entra en la base de datos tal como llegó:
- primero se enmascara a su llegada, con la lista propia de Quiet Guard, que retoma los secretos de la lista por defecto del SDK de Laravel, y con la regla de los clientes: subcadena sin distinción de mayúsculas, un guion contando como un guion bajo, un nombre buscado también sin sus separadores interiores (
api_keyenmascaraapikeyyapiKey), una cadena JSON abierta, o enmascarada entera cuando no se decodifica y un nombre entre comillas seguido de:coincide en ella, los parámetros de consulta y los segmentos de ruta con forma de token de una URL, y la contraseña de unusuario:contraseña@, incluida la de un DSN (mysql://...) (vea el enmascaramiento del núcleo). Un secreto de esa lista que un cliente quitó de la suya se enmascara igualmente, y también lo que un cliente instalado antes de la versión 0.3.1 no sabía enmascarar con esos nombres: sigue enviando la URL sin enmascarar, una instantánea de Livewire cerrada y cabeceras con guiones. Las cabeceras de esa lista que llevan la dirección IP del visitante (x-forwarded-for,x-real-ip...) no se retoman: son datos personales y no secretos, y conservarlas o enmascararlas es una decisión del responsable del tratamiento. El servidor las almacena tal como llegan; - después se acota a 65 535 bytes de JSON codificado y a 30 niveles de anidamiento, porque MariaDB rechaza un documento JSON de 32 niveles en una columna
json: por encima de uno u otro límite, o si no puede codificarse, el contexto almacenado es{"_truncated": true}.
environment, release y occurred_at se leen antes de ese límite y conservan sus columnas: un contexto demasiado grande no se los lleva. Sellado o en claro, lo que se escribe es este contexto enmascarado y acotado, y un equipo con el almacenamiento cifrado activo nunca sella el valor en bruto.
Los marcos. El servidor solo conserva de cada marco file (cortado a 1024 caracteres), line, function y class (255 caracteres cada uno), type (8), is_site y, de code, solo start y lines. Un valor de otro tipo que el esperado se descarta en lugar de convertirse, salvo para line, que acepta cualquier número, incluso escrito como cadena ("88"), y lo conserva como entero, y para code.start, que acepta un entero escrito como cadena ("40", ya que la validación rechaza cualquier otro número) y también lo conserva como entero; cualquier otra clave, empezando por los argumentos de una llamada, nunca entra en la base de datos.
Marco cero y fragmento de código. A partir de la versión 0.3, los clientes colocan el punto de lanzamiento al principio de trace: un marco reducido al file y a la line de la propia excepción, ya que la traza de PHP empieza en el llamador y nunca contuvo la línea que falló. Un trace_limit cuenta ese marco (1 = solo el punto de lanzamiento). Cada marco puede llevar un objeto code opcional: start, el número de su primera línea, y lines, las líneas de código fuente que rodean la línea del marco. Los clientes envían las cinco líneas a cada lado (once como máximo), cada una cortada a 500 caracteres, en como máximo diez marcos por informe, nunca para una ruta que contenga /vendor/, y sustituyen entera por [scrubbed] cualquier línea que nombre una clave de su lista de enmascaramiento y le dé un valor. El servidor no vuelve a pasar por esas líneas, solo el contexto se enmascara a su llegada; rechaza un fragmento de más de 40 líneas o de más de 500 caracteres por línea, y sella los fragmentos en reposo junto con el resto de la traza cuando el almacenamiento cifrado del equipo está activo.
Respuestas
202 Accepted: almacenada:
``json { "message": "accepted", "stored": true, "issue_id": "01kw6tkrd8wjbfv2pktr7ssw43" } ``
202 Accepted: cuota superada, confirmada pero no almacenada:
``json { "message": "accepted", "stored": false } ``
422 Unprocessable Entity: fallo de validación.
issue_id es el identificador público de la incidencia, un ULID. Es el mismo valor que lleva la URL del panel, así que puede registrarlo y pegarlo en la barra de direcciones. No es un número de fila, y la respuesta no incluye ningún contador de ocurrencias: esos números habrían indicado a cada proyecto cuántas excepciones había almacenado toda la plataforma.
POST /api/v1/logs, reportar logs de aplicación (lote)
Almacena un lote de entradas de log. Condicionado a la funcionalidad de plan logs.
Cuerpo de la petición
Reglas de los campos
| Campo | Requerido | Tipo / límite |
|---|---|---|
logs | sí | array, de 1 a 500 entradas |
logs.*.level | no | cadena, máx. 20 |
logs.*.message | sí | cadena, máx. 8192 |
logs.*.context | no | array |
logs.*.channel | no | cadena, máx. 100 |
logs.*.environment | no | cadena, máx. 100 |
logs.*.release | no | cadena, máx. 255 |
logs.*.logged_at | no | cadena, máx. 64 |
El contexto de un log. El servidor no lo enmascara: es lo que su aplicación decidió escribir en su propio log, y lo que se le aplica es la lista de enmascaramiento de su cliente, antes del envío. El servidor lo acota con la misma regla que el contexto de una excepción: por encima de 65 535 bytes de JSON codificado, se sustituye por {"_truncated": true}, y la entrada se almacena con el resto del lote.
Respuestas
202 Accepted:
``json { "message": "accepted", "stored": 137 } ``
stored indica cuántas entradas se han registrado realmente (un lote parcial se almacena cuando la cuota está casi llena).
403 Forbidden: el plan no incluye los logs:
``json { "message": "Application logs are not available on your current plan." } ``
POST /api/v1/dependencies, reportar el composer.lock
Sustituye la instantánea de dependencias del proyecto y desencadena un escaneo de vulnerabilidades en segundo plano.
Cuerpo de la petición
Reglas de los campos
| Campo | Requerido | Tipo / límite |
|---|---|---|
packages | sí | array, de 1 a 5000 entradas |
packages.*.name | sí | cadena, máx. 255 |
packages.*.version | sí | cadena, máx. 100 |
packages.*.is_dev | no | booleano |
Respuesta
202 Accepted:
``json { "message": "accepted", "dependencies": 142 } ``
POST /api/v1/heartbeats/{slug}, hacer ping a una tarea programada
Señala que una tarea programada acaba de ejecutarse. El cuerpo es opcional: sin cuerpo, el ping se comporta como siempre lo ha hecho. El SDK de Laravel envía, para sus heartbeats automáticos:
auto debe valer true para ser tenido en cuenta; cualquier otro valor deja que el ping se comporte como si no llevara cuerpo. kind vale scheduler o queue. queue lleva hasta 100 caracteres. Un heartbeat creado por un ping que lleva auto, kind, period (1 a 1440) y grace (0 a 1440) nace armado; un heartbeat existente nunca se vuelve a armar. lag_ms (0 o más; un valor superior a 86.400.000, un día, se guarda como un día) solo se registra para un heartbeat de cola, y un ping de cola sin lag_ms borra el valor anterior. Un campo ilegible se ignora, nunca se rechaza: el ping siempre se registra.
{slug} se normaliza a minúsculas, dígitos y guiones. El primer ping sobre un slug desconocido registra el heartbeat, armado cuando el cuerpo anterior basta para armarlo, sin armar en caso contrario: consignado en ambos casos, nunca alerta hasta que se define un periodo esperado. Vea Heartbeats.
Respuestas
202 Accepted:
``json { "message": "pong", "armed": true } ``
armed indica si hay un periodo esperado definido, para que un cliente distinga un ping consignado de uno vigilado.
422 Unprocessable Entity: el slug se normaliza a una cadena vacía, o el proyecto ya contiene el número máximo de heartbeats.
Puntos de entrada de la bóveda de copias de seguridad
Estos alimentan la bóveda de copias de seguridad cifradas. Todos requieren la funcionalidad de plan backups. Mientras quede un pago pendiente, POST /backups responde 402 Payment Required; recuperar el material de clave, listar, descargar y eliminar siguen abiertos, para que monitor:restore siga funcionando sobre los archivos que ya tiene.
GET /api/v1/encryption-key
Sirve el material de clave del equipo para que el cliente pueda sellar y (con la frase secreta, en local) desenvolver.
403si el plan no incluye las copias de seguridad.404si el equipo no ha configurado claves de cifrado.
GET /api/v1/backups
POST /api/v1/backups
Subida multipart/form-data de un blob ya cifrado.
| Campo | Requerido | Observaciones |
|---|---|---|
type | sí | database o files |
name | no | cadena, máx. 255 |
file | sí | el archivo cifrado |
201 Createddevuelve los metadatos de la copia de seguridad almacenada (misma forma que una entrada de la lista).403si el plan no incluye las copias de seguridad.413 Request Entity Too Largesi la subida superaría la cuota de almacenamiento:
``json { "message": "Backup storage quota exceeded.", "used_bytes": 5012345678, "limit_bytes": 5000000000 } ``
GET /api/v1/backups/{uuid}
Transmite el blob cifrado como descarga (<nombre>.enc).
DELETE /api/v1/backups/{uuid}
Elimina la copia de seguridad. Devuelve 204 No Content.
Recapitulación de errores
| Estado | Significado |
|---|---|
202 Accepted | Ingerido (consulte stored para saber qué se ha conservado). |
201 Created | Copia de seguridad subida. |
204 No Content | Copia de seguridad eliminada. |
401 Unauthorized | Clave de API ausente o inválida. |
402 Payment Required | Ingesta y subidas de copias suspendidas mientras quede un pago pendiente. |
403 Forbidden | Funcionalidad ausente del plan del equipo. |
404 Not Found | Claves de cifrado no configuradas (punto de entrada de clave). |
413 Payload Too Large | Cuota de almacenamiento de copias de seguridad superada. |
422 Unprocessable Entity | Fallo de validación. |
429 Too Many Requests | Límite de tasa superado. |
Está leyendo la documentación Quiet Guard v1.0.