Saltar al contenido
megapromotingVamos a hablar

Experiencia · Mantenimiento

Mantenimiento significa saber que el formulario entrega hoy, no que el servidor responde 200.

Monitorización que verifica el recorrido completo de una solicitud hasta una persona, actualizaciones con gate antes de producción y una verificación de aceptación ejecutada después de cada publicación. Las tres se ejecutan en este sitio y se pueden abrir desde el exterior.

Ya construidoTres implementaciones propias, todas en el repositorio de este sitio y todas verificables hoy: `src/app/api/health/contact/route.ts` (la sonda de entrega, responde 200 en producción justo ahora), `src/lib/lead-store.ts` (el registro append-only con retención aplicada al escribir) y `scripts/verify-redesign.ts` (la verificación de aceptación ejecutada después de la publicación). Las escribimos porque el 06.09.2026 nuestro propio formulario falló en silencio: el token del bot estaba revocado, el endpoint devolvía 500 y las solicitudes desaparecían sin rastro. No es una historia de venta — es el commit que produjo los archivos de arriba.

«El sitio funciona» es una afirmación sobre la página de inicio. Un visitante que completó el formulario y recibió un error no fue detenido por una página caída, sino por un canal de entrega que expiró en silencio. La diferencia entre ambas cosas es todo lo que significa un mantenimiento hecho en serio: no seguimos si el servidor responde, sino si una solicitud de una persona llega a otra persona.

El 6 de septiembre de 2026 ocurrió exactamente esto en este sitio. El token del bot que llevaba las solicitudes del formulario al equipo estaba revocado; la interfaz de Telegram respondía `401 Unauthorized`, nuestra ruta devolvía 500 y el visitante veía “el mensaje no se pudo enviar”. La solicitud no se escribía en ninguna parte. En el registro de errores del proceso había tres fallos reales en las aproximadamente siete horas transcurridas desde la publicación anterior. Nadie miraba.

De eso salieron tres piezas que ahora montamos en cada proyecto que mantenemos. Un registro append-only escrito *antes* del intento de entrega, para que un canal roto degrade a «tenemos que mirar el archivo» en lugar de «la solicitud nunca existió». Una sonda de salud que responde a una sola petición si un lead puede llegar a una persona ahora mismo. Y una verificación de aceptación que se ejecuta después de cada publicación y falla si desapareció una dirección canónica, un ancla de preguntas o si en el mapa del sitio aparecieron direcciones que no se pueden abrir.

El resto es disciplina aburrida y verificable: actualizaciones con una compuerta de auditoría antes de tocar producción, reinicio automático con umbral de memoria y límite de reinicios inestables, retención aplicada por código, no declarada en política, y direcciones IP cortadas al prefijo de red antes de escribirse en ningún sitio.

Qué incluye

El trabajo, por componentes

Sonda que verifica la entrega, no la disponibilidad

`GET /api/health/contact` responde 200 si un lead puede llegar a una persona ahora y 503 si no. Verifica dos cosas por separado: que el token del canal de notificación siga siendo válido (una petición real al proveedor, con timeout de 8 segundos) y que el registro de solicitudes sea escribible. La respuesta se compone solo de valores lógicos — nunca el token, el identificador del canal ni el nombre del bot. El resultado se mantiene en memoria 60 segundos, para que una sonda externa pulsada con frecuencia no se convierta ella misma en tráfico. Cuando el proveedor es inaccesible, el campo pasa a `null`, no `false`: «no sé» e «inválido» son estados distintos.

Registro escrito antes de la entrega, no después

Cada solicitud recibe un identificador y se escribe en un registro append-only, una línea JSON por evento, *antes* de intentar la notificación. La segunda línea, con el mismo identificador, dice qué ocurrió realmente: `delivered` o `failed`, con el motivo truncado a 300 caracteres. El directorio se crea con permisos `0700`, los archivos con `0600`, y la ruta se coloca deliberadamente fuera del directorio de versión, para que el historial sobreviva a una publicación.

Verificación de aceptación ejecutada después de la publicación

Un script de verificación abre cada página de producto, en grupos de tres, y falla si falta el código 200, el ancla de preguntas, el ancla de ejemplo o la dirección canónica. Luego comprueba el mapa del sitio — que no contenga variantes de idioma que no se puedan abrir y que contenga cada producto — y `robots.txt`, para que no bloquee los recursos necesarios para el renderizado. También falla si reaparece en la página contenido de un cliente antiguo. Es una lista de cosas que ya se rompieron una vez.

Actualizaciones con gate, no con esperanza

El script de publicación se niega a arrancar si el servidor tiene menos de 500 MB de memoria libre, ejecuta `npm audit --audit-level=high` y se detiene ante vulnerabilidades de nivel alto si no confirmas explícitamente, luego construye desde limpio — `.next` y `node_modules` eliminados, instalación desde el archivo de bloqueo. Si el proceso no aparece `online` tras el arranque, el script muestra las últimas 50 líneas del registro y sale con error, en lugar de informar éxito.

Reinicio con umbrales, no al azar

El proceso se reinicia automáticamente por encima de 500 MB de memoria, con una demora de 4 segundos entre intentos, crecimiento exponencial de la demora y parada después de 10 reinicios inestables — para que un bucle de caída se vuelva visible en lugar de consumir el servidor en silencio. Tiempo de gracia al detenerse: 5 segundos, luego terminación forzada. Los registros tienen fecha y zona horaria, en un solo flujo.

Duplicados tratados, no contados dos veces

La misma persona, el mismo mensaje, dos veces — un doble clic, una recarga de la página — producía dos notificaciones idénticas para una sola solicitud. Ahora la huella `sha256` del par dirección + mensaje se mantiene 10 minutos en memoria; el segundo envío recibe el mismo identificador de solicitud y la marca `duplicate`, y la notificación no se repite. Solo se suprimen los duplicados de una entrega exitosa; una fallida puede pasar de nuevo.

Códigos de error que dicen qué se rompió

400 para datos inválidos, 405 por método incorrecto, 500 para cuerpo de solicitud roto, 502 cuando el canal de entrega respondió mal, 503 cuando faltan credenciales. La diferencia importa a las 3 de la mañana: 502 significa «el proveedor», 503 significa «nuestra configuración». Al visitante se le dice de forma distinta «hemos recibido la solicitud, pero no pudimos notificar» — no un falso éxito.

Retención aplicada por código, no prometida en la política

La nota de confidencialidad publicada dice que las solicitudes del formulario se conservan 24 meses. El código aplica exactamente el mismo número: `LEAD_RETENTION_DAYS` por defecto 730, y los archivos más antiguos que el umbral se eliminan en cada escritura, sin un planificador que pueda olvidarse. La dirección IP se recorta al prefijo de red — `/24` en IPv4, `/48` en IPv6 — y se lee el último salto del encabezado, no el primero, porque el primero lo envía el cliente.

Encontramos también lo que nadie reclamó

El mismo recorrido por el código sacó a la luz dos cosas que ningún usuario había reportado: el límite de 10 solicitudes por minuto se podía eludir por completo, porque se leía el primer elemento del encabezado de direcciones redirigidas — el controlado por el cliente — y una ruta de iniciación de llamadas telefónicas estaba abierta de forma anónima, con nuestro coste y desde nuestro número, aunque el componente que la usaba ya no estaba montado en ningún sitio. Ambas reparadas y verificadas en producción.

Qué aspecto tiene

El recorrido, paso a paso.

01

El inventario de los caminos por los que una solicitud llega a una persona

La primera entrega no es una herramienta, es una lista: por qué pasa una solicitud desde que se pulsa el botón hasta que alguien la lee, qué se rompe en cada paso y qué debería ocurrir cuando se rompe. En este sitio la lista tenía tres eslabones y uno era invisible.

02

La sonda y el registro, montados antes que cualquier otra cosa

Entregamos una dirección de salud que verifica el camino completo, no el proceso, y el registro que escribe antes de la entrega. A partir de aquí, una caída es una pregunta con respuesta, no una excavación por los registros. La dirección puede consultarse desde cualquier servicio externo de monitorización, porque no devuelve nada sensible.

03

La verificación de aceptación, escrita a partir de defectos reales

Todo lo que se rompió una vez entra en el script de verificación que se ejecuta después de la publicación. No escribimos pruebas para casos hipotéticos; escribimos para los que ya nos costaron. Entregamos el script, no solo su resultado: tú también lo puedes ejecutar.

04

El ritmo de actualización y la puerta previa a producción

Establecemos qué se actualiza automáticamente, qué pasa por verificación humana y qué no se toca sin una ventana anunciada. La puerta incluye la auditoría de seguridad de las dependencias y la comprobación de recursos del servidor, ambas ejecutadas antes de tocar producción.

05

La entrega, con la lista de faltantes

Al final entregamos el procedimiento de publicación, el procedimiento de reversión, las direcciones de salud y la lista escrita de lo que no está cubierto. En este sitio, por ejemplo, la rama de expiración de la solicitud al proveedor está implementada y cae en el mismo tratamiento que el error de red, pero el abort en sí no fue provocado en la prueba: está escrito así en el registro de ejecución, no en una nota interna.

1Sonda care verificălivrarea până la unom2jurnalul scrisînainte de încercare3verificarea deacceptare rulată dupăfiecare publicareTrei piese, toate în depozitul acestui site.
El recorrido, en 3 pasos

Los datos

Qué tocamos, dónde están y cuánto se quedan

Las preguntas que hace cualquiera que tenga un delegado de protección de datos — hechas aquí antes de que las haga él.

Registro de solicitudes
Nombre, dirección de correo electrónico, teléfono, empresa, servicio elegido, mensaje, la página desde la que se envió, el host de la página de origen (solo el host, no la dirección completa) y el prefijo de red del visitante. Archivos JSON por día, en el servidor propio, fuera del directorio de versión. Plazo: 24 meses, aplicado al escribir.
Los registros de proceso y de servidor
La salida estándar y los errores del proceso, con fecha y huso horario, además de los registros del servidor web. Aquí se ve una caída silenciosa: los tres fallos de entrega del incidente de referencia estaban en el registro de errores, no en ninguna alerta. La rotación y el plazo se establecen para cada servidor; son datos técnicos, no contenido de cuenta.
Lo que nunca sale del servidor
Los tokens, los identificadores de canal y las claves del proveedor. La sonda de salud responde exclusivamente con valores lógicos, precisamente para poder ser llamada por una herramienta externa sin revelar nada. La misma regla en la notificación: el mensaje contiene el identificador de la solicitud y la página, no credenciales.
Estadísticas de tráfico
La medición del tráfico pasa por la herramienta de análisis configurada en el proyecto y se activa después del consentimiento. La configuración real de retención de la cuenta de análisis es un valor que leemos de la cuenta, no uno que suponemos: el registro de tratamientos de este sitio la tiene marcada explícitamente como elemento por aclarar, no como hecho establecido.
Registro de tratamientos
Cada tipo de datos tocado por el sitio tiene una entrada con finalidad, base, categorías y plazo, en un documento versionado junto al código. Cuando el código cambia un plazo, el documento cambia en el mismo commit; de lo contrario, la política y el programa dicen cosas distintas, y quien se equivoca suele ser el documento.

Un caso

Un formulario que entregó 500 en lugar de leads, siete horas

La situación

Un sitio de presentación con formulario de contacto, publicado recientemente. Todas las páginas responden 200, el panel está en verde, nadie reclama nada. El único canal por el que una solicitud llegaba al equipo era una notificación en una aplicación de mensajería.

Qué construimos

El token del bot de notificación había sido revocado; la interfaz del proveedor respondía `401 Unauthorized`. La ruta del formulario devolvía 500 y no escribía nada. La primera corrección no fue el token, sino el orden de las operaciones: la solicitud ahora se escribe en un registro append-only, con identificador propio, *antes* de intentar la entrega, y el resultado de la entrega se añade como segunda línea. Sobre eso se montaron una sonda pública de salud que verifica el token y la posibilidad de escritura, códigos de error distintos para «el proveedor respondió mal» y «nos faltan credenciales», un timeout de 10 segundos en la entrega y la supresión de duplicados en una ventana de 10 minutos. El mismo paso por el código sacó también un límite de solicitudes que se podía eludir, y una ruta de llamadas telefónicas que había quedado abierta de forma anónima.

Qué salió

La sonda responde ahora 200 con el token válido y el registro escribible; se puede consultar desde cualquier servicio externo de monitorización, sin revelar nada. Las pruebas en producción cubrieron cada código de respuesta — 400, 405, 500, 502, 503 — y dos envíos idénticos devolvieron el mismo identificador de solicitud, el segundo marcado como duplicado, con dos líneas en el registro, no cuatro. Una caída futura del canal de notificación ya no borra la solicitud: queda en el registro, con el motivo escrito.

Qué no dice el caso

La sonda dice que la entrega es posible ahora, no que alguien lea las notificaciones. Quién las mira y en cuánto tiempo es una decisión del equipo, no una función del código. Y la rama de expiración de la solicitud al proveedor, aunque implementada, no se provocó en la prueba: cae en el mismo tratamiento que el error de red, que sí fue probado; la anotamos como no ejercitada, no como verificada.

Preguntas

Lo que nos pregunta la gente antes de llamar

¿Qué monitorizas, concretamente?

El recorrido de una solicitud hasta una persona, no la disponibilidad del servidor. `GET /api/health/contact` verifica en una sola solicitud dos cosas: que el token del canal de notificación siga siendo válido —mediante una llamada real al proveedor, con timeout de 8 segundos— y que el registro de solicitudes se pueda escribir. Responde 200 cuando ambas son verdaderas y 503 cuando no. Puedes llamarla ahora mismo en este sitio: es pública, precisamente porque no devuelve más que valores lógicos.

¿Por qué caería un formulario sin que nadie se diera cuenta?

Porque la parte visible sigue funcionando. La página carga, el botón responde, el servidor devuelve 200 en todas las páginas: solo se rompe el eslabón final, que no tiene interfaz. En este sitio el token del bot de notificación fue revocado: el proveedor respondía `401 Unauthorized`, la ruta devolvía 500 y la solicitud no se escribía en ninguna parte. Tres fallos reales en aproximadamente siete horas, visibles solo en el registro de errores del proceso. Por eso ahora el registro se escribe antes de la entrega: aunque la notificación falle, la solicitud existe.

¿Qué pasa si la notificación falla después de que lo hayáis reparado?

La solicitud ya está escrita en el registro con un identificador propio, y la segunda línea del registro dice `failed` y el motivo. Al visitante se le responde de forma distinta — «hemos recibido la solicitud, pero no hemos podido notificar» — no con un éxito falso. El código de respuesta diferencia la causa: 502 significa que el proveedor respondió mal, 503 que faltan credenciales nuestras. A las 3 de la mañana, la diferencia entre ambas decide si llamas a alguien o no.

¿Actualizáis automáticamente las dependencias?

No sin gate. La publicación ejecuta `npm audit` a nivel `high` y se detiene ante vulnerabilidades de ese nivel si no se confirma explícitamente continuar; primero comprueba también la memoria disponible del servidor, con umbral de 500 MB, porque una build iniciada en un servidor ajustado deja la aplicación detenida. La build se hace limpia: se borran el directorio de construcción y las dependencias, la instalación se hace desde el archivo de bloqueo. Qué se actualiza automáticamente y qué pasa por verificación humana se define por proyecto, no por defecto.

¿Cómo sabéis que una publicación no ha roto otra cosa?

Mediante un script de verificación ejecutado después de la publicación, que abre cada página de producto y falla si faltan el código 200, el ancla de preguntas, el ancla de ejemplo o la dirección canónica. Después verifica el sitemap — que contenga cada producto y no contenga variantes de idioma que no se puedan abrir — y `robots.txt`, para que no bloquee recursos de renderizado. Cada verificación de la lista corresponde a algo que ya se rompió una vez. El script se entrega junto con el proyecto.

¿Qué hacéis cuando la aplicación se bloquea o consume memoria?

El proceso se reinicia automáticamente por encima del umbral de 500 MB, con 4 segundos de retraso entre intentos y crecimiento exponencial, pero se detiene después de 10 reinicios inestables en un intervalo corto. Eso es importante: un bucle de caída que se reinicia infinitamente parece sano en un panel y consume el servidor en silencio. Preferimos que el proceso permanezca detenido y visible.

¿Cuánto tiempo conserváis los datos del formulario y quién puede leerlos?

24 meses, aplicados por código, no solo declarados: el umbral es una variable con el valor por defecto de 730 días, y los archivos más antiguos se eliminan en cada nueva escritura, sin planificador que pueda olvidarse. El directorio tiene permisos `0700`, los archivos `0600`, y la ruta queda fuera del directorio de versión, para que el historial sobreviva a la publicación. La dirección IP no se conserva íntegra: se recorta a `/24` para IPv4 y `/48` para IPv6, leyendo el último salto del encabezado, no el primero — el primero lo envía el cliente y no significa nada.

¿Detectáis también problemas que no hemos reclamado?

Pasa, y normalmente esos son los caros. El mismo paso por el código que arregló el formulario sacó dos cosas que nadie había señalado: el límite de solicitudes se podía saltar por completo, porque se leía el primer elemento del encabezado de direcciones redirigidas — exactamente el que envía el cliente; y una ruta que iniciaba llamadas telefónicas estaba abierta de forma anónima, a nuestro coste, aunque el componente que la usaba ya había sido desmontado. Ambas se repararon y se verificaron en producción. Lo que no podemos prometer es que encontremos todo; lo que sí podemos prometer es que reportamos también lo que no habéis pedido.

¿Qué no cubre el mantenimiento?

No es un servicio de seguridad con monitorización continua y no es un centro de operaciones. No garantizamos un porcentaje de disponibilidad sin una medición que lo respalde —y, para que quede claro, en este momento no publicamos una cifra así para el sitio propio. No asumimos la responsabilidad de una plataforma de terceros a la que no tenemos acceso. Y no tratamos una página que responde 200 como prueba de que todo funciona: exactamente ese fue el problema del que partió todo lo escrito arriba.

En qué se basan las afirmaciones anteriores (13 fuentes)
  1. La sonda de entrega responde 200 en producción: `{"ok":true,"telegram":{"configured":true,"credentialsValid":true},"leadJournal":{"writable":true}}`https://www.megapromoting.com/api/health/contact · 2026-09-06
  2. Cabeceras de seguridad activas en producción: HSTS `max-age=63072000; includeSubDomains; preload`, `X-Content-Type-Options: nosniff`, `X-Frame-Options: SAMEORIGIN`, `Referrer-Policy: strict-origin-when-cross-origin`, `Permissions-Policy` con `microphone=(self)`https://www.megapromoting.com/servicii · 2026-09-06

11 de ellas son código y archivos de nuestros repositorios. No publicamos su nombre ni la línea: juntos, en una sola página, describirían con demasiada precisión cómo están construidos sistemas que no son solo nuestros. Los repasamos contigo, en el repositorio, a petición — la verificación sigue siendo posible, solo que se hace en una conversación.

¿Qué te gustaría que funcionara mejor?

Cuéntanos tu proceso. Juntos decidimos qué merece la pena construir, qué podemos conectar y cómo verificamos el resultado.

Vamos a hablar